blob: 9b000a1b1a73e477393f7e401225213f6a1048a6 [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 Lightfc1ce4e2021-01-22 14:05:13 +000017#include "load_store_elimination.h"
Alex Lightb8686ce2020-11-02 08:48:33 -080018
Alex Lightfc1ce4e2021-01-22 14:05:13 +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 Lightfc1ce4e2021-01-22 14:05:13 +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 Lightfc1ce4e2021-01-22 14:05:13 +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 Lightfc1ce4e2021-01-22 14:05:13 +000037#include "scoped_thread_state_change.h"
xueliang.zhongd71f1dc2018-01-24 17:24:16 +000038
39namespace art {
40
Alex Lightfc1ce4e2021-01-22 14:05:13 +000041struct InstructionDumper {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +000042 public:
Alex Lightfc1ce4e2021-01-22 14:05:13 +000043 HInstruction* ins_;
44};
45
46bool operator==(const InstructionDumper& a, const InstructionDumper& b) {
47 return a.ins_ == b.ins_;
48}
49bool operator!=(const InstructionDumper& a, const InstructionDumper& b) {
50 return !(a == b);
51}
52
53std::ostream& operator<<(std::ostream& os, const InstructionDumper& id) {
54 if (id.ins_ == nullptr) {
55 return os << "NULL";
56 } else {
57 return os << "(" << id.ins_ << "): " << id.ins_->DumpWithArgs();
58 }
59}
60
61#define CHECK_SUBROUTINE_FAILURE() \
62 do { \
63 if (HasFatalFailure()) { \
64 return; \
65 } \
66 } while (false)
67
68#define EXPECT_INS_EQ(a, b) EXPECT_EQ(InstructionDumper{a}, InstructionDumper{b})
69#define EXPECT_INS_REMOVED(a) EXPECT_TRUE(IsRemoved(a)) << "Not removed: " << (InstructionDumper{a})
70#define EXPECT_INS_RETAINED(a) EXPECT_FALSE(IsRemoved(a)) << "Removed: " << (InstructionDumper{a})
71#define ASSERT_INS_EQ(a, b) ASSERT_EQ(InstructionDumper{a}, InstructionDumper{b})
72#define ASSERT_INS_REMOVED(a) ASSERT_TRUE(IsRemoved(a)) << "Not removed: " << (InstructionDumper{a})
73#define ASSERT_INS_RETAINED(a) ASSERT_FALSE(IsRemoved(a)) << "Removed: " << (InstructionDumper{a})
74
75template <typename SuperTest>
76class LoadStoreEliminationTestBase : public SuperTest, public OptimizingUnitTestHelper {
77 public:
78 void SetUp() override {
79 SuperTest::SetUp();
80 gLogVerbosity.compiler = true;
81 }
82
83 void TearDown() override {
84 SuperTest::TearDown();
85 gLogVerbosity.compiler = false;
86 }
87
88 AdjacencyListGraph SetupFromAdjacencyList(const std::string_view entry_name,
89 const std::string_view exit_name,
90 const std::vector<AdjacencyListGraph::Edge>& adj) {
Alex Light86fe9b82020-11-16 16:54:01 +000091 return AdjacencyListGraph(graph_, GetAllocator(), entry_name, exit_name, adj);
92 }
93
Alex Lightfc1ce4e2021-01-22 14:05:13 +000094 void PerformLSE(bool with_partial = true) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +000095 graph_->BuildDominatorTree();
Alex Lightfc1ce4e2021-01-22 14:05:13 +000096 LoadStoreElimination lse(graph_, /*stats=*/nullptr);
97 lse.Run(with_partial);
Alex Light86fe9b82020-11-16 16:54:01 +000098 std::ostringstream oss;
99 EXPECT_TRUE(CheckGraphSkipRefTypeInfoChecks(oss)) << oss.str();
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000100 }
101
Alex Lightfc1ce4e2021-01-22 14:05:13 +0000102 void PerformLSEWithPartial() {
103 PerformLSE(true);
104 }
105
106 void PerformLSENoPartial() {
107 PerformLSE(false);
108 }
109
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000110 // Create instructions shared among tests.
111 void CreateEntryBlockInstructions() {
112 HInstruction* c1 = graph_->GetIntConstant(1);
113 HInstruction* c4 = graph_->GetIntConstant(4);
114 i_add1_ = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_, c1);
115 i_add4_ = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_, c4);
116 entry_block_->AddInstruction(i_add1_);
117 entry_block_->AddInstruction(i_add4_);
118 entry_block_->AddInstruction(new (GetAllocator()) HGoto());
119 }
120
121 // Create the major CFG used by tests:
122 // entry
123 // |
124 // pre_header
125 // |
126 // loop[]
127 // |
128 // return
129 // |
130 // exit
131 void CreateTestControlFlowGraph() {
Vladimir Marko5d2311a2020-05-13 17:30:32 +0100132 InitGraphAndParameters();
133 pre_header_ = AddNewBlock();
134 loop_ = AddNewBlock();
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000135
136 entry_block_->ReplaceSuccessor(return_block_, pre_header_);
137 pre_header_->AddSuccessor(loop_);
138 loop_->AddSuccessor(loop_);
139 loop_->AddSuccessor(return_block_);
140
141 HInstruction* c0 = graph_->GetIntConstant(0);
142 HInstruction* c1 = graph_->GetIntConstant(1);
143 HInstruction* c128 = graph_->GetIntConstant(128);
144
145 CreateEntryBlockInstructions();
146
147 // pre_header block
148 // phi = 0;
149 phi_ = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
150 loop_->AddPhi(phi_);
151 pre_header_->AddInstruction(new (GetAllocator()) HGoto());
152 phi_->AddInput(c0);
153
154 // loop block:
155 // suspend_check
156 // phi++;
157 // if (phi >= 128)
158 suspend_check_ = new (GetAllocator()) HSuspendCheck();
159 HInstruction* inc_phi = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi_, c1);
160 HInstruction* cmp = new (GetAllocator()) HGreaterThanOrEqual(phi_, c128);
161 HInstruction* hif = new (GetAllocator()) HIf(cmp);
162 loop_->AddInstruction(suspend_check_);
163 loop_->AddInstruction(inc_phi);
164 loop_->AddInstruction(cmp);
165 loop_->AddInstruction(hif);
166 phi_->AddInput(inc_phi);
167
168 CreateEnvForSuspendCheck();
169 }
170
171 void CreateEnvForSuspendCheck() {
Alex Lightfc1ce4e2021-01-22 14:05:13 +0000172 ManuallyBuildEnvFor(suspend_check_, {array_, i_, j_});
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000173 }
174
175 // Create the diamond-shaped CFG:
176 // upper
177 // / \
178 // left right
179 // \ /
180 // down
181 //
182 // Return: the basic blocks forming the CFG in the following order {upper, left, right, down}.
183 std::tuple<HBasicBlock*, HBasicBlock*, HBasicBlock*, HBasicBlock*> CreateDiamondShapedCFG() {
Vladimir Marko5d2311a2020-05-13 17:30:32 +0100184 InitGraphAndParameters();
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000185 CreateEntryBlockInstructions();
186
Vladimir Marko5d2311a2020-05-13 17:30:32 +0100187 HBasicBlock* upper = AddNewBlock();
188 HBasicBlock* left = AddNewBlock();
189 HBasicBlock* right = AddNewBlock();
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000190
191 entry_block_->ReplaceSuccessor(return_block_, upper);
192 upper->AddSuccessor(left);
193 upper->AddSuccessor(right);
194 left->AddSuccessor(return_block_);
195 right->AddSuccessor(return_block_);
196
197 HInstruction* cmp = new (GetAllocator()) HGreaterThanOrEqual(i_, j_);
198 HInstruction* hif = new (GetAllocator()) HIf(cmp);
199 upper->AddInstruction(cmp);
200 upper->AddInstruction(hif);
201
202 left->AddInstruction(new (GetAllocator()) HGoto());
203 right->AddInstruction(new (GetAllocator()) HGoto());
204
205 return std::make_tuple(upper, left, right, return_block_);
206 }
207
208 // Add a HVecLoad instruction to the end of the provided basic block.
209 //
210 // Return: the created HVecLoad instruction.
211 HInstruction* AddVecLoad(HBasicBlock* block, HInstruction* array, HInstruction* index) {
212 DCHECK(block != nullptr);
213 DCHECK(array != nullptr);
214 DCHECK(index != nullptr);
Alex Lightfc1ce4e2021-01-22 14:05:13 +0000215 HInstruction* vload =
216 new (GetAllocator()) HVecLoad(GetAllocator(),
217 array,
218 index,
219 DataType::Type::kInt32,
220 SideEffects::ArrayReadOfType(DataType::Type::kInt32),
221 4,
222 /*is_string_char_at*/ false,
223 kNoDexPc);
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000224 block->InsertInstructionBefore(vload, block->GetLastInstruction());
225 return vload;
226 }
227
228 // Add a HVecStore instruction to the end of the provided basic block.
229 // If no vdata is specified, generate HVecStore: array[index] = [1,1,1,1].
230 //
231 // Return: the created HVecStore instruction.
232 HInstruction* AddVecStore(HBasicBlock* block,
233 HInstruction* array,
234 HInstruction* index,
235 HInstruction* vdata = nullptr) {
236 DCHECK(block != nullptr);
237 DCHECK(array != nullptr);
238 DCHECK(index != nullptr);
239 if (vdata == nullptr) {
240 HInstruction* c1 = graph_->GetIntConstant(1);
Alex Lightfc1ce4e2021-01-22 14:05:13 +0000241 vdata = new (GetAllocator())
242 HVecReplicateScalar(GetAllocator(), c1, DataType::Type::kInt32, 4, kNoDexPc);
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000243 block->InsertInstructionBefore(vdata, block->GetLastInstruction());
244 }
Alex Lightfc1ce4e2021-01-22 14:05:13 +0000245 HInstruction* vstore =
246 new (GetAllocator()) HVecStore(GetAllocator(),
247 array,
248 index,
249 vdata,
250 DataType::Type::kInt32,
251 SideEffects::ArrayWriteOfType(DataType::Type::kInt32),
252 4,
253 kNoDexPc);
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000254 block->InsertInstructionBefore(vstore, block->GetLastInstruction());
255 return vstore;
256 }
257
258 // Add a HArrayGet instruction to the end of the provided basic block.
259 //
260 // Return: the created HArrayGet instruction.
261 HInstruction* AddArrayGet(HBasicBlock* block, HInstruction* array, HInstruction* index) {
262 DCHECK(block != nullptr);
263 DCHECK(array != nullptr);
264 DCHECK(index != nullptr);
265 HInstruction* get = new (GetAllocator()) HArrayGet(array, index, DataType::Type::kInt32, 0);
266 block->InsertInstructionBefore(get, block->GetLastInstruction());
267 return get;
268 }
269
270 // Add a HArraySet instruction to the end of the provided basic block.
271 // If no data is specified, generate HArraySet: array[index] = 1.
272 //
273 // Return: the created HArraySet instruction.
274 HInstruction* AddArraySet(HBasicBlock* block,
275 HInstruction* array,
276 HInstruction* index,
277 HInstruction* data = nullptr) {
278 DCHECK(block != nullptr);
279 DCHECK(array != nullptr);
280 DCHECK(index != nullptr);
281 if (data == nullptr) {
282 data = graph_->GetIntConstant(1);
283 }
Alex Lightfc1ce4e2021-01-22 14:05:13 +0000284 HInstruction* store =
285 new (GetAllocator()) HArraySet(array, index, data, DataType::Type::kInt32, 0);
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000286 block->InsertInstructionBefore(store, block->GetLastInstruction());
287 return store;
288 }
289
Vladimir Marko5d2311a2020-05-13 17:30:32 +0100290 void InitGraphAndParameters() {
291 InitGraph();
Alex Lightfc1ce4e2021-01-22 14:05:13 +0000292 AddParameter(new (GetAllocator()) HParameterValue(
293 graph_->GetDexFile(), dex::TypeIndex(0), 0, DataType::Type::kInt32));
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000294 array_ = parameters_.back();
Alex Lightfc1ce4e2021-01-22 14:05:13 +0000295 AddParameter(new (GetAllocator()) HParameterValue(
296 graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kInt32));
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000297 i_ = parameters_.back();
Alex Lightfc1ce4e2021-01-22 14:05:13 +0000298 AddParameter(new (GetAllocator()) HParameterValue(
299 graph_->GetDexFile(), dex::TypeIndex(1), 2, DataType::Type::kInt32));
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000300 j_ = parameters_.back();
301 }
302
Alex Lightfc1ce4e2021-01-22 14:05:13 +0000303 void ManuallyBuildEnvFor(HInstruction* ins, const std::initializer_list<HInstruction*>& env) {
304 ArenaVector<HInstruction*> current_locals(env, GetAllocator()->Adapter(kArenaAllocInstruction));
305 OptimizingUnitTestHelper::ManuallyBuildEnvFor(ins, &current_locals);
306 }
307
308 HLoadClass* MakeClassLoad(std::optional<dex::TypeIndex> ti = std::nullopt) {
309 return new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(),
310 ti ? *ti : dex::TypeIndex(class_idx_++),
311 graph_->GetDexFile(),
312 /* klass= */ null_klass_,
313 /* is_referrers_class= */ false,
314 /* dex_pc= */ 0,
315 /* needs_access_check= */ false);
316 }
317
318 HNewInstance* MakeNewInstance(HInstruction* cls, uint32_t dex_pc = 0u) {
319 EXPECT_TRUE(cls->IsLoadClass() || cls->IsClinitCheck()) << *cls;
320 HLoadClass* load =
321 cls->IsLoadClass() ? cls->AsLoadClass() : cls->AsClinitCheck()->GetLoadClass();
322 return new (GetAllocator()) HNewInstance(cls,
323 dex_pc,
324 load->GetTypeIndex(),
325 graph_->GetDexFile(),
326 /* finalizable= */ false,
327 QuickEntrypointEnum::kQuickAllocObjectInitialized);
328 }
329
330 HInstanceFieldSet* MakeIFieldSet(HInstruction* inst,
331 HInstruction* data,
332 MemberOffset off,
333 uint32_t dex_pc = 0u) {
334 return new (GetAllocator()) HInstanceFieldSet(inst,
335 data,
336 /* field= */ nullptr,
337 /* field_type= */ data->GetType(),
338 /* field_offset= */ off,
339 /* is_volatile= */ false,
340 /* field_idx= */ 0,
341 /* declaring_class_def_index= */ 0,
342 graph_->GetDexFile(),
343 dex_pc);
344 }
345
346 HInstanceFieldGet* MakeIFieldGet(HInstruction* inst,
347 DataType::Type type,
348 MemberOffset off,
349 uint32_t dex_pc = 0u) {
350 return new (GetAllocator()) HInstanceFieldGet(inst,
351 /* field= */ nullptr,
352 /* field_type= */ type,
353 /* field_offset= */ off,
354 /* is_volatile= */ false,
355 /* field_idx= */ 0,
356 /* declaring_class_def_index= */ 0,
357 graph_->GetDexFile(),
358 dex_pc);
359 }
360
361 HInvokeStaticOrDirect* MakeInvoke(DataType::Type return_type,
362 const std::vector<HInstruction*>& args) {
363 MethodReference method_reference{/* file= */ &graph_->GetDexFile(), /* index= */ method_idx_++};
364 HInvokeStaticOrDirect* res = new (GetAllocator())
365 HInvokeStaticOrDirect(GetAllocator(),
366 args.size(),
367 return_type,
368 /* dex_pc= */ 0,
369 method_reference,
370 /* resolved_method= */ nullptr,
371 HInvokeStaticOrDirect::DispatchInfo{},
372 InvokeType::kStatic,
373 /* resolved_method_reference= */ method_reference,
374 HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
375 for (auto [ins, idx] : ZipCount(MakeIterationRange(args))) {
376 res->SetRawInputAt(idx, ins);
377 }
378 return res;
379 }
380
381 HPhi* MakePhi(const std::vector<HInstruction*>& ins) {
382 EXPECT_GE(ins.size(), 2u) << "Phi requires at least 2 inputs";
383 HPhi* phi =
384 new (GetAllocator()) HPhi(GetAllocator(), kNoRegNumber, ins.size(), ins[0]->GetType());
385 for (auto [i, idx] : ZipCount(MakeIterationRange(ins))) {
386 phi->SetRawInputAt(idx, i);
387 }
388 return phi;
389 }
390
391 void SetupExit(HBasicBlock* exit) {
392 exit->AddInstruction(new (GetAllocator()) HExit());
393 }
394
395 dex::TypeIndex DefaultTypeIndexForType(DataType::Type type) {
396 switch (type) {
397 case DataType::Type::kBool:
398 return dex::TypeIndex(1);
399 case DataType::Type::kUint8:
400 case DataType::Type::kInt8:
401 return dex::TypeIndex(2);
402 case DataType::Type::kUint16:
403 case DataType::Type::kInt16:
404 return dex::TypeIndex(3);
405 case DataType::Type::kUint32:
406 case DataType::Type::kInt32:
407 return dex::TypeIndex(4);
408 case DataType::Type::kUint64:
409 case DataType::Type::kInt64:
410 return dex::TypeIndex(5);
411 case DataType::Type::kReference:
412 return dex::TypeIndex(6);
413 case DataType::Type::kFloat32:
414 return dex::TypeIndex(7);
415 case DataType::Type::kFloat64:
416 return dex::TypeIndex(8);
417 case DataType::Type::kVoid:
418 EXPECT_TRUE(false) << "No type for void!";
419 return dex::TypeIndex(1000);
420 }
421 }
422
423 // Creates a parameter. The instruction is automatically added to the entry-block
424 HParameterValue* MakeParam(DataType::Type type, std::optional<dex::TypeIndex> ti = std::nullopt) {
425 HParameterValue* val = new (GetAllocator()) HParameterValue(
426 graph_->GetDexFile(), ti ? *ti : DefaultTypeIndexForType(type), param_count_++, type);
427 graph_->GetEntryBlock()->AddInstruction(val);
428 return val;
429 }
430
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000431 HBasicBlock* pre_header_;
432 HBasicBlock* loop_;
433
434 HInstruction* array_;
435 HInstruction* i_;
436 HInstruction* j_;
437 HInstruction* i_add1_;
438 HInstruction* i_add4_;
439 HInstruction* suspend_check_;
440
441 HPhi* phi_;
Alex Lightfc1ce4e2021-01-22 14:05:13 +0000442
443 size_t param_count_ = 0;
444 size_t class_idx_ = 42;
445 uint32_t method_idx_ = 100;
446
447 ScopedNullHandle<mirror::Class> null_klass_;
448};
449
450class LoadStoreEliminationTest : public LoadStoreEliminationTestBase<CommonCompilerTest> {};
451
452enum class TestOrder { kSameAsAlloc, kReverseOfAlloc };
453std::ostream& operator<<(std::ostream& os, const TestOrder& ord) {
454 switch (ord) {
455 case TestOrder::kSameAsAlloc:
456 return os << "SameAsAlloc";
457 case TestOrder::kReverseOfAlloc:
458 return os << "ReverseOfAlloc";
459 }
460}
461
462class OrderDependentTestGroup
463 : public LoadStoreEliminationTestBase<CommonCompilerTestWithParam<TestOrder>> {};
464
465// Various configs we can use for testing. Currently used in PartialComparison tests.
466struct PartialComparisonKind {
467 public:
468 enum class Type : uint8_t { kEquals, kNotEquals };
469 enum class Target : uint8_t { kNull, kValue, kSelf };
470 enum class Position : uint8_t { kLeft, kRight };
471
472 const Type type_;
473 const Target target_;
474 const Position position_;
475
476 bool IsDefinitelyFalse() const {
477 return !IsPossiblyTrue();
478 }
479 bool IsPossiblyFalse() const {
480 return !IsDefinitelyTrue();
481 }
482 bool IsDefinitelyTrue() const {
483 if (target_ == Target::kSelf) {
484 return type_ == Type::kEquals;
485 } else if (target_ == Target::kNull) {
486 return type_ == Type::kNotEquals;
487 } else {
488 return false;
489 }
490 }
491 bool IsPossiblyTrue() const {
492 if (target_ == Target::kSelf) {
493 return type_ == Type::kEquals;
494 } else if (target_ == Target::kNull) {
495 return type_ == Type::kNotEquals;
496 } else {
497 return true;
498 }
499 }
500 std::ostream& Dump(std::ostream& os) const {
501 os << "PartialComparisonKind{" << (type_ == Type::kEquals ? "kEquals" : "kNotEquals") << ", "
502 << (target_ == Target::kNull ? "kNull" : (target_ == Target::kSelf ? "kSelf" : "kValue"))
503 << ", " << (position_ == Position::kLeft ? "kLeft" : "kRight") << "}";
504 return os;
505 }
506};
507
508std::ostream& operator<<(std::ostream& os, const PartialComparisonKind& comp) {
509 return comp.Dump(os);
510}
511
512class PartialComparisonTestGroup
513 : public LoadStoreEliminationTestBase<CommonCompilerTestWithParam<PartialComparisonKind>> {
514 public:
515 enum class ComparisonPlacement {
516 kBeforeEscape,
517 kInEscape,
518 kAfterEscape,
519 };
520 void CheckFinalInstruction(HInstruction* ins, ComparisonPlacement placement) {
521 using Target = PartialComparisonKind::Target;
522 using Type = PartialComparisonKind::Type;
523 using Position = PartialComparisonKind::Position;
524 PartialComparisonKind kind = GetParam();
525 if (ins->IsIntConstant()) {
526 if (kind.IsDefinitelyTrue()) {
527 EXPECT_TRUE(ins->AsIntConstant()->IsTrue()) << kind << " " << *ins;
528 } else if (kind.IsDefinitelyFalse()) {
529 EXPECT_TRUE(ins->AsIntConstant()->IsFalse()) << kind << " " << *ins;
530 } else {
531 EXPECT_EQ(placement, ComparisonPlacement::kBeforeEscape);
532 EXPECT_EQ(kind.target_, Target::kValue);
533 // We are before escape so value is not the object
534 if (kind.type_ == Type::kEquals) {
535 EXPECT_TRUE(ins->AsIntConstant()->IsFalse()) << kind << " " << *ins;
536 } else {
537 EXPECT_TRUE(ins->AsIntConstant()->IsTrue()) << kind << " " << *ins;
538 }
539 }
540 return;
541 }
542 EXPECT_NE(placement, ComparisonPlacement::kBeforeEscape)
543 << "For comparisons before escape we should always be able to transform into a constant."
544 << " Instead we got:" << std::endl << ins->DumpWithArgs();
545 if (placement == ComparisonPlacement::kInEscape) {
546 // Should be the same type.
547 ASSERT_TRUE(ins->IsEqual() || ins->IsNotEqual()) << *ins;
548 HInstruction* other = kind.position_ == Position::kLeft ? ins->AsBinaryOperation()->GetRight()
549 : ins->AsBinaryOperation()->GetLeft();
550 if (kind.target_ == Target::kSelf) {
551 EXPECT_INS_EQ(ins->AsBinaryOperation()->GetLeft(), ins->AsBinaryOperation()->GetRight())
552 << " ins is: " << *ins;
553 } else if (kind.target_ == Target::kNull) {
554 EXPECT_INS_EQ(other, graph_->GetNullConstant()) << " ins is: " << *ins;
555 } else {
556 EXPECT_TRUE(other->IsStaticFieldGet()) << " ins is: " << *ins;
557 }
558 if (kind.type_ == Type::kEquals) {
559 EXPECT_TRUE(ins->IsEqual()) << *ins;
560 } else {
561 EXPECT_TRUE(ins->IsNotEqual()) << *ins;
562 }
563 } else {
564 ASSERT_EQ(placement, ComparisonPlacement::kAfterEscape);
565 if (kind.type_ == Type::kEquals) {
566 // obj == <anything> can only be true if (1) it's obj == obj or (2) obj has escaped.
567 ASSERT_TRUE(ins->IsAnd()) << ins->DumpWithArgs();
568 EXPECT_TRUE(ins->InputAt(1)->IsEqual()) << ins->DumpWithArgs();
569 } else {
570 // obj != <anything> is true if (2) obj has escaped.
571 ASSERT_TRUE(ins->IsOr()) << ins->DumpWithArgs();
572 EXPECT_TRUE(ins->InputAt(1)->IsNotEqual()) << ins->DumpWithArgs();
573 }
574 // Check the first part of AND is the obj-has-escaped
575 ASSERT_TRUE(ins->InputAt(0)->IsNotEqual()) << ins->DumpWithArgs();
576 EXPECT_TRUE(ins->InputAt(0)->InputAt(0)->IsPhi()) << ins->DumpWithArgs();
577 EXPECT_TRUE(ins->InputAt(0)->InputAt(1)->IsNullConstant()) << ins->DumpWithArgs();
578 // Check the second part of AND is the eq other
579 EXPECT_INS_EQ(ins->InputAt(1)->InputAt(kind.position_ == Position::kLeft ? 0 : 1),
580 ins->InputAt(0)->InputAt(0))
581 << ins->DumpWithArgs();
582 }
583 }
584
585 struct ComparisonInstructions {
586 void AddSetup(HBasicBlock* blk) const {
587 for (HInstruction* i : setup_instructions_) {
588 blk->AddInstruction(i);
589 }
590 }
591
592 void AddEnvironment(HEnvironment* env) const {
593 for (HInstruction* i : setup_instructions_) {
594 if (i->NeedsEnvironment()) {
595 i->CopyEnvironmentFrom(env);
596 }
597 }
598 }
599
600 const std::vector<HInstruction*> setup_instructions_;
601 HInstruction* const cmp_;
602 };
603
604 ComparisonInstructions GetComparisonInstructions(HInstruction* partial) {
605 PartialComparisonKind kind = GetParam();
606 std::vector<HInstruction*> setup;
607 HInstruction* target_other;
608 switch (kind.target_) {
609 case PartialComparisonKind::Target::kSelf:
610 target_other = partial;
611 break;
612 case PartialComparisonKind::Target::kNull:
613 target_other = graph_->GetNullConstant();
614 break;
615 case PartialComparisonKind::Target::kValue: {
616 HInstruction* cls = MakeClassLoad();
617 HInstruction* static_read =
618 new (GetAllocator()) HStaticFieldGet(cls,
619 /* field= */ nullptr,
620 DataType::Type::kReference,
621 /* field_offset= */ MemberOffset(40),
622 /* is_volatile= */ false,
623 /* field_idx= */ 0,
624 /* declaring_class_def_index= */ 0,
625 graph_->GetDexFile(),
626 /* dex_pc= */ 0);
627 setup.push_back(cls);
628 setup.push_back(static_read);
629 target_other = static_read;
630 break;
631 }
632 }
633 HInstruction* target_left;
634 HInstruction* target_right;
635 std::tie(target_left, target_right) = kind.position_ == PartialComparisonKind::Position::kLeft
636 ? std::pair{partial, target_other}
637 : std::pair{target_other, partial};
638 HInstruction* cmp =
639 kind.type_ == PartialComparisonKind::Type::kEquals
640 ? static_cast<HInstruction*>(new (GetAllocator()) HEqual(target_left, target_right))
641 : static_cast<HInstruction*>(new (GetAllocator()) HNotEqual(target_left, target_right));
642 return {setup, cmp};
643 }
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000644};
645
646TEST_F(LoadStoreEliminationTest, ArrayGetSetElimination) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000647 CreateTestControlFlowGraph();
648
649 HInstruction* c1 = graph_->GetIntConstant(1);
650 HInstruction* c2 = graph_->GetIntConstant(2);
651 HInstruction* c3 = graph_->GetIntConstant(3);
652
653 // array[1] = 1;
654 // x = array[1]; <--- Remove.
655 // y = array[2];
656 // array[1] = 1; <--- Remove, since it stores same value.
657 // array[i] = 3; <--- MAY alias.
658 // array[1] = 1; <--- Cannot remove, even if it stores the same value.
659 AddArraySet(entry_block_, array_, c1, c1);
660 HInstruction* load1 = AddArrayGet(entry_block_, array_, c1);
661 HInstruction* load2 = AddArrayGet(entry_block_, array_, c2);
662 HInstruction* store1 = AddArraySet(entry_block_, array_, c1, c1);
663 AddArraySet(entry_block_, array_, i_, c3);
664 HInstruction* store2 = AddArraySet(entry_block_, array_, c1, c1);
665
666 PerformLSE();
667
668 ASSERT_TRUE(IsRemoved(load1));
669 ASSERT_FALSE(IsRemoved(load2));
670 ASSERT_TRUE(IsRemoved(store1));
671 ASSERT_FALSE(IsRemoved(store2));
672}
673
674TEST_F(LoadStoreEliminationTest, SameHeapValue1) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000675 CreateTestControlFlowGraph();
676
677 HInstruction* c1 = graph_->GetIntConstant(1);
678 HInstruction* c2 = graph_->GetIntConstant(2);
679
680 // Test LSE handling same value stores on array.
681 // array[1] = 1;
682 // array[2] = 1;
683 // array[1] = 1; <--- Can remove.
684 // array[1] = 2; <--- Can NOT remove.
685 AddArraySet(entry_block_, array_, c1, c1);
686 AddArraySet(entry_block_, array_, c2, c1);
687 HInstruction* store1 = AddArraySet(entry_block_, array_, c1, c1);
688 HInstruction* store2 = AddArraySet(entry_block_, array_, c1, c2);
689
690 PerformLSE();
691
692 ASSERT_TRUE(IsRemoved(store1));
693 ASSERT_FALSE(IsRemoved(store2));
694}
695
696TEST_F(LoadStoreEliminationTest, SameHeapValue2) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000697 CreateTestControlFlowGraph();
698
699 // Test LSE handling same value stores on vector.
700 // vdata = [0x1, 0x2, 0x3, 0x4, ...]
701 // VecStore array[i...] = vdata;
702 // VecStore array[j...] = vdata; <--- MAY ALIAS.
703 // VecStore array[i...] = vdata; <--- Cannot Remove, even if it's same value.
704 AddVecStore(entry_block_, array_, i_);
705 AddVecStore(entry_block_, array_, j_);
706 HInstruction* vstore = AddVecStore(entry_block_, array_, i_);
707
708 PerformLSE();
709
710 ASSERT_FALSE(IsRemoved(vstore));
711}
712
713TEST_F(LoadStoreEliminationTest, SameHeapValue3) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000714 CreateTestControlFlowGraph();
715
716 // VecStore array[i...] = vdata;
717 // VecStore array[i+1...] = vdata; <--- MAY alias due to partial overlap.
718 // VecStore array[i...] = vdata; <--- Cannot remove, even if it's same value.
719 AddVecStore(entry_block_, array_, i_);
720 AddVecStore(entry_block_, array_, i_add1_);
721 HInstruction* vstore = AddVecStore(entry_block_, array_, i_);
722
723 PerformLSE();
724
725 ASSERT_FALSE(IsRemoved(vstore));
726}
727
728TEST_F(LoadStoreEliminationTest, OverlappingLoadStore) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000729 CreateTestControlFlowGraph();
730
731 HInstruction* c1 = graph_->GetIntConstant(1);
732
733 // Test LSE handling array LSE when there is vector store in between.
734 // a[i] = 1;
735 // .. = a[i]; <-- Remove.
736 // a[i,i+1,i+2,i+3] = data; <-- PARTIAL OVERLAP !
737 // .. = a[i]; <-- Cannot remove.
738 AddArraySet(entry_block_, array_, i_, c1);
739 HInstruction* load1 = AddArrayGet(entry_block_, array_, i_);
740 AddVecStore(entry_block_, array_, i_);
741 HInstruction* load2 = AddArrayGet(entry_block_, array_, i_);
742
743 // Test LSE handling vector load/store partial overlap.
744 // a[i,i+1,i+2,i+3] = data;
745 // a[i+4,i+5,i+6,i+7] = data;
746 // .. = a[i,i+1,i+2,i+3];
747 // .. = a[i+4,i+5,i+6,i+7];
748 // a[i+1,i+2,i+3,i+4] = data; <-- PARTIAL OVERLAP !
749 // .. = a[i,i+1,i+2,i+3];
750 // .. = a[i+4,i+5,i+6,i+7];
751 AddVecStore(entry_block_, array_, i_);
752 AddVecStore(entry_block_, array_, i_add4_);
753 HInstruction* vload1 = AddVecLoad(entry_block_, array_, i_);
754 HInstruction* vload2 = AddVecLoad(entry_block_, array_, i_add4_);
755 AddVecStore(entry_block_, array_, i_add1_);
756 HInstruction* vload3 = AddVecLoad(entry_block_, array_, i_);
757 HInstruction* vload4 = AddVecLoad(entry_block_, array_, i_add4_);
758
759 // Test LSE handling vector LSE when there is array store in between.
760 // a[i,i+1,i+2,i+3] = data;
761 // a[i+1] = 1; <-- PARTIAL OVERLAP !
762 // .. = a[i,i+1,i+2,i+3];
763 AddVecStore(entry_block_, array_, i_);
764 AddArraySet(entry_block_, array_, i_, c1);
765 HInstruction* vload5 = AddVecLoad(entry_block_, array_, i_);
766
767 PerformLSE();
768
769 ASSERT_TRUE(IsRemoved(load1));
770 ASSERT_FALSE(IsRemoved(load2));
771
772 ASSERT_TRUE(IsRemoved(vload1));
773 ASSERT_TRUE(IsRemoved(vload2));
774 ASSERT_FALSE(IsRemoved(vload3));
775 ASSERT_FALSE(IsRemoved(vload4));
776
777 ASSERT_FALSE(IsRemoved(vload5));
778}
779// function (int[] a, int j) {
780// a[j] = 1;
781// for (int i=0; i<128; i++) {
782// /* doesn't do any write */
783// }
784// a[j] = 1;
785TEST_F(LoadStoreEliminationTest, StoreAfterLoopWithoutSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000786 CreateTestControlFlowGraph();
787
788 HInstruction* c1 = graph_->GetIntConstant(1);
789
790 // a[j] = 1
791 AddArraySet(pre_header_, array_, j_, c1);
792
793 // LOOP BODY:
794 // .. = a[i,i+1,i+2,i+3];
795 AddVecLoad(loop_, array_, phi_);
796
797 // a[j] = 1;
798 HInstruction* array_set = AddArraySet(return_block_, array_, j_, c1);
799
800 PerformLSE();
801
802 ASSERT_TRUE(IsRemoved(array_set));
803}
804
805// function (int[] a, int j) {
806// int[] b = new int[128];
807// a[j] = 0;
808// for (int phi=0; phi<128; phi++) {
809// a[phi,phi+1,phi+2,phi+3] = [1,1,1,1];
810// b[phi,phi+1,phi+2,phi+3] = a[phi,phi+1,phi+2,phi+3];
811// }
812// a[j] = 0;
813// }
814TEST_F(LoadStoreEliminationTest, StoreAfterSIMDLoopWithSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000815 CreateTestControlFlowGraph();
816
817 HInstruction* c0 = graph_->GetIntConstant(0);
818 HInstruction* c128 = graph_->GetIntConstant(128);
819
820 HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
821 pre_header_->InsertInstructionBefore(array_b, pre_header_->GetLastInstruction());
822 array_b->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
823
824 // a[j] = 0;
825 AddArraySet(pre_header_, array_, j_, c0);
826
827 // LOOP BODY:
828 // a[phi,phi+1,phi+2,phi+3] = [1,1,1,1];
829 // b[phi,phi+1,phi+2,phi+3] = a[phi,phi+1,phi+2,phi+3];
830 AddVecStore(loop_, array_, phi_);
831 HInstruction* vload = AddVecLoad(loop_, array_, phi_);
832 AddVecStore(loop_, array_b, phi_, vload->AsVecLoad());
833
834 // a[j] = 0;
835 HInstruction* a_set = AddArraySet(return_block_, array_, j_, c0);
836
837 PerformLSE();
838
839 ASSERT_TRUE(IsRemoved(vload));
840 ASSERT_FALSE(IsRemoved(a_set)); // Cannot remove due to write side-effect in the loop.
841}
842
843// function (int[] a, int j) {
844// int[] b = new int[128];
845// a[j] = 0;
846// for (int phi=0; phi<128; phi++) {
847// a[phi,phi+1,phi+2,phi+3] = [1,1,1,1];
848// b[phi,phi+1,phi+2,phi+3] = a[phi,phi+1,phi+2,phi+3];
849// }
850// x = a[j];
851// }
852TEST_F(LoadStoreEliminationTest, LoadAfterSIMDLoopWithSideEffects) {
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_b = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
859 pre_header_->InsertInstructionBefore(array_b, pre_header_->GetLastInstruction());
860 array_b->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
861
862 // a[j] = 0;
863 AddArraySet(pre_header_, array_, j_, c0);
864
865 // LOOP BODY:
866 // a[phi,phi+1,phi+2,phi+3] = [1,1,1,1];
867 // b[phi,phi+1,phi+2,phi+3] = a[phi,phi+1,phi+2,phi+3];
868 AddVecStore(loop_, array_, phi_);
869 HInstruction* vload = AddVecLoad(loop_, array_, phi_);
870 AddVecStore(loop_, array_b, phi_, vload->AsVecLoad());
871
872 // x = a[j];
873 HInstruction* load = AddArrayGet(return_block_, array_, j_);
874
875 PerformLSE();
876
877 ASSERT_TRUE(IsRemoved(vload));
878 ASSERT_FALSE(IsRemoved(load)); // Cannot remove due to write side-effect in the loop.
879}
880
881// Check that merging works correctly when there are VecStors in predecessors.
882//
883// vstore1: a[i,... i + 3] = [1,...1]
884// / \
885// / \
886// vstore2: a[i,... i + 3] = [1,...1] vstore3: a[i+1, ... i + 4] = [1, ... 1]
887// \ /
888// \ /
889// vstore4: a[i,... i + 3] = [1,...1]
890//
891// Expected:
892// 'vstore2' is removed.
893// 'vstore3' is not removed.
894// 'vstore4' is not removed. Such cases are not supported at the moment.
895TEST_F(LoadStoreEliminationTest, MergePredecessorVecStores) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000896 HBasicBlock* upper;
897 HBasicBlock* left;
898 HBasicBlock* right;
899 HBasicBlock* down;
900 std::tie(upper, left, right, down) = CreateDiamondShapedCFG();
901
902 // upper: a[i,... i + 3] = [1,...1]
903 HInstruction* vstore1 = AddVecStore(upper, array_, i_);
904 HInstruction* vdata = vstore1->InputAt(2);
905
906 // left: a[i,... i + 3] = [1,...1]
907 HInstruction* vstore2 = AddVecStore(left, array_, i_, vdata);
908
909 // right: a[i+1, ... i + 4] = [1, ... 1]
910 HInstruction* vstore3 = AddVecStore(right, array_, i_add1_, vdata);
911
912 // down: a[i,... i + 3] = [1,...1]
913 HInstruction* vstore4 = AddVecStore(down, array_, i_, vdata);
914
915 PerformLSE();
916
917 ASSERT_TRUE(IsRemoved(vstore2));
918 ASSERT_FALSE(IsRemoved(vstore3));
919 ASSERT_FALSE(IsRemoved(vstore4));
920}
921
922// Check that merging works correctly when there are ArraySets in predecessors.
923//
924// a[i] = 1
925// / \
926// / \
927// store1: a[i] = 1 store2: a[i+1] = 1
928// \ /
929// \ /
930// store3: a[i] = 1
931//
932// Expected:
933// 'store1' is removed.
934// 'store2' is not removed.
935// 'store3' is removed.
936TEST_F(LoadStoreEliminationTest, MergePredecessorStores) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000937 HBasicBlock* upper;
938 HBasicBlock* left;
939 HBasicBlock* right;
940 HBasicBlock* down;
941 std::tie(upper, left, right, down) = CreateDiamondShapedCFG();
942
943 // upper: a[i,... i + 3] = [1,...1]
944 AddArraySet(upper, array_, i_);
945
946 // left: a[i,... i + 3] = [1,...1]
947 HInstruction* store1 = AddArraySet(left, array_, i_);
948
949 // right: a[i+1, ... i + 4] = [1, ... 1]
950 HInstruction* store2 = AddArraySet(right, array_, i_add1_);
951
952 // down: a[i,... i + 3] = [1,...1]
953 HInstruction* store3 = AddArraySet(down, array_, i_);
954
955 PerformLSE();
956
957 ASSERT_TRUE(IsRemoved(store1));
958 ASSERT_FALSE(IsRemoved(store2));
959 ASSERT_TRUE(IsRemoved(store3));
960}
961
962// Check that redundant VStore/VLoad are removed from a SIMD loop.
963//
964// LOOP BODY
965// vstore1: a[i,... i + 3] = [1,...1]
966// vload: x = a[i,... i + 3]
967// vstore2: b[i,... i + 3] = x
968// vstore3: a[i,... i + 3] = [1,...1]
969//
Vladimir Marko3224f382020-06-23 14:19:53 +0100970// Return 'a' from the method to make it escape.
971//
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000972// Expected:
973// 'vstore1' is not removed.
974// 'vload' is removed.
Vladimir Marko3224f382020-06-23 14:19:53 +0100975// 'vstore2' is removed because 'b' does not escape.
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000976// 'vstore3' is removed.
977TEST_F(LoadStoreEliminationTest, RedundantVStoreVLoadInLoop) {
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
Vladimir Marko3224f382020-06-23 14:19:53 +0100987 ASSERT_TRUE(return_block_->GetLastInstruction()->IsReturnVoid());
988 HInstruction* ret = new (GetAllocator()) HReturn(array_a);
989 return_block_->ReplaceAndRemoveInstructionWith(return_block_->GetLastInstruction(), ret);
990
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000991 HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
992 pre_header_->InsertInstructionBefore(array_b, pre_header_->GetLastInstruction());
993 array_b->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
994
995 // LOOP BODY:
996 // a[i,... i + 3] = [1,...1]
997 // x = a[i,... i + 3]
998 // b[i,... i + 3] = x
999 // a[i,... i + 3] = [1,...1]
1000 HInstruction* vstore1 = AddVecStore(loop_, array_a, phi_);
1001 HInstruction* vload = AddVecLoad(loop_, array_a, phi_);
Vladimir Marko3224f382020-06-23 14:19:53 +01001002 HInstruction* vstore2 = AddVecStore(loop_, array_b, phi_, vload->AsVecLoad());
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001003 HInstruction* vstore3 = AddVecStore(loop_, array_a, phi_, vstore1->InputAt(2));
1004
1005 PerformLSE();
1006
1007 ASSERT_FALSE(IsRemoved(vstore1));
1008 ASSERT_TRUE(IsRemoved(vload));
Vladimir Marko3224f382020-06-23 14:19:53 +01001009 ASSERT_TRUE(IsRemoved(vstore2));
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001010 ASSERT_TRUE(IsRemoved(vstore3));
1011}
1012
Vladimir Marko3224f382020-06-23 14:19:53 +01001013// Loop writes invalidate only possibly aliased heap locations.
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001014TEST_F(LoadStoreEliminationTest, StoreAfterLoopWithSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001015 CreateTestControlFlowGraph();
1016
1017 HInstruction* c0 = graph_->GetIntConstant(0);
1018 HInstruction* c2 = graph_->GetIntConstant(2);
1019 HInstruction* c128 = graph_->GetIntConstant(128);
1020
1021 // array[0] = 2;
1022 // loop:
1023 // b[i] = array[i]
1024 // array[0] = 2
Vladimir Marko3224f382020-06-23 14:19:53 +01001025 HInstruction* store1 = AddArraySet(entry_block_, array_, c0, c2);
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001026
1027 HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
1028 pre_header_->InsertInstructionBefore(array_b, pre_header_->GetLastInstruction());
1029 array_b->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
1030
1031 HInstruction* load = AddArrayGet(loop_, array_, phi_);
Vladimir Marko3224f382020-06-23 14:19:53 +01001032 HInstruction* store2 = AddArraySet(loop_, array_b, phi_, load);
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001033
Vladimir Marko3224f382020-06-23 14:19:53 +01001034 HInstruction* store3 = AddArraySet(return_block_, array_, c0, c2);
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001035
1036 PerformLSE();
1037
Vladimir Marko3224f382020-06-23 14:19:53 +01001038 ASSERT_FALSE(IsRemoved(store1));
1039 ASSERT_TRUE(IsRemoved(store2));
1040 ASSERT_TRUE(IsRemoved(store3));
1041}
1042
1043// Loop writes invalidate only possibly aliased heap locations.
1044TEST_F(LoadStoreEliminationTest, StoreAfterLoopWithSideEffects2) {
1045 CreateTestControlFlowGraph();
1046
1047 // Add another array parameter that may alias with `array_`.
1048 // Note: We're not adding it to the suspend check environment.
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001049 AddParameter(new (GetAllocator()) HParameterValue(
1050 graph_->GetDexFile(), dex::TypeIndex(0), 3, DataType::Type::kInt32));
Vladimir Marko3224f382020-06-23 14:19:53 +01001051 HInstruction* array2 = parameters_.back();
1052
1053 HInstruction* c0 = graph_->GetIntConstant(0);
1054 HInstruction* c2 = graph_->GetIntConstant(2);
1055
1056 // array[0] = 2;
1057 // loop:
1058 // array2[i] = array[i]
1059 // array[0] = 2
1060 HInstruction* store1 = AddArraySet(entry_block_, array_, c0, c2);
1061
1062 HInstruction* load = AddArrayGet(loop_, array_, phi_);
1063 HInstruction* store2 = AddArraySet(loop_, array2, phi_, load);
1064
1065 HInstruction* store3 = AddArraySet(return_block_, array_, c0, c2);
1066
1067 PerformLSE();
1068
1069 ASSERT_FALSE(IsRemoved(store1));
1070 ASSERT_FALSE(IsRemoved(store2));
1071 ASSERT_FALSE(IsRemoved(store3));
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001072}
1073
1074// As it is not allowed to use defaults for VecLoads, check if there is a new created array
1075// a VecLoad used in a loop and after it is not replaced with a default.
1076TEST_F(LoadStoreEliminationTest, VLoadDefaultValueInLoopWithoutWriteSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001077 CreateTestControlFlowGraph();
1078
1079 HInstruction* c0 = graph_->GetIntConstant(0);
1080 HInstruction* c128 = graph_->GetIntConstant(128);
1081
1082 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
1083 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
1084 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
1085
1086 // LOOP BODY:
1087 // v = a[i,... i + 3]
1088 // array[0,... 3] = v
1089 HInstruction* vload = AddVecLoad(loop_, array_a, phi_);
1090 HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
1091
1092 PerformLSE();
1093
1094 ASSERT_FALSE(IsRemoved(vload));
1095 ASSERT_FALSE(IsRemoved(vstore));
1096}
1097
1098// As it is not allowed to use defaults for VecLoads, check if there is a new created array
1099// a VecLoad is not replaced with a default.
1100TEST_F(LoadStoreEliminationTest, VLoadDefaultValue) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001101 CreateTestControlFlowGraph();
1102
1103 HInstruction* c0 = graph_->GetIntConstant(0);
1104 HInstruction* c128 = graph_->GetIntConstant(128);
1105
1106 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
1107 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
1108 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
1109
1110 // v = a[0,... 3]
1111 // array[0,... 3] = v
1112 HInstruction* vload = AddVecLoad(pre_header_, array_a, c0);
1113 HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
1114
1115 PerformLSE();
1116
1117 ASSERT_FALSE(IsRemoved(vload));
1118 ASSERT_FALSE(IsRemoved(vstore));
1119}
1120
1121// As it is allowed to use defaults for ordinary loads, check if there is a new created array
1122// a load used in a loop and after it is replaced with a default.
1123TEST_F(LoadStoreEliminationTest, LoadDefaultValueInLoopWithoutWriteSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001124 CreateTestControlFlowGraph();
1125
1126 HInstruction* c0 = graph_->GetIntConstant(0);
1127 HInstruction* c128 = graph_->GetIntConstant(128);
1128
1129 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
1130 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
1131 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
1132
1133 // LOOP BODY:
1134 // v = a[i]
1135 // array[0] = v
1136 HInstruction* load = AddArrayGet(loop_, array_a, phi_);
1137 HInstruction* store = AddArraySet(return_block_, array_, c0, load);
1138
1139 PerformLSE();
1140
1141 ASSERT_TRUE(IsRemoved(load));
1142 ASSERT_FALSE(IsRemoved(store));
1143}
1144
1145// As it is allowed to use defaults for ordinary loads, check if there is a new created array
1146// a load is replaced with a default.
1147TEST_F(LoadStoreEliminationTest, LoadDefaultValue) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001148 CreateTestControlFlowGraph();
1149
1150 HInstruction* c0 = graph_->GetIntConstant(0);
1151 HInstruction* c128 = graph_->GetIntConstant(128);
1152
1153 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
1154 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
1155 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
1156
1157 // v = a[0]
1158 // array[0] = v
1159 HInstruction* load = AddArrayGet(pre_header_, array_a, c0);
1160 HInstruction* store = AddArraySet(return_block_, array_, c0, load);
1161
1162 PerformLSE();
1163
1164 ASSERT_TRUE(IsRemoved(load));
1165 ASSERT_FALSE(IsRemoved(store));
1166}
1167
1168// As it is not allowed to use defaults for VecLoads but allowed for regular loads,
1169// check if there is a new created array, a VecLoad and a load used in a loop and after it,
1170// VecLoad is not replaced with a default but the load is.
1171TEST_F(LoadStoreEliminationTest, VLoadAndLoadDefaultValueInLoopWithoutWriteSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001172 CreateTestControlFlowGraph();
1173
1174 HInstruction* c0 = graph_->GetIntConstant(0);
1175 HInstruction* c128 = graph_->GetIntConstant(128);
1176
1177 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
1178 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
1179 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
1180
1181 // LOOP BODY:
1182 // v = a[i,... i + 3]
1183 // v1 = a[i]
1184 // array[0,... 3] = v
1185 // array[0] = v1
1186 HInstruction* vload = AddVecLoad(loop_, array_a, phi_);
1187 HInstruction* load = AddArrayGet(loop_, array_a, phi_);
1188 HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
1189 HInstruction* store = AddArraySet(return_block_, array_, c0, load);
1190
1191 PerformLSE();
1192
1193 ASSERT_FALSE(IsRemoved(vload));
1194 ASSERT_TRUE(IsRemoved(load));
1195 ASSERT_FALSE(IsRemoved(vstore));
1196 ASSERT_FALSE(IsRemoved(store));
1197}
1198
1199// As it is not allowed to use defaults for VecLoads but allowed for regular loads,
1200// check if there is a new created array, a VecLoad and a load,
1201// VecLoad is not replaced with a default but the load is.
1202TEST_F(LoadStoreEliminationTest, VLoadAndLoadDefaultValue) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001203 CreateTestControlFlowGraph();
1204
1205 HInstruction* c0 = graph_->GetIntConstant(0);
1206 HInstruction* c128 = graph_->GetIntConstant(128);
1207
1208 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
1209 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
1210 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
1211
1212 // v = a[0,... 3]
1213 // v1 = a[0]
1214 // array[0,... 3] = v
1215 // array[0] = v1
1216 HInstruction* vload = AddVecLoad(pre_header_, array_a, c0);
1217 HInstruction* load = AddArrayGet(pre_header_, array_a, c0);
1218 HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
1219 HInstruction* store = AddArraySet(return_block_, array_, c0, load);
1220
1221 PerformLSE();
1222
1223 ASSERT_FALSE(IsRemoved(vload));
1224 ASSERT_TRUE(IsRemoved(load));
1225 ASSERT_FALSE(IsRemoved(vstore));
1226 ASSERT_FALSE(IsRemoved(store));
1227}
1228
1229// It is not allowed to use defaults for VecLoads. However it should not prevent from removing
1230// loads getting the same value.
1231// Check a load getting a known value is eliminated (a loop test case).
1232TEST_F(LoadStoreEliminationTest, VLoadDefaultValueAndVLoadInLoopWithoutWriteSideEffects) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001233 CreateTestControlFlowGraph();
1234
1235 HInstruction* c0 = graph_->GetIntConstant(0);
1236 HInstruction* c128 = graph_->GetIntConstant(128);
1237
1238 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
1239 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
1240 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
1241
1242 // LOOP BODY:
1243 // v = a[i,... i + 3]
1244 // v1 = a[i,... i + 3]
1245 // array[0,... 3] = v
1246 // array[128,... 131] = v1
1247 HInstruction* vload1 = AddVecLoad(loop_, array_a, phi_);
1248 HInstruction* vload2 = AddVecLoad(loop_, array_a, phi_);
1249 HInstruction* vstore1 = AddVecStore(return_block_, array_, c0, vload1->AsVecLoad());
1250 HInstruction* vstore2 = AddVecStore(return_block_, array_, c128, vload2->AsVecLoad());
1251
1252 PerformLSE();
1253
1254 ASSERT_FALSE(IsRemoved(vload1));
1255 ASSERT_TRUE(IsRemoved(vload2));
1256 ASSERT_FALSE(IsRemoved(vstore1));
1257 ASSERT_FALSE(IsRemoved(vstore2));
1258}
1259
1260// It is not allowed to use defaults for VecLoads. However it should not prevent from removing
1261// loads getting the same value.
1262// Check a load getting a known value is eliminated.
1263TEST_F(LoadStoreEliminationTest, VLoadDefaultValueAndVLoad) {
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001264 CreateTestControlFlowGraph();
1265
1266 HInstruction* c0 = graph_->GetIntConstant(0);
1267 HInstruction* c128 = graph_->GetIntConstant(128);
1268
1269 HInstruction* array_a = new (GetAllocator()) HNewArray(c0, c128, 0, 0);
1270 pre_header_->InsertInstructionBefore(array_a, pre_header_->GetLastInstruction());
1271 array_a->CopyEnvironmentFrom(suspend_check_->GetEnvironment());
1272
1273 // v = a[0,... 3]
1274 // v1 = a[0,... 3]
1275 // array[0,... 3] = v
1276 // array[128,... 131] = v1
1277 HInstruction* vload1 = AddVecLoad(pre_header_, array_a, c0);
1278 HInstruction* vload2 = AddVecLoad(pre_header_, array_a, c0);
1279 HInstruction* vstore1 = AddVecStore(return_block_, array_, c0, vload1->AsVecLoad());
1280 HInstruction* vstore2 = AddVecStore(return_block_, array_, c128, vload2->AsVecLoad());
1281
1282 PerformLSE();
1283
1284 ASSERT_FALSE(IsRemoved(vload1));
1285 ASSERT_TRUE(IsRemoved(vload2));
1286 ASSERT_FALSE(IsRemoved(vstore1));
1287 ASSERT_FALSE(IsRemoved(vstore2));
1288}
1289
Alex Light2610dfe2020-12-07 16:26:43 -08001290// Object o = new Obj();
1291// // Needed because otherwise we short-circuit LSA since GVN would get almost
1292// // everything other than this. Also since this isn't expected to be a very
1293// // common pattern it's not worth changing the LSA logic.
1294// o.foo = 3;
1295// return o.shadow$_klass_;
1296TEST_F(LoadStoreEliminationTest, DefaultShadowClass) {
1297 CreateGraph();
1298 AdjacencyListGraph blocks(
1299 graph_, GetAllocator(), "entry", "exit", {{"entry", "main"}, {"main", "exit"}});
1300#define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1301 GET_BLOCK(entry);
1302 GET_BLOCK(main);
1303 GET_BLOCK(exit);
1304#undef GET_BLOCK
1305
1306 HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck();
1307 entry->AddInstruction(suspend_check);
1308 entry->AddInstruction(new (GetAllocator()) HGoto());
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001309 ManuallyBuildEnvFor(suspend_check, {});
Alex Light2610dfe2020-12-07 16:26:43 -08001310
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001311 HInstruction* cls = MakeClassLoad();
1312 HInstruction* new_inst = MakeNewInstance(cls);
Alex Light2610dfe2020-12-07 16:26:43 -08001313 HInstruction* const_fence = new (GetAllocator()) HConstructorFence(new_inst, 0, GetAllocator());
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001314 HInstruction* set_field = MakeIFieldSet(new_inst, graph_->GetIntConstant(33), MemberOffset(32));
1315 HInstruction* get_field =
1316 MakeIFieldGet(new_inst, DataType::Type::kReference, mirror::Object::ClassOffset());
Alex Light2610dfe2020-12-07 16:26:43 -08001317 HInstruction* return_val = new (GetAllocator()) HReturn(get_field);
1318 main->AddInstruction(cls);
1319 main->AddInstruction(new_inst);
1320 main->AddInstruction(const_fence);
1321 main->AddInstruction(set_field);
1322 main->AddInstruction(get_field);
1323 main->AddInstruction(return_val);
1324 cls->CopyEnvironmentFrom(suspend_check->GetEnvironment());
1325 new_inst->CopyEnvironmentFrom(suspend_check->GetEnvironment());
1326
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001327 SetupExit(exit);
Alex Light2610dfe2020-12-07 16:26:43 -08001328
1329 graph_->ClearDominanceInformation();
1330 PerformLSE();
1331
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001332 EXPECT_INS_REMOVED(new_inst);
1333 EXPECT_INS_REMOVED(const_fence);
1334 EXPECT_INS_REMOVED(get_field);
1335 EXPECT_INS_REMOVED(set_field);
1336 EXPECT_INS_RETAINED(cls);
1337 EXPECT_INS_EQ(cls, return_val->InputAt(0));
Alex Light2610dfe2020-12-07 16:26:43 -08001338}
1339
Alex Lightc6da1be2021-01-22 06:58:44 -08001340// Object o = new Obj();
1341// // Needed because otherwise we short-circuit LSA since GVN would get almost
1342// // everything other than this. Also since this isn't expected to be a very
1343// // common pattern (only a single java function, Object.identityHashCode,
1344// // ever reads this field) it's not worth changing the LSA logic.
1345// o.foo = 3;
1346// return o.shadow$_monitor_;
1347TEST_F(LoadStoreEliminationTest, DefaultShadowMonitor) {
1348 CreateGraph();
1349 AdjacencyListGraph blocks(
1350 graph_, GetAllocator(), "entry", "exit", {{"entry", "main"}, {"main", "exit"}});
1351#define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1352 GET_BLOCK(entry);
1353 GET_BLOCK(main);
1354 GET_BLOCK(exit);
1355#undef GET_BLOCK
1356
1357 HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck();
1358 entry->AddInstruction(suspend_check);
1359 entry->AddInstruction(new (GetAllocator()) HGoto());
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001360 ManuallyBuildEnvFor(suspend_check, {});
Alex Lightc6da1be2021-01-22 06:58:44 -08001361
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001362 HInstruction* cls = MakeClassLoad();
1363 HInstruction* new_inst = MakeNewInstance(cls);
Alex Lightc6da1be2021-01-22 06:58:44 -08001364 HInstruction* const_fence = new (GetAllocator()) HConstructorFence(new_inst, 0, GetAllocator());
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001365 HInstruction* set_field = MakeIFieldSet(new_inst, graph_->GetIntConstant(33), MemberOffset(32));
1366 HInstruction* get_field =
1367 MakeIFieldGet(new_inst, DataType::Type::kInt32, mirror::Object::MonitorOffset());
Alex Lightc6da1be2021-01-22 06:58:44 -08001368 HInstruction* return_val = new (GetAllocator()) HReturn(get_field);
1369 main->AddInstruction(cls);
1370 main->AddInstruction(new_inst);
1371 main->AddInstruction(const_fence);
1372 main->AddInstruction(set_field);
1373 main->AddInstruction(get_field);
1374 main->AddInstruction(return_val);
1375 cls->CopyEnvironmentFrom(suspend_check->GetEnvironment());
1376 new_inst->CopyEnvironmentFrom(suspend_check->GetEnvironment());
1377
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001378 SetupExit(exit);
Alex Lightc6da1be2021-01-22 06:58:44 -08001379
1380 graph_->ClearDominanceInformation();
1381 PerformLSE();
1382
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001383 EXPECT_INS_REMOVED(new_inst);
1384 EXPECT_INS_REMOVED(const_fence);
1385 EXPECT_INS_REMOVED(get_field);
1386 EXPECT_INS_REMOVED(set_field);
1387 EXPECT_INS_RETAINED(cls);
1388 EXPECT_INS_EQ(graph_->GetIntConstant(0), return_val->InputAt(0));
Alex Lightc6da1be2021-01-22 06:58:44 -08001389}
1390
Alex Light9dec90a2020-09-14 17:58:28 -07001391// void DO_CAL() {
1392// int i = 1;
1393// int[] w = new int[80];
1394// int t = 0;
1395// while (i < 80) {
1396// w[i] = PLEASE_INTERLEAVE(w[i - 1], 1)
1397// t = PLEASE_SELECT(w[i], t);
1398// i++;
1399// }
1400// return t;
1401// }
1402TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001403 VariableSizedHandleScope vshs(Thread::Current());
1404 CreateGraph(&vshs);
Alex Light9dec90a2020-09-14 17:58:28 -07001405 AdjacencyListGraph blocks(graph_,
1406 GetAllocator(),
1407 "entry",
1408 "exit",
1409 { { "entry", "loop_pre_header" },
1410 { "loop_pre_header", "loop_entry" },
1411 { "loop_entry", "loop_body" },
1412 { "loop_entry", "loop_post" },
1413 { "loop_body", "loop_entry" },
1414 { "loop_post", "exit" } });
1415#define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1416 GET_BLOCK(entry);
1417 GET_BLOCK(loop_pre_header);
1418 GET_BLOCK(loop_entry);
1419 GET_BLOCK(loop_body);
1420 GET_BLOCK(loop_post);
1421 GET_BLOCK(exit);
1422#undef GET_BLOCK
1423
1424 HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0);
1425 HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1);
1426 HInstruction* eighty_const = graph_->GetConstant(DataType::Type::kInt32, 80);
1427 HInstruction* entry_goto = new (GetAllocator()) HGoto();
1428 entry->AddInstruction(entry_goto);
1429
1430 HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, eighty_const, 0, 0);
1431 HInstruction* pre_header_goto = new (GetAllocator()) HGoto();
1432 loop_pre_header->AddInstruction(alloc_w);
1433 loop_pre_header->AddInstruction(pre_header_goto);
1434 // environment
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001435 ManuallyBuildEnvFor(alloc_w, {});
Alex Light9dec90a2020-09-14 17:58:28 -07001436
1437 // loop-start
1438 HPhi* i_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
1439 HPhi* t_phi = new (GetAllocator()) HPhi(GetAllocator(), 1, 0, DataType::Type::kInt32);
1440 HInstruction* suspend = new (GetAllocator()) HSuspendCheck();
1441 HInstruction* i_cmp_top = new (GetAllocator()) HGreaterThanOrEqual(i_phi, eighty_const);
1442 HInstruction* loop_start_branch = new (GetAllocator()) HIf(i_cmp_top);
1443 loop_entry->AddPhi(i_phi);
1444 loop_entry->AddPhi(t_phi);
1445 loop_entry->AddInstruction(suspend);
1446 loop_entry->AddInstruction(i_cmp_top);
1447 loop_entry->AddInstruction(loop_start_branch);
1448 CHECK_EQ(loop_entry->GetSuccessors().size(), 2u);
1449 if (loop_entry->GetNormalSuccessors()[1] != loop_body) {
1450 loop_entry->SwapSuccessors();
1451 }
1452 CHECK_EQ(loop_entry->GetPredecessors().size(), 2u);
1453 if (loop_entry->GetPredecessors()[0] != loop_pre_header) {
1454 loop_entry->SwapPredecessors();
1455 }
1456 i_phi->AddInput(one_const);
1457 t_phi->AddInput(zero_const);
1458
1459 // environment
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001460 ManuallyBuildEnvFor(suspend, { alloc_w, i_phi, t_phi });
Alex Light9dec90a2020-09-14 17:58:28 -07001461
1462 // BODY
1463 HInstruction* last_i = new (GetAllocator()) HSub(DataType::Type::kInt32, i_phi, one_const);
1464 HInstruction* last_get =
1465 new (GetAllocator()) HArrayGet(alloc_w, last_i, DataType::Type::kInt32, 0);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001466 HInvoke* body_value = MakeInvoke(DataType::Type::kInt32, { last_get, one_const });
Alex Light9dec90a2020-09-14 17:58:28 -07001467 HInstruction* body_set =
1468 new (GetAllocator()) HArraySet(alloc_w, i_phi, body_value, DataType::Type::kInt32, 0);
1469 HInstruction* body_get =
1470 new (GetAllocator()) HArrayGet(alloc_w, i_phi, DataType::Type::kInt32, 0);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001471 HInvoke* t_next = MakeInvoke(DataType::Type::kInt32, { body_get, t_phi });
Alex Light9dec90a2020-09-14 17:58:28 -07001472 HInstruction* i_next = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_phi, one_const);
1473 HInstruction* body_goto = new (GetAllocator()) HGoto();
1474 loop_body->AddInstruction(last_i);
1475 loop_body->AddInstruction(last_get);
1476 loop_body->AddInstruction(body_value);
1477 loop_body->AddInstruction(body_set);
1478 loop_body->AddInstruction(body_get);
1479 loop_body->AddInstruction(t_next);
1480 loop_body->AddInstruction(i_next);
1481 loop_body->AddInstruction(body_goto);
1482 body_value->CopyEnvironmentFrom(suspend->GetEnvironment());
1483
1484 i_phi->AddInput(i_next);
1485 t_phi->AddInput(t_next);
1486 t_next->CopyEnvironmentFrom(suspend->GetEnvironment());
1487
1488 // loop-post
1489 HInstruction* return_inst = new (GetAllocator()) HReturn(t_phi);
1490 loop_post->AddInstruction(return_inst);
1491
1492 // exit
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001493 SetupExit(exit);
Alex Light9dec90a2020-09-14 17:58:28 -07001494
1495 graph_->ClearDominanceInformation();
1496 graph_->ClearLoopInformation();
1497 PerformLSE();
1498
1499 // TODO Technically this is optimizable. LSE just needs to add phis to keep
1500 // track of the last `N` values set where `N` is how many locations we can go
1501 // back into the array.
1502 if (IsRemoved(last_get)) {
1503 // If we were able to remove the previous read the entire array should be removable.
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001504 EXPECT_INS_REMOVED(body_set);
1505 EXPECT_INS_REMOVED(alloc_w);
Alex Light9dec90a2020-09-14 17:58:28 -07001506 } else {
1507 // This is the branch we actually take for now. If we rely on being able to
1508 // read the array we'd better remember to write to it as well.
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001509 EXPECT_INS_RETAINED(body_set);
Alex Light9dec90a2020-09-14 17:58:28 -07001510 }
1511 // The last 'get' should always be removable.
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001512 EXPECT_INS_REMOVED(body_get);
Alex Light9dec90a2020-09-14 17:58:28 -07001513}
1514
Alex Light9dec90a2020-09-14 17:58:28 -07001515// void DO_CAL2() {
1516// int i = 1;
1517// int[] w = new int[80];
1518// int t = 0;
1519// while (i < 80) {
1520// w[i] = PLEASE_INTERLEAVE(w[i - 1], 1) // <-- removed
1521// t = PLEASE_SELECT(w[i], t);
1522// w[i] = PLEASE_INTERLEAVE(w[i - 1], 1) // <-- removed
1523// t = PLEASE_SELECT(w[i], t);
1524// w[i] = PLEASE_INTERLEAVE(w[i - 1], 1) // <-- kept
1525// t = PLEASE_SELECT(w[i], t);
1526// i++;
1527// }
1528// return t;
1529// }
1530TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap2) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001531 VariableSizedHandleScope vshs(Thread::Current());
1532 CreateGraph(&vshs);
Alex Light9dec90a2020-09-14 17:58:28 -07001533 AdjacencyListGraph blocks(graph_,
1534 GetAllocator(),
1535 "entry",
1536 "exit",
1537 { { "entry", "loop_pre_header" },
1538 { "loop_pre_header", "loop_entry" },
1539 { "loop_entry", "loop_body" },
1540 { "loop_entry", "loop_post" },
1541 { "loop_body", "loop_entry" },
1542 { "loop_post", "exit" } });
1543#define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1544 GET_BLOCK(entry);
1545 GET_BLOCK(loop_pre_header);
1546 GET_BLOCK(loop_entry);
1547 GET_BLOCK(loop_body);
1548 GET_BLOCK(loop_post);
1549 GET_BLOCK(exit);
1550#undef GET_BLOCK
1551
1552 HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0);
1553 HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1);
1554 HInstruction* eighty_const = graph_->GetConstant(DataType::Type::kInt32, 80);
1555 HInstruction* entry_goto = new (GetAllocator()) HGoto();
1556 entry->AddInstruction(entry_goto);
1557
1558 HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, eighty_const, 0, 0);
1559 HInstruction* pre_header_goto = new (GetAllocator()) HGoto();
1560 loop_pre_header->AddInstruction(alloc_w);
1561 loop_pre_header->AddInstruction(pre_header_goto);
1562 // environment
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001563 ManuallyBuildEnvFor(alloc_w, {});
Alex Light9dec90a2020-09-14 17:58:28 -07001564
1565 // loop-start
1566 HPhi* i_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
1567 HPhi* t_phi = new (GetAllocator()) HPhi(GetAllocator(), 1, 0, DataType::Type::kInt32);
1568 HInstruction* suspend = new (GetAllocator()) HSuspendCheck();
1569 HInstruction* i_cmp_top = new (GetAllocator()) HGreaterThanOrEqual(i_phi, eighty_const);
1570 HInstruction* loop_start_branch = new (GetAllocator()) HIf(i_cmp_top);
1571 loop_entry->AddPhi(i_phi);
1572 loop_entry->AddPhi(t_phi);
1573 loop_entry->AddInstruction(suspend);
1574 loop_entry->AddInstruction(i_cmp_top);
1575 loop_entry->AddInstruction(loop_start_branch);
1576 CHECK_EQ(loop_entry->GetSuccessors().size(), 2u);
1577 if (loop_entry->GetNormalSuccessors()[1] != loop_body) {
1578 loop_entry->SwapSuccessors();
1579 }
1580 CHECK_EQ(loop_entry->GetPredecessors().size(), 2u);
1581 if (loop_entry->GetPredecessors()[0] != loop_pre_header) {
1582 loop_entry->SwapPredecessors();
1583 }
1584 i_phi->AddInput(one_const);
1585 t_phi->AddInput(zero_const);
1586
1587 // environment
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001588 ManuallyBuildEnvFor(suspend, { alloc_w, i_phi, t_phi });
Alex Light9dec90a2020-09-14 17:58:28 -07001589
1590 // BODY
1591 HInstruction* last_i = new (GetAllocator()) HSub(DataType::Type::kInt32, i_phi, one_const);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001592 HInstruction *last_get_1, *last_get_2, *last_get_3;
1593 HInstruction *body_value_1, *body_value_2, *body_value_3;
1594 HInstruction *body_set_1, *body_set_2, *body_set_3;
1595 HInstruction *body_get_1, *body_get_2, *body_get_3;
1596 HInstruction *t_next_1, *t_next_2, *t_next_3;
Alex Light9dec90a2020-09-14 17:58:28 -07001597 auto make_instructions = [&](HInstruction* last_t_value) {
1598 HInstruction* last_get =
1599 new (GetAllocator()) HArrayGet(alloc_w, last_i, DataType::Type::kInt32, 0);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001600 HInvoke* body_value = MakeInvoke(DataType::Type::kInt32, { last_get, one_const });
Alex Light9dec90a2020-09-14 17:58:28 -07001601 HInstruction* body_set =
1602 new (GetAllocator()) HArraySet(alloc_w, i_phi, body_value, DataType::Type::kInt32, 0);
1603 HInstruction* body_get =
1604 new (GetAllocator()) HArrayGet(alloc_w, i_phi, DataType::Type::kInt32, 0);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001605 HInvoke* t_next = MakeInvoke(DataType::Type::kInt32, { body_get, last_t_value });
Alex Light9dec90a2020-09-14 17:58:28 -07001606 loop_body->AddInstruction(last_get);
1607 loop_body->AddInstruction(body_value);
1608 loop_body->AddInstruction(body_set);
1609 loop_body->AddInstruction(body_get);
1610 loop_body->AddInstruction(t_next);
1611 return std::make_tuple(last_get, body_value, body_set, body_get, t_next);
1612 };
1613 std::tie(last_get_1, body_value_1, body_set_1, body_get_1, t_next_1) = make_instructions(t_phi);
1614 std::tie(last_get_2, body_value_2, body_set_2, body_get_2, t_next_2) =
1615 make_instructions(t_next_1);
1616 std::tie(last_get_3, body_value_3, body_set_3, body_get_3, t_next_3) =
1617 make_instructions(t_next_2);
1618 HInstruction* i_next = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_phi, one_const);
1619 HInstruction* body_goto = new (GetAllocator()) HGoto();
1620 loop_body->InsertInstructionBefore(last_i, last_get_1);
1621 loop_body->AddInstruction(i_next);
1622 loop_body->AddInstruction(body_goto);
1623 body_value_1->CopyEnvironmentFrom(suspend->GetEnvironment());
1624 body_value_2->CopyEnvironmentFrom(suspend->GetEnvironment());
1625 body_value_3->CopyEnvironmentFrom(suspend->GetEnvironment());
1626
1627 i_phi->AddInput(i_next);
1628 t_phi->AddInput(t_next_3);
1629 t_next_1->CopyEnvironmentFrom(suspend->GetEnvironment());
1630 t_next_2->CopyEnvironmentFrom(suspend->GetEnvironment());
1631 t_next_3->CopyEnvironmentFrom(suspend->GetEnvironment());
1632
1633 // loop-post
1634 HInstruction* return_inst = new (GetAllocator()) HReturn(t_phi);
1635 loop_post->AddInstruction(return_inst);
1636
1637 // exit
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001638 SetupExit(exit);
Alex Light9dec90a2020-09-14 17:58:28 -07001639
1640 graph_->ClearDominanceInformation();
1641 graph_->ClearLoopInformation();
1642 PerformLSE();
1643
1644 // TODO Technically this is optimizable. LSE just needs to add phis to keep
1645 // track of the last `N` values set where `N` is how many locations we can go
1646 // back into the array.
1647 if (IsRemoved(last_get_1)) {
1648 // If we were able to remove the previous read the entire array should be removable.
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001649 EXPECT_INS_REMOVED(body_set_1);
1650 EXPECT_INS_REMOVED(body_set_2);
1651 EXPECT_INS_REMOVED(body_set_3);
1652 EXPECT_INS_REMOVED(last_get_1);
1653 EXPECT_INS_REMOVED(last_get_2);
1654 EXPECT_INS_REMOVED(alloc_w);
Alex Light9dec90a2020-09-14 17:58:28 -07001655 } else {
1656 // This is the branch we actually take for now. If we rely on being able to
1657 // read the array we'd better remember to write to it as well.
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001658 EXPECT_INS_RETAINED(body_set_3);
Alex Light9dec90a2020-09-14 17:58:28 -07001659 }
1660 // The last 'get' should always be removable.
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001661 EXPECT_INS_REMOVED(body_get_1);
1662 EXPECT_INS_REMOVED(body_get_2);
1663 EXPECT_INS_REMOVED(body_get_3);
Alex Light9dec90a2020-09-14 17:58:28 -07001664 // shadowed writes should always be removed
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001665 EXPECT_INS_REMOVED(body_set_1);
1666 EXPECT_INS_REMOVED(body_set_2);
Alex Light9dec90a2020-09-14 17:58:28 -07001667}
1668
1669TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001670 VariableSizedHandleScope vshs(Thread::Current());
1671 CreateGraph(&vshs);
Alex Light9dec90a2020-09-14 17:58:28 -07001672 AdjacencyListGraph blocks(graph_,
1673 GetAllocator(),
1674 "entry",
1675 "exit",
1676 { { "entry", "start" },
1677 { "start", "left" },
1678 { "start", "right" },
1679 { "left", "ret" },
1680 { "right", "ret" },
1681 { "ret", "exit" } });
1682#define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1683 GET_BLOCK(entry);
1684 GET_BLOCK(start);
1685 GET_BLOCK(left);
1686 GET_BLOCK(right);
1687 GET_BLOCK(ret);
1688 GET_BLOCK(exit);
1689#undef GET_BLOCK
1690
1691 HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0);
1692 HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1);
1693 HInstruction* two_const = graph_->GetConstant(DataType::Type::kInt32, 2);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001694 HInstruction* param = MakeParam(DataType::Type::kBool);
1695
Alex Light9dec90a2020-09-14 17:58:28 -07001696 HInstruction* entry_goto = new (GetAllocator()) HGoto();
Alex Light9dec90a2020-09-14 17:58:28 -07001697 entry->AddInstruction(entry_goto);
1698
1699 HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, two_const, 0, 0);
1700 HInstruction* branch = new (GetAllocator()) HIf(param);
1701 start->AddInstruction(alloc_w);
1702 start->AddInstruction(branch);
1703 // environment
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001704 ManuallyBuildEnvFor(alloc_w, {});
Alex Light9dec90a2020-09-14 17:58:28 -07001705
1706 // left
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001707 HInvoke* left_value = MakeInvoke(DataType::Type::kInt32, { zero_const });
Alex Light9dec90a2020-09-14 17:58:28 -07001708 HInstruction* left_set_1 =
1709 new (GetAllocator()) HArraySet(alloc_w, zero_const, left_value, DataType::Type::kInt32, 0);
1710 HInstruction* left_set_2 =
1711 new (GetAllocator()) HArraySet(alloc_w, one_const, zero_const, DataType::Type::kInt32, 0);
1712 HInstruction* left_goto = new (GetAllocator()) HGoto();
1713 left->AddInstruction(left_value);
1714 left->AddInstruction(left_set_1);
1715 left->AddInstruction(left_set_2);
1716 left->AddInstruction(left_goto);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001717 ManuallyBuildEnvFor(left_value, { alloc_w });
Alex Light9dec90a2020-09-14 17:58:28 -07001718
1719 // right
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001720 HInvoke* right_value = MakeInvoke(DataType::Type::kInt32, { one_const });
Alex Light9dec90a2020-09-14 17:58:28 -07001721 HInstruction* right_set_1 =
1722 new (GetAllocator()) HArraySet(alloc_w, zero_const, right_value, DataType::Type::kInt32, 0);
1723 HInstruction* right_set_2 =
1724 new (GetAllocator()) HArraySet(alloc_w, one_const, zero_const, DataType::Type::kInt32, 0);
1725 HInstruction* right_goto = new (GetAllocator()) HGoto();
1726 right->AddInstruction(right_value);
1727 right->AddInstruction(right_set_1);
1728 right->AddInstruction(right_set_2);
1729 right->AddInstruction(right_goto);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001730 ManuallyBuildEnvFor(right_value, { alloc_w });
Alex Light9dec90a2020-09-14 17:58:28 -07001731
1732 // ret
1733 HInstruction* read_1 =
1734 new (GetAllocator()) HArrayGet(alloc_w, zero_const, DataType::Type::kInt32, 0);
1735 HInstruction* read_2 =
1736 new (GetAllocator()) HArrayGet(alloc_w, one_const, DataType::Type::kInt32, 0);
1737 HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, read_1, read_2);
1738 HInstruction* return_inst = new (GetAllocator()) HReturn(add);
1739 ret->AddInstruction(read_1);
1740 ret->AddInstruction(read_2);
1741 ret->AddInstruction(add);
1742 ret->AddInstruction(return_inst);
1743
1744 // exit
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001745 SetupExit(exit);
Alex Light9dec90a2020-09-14 17:58:28 -07001746
1747 graph_->ClearDominanceInformation();
1748 graph_->ClearLoopInformation();
1749 PerformLSE();
1750
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001751 EXPECT_INS_REMOVED(read_1);
1752 EXPECT_INS_REMOVED(read_2);
1753 EXPECT_INS_REMOVED(left_set_1);
1754 EXPECT_INS_REMOVED(left_set_2);
1755 EXPECT_INS_REMOVED(right_set_1);
1756 EXPECT_INS_REMOVED(right_set_2);
1757 EXPECT_INS_REMOVED(alloc_w);
Alex Light9dec90a2020-09-14 17:58:28 -07001758
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001759 EXPECT_INS_RETAINED(left_value);
1760 EXPECT_INS_RETAINED(right_value);
Alex Light9dec90a2020-09-14 17:58:28 -07001761}
1762
1763TEST_F(LoadStoreEliminationTest, ArrayMergeDefault) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001764 VariableSizedHandleScope vshs(Thread::Current());
1765 CreateGraph(&vshs);
Alex Light9dec90a2020-09-14 17:58:28 -07001766 AdjacencyListGraph blocks(graph_,
1767 GetAllocator(),
1768 "entry",
1769 "exit",
1770 { { "entry", "start" },
1771 { "start", "left" },
1772 { "start", "right" },
1773 { "left", "ret" },
1774 { "right", "ret" },
1775 { "ret", "exit" } });
1776#define GET_BLOCK(name) HBasicBlock* name = blocks.Get(#name)
1777 GET_BLOCK(entry);
1778 GET_BLOCK(start);
1779 GET_BLOCK(left);
1780 GET_BLOCK(right);
1781 GET_BLOCK(ret);
1782 GET_BLOCK(exit);
1783#undef GET_BLOCK
1784
1785 HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0);
1786 HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1);
1787 HInstruction* two_const = graph_->GetConstant(DataType::Type::kInt32, 2);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001788 HInstruction* param = MakeParam(DataType::Type::kBool);
Alex Light9dec90a2020-09-14 17:58:28 -07001789 HInstruction* entry_goto = new (GetAllocator()) HGoto();
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001790
Alex Light9dec90a2020-09-14 17:58:28 -07001791 entry->AddInstruction(entry_goto);
1792
1793 HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, two_const, 0, 0);
1794 HInstruction* branch = new (GetAllocator()) HIf(param);
1795 start->AddInstruction(alloc_w);
1796 start->AddInstruction(branch);
1797 // environment
1798 ArenaVector<HInstruction*> alloc_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction));
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001799 ManuallyBuildEnvFor(alloc_w, {});
Alex Light9dec90a2020-09-14 17:58:28 -07001800
1801 // left
1802 HInstruction* left_set_1 =
1803 new (GetAllocator()) HArraySet(alloc_w, zero_const, one_const, DataType::Type::kInt32, 0);
1804 HInstruction* left_set_2 =
1805 new (GetAllocator()) HArraySet(alloc_w, zero_const, zero_const, DataType::Type::kInt32, 0);
1806 HInstruction* left_goto = new (GetAllocator()) HGoto();
1807 left->AddInstruction(left_set_1);
1808 left->AddInstruction(left_set_2);
1809 left->AddInstruction(left_goto);
1810
1811 // right
1812 HInstruction* right_set_1 =
1813 new (GetAllocator()) HArraySet(alloc_w, one_const, one_const, DataType::Type::kInt32, 0);
1814 HInstruction* right_set_2 =
1815 new (GetAllocator()) HArraySet(alloc_w, one_const, zero_const, DataType::Type::kInt32, 0);
1816 HInstruction* right_goto = new (GetAllocator()) HGoto();
1817 right->AddInstruction(right_set_1);
1818 right->AddInstruction(right_set_2);
1819 right->AddInstruction(right_goto);
1820
1821 // ret
1822 HInstruction* read_1 =
1823 new (GetAllocator()) HArrayGet(alloc_w, zero_const, DataType::Type::kInt32, 0);
1824 HInstruction* read_2 =
1825 new (GetAllocator()) HArrayGet(alloc_w, one_const, DataType::Type::kInt32, 0);
1826 HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, read_1, read_2);
1827 HInstruction* return_inst = new (GetAllocator()) HReturn(add);
1828 ret->AddInstruction(read_1);
1829 ret->AddInstruction(read_2);
1830 ret->AddInstruction(add);
1831 ret->AddInstruction(return_inst);
1832
1833 // exit
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001834 SetupExit(exit);
Alex Light9dec90a2020-09-14 17:58:28 -07001835
1836 graph_->ClearDominanceInformation();
1837 graph_->ClearLoopInformation();
1838 PerformLSE();
1839
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001840 EXPECT_INS_REMOVED(read_1);
1841 EXPECT_INS_REMOVED(read_2);
1842 EXPECT_INS_REMOVED(left_set_1);
1843 EXPECT_INS_REMOVED(left_set_2);
1844 EXPECT_INS_REMOVED(right_set_1);
1845 EXPECT_INS_REMOVED(right_set_2);
1846 EXPECT_INS_REMOVED(alloc_w);
Alex Light9dec90a2020-09-14 17:58:28 -07001847}
1848
Alex Light86fe9b82020-11-16 16:54:01 +00001849// // ENTRY
1850// obj = new Obj();
1851// // ALL should be kept
1852// switch (parameter_value) {
1853// case 1:
1854// // Case1
1855// obj.field = 1;
1856// call_func(obj);
1857// break;
1858// case 2:
1859// // Case2
1860// obj.field = 2;
1861// call_func(obj);
1862// // We don't know what obj.field is now we aren't able to eliminate the read below!
1863// break;
1864// default:
1865// // Case3
1866// // TODO This only happens because of limitations on our LSE which is unable
1867// // to materialize co-dependent loop and non-loop phis.
1868// // Ideally we'd want to generate
1869// // P1 = PHI[3, loop_val]
1870// // while (test()) {
1871// // if (test2()) { goto; } else { goto; }
1872// // loop_val = [P1, 5]
1873// // }
1874// // Currently we aren't able to unfortunately.
1875// obj.field = 3;
1876// while (test()) {
1877// if (test2()) { } else { obj.field = 5; }
1878// }
1879// break;
1880// }
1881// EXIT
1882// return obj.field
1883TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) {
1884 CreateGraph();
1885 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
1886 "exit",
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001887 {{"entry", "bswitch"},
1888 {"bswitch", "case1"},
1889 {"bswitch", "case2"},
1890 {"bswitch", "case3"},
1891 {"case1", "breturn"},
1892 {"case2", "breturn"},
1893 {"case3", "loop_pre_header"},
1894 {"loop_pre_header", "loop_header"},
1895 {"loop_header", "loop_body"},
1896 {"loop_body", "loop_if_left"},
1897 {"loop_body", "loop_if_right"},
1898 {"loop_if_left", "loop_end"},
1899 {"loop_if_right", "loop_end"},
1900 {"loop_end", "loop_header"},
1901 {"loop_header", "breturn"},
1902 {"breturn", "exit"}}));
Alex Light86fe9b82020-11-16 16:54:01 +00001903#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
1904 GET_BLOCK(entry);
1905 GET_BLOCK(bswitch);
1906 GET_BLOCK(exit);
1907 GET_BLOCK(breturn);
1908 GET_BLOCK(case1);
1909 GET_BLOCK(case2);
1910 GET_BLOCK(case3);
1911
1912 GET_BLOCK(loop_pre_header);
1913 GET_BLOCK(loop_header);
1914 GET_BLOCK(loop_body);
1915 GET_BLOCK(loop_if_left);
1916 GET_BLOCK(loop_if_right);
1917 GET_BLOCK(loop_end);
1918#undef GET_BLOCK
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001919 HInstruction* switch_val = MakeParam(DataType::Type::kInt32);
Alex Light86fe9b82020-11-16 16:54:01 +00001920 HInstruction* c1 = graph_->GetIntConstant(1);
1921 HInstruction* c2 = graph_->GetIntConstant(2);
1922 HInstruction* c3 = graph_->GetIntConstant(3);
1923 HInstruction* c5 = graph_->GetIntConstant(5);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001924
1925 HInstruction* cls = MakeClassLoad();
1926 HInstruction* new_inst = MakeNewInstance(cls);
Alex Light86fe9b82020-11-16 16:54:01 +00001927 HInstruction* entry_goto = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00001928 entry->AddInstruction(cls);
1929 entry->AddInstruction(new_inst);
1930 entry->AddInstruction(entry_goto);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001931 ManuallyBuildEnvFor(cls, {});
Alex Light86fe9b82020-11-16 16:54:01 +00001932 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
1933
1934 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, switch_val);
1935 bswitch->AddInstruction(switch_inst);
1936
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001937 HInstruction* write_c1 = MakeIFieldSet(new_inst, c1, MemberOffset(32));
1938 HInstruction* call_c1 = MakeInvoke(DataType::Type::kVoid, { new_inst });
Alex Light86fe9b82020-11-16 16:54:01 +00001939 HInstruction* goto_c1 = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00001940 case1->AddInstruction(write_c1);
1941 case1->AddInstruction(call_c1);
1942 case1->AddInstruction(goto_c1);
1943 call_c1->CopyEnvironmentFrom(cls->GetEnvironment());
1944
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001945 HInstruction* write_c2 = MakeIFieldSet(new_inst, c2, MemberOffset(32));
1946 HInstruction* call_c2 = MakeInvoke(DataType::Type::kVoid, { new_inst });
Alex Light86fe9b82020-11-16 16:54:01 +00001947 HInstruction* goto_c2 = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00001948 case2->AddInstruction(write_c2);
1949 case2->AddInstruction(call_c2);
1950 case2->AddInstruction(goto_c2);
1951 call_c2->CopyEnvironmentFrom(cls->GetEnvironment());
1952
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001953 HInstruction* write_c3 = MakeIFieldSet(new_inst, c3, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00001954 HInstruction* goto_c3 = new (GetAllocator()) HGoto();
1955 case3->AddInstruction(write_c3);
1956 case3->AddInstruction(goto_c3);
1957
1958 HInstruction* goto_preheader = new (GetAllocator()) HGoto();
1959 loop_pre_header->AddInstruction(goto_preheader);
1960
1961 HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck();
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001962 HInstruction* call_loop_header = MakeInvoke(DataType::Type::kBool, {});
Alex Light86fe9b82020-11-16 16:54:01 +00001963 HInstruction* if_loop_header = new (GetAllocator()) HIf(call_loop_header);
1964 loop_header->AddInstruction(suspend_check_header);
1965 loop_header->AddInstruction(call_loop_header);
1966 loop_header->AddInstruction(if_loop_header);
1967 call_loop_header->CopyEnvironmentFrom(cls->GetEnvironment());
1968 suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment());
1969
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001970 HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {});
Alex Light86fe9b82020-11-16 16:54:01 +00001971 HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body);
1972 loop_body->AddInstruction(call_loop_body);
1973 loop_body->AddInstruction(if_loop_body);
1974 call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment());
1975
1976 HInstruction* goto_loop_left = new (GetAllocator()) HGoto();
1977 loop_if_left->AddInstruction(goto_loop_left);
1978
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001979 HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00001980 HInstruction* goto_loop_right = new (GetAllocator()) HGoto();
1981 loop_if_right->AddInstruction(write_loop_right);
1982 loop_if_right->AddInstruction(goto_loop_right);
1983
1984 HInstruction* goto_loop_end = new (GetAllocator()) HGoto();
1985 loop_end->AddInstruction(goto_loop_end);
1986
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001987 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00001988 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
1989 breturn->AddInstruction(read_bottom);
1990 breturn->AddInstruction(return_exit);
1991
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001992 SetupExit(exit);
1993
Alex Light86fe9b82020-11-16 16:54:01 +00001994 // PerformLSE expects this to be empty.
1995 graph_->ClearDominanceInformation();
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001996 LOG(INFO) << "Pre LSE " << blks;
1997 PerformLSENoPartial();
Alex Light86fe9b82020-11-16 16:54:01 +00001998
Alex Lightfc1ce4e2021-01-22 14:05:13 +00001999 EXPECT_INS_RETAINED(read_bottom);
2000 EXPECT_INS_RETAINED(write_c1);
2001 EXPECT_INS_RETAINED(write_c2);
2002 EXPECT_INS_RETAINED(write_c3);
2003 EXPECT_INS_RETAINED(write_loop_right);
Alex Light86fe9b82020-11-16 16:54:01 +00002004}
2005
2006// // ENTRY
2007// obj = new Obj();
2008// if (parameter_value) {
2009// // LEFT
2010// obj.field = 1;
2011// call_func(obj);
2012// foo_r = obj.field
2013// } else {
2014// // TO BE ELIMINATED
2015// obj.field = 2;
2016// // RIGHT
2017// // TO BE ELIMINATED
2018// foo_l = obj.field;
2019// }
2020// EXIT
2021// return PHI(foo_l, foo_r)
2022TEST_F(LoadStoreEliminationTest, PartialLoadElimination) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002023 VariableSizedHandleScope vshs(Thread::Current());
2024 CreateGraph(&vshs);
Alex Light86fe9b82020-11-16 16:54:01 +00002025 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2026 "exit_REAL",
2027 { { "entry", "left" },
2028 { "entry", "right" },
2029 { "left", "exit" },
2030 { "right", "exit" },
2031 { "exit", "exit_REAL" } }));
2032 HBasicBlock* entry = blks.Get("entry");
2033 HBasicBlock* left = blks.Get("left");
2034 HBasicBlock* right = blks.Get("right");
2035 HBasicBlock* exit = blks.Get("exit");
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002036 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
Alex Light86fe9b82020-11-16 16:54:01 +00002037 HInstruction* c1 = graph_->GetIntConstant(1);
2038 HInstruction* c2 = graph_->GetIntConstant(2);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002039
2040 HInstruction* cls = MakeClassLoad();
2041 HInstruction* new_inst = MakeNewInstance(cls);
Alex Light86fe9b82020-11-16 16:54:01 +00002042 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
Alex Light86fe9b82020-11-16 16:54:01 +00002043 entry->AddInstruction(cls);
2044 entry->AddInstruction(new_inst);
2045 entry->AddInstruction(if_inst);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002046 ManuallyBuildEnvFor(cls, {});
Alex Light86fe9b82020-11-16 16:54:01 +00002047 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
2048
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002049 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
2050 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
2051 HInstruction* read_left = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(16));
Alex Light86fe9b82020-11-16 16:54:01 +00002052 HInstruction* goto_left = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00002053 left->AddInstruction(write_left);
2054 left->AddInstruction(call_left);
2055 left->AddInstruction(read_left);
2056 left->AddInstruction(goto_left);
2057 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
2058
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002059 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(16));
2060 HInstruction* read_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(16));
Alex Light86fe9b82020-11-16 16:54:01 +00002061 HInstruction* goto_right = new (GetAllocator()) HGoto();
2062 right->AddInstruction(write_right);
2063 right->AddInstruction(read_right);
2064 right->AddInstruction(goto_right);
2065
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002066 HInstruction* phi_final = MakePhi({read_left, read_right});
Alex Light86fe9b82020-11-16 16:54:01 +00002067 HInstruction* return_exit = new (GetAllocator()) HReturn(phi_final);
2068 exit->AddPhi(phi_final->AsPhi());
2069 exit->AddInstruction(return_exit);
2070
2071 // PerformLSE expects this to be empty.
2072 graph_->ClearDominanceInformation();
2073 PerformLSE();
2074
2075 ASSERT_TRUE(IsRemoved(read_right));
2076 ASSERT_FALSE(IsRemoved(read_left));
2077 ASSERT_FALSE(IsRemoved(phi_final));
2078 ASSERT_TRUE(phi_final->GetInputs()[1] == c2);
2079 ASSERT_TRUE(phi_final->GetInputs()[0] == read_left);
2080 ASSERT_TRUE(IsRemoved(write_right));
2081}
2082
2083// // ENTRY
2084// obj = new Obj();
2085// if (parameter_value) {
2086// // LEFT
2087// obj.field = 1;
2088// call_func(obj);
2089// // We don't know what obj.field is now we aren't able to eliminate the read below!
2090// } else {
2091// // DO NOT ELIMINATE
2092// obj.field = 2;
2093// // RIGHT
2094// }
2095// EXIT
2096// return obj.field
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002097// This test runs with partial LSE disabled.
Alex Light86fe9b82020-11-16 16:54:01 +00002098TEST_F(LoadStoreEliminationTest, PartialLoadPreserved) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002099 VariableSizedHandleScope vshs(Thread::Current());
2100 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 Lightfc1ce4e2021-01-22 14:05:13 +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 Lightfc1ce4e2021-01-22 14:05:13 +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 Lightfc1ce4e2021-01-22 14:05:13 +00002122 ManuallyBuildEnvFor(cls, {});
Alex Light86fe9b82020-11-16 16:54:01 +00002123 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
2124
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002125 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
2126 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
Alex Light86fe9b82020-11-16 16:54:01 +00002127 HInstruction* goto_left = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00002128 left->AddInstruction(write_left);
2129 left->AddInstruction(call_left);
2130 left->AddInstruction(goto_left);
2131 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
2132
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002133 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00002134 HInstruction* goto_right = new (GetAllocator()) HGoto();
2135 right->AddInstruction(write_right);
2136 right->AddInstruction(goto_right);
2137
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002138 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00002139 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
2140 exit->AddInstruction(read_bottom);
2141 exit->AddInstruction(return_exit);
2142 // PerformLSE expects this to be empty.
2143 graph_->ClearDominanceInformation();
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002144 PerformLSENoPartial();
Alex Light86fe9b82020-11-16 16:54:01 +00002145
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002146 EXPECT_INS_RETAINED(read_bottom) << *read_bottom;
2147 EXPECT_INS_RETAINED(write_right) << *write_right;
Alex Light86fe9b82020-11-16 16:54:01 +00002148}
2149
2150// // ENTRY
2151// obj = new Obj();
2152// if (parameter_value) {
2153// // LEFT
2154// obj.field = 1;
2155// call_func(obj);
2156// // We don't know what obj.field is now we aren't able to eliminate the read below!
2157// } else {
2158// // DO NOT ELIMINATE
2159// if (param2) {
2160// obj.field = 2;
2161// } else {
2162// obj.field = 3;
2163// }
2164// // RIGHT
2165// }
2166// EXIT
2167// return obj.field
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002168// NB This test is for non-partial LSE flow. Normally the obj.field writes will be removed
Alex Light86fe9b82020-11-16 16:54:01 +00002169TEST_F(LoadStoreEliminationTest, PartialLoadPreserved2) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002170 VariableSizedHandleScope vshs(Thread::Current());
2171 CreateGraph(&vshs);
Alex Light86fe9b82020-11-16 16:54:01 +00002172 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2173 "exit_REAL",
2174 { { "entry", "left" },
2175 { "entry", "right_start" },
2176 { "left", "exit" },
2177 { "right_start", "right_first" },
2178 { "right_start", "right_second" },
2179 { "right_first", "right_end" },
2180 { "right_second", "right_end" },
2181 { "right_end", "exit" },
2182 { "exit", "exit_REAL" } }));
2183 HBasicBlock* entry = blks.Get("entry");
2184 HBasicBlock* left = blks.Get("left");
2185 HBasicBlock* right_start = blks.Get("right_start");
2186 HBasicBlock* right_first = blks.Get("right_first");
2187 HBasicBlock* right_second = blks.Get("right_second");
2188 HBasicBlock* right_end = blks.Get("right_end");
2189 HBasicBlock* exit = blks.Get("exit");
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002190 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
2191 HInstruction* bool_value_2 = MakeParam(DataType::Type::kBool);
Alex Light86fe9b82020-11-16 16:54:01 +00002192 HInstruction* c1 = graph_->GetIntConstant(1);
2193 HInstruction* c2 = graph_->GetIntConstant(2);
2194 HInstruction* c3 = graph_->GetIntConstant(3);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002195
2196 HInstruction* cls = MakeClassLoad();
2197 HInstruction* new_inst = MakeNewInstance(cls);
Alex Light86fe9b82020-11-16 16:54:01 +00002198 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
Alex Light86fe9b82020-11-16 16:54:01 +00002199 entry->AddInstruction(cls);
2200 entry->AddInstruction(new_inst);
2201 entry->AddInstruction(if_inst);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002202 ManuallyBuildEnvFor(cls, {});
Alex Light86fe9b82020-11-16 16:54:01 +00002203 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
2204
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002205 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
2206 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
Alex Light86fe9b82020-11-16 16:54:01 +00002207 HInstruction* goto_left = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00002208 left->AddInstruction(write_left);
2209 left->AddInstruction(call_left);
2210 left->AddInstruction(goto_left);
2211 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
2212
2213 HInstruction* right_if = new (GetAllocator()) HIf(bool_value_2);
2214 right_start->AddInstruction(right_if);
2215
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002216 HInstruction* write_right_first = MakeIFieldSet(new_inst, c2, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00002217 HInstruction* goto_right_first = new (GetAllocator()) HGoto();
2218 right_first->AddInstruction(write_right_first);
2219 right_first->AddInstruction(goto_right_first);
2220
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002221 HInstruction* write_right_second = MakeIFieldSet(new_inst, c3, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00002222 HInstruction* goto_right_second = new (GetAllocator()) HGoto();
2223 right_second->AddInstruction(write_right_second);
2224 right_second->AddInstruction(goto_right_second);
2225
2226 HInstruction* goto_right_end = new (GetAllocator()) HGoto();
2227 right_end->AddInstruction(goto_right_end);
2228
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002229 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00002230 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
2231 exit->AddInstruction(read_bottom);
2232 exit->AddInstruction(return_exit);
2233 // PerformLSE expects this to be empty.
2234 graph_->ClearDominanceInformation();
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002235 PerformLSENoPartial();
Alex Light86fe9b82020-11-16 16:54:01 +00002236
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002237 EXPECT_INS_RETAINED(read_bottom);
2238 EXPECT_INS_RETAINED(write_right_first);
2239 EXPECT_INS_RETAINED(write_right_second);
Alex Light86fe9b82020-11-16 16:54:01 +00002240}
2241
2242// // ENTRY
2243// obj = new Obj();
2244// if (parameter_value) {
2245// // LEFT
2246// // DO NOT ELIMINATE
2247// escape(obj);
2248// obj.field = 1;
2249// } else {
2250// // RIGHT
2251// // ELIMINATE
2252// obj.field = 2;
2253// }
2254// EXIT
2255// ELIMINATE
2256// return obj.field
2257TEST_F(LoadStoreEliminationTest, PartialLoadElimination2) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002258 VariableSizedHandleScope vshs(Thread::Current());
2259 CreateGraph(&vshs);
Alex Light86fe9b82020-11-16 16:54:01 +00002260 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2261 "exit",
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002262 {{"entry", "left"},
2263 {"entry", "right"},
2264 {"left", "breturn"},
2265 {"right", "breturn"},
2266 {"breturn", "exit"}}));
Alex Light86fe9b82020-11-16 16:54:01 +00002267#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
2268 GET_BLOCK(entry);
2269 GET_BLOCK(exit);
2270 GET_BLOCK(breturn);
2271 GET_BLOCK(left);
2272 GET_BLOCK(right);
2273#undef GET_BLOCK
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002274 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
Alex Light86fe9b82020-11-16 16:54:01 +00002275 HInstruction* c1 = graph_->GetIntConstant(1);
2276 HInstruction* c2 = graph_->GetIntConstant(2);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002277
2278 HInstruction* cls = MakeClassLoad();
2279 HInstruction* new_inst = MakeNewInstance(cls);
Alex Light86fe9b82020-11-16 16:54:01 +00002280 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
Alex Light86fe9b82020-11-16 16:54:01 +00002281 entry->AddInstruction(cls);
2282 entry->AddInstruction(new_inst);
2283 entry->AddInstruction(if_inst);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002284 ManuallyBuildEnvFor(cls, {});
Alex Light86fe9b82020-11-16 16:54:01 +00002285 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
2286
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002287 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
2288 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00002289 HInstruction* goto_left = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00002290 left->AddInstruction(call_left);
2291 left->AddInstruction(write_left);
2292 left->AddInstruction(goto_left);
2293 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
2294
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002295 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00002296 HInstruction* goto_right = new (GetAllocator()) HGoto();
2297 right->AddInstruction(write_right);
2298 right->AddInstruction(goto_right);
2299
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002300 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00002301 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
2302 breturn->AddInstruction(read_bottom);
2303 breturn->AddInstruction(return_exit);
2304
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002305 SetupExit(exit);
2306
Alex Light86fe9b82020-11-16 16:54:01 +00002307 // PerformLSE expects this to be empty.
2308 graph_->ClearDominanceInformation();
2309 PerformLSE();
2310
Alex Lightfc1ce4e2021-01-22 14:05:13 +00002311 EXPECT_INS_REMOVED(read_bottom);
2312 EXPECT_INS_REMOVED(write_right);
2313 EXPECT_INS_RETAINED(write_left);
2314 EXPECT_INS_RETAINED(call_left);
2315}
2316
2317class PatternMatchGraphVisitor : public HGraphVisitor {
2318 private:
2319 struct HandlerWrapper {
2320 public:
2321 virtual ~HandlerWrapper() {}
2322 virtual void operator()(HInstruction* h) = 0;
2323 };
2324
2325 template <HInstruction::InstructionKind kKind, typename F>
2326 struct KindWrapper;
2327
2328#define GEN_HANDLER(nm, unused) \
2329 template <typename F> \
2330 struct KindWrapper<HInstruction::InstructionKind::k##nm, F> : public HandlerWrapper { \
2331 public: \
2332 explicit KindWrapper(F f) : f_(f) {} \
2333 void operator()(HInstruction* h) override { \
2334 if constexpr (std::is_invocable_v<F, H##nm*>) { \
2335 f_(h->As##nm()); \
2336 } else { \
2337 LOG(FATAL) << "Incorrect call with " << #nm; \
2338 } \
2339 } \
2340 \
2341 private: \
2342 F f_; \
2343 };
2344
2345 FOR_EACH_CONCRETE_INSTRUCTION(GEN_HANDLER)
2346#undef GEN_HANDLER
2347
2348 template <typename F>
2349 std::unique_ptr<HandlerWrapper> GetWrapper(HInstruction::InstructionKind kind, F f) {
2350 switch (kind) {
2351#define GEN_GETTER(nm, unused) \
2352 case HInstruction::InstructionKind::k##nm: \
2353 return std::unique_ptr<HandlerWrapper>( \
2354 new KindWrapper<HInstruction::InstructionKind::k##nm, F>(f));
2355 FOR_EACH_CONCRETE_INSTRUCTION(GEN_GETTER)
2356#undef GEN_GETTER
2357 default:
2358 LOG(FATAL) << "Unable to handle kind " << kind;
2359 return nullptr;
2360 }
2361 }
2362
2363 public:
2364 template <typename... Inst>
2365 explicit PatternMatchGraphVisitor(HGraph* graph, Inst... handlers) : HGraphVisitor(graph) {
2366 FillHandlers(handlers...);
2367 }
2368
2369 void VisitInstruction(HInstruction* instruction) override {
2370 auto& h = handlers_[instruction->GetKind()];
2371 if (h.get() != nullptr) {
2372 (*h)(instruction);
2373 }
2374 }
2375
2376 private:
2377 template <typename Func>
2378 constexpr HInstruction::InstructionKind GetKind() {
2379#define CHECK_INST(nm, unused) \
2380 if constexpr (std::is_invocable_v<Func, H##nm*>) { \
2381 return HInstruction::InstructionKind::k##nm; \
2382 }
2383 FOR_EACH_CONCRETE_INSTRUCTION(CHECK_INST);
2384#undef CHECK_INST
2385 static_assert(!std::is_invocable_v<Func, HInstruction*>,
2386 "Use on generic HInstruction not allowed");
2387#define STATIC_ASSERT_ABSTRACT(nm, unused) && !std::is_invocable_v<Func, H##nm*>
2388 static_assert(true FOR_EACH_ABSTRACT_INSTRUCTION(STATIC_ASSERT_ABSTRACT),
2389 "Must not be abstract instruction");
2390#undef STATIC_ASSERT_ABSTRACT
2391#define STATIC_ASSERT_CONCRETE(nm, unused) || std::is_invocable_v<Func, H##nm*>
2392 static_assert(false FOR_EACH_CONCRETE_INSTRUCTION(STATIC_ASSERT_CONCRETE),
2393 "Must be a concrete instruction");
2394#undef STATIC_ASSERT_CONCRETE
2395 return HInstruction::InstructionKind::kLastInstructionKind;
2396 }
2397 template <typename First>
2398 void FillHandlers(First h1) {
2399 HInstruction::InstructionKind type = GetKind<First>();
2400 CHECK_NE(type, HInstruction::kLastInstructionKind)
2401 << "Unknown instruction kind. Only concrete ones please.";
2402 handlers_[type] = GetWrapper(type, h1);
2403 }
2404
2405 template <typename First, typename... Inst>
2406 void FillHandlers(First h1, Inst... handlers) {
2407 FillHandlers(h1);
2408 FillHandlers<Inst...>(handlers...);
2409 }
2410
2411 std::array<std::unique_ptr<HandlerWrapper>, HInstruction::InstructionKind::kLastInstructionKind>
2412 handlers_;
2413};
2414
2415template <typename... Target>
2416std::tuple<std::vector<Target*>...> FindAllInstructions(
2417 HGraph* graph,
2418 std::variant<std::nullopt_t, HBasicBlock*, std::initializer_list<HBasicBlock*>> blks =
2419 std::nullopt) {
2420 std::tuple<std::vector<Target*>...> res;
2421 PatternMatchGraphVisitor vis(
2422 graph, [&](Target* t) { std::get<std::vector<Target*>>(res).push_back(t); }...);
2423
2424 if (std::holds_alternative<std::initializer_list<HBasicBlock*>>(blks)) {
2425 for (HBasicBlock* blk : std::get<std::initializer_list<HBasicBlock*>>(blks)) {
2426 vis.VisitBasicBlock(blk);
2427 }
2428 } else if (std::holds_alternative<std::nullopt_t>(blks)) {
2429 vis.VisitInsertionOrder();
2430 } else {
2431 vis.VisitBasicBlock(std::get<HBasicBlock*>(blks));
2432 }
2433 return res;
2434}
2435
2436template <typename... Target>
2437std::tuple<Target*...> FindSingleInstructions(
2438 HGraph* graph,
2439 std::variant<std::nullopt_t, HBasicBlock*, std::initializer_list<HBasicBlock*>> blks =
2440 std::nullopt) {
2441 std::tuple<Target*...> res;
2442 PatternMatchGraphVisitor vis(graph, [&](Target* t) {
2443 EXPECT_EQ(std::get<Target*>(res), nullptr)
2444 << *std::get<Target*>(res) << " already found but found " << *t << "!";
2445 std::get<Target*>(res) = t;
2446 }...);
2447 if (std::holds_alternative<std::initializer_list<HBasicBlock*>>(blks)) {
2448 for (HBasicBlock* blk : std::get<std::initializer_list<HBasicBlock*>>(blks)) {
2449 vis.VisitBasicBlock(blk);
2450 }
2451 } else if (std::holds_alternative<std::nullopt_t>(blks)) {
2452 vis.VisitInsertionOrder();
2453 } else {
2454 vis.VisitBasicBlock(std::get<HBasicBlock*>(blks));
2455 }
2456 return res;
2457}
2458
2459template <typename Target>
2460Target* FindSingleInstruction(
2461 HGraph* graph,
2462 std::variant<std::nullopt_t, HBasicBlock*, std::initializer_list<HBasicBlock*>> blks =
2463 std::nullopt) {
2464 return std::get<Target*>(FindSingleInstructions<Target>(graph, blks));
2465}
2466
2467template<typename Iter, typename Func>
2468typename Iter::value_type FindOrNull(Iter begin, Iter end, Func func) {
2469 static_assert(std::is_pointer_v<typename Iter::value_type>);
2470 auto it = std::find_if(begin, end, func);
2471 if (it == end) {
2472 return nullptr;
2473 } else {
2474 return *it;
2475 }
2476}
2477
2478// // ENTRY
2479// Obj new_inst = new Obj();
2480// new_inst.foo = 12;
2481// Obj obj;
2482// Obj out;
2483// int first;
2484// if (param0) {
2485// // ESCAPE_ROUTE
2486// if (param1) {
2487// // LEFT_START
2488// if (param2) {
2489// // LEFT_LEFT
2490// obj = new_inst;
2491// } else {
2492// // LEFT_RIGHT
2493// obj = obj_param;
2494// }
2495// // LEFT_MERGE
2496// // technically the phi is enough to cause an escape but might as well be
2497// // thorough.
2498// // obj = phi[new_inst, param]
2499// escape(obj);
2500// out = obj;
2501// } else {
2502// // RIGHT
2503// out = obj_param;
2504// }
2505// // EXIT
2506// // Can't do anything with this since we don't have good tracking for the heap-locations
2507// // out = phi[param, phi[new_inst, param]]
2508// first = out.foo
2509// } else {
2510// new_inst.foo = 15;
2511// first = 13;
2512// }
2513// // first = phi[out.foo, 13]
2514// return first + new_inst.foo;
2515TEST_F(LoadStoreEliminationTest, PartialPhiPropagation) {
2516 VariableSizedHandleScope vshs(Thread::Current());
2517 CreateGraph(&vshs);
2518 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2519 "exit",
2520 {{"entry", "escape_route"},
2521 {"entry", "noescape_route"},
2522 {"escape_route", "left"},
2523 {"escape_route", "right"},
2524 {"left", "left_left"},
2525 {"left", "left_right"},
2526 {"left_left", "left_merge"},
2527 {"left_right", "left_merge"},
2528 {"left_merge", "escape_end"},
2529 {"right", "escape_end"},
2530 {"escape_end", "breturn"},
2531 {"noescape_route", "breturn"},
2532 {"breturn", "exit"}}));
2533#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
2534 GET_BLOCK(entry);
2535 GET_BLOCK(exit);
2536 GET_BLOCK(breturn);
2537 GET_BLOCK(left);
2538 GET_BLOCK(right);
2539 GET_BLOCK(left_left);
2540 GET_BLOCK(left_right);
2541 GET_BLOCK(left_merge);
2542 GET_BLOCK(escape_end);
2543 GET_BLOCK(escape_route);
2544 GET_BLOCK(noescape_route);
2545#undef GET_BLOCK
2546 EnsurePredecessorOrder(escape_end, {left_merge, right});
2547 EnsurePredecessorOrder(left_merge, {left_left, left_right});
2548 EnsurePredecessorOrder(breturn, {escape_end, noescape_route});
2549 HInstruction* param0 = MakeParam(DataType::Type::kBool);
2550 HInstruction* param1 = MakeParam(DataType::Type::kBool);
2551 HInstruction* param2 = MakeParam(DataType::Type::kBool);
2552 HInstruction* obj_param = MakeParam(DataType::Type::kReference);
2553 HInstruction* c12 = graph_->GetIntConstant(12);
2554 HInstruction* c13 = graph_->GetIntConstant(13);
2555 HInstruction* c15 = graph_->GetIntConstant(15);
2556
2557 HInstruction* cls = MakeClassLoad();
2558 HInstruction* new_inst = MakeNewInstance(cls);
2559 HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32));
2560 HInstruction* if_param0 = new (GetAllocator()) HIf(param0);
2561 entry->AddInstruction(cls);
2562 entry->AddInstruction(new_inst);
2563 entry->AddInstruction(store);
2564 entry->AddInstruction(if_param0);
2565 ManuallyBuildEnvFor(cls, {});
2566 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
2567
2568 HInstruction* store_noescape = MakeIFieldSet(new_inst, c15, MemberOffset(32));
2569 noescape_route->AddInstruction(store_noescape);
2570 noescape_route->AddInstruction(new (GetAllocator()) HGoto());
2571
2572 escape_route->AddInstruction(new (GetAllocator()) HIf(param1));
2573
2574 HInstruction* if_left = new (GetAllocator()) HIf(param2);
2575 left->AddInstruction(if_left);
2576
2577 HInstruction* goto_left_left = new (GetAllocator()) HGoto();
2578 left_left->AddInstruction(goto_left_left);
2579
2580 HInstruction* goto_left_right = new (GetAllocator()) HGoto();
2581 left_right->AddInstruction(goto_left_right);
2582
2583 HPhi* left_phi = MakePhi({obj_param, new_inst});
2584 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { left_phi });
2585 HInstruction* goto_left_merge = new (GetAllocator()) HGoto();
2586 left_merge->AddPhi(left_phi);
2587 left_merge->AddInstruction(call_left);
2588 left_merge->AddInstruction(goto_left_merge);
2589 left_phi->SetCanBeNull(true);
2590 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
2591
2592 HInstruction* goto_right = new (GetAllocator()) HGoto();
2593 right->AddInstruction(goto_right);
2594
2595 HPhi* escape_end_phi = MakePhi({left_phi, obj_param});
2596 HInstruction* read_escape_end =
2597 MakeIFieldGet(escape_end_phi, DataType::Type::kInt32, MemberOffset(32));
2598 HInstruction* goto_escape_end = new (GetAllocator()) HGoto();
2599 escape_end->AddPhi(escape_end_phi);
2600 escape_end->AddInstruction(read_escape_end);
2601 escape_end->AddInstruction(goto_escape_end);
2602
2603 HPhi* return_phi = MakePhi({read_escape_end, c13});
2604 HInstruction* read_exit = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
2605 HInstruction* add_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, return_phi, read_exit);
2606 HInstruction* return_exit = new (GetAllocator()) HReturn(add_exit);
2607 breturn->AddPhi(return_phi);
2608 breturn->AddInstruction(read_exit);
2609 breturn->AddInstruction(add_exit);
2610 breturn->AddInstruction(return_exit);
2611
2612 SetupExit(exit);
2613
2614 // PerformLSE expects this to be empty.
2615 graph_->ClearDominanceInformation();
2616 LOG(INFO) << "Pre LSE " << blks;
2617 PerformLSEWithPartial();
2618 LOG(INFO) << "Post LSE " << blks;
2619
2620 HPredicatedInstanceFieldGet* pred_get =
2621 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_);
2622 std::vector<HPhi*> all_return_phis;
2623 std::tie(all_return_phis) = FindAllInstructions<HPhi>(graph_, breturn);
2624 EXPECT_EQ(all_return_phis.size(), 3u);
2625 EXPECT_INS_RETAINED(return_phi);
2626 EXPECT_TRUE(std::find(all_return_phis.begin(), all_return_phis.end(), return_phi) !=
2627 all_return_phis.end());
2628 HPhi* instance_phi =
2629 FindOrNull(all_return_phis.begin(), all_return_phis.end(), [&](HPhi* phi) {
2630 return phi != return_phi && phi->GetType() == DataType::Type::kReference;
2631 });
2632 ASSERT_NE(instance_phi, nullptr);
2633 HPhi* value_phi = FindOrNull(all_return_phis.begin(), all_return_phis.end(), [&](HPhi* phi) {
2634 return phi != return_phi && phi->GetType() == DataType::Type::kInt32;
2635 });
2636 ASSERT_NE(value_phi, nullptr);
2637 EXPECT_INS_EQ(
2638 instance_phi->InputAt(0),
2639 FindSingleInstruction<HNewInstance>(graph_, escape_route->GetSinglePredecessor()));
2640 // Check materialize block
2641 EXPECT_INS_EQ(FindSingleInstruction<HInstanceFieldSet>(
2642 graph_, escape_route->GetSinglePredecessor())
2643 ->InputAt(1),
2644 c12);
2645
2646 EXPECT_INS_EQ(instance_phi->InputAt(1), graph_->GetNullConstant());
2647 EXPECT_INS_EQ(value_phi->InputAt(0), graph_->GetIntConstant(0));
2648 EXPECT_INS_EQ(value_phi->InputAt(1), c15);
2649 EXPECT_INS_REMOVED(store_noescape);
2650 EXPECT_INS_EQ(pred_get->GetTarget(), instance_phi);
2651 EXPECT_INS_EQ(pred_get->GetDefaultValue(), value_phi);
2652}
2653
2654// // ENTRY
2655// // To be moved
2656// // NB Order important. By having alloc and store of obj1 before obj2 that
2657// // ensure we'll build the materialization for obj1 first (just due to how
2658// // we iterate.)
2659// obj1 = new Obj();
2660// obj2 = new Obj(); // has env[obj1]
2661// // Swap the order of these
2662// obj1.foo = param_obj1;
2663// obj2.foo = param_obj2;
2664// if (param1) {
2665// // LEFT
2666// obj2.foo = obj1;
2667// if (param2) {
2668// // LEFT_LEFT
2669// escape(obj2);
2670// } else {}
2671// } else {}
2672// return select(param3, obj1.foo, obj2.foo);
2673// EXIT
2674TEST_P(OrderDependentTestGroup, PredicatedUse) {
2675 VariableSizedHandleScope vshs(Thread::Current());
2676 CreateGraph(&vshs);
2677 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2678 "exit",
2679 {{"entry", "left"},
2680 {"entry", "right"},
2681 {"left", "left_left"},
2682 {"left", "left_right"},
2683 {"left_left", "left_end"},
2684 {"left_right", "left_end"},
2685 {"left_end", "breturn"},
2686 {"right", "breturn"},
2687 {"breturn", "exit"}}));
2688#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
2689 GET_BLOCK(entry);
2690 GET_BLOCK(exit);
2691 GET_BLOCK(breturn);
2692 GET_BLOCK(right);
2693 GET_BLOCK(left);
2694 GET_BLOCK(left_left);
2695 GET_BLOCK(left_right);
2696 GET_BLOCK(left_end);
2697#undef GET_BLOCK
2698 TestOrder order = GetParam();
2699 EnsurePredecessorOrder(breturn, {left_end, right});
2700 EnsurePredecessorOrder(left_end, {left_left, left_right});
2701 HInstruction* param1 = MakeParam(DataType::Type::kBool);
2702 HInstruction* param2 = MakeParam(DataType::Type::kBool);
2703 HInstruction* param3 = MakeParam(DataType::Type::kBool);
2704 HInstruction* param_obj1 = MakeParam(DataType::Type::kReference);
2705 HInstruction* param_obj2 = MakeParam(DataType::Type::kReference);
2706
2707 HInstruction* cls1 = MakeClassLoad();
2708 HInstruction* cls2 = MakeClassLoad();
2709 HInstruction* new_inst1 = MakeNewInstance(cls1);
2710 HInstruction* new_inst2 = MakeNewInstance(cls2);
2711 HInstruction* store1 = MakeIFieldSet(new_inst1, param_obj1, MemberOffset(32));
2712 HInstruction* store2 = MakeIFieldSet(new_inst2, param_obj2, MemberOffset(32));
2713 HInstruction* null_const = graph_->GetNullConstant();
2714 HInstruction* if_inst = new (GetAllocator()) HIf(param1);
2715 entry->AddInstruction(cls1);
2716 entry->AddInstruction(cls2);
2717 entry->AddInstruction(new_inst1);
2718 entry->AddInstruction(new_inst2);
2719 if (order == TestOrder::kSameAsAlloc) {
2720 entry->AddInstruction(store1);
2721 entry->AddInstruction(store2);
2722 } else {
2723 entry->AddInstruction(store2);
2724 entry->AddInstruction(store1);
2725 }
2726 entry->AddInstruction(if_inst);
2727 ManuallyBuildEnvFor(cls1, {});
2728 cls2->CopyEnvironmentFrom(cls1->GetEnvironment());
2729 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
2730 new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment());
2731
2732 // This is the escape of new_inst1
2733 HInstruction* store_left = MakeIFieldSet(new_inst2, new_inst1, MemberOffset(32));
2734 HInstruction* if_left = new (GetAllocator()) HIf(param2);
2735 left->AddInstruction(store_left);
2736 left->AddInstruction(if_left);
2737
2738 HInstruction* call_left_left = MakeInvoke(DataType::Type::kVoid, { new_inst2 });
2739 HInstruction* goto_left_left = new (GetAllocator()) HGoto();
2740 left_left->AddInstruction(call_left_left);
2741 left_left->AddInstruction(goto_left_left);
2742 call_left_left->CopyEnvironmentFrom(new_inst2->GetEnvironment());
2743
2744 left_right->AddInstruction(new (GetAllocator()) HGoto());
2745 left_end->AddInstruction(new (GetAllocator()) HGoto());
2746
2747 right->AddInstruction(new (GetAllocator()) HGoto());
2748
2749 // Used to distinguish the pred-gets without having to dig through the
2750 // multiple phi layers.
2751 constexpr uint32_t kRead1DexPc = 10;
2752 constexpr uint32_t kRead2DexPc = 20;
2753 HInstruction* read1 =
2754 MakeIFieldGet(new_inst1, DataType::Type::kReference, MemberOffset(32), kRead1DexPc);
2755 read1->SetReferenceTypeInfo(
2756 ReferenceTypeInfo::CreateUnchecked(graph_->GetHandleCache()->GetObjectClassHandle(), false));
2757 HInstruction* read2 =
2758 MakeIFieldGet(new_inst2, DataType::Type::kReference, MemberOffset(32), kRead2DexPc);
2759 read2->SetReferenceTypeInfo(
2760 ReferenceTypeInfo::CreateUnchecked(graph_->GetHandleCache()->GetObjectClassHandle(), false));
2761 HInstruction* sel_return = new (GetAllocator()) HSelect(param3, read1, read2, 0);
2762 HInstruction* return_exit = new (GetAllocator()) HReturn(sel_return);
2763 breturn->AddInstruction(read1);
2764 breturn->AddInstruction(read2);
2765 breturn->AddInstruction(sel_return);
2766 breturn->AddInstruction(return_exit);
2767
2768 SetupExit(exit);
2769
2770 // PerformLSE expects this to be empty.
2771 graph_->ClearDominanceInformation();
2772 LOG(INFO) << "Pre LSE " << blks;
2773 PerformLSEWithPartial();
2774 LOG(INFO) << "Post LSE " << blks;
2775
2776 EXPECT_INS_RETAINED(call_left_left);
2777 EXPECT_INS_REMOVED(read1);
2778 EXPECT_INS_REMOVED(read2);
2779 EXPECT_INS_REMOVED(new_inst1);
2780 EXPECT_INS_REMOVED(new_inst2);
2781 EXPECT_TRUE(new_inst1->GetUses().empty()) << *new_inst1 << " " << new_inst1->GetUses();
2782 EXPECT_TRUE(new_inst2->GetUses().empty()) << *new_inst2 << " " << new_inst2->GetUses();
2783 EXPECT_INS_RETAINED(sel_return);
2784 // Make sure the selector is the same
2785 EXPECT_INS_EQ(sel_return->InputAt(2), param3);
2786 std::vector<HPredicatedInstanceFieldGet*> pred_gets;
2787 std::tie(pred_gets) = FindAllInstructions<HPredicatedInstanceFieldGet>(graph_, breturn);
2788 HPredicatedInstanceFieldGet* pred1 = FindOrNull(pred_gets.begin(), pred_gets.end(), [&](auto i) {
2789 return i->GetDexPc() == kRead1DexPc;
2790 });
2791 HPredicatedInstanceFieldGet* pred2 = FindOrNull(pred_gets.begin(), pred_gets.end(), [&](auto i) {
2792 return i->GetDexPc() == kRead2DexPc;
2793 });
2794 ASSERT_NE(pred1, nullptr);
2795 ASSERT_NE(pred2, nullptr);
2796 EXPECT_INS_EQ(sel_return->InputAt(0), pred2);
2797 EXPECT_INS_EQ(sel_return->InputAt(1), pred1);
2798 // Check targets
2799 EXPECT_TRUE(pred1->GetTarget()->IsPhi()) << pred1->DumpWithArgs();
2800 EXPECT_TRUE(pred2->GetTarget()->IsPhi()) << pred2->DumpWithArgs();
2801 HInstruction* mat1 = FindSingleInstruction<HNewInstance>(graph_, left->GetSinglePredecessor());
2802 HInstruction* mat2 =
2803 FindSingleInstruction<HNewInstance>(graph_, left_left->GetSinglePredecessor());
2804 EXPECT_INS_EQ(pred1->GetTarget()->InputAt(0), mat1);
2805 EXPECT_INS_EQ(pred1->GetTarget()->InputAt(1), null_const);
2806 EXPECT_TRUE(pred2->GetTarget()->InputAt(0)->IsPhi()) << pred2->DumpWithArgs();
2807 EXPECT_INS_EQ(pred2->GetTarget()->InputAt(0)->InputAt(0), mat2);
2808 EXPECT_INS_EQ(pred2->GetTarget()->InputAt(0)->InputAt(1), null_const);
2809 EXPECT_INS_EQ(pred2->GetTarget()->InputAt(1), null_const);
2810 // Check default values.
2811 EXPECT_TRUE(pred1->GetDefaultValue()->IsPhi()) << pred1->DumpWithArgs();
2812 EXPECT_TRUE(pred2->GetDefaultValue()->IsPhi()) << pred2->DumpWithArgs();
2813 EXPECT_INS_EQ(pred1->GetDefaultValue()->InputAt(0), null_const);
2814 EXPECT_INS_EQ(pred1->GetDefaultValue()->InputAt(1), param_obj1);
2815 EXPECT_TRUE(pred2->GetDefaultValue()->InputAt(0)->IsPhi()) << pred2->DumpWithArgs();
2816 EXPECT_INS_EQ(pred2->GetDefaultValue()->InputAt(0)->InputAt(0), null_const);
2817 EXPECT_INS_EQ(pred2->GetDefaultValue()->InputAt(0)->InputAt(1), mat1);
2818 EXPECT_INS_EQ(pred2->GetDefaultValue()->InputAt(1), param_obj2);
2819}
2820
2821// // ENTRY
2822// // To be moved
2823// // NB Order important. By having alloc and store of obj1 before obj2 that
2824// // ensure we'll build the materialization for obj1 first (just due to how
2825// // we iterate.)
2826// obj1 = new Obj();
2827// obj.foo = 12;
2828// obj2 = new Obj(); // has env[obj1]
2829// obj2.foo = 15;
2830// if (param1) {
2831// // LEFT
2832// // Need to update env to nullptr
2833// escape(obj1/2);
2834// if (param2) {
2835// // LEFT_LEFT
2836// escape(obj2/1);
2837// } else {}
2838// } else {}
2839// return obj1.foo + obj2.foo;
2840// EXIT
2841TEST_P(OrderDependentTestGroup, PredicatedEnvUse) {
2842 VariableSizedHandleScope vshs(Thread::Current());
2843 CreateGraph(&vshs);
2844 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2845 "exit",
2846 {{"entry", "left"},
2847 {"entry", "right"},
2848 {"left", "left_left"},
2849 {"left", "left_right"},
2850 {"left_left", "left_end"},
2851 {"left_right", "left_end"},
2852 {"left_end", "breturn"},
2853 {"right", "breturn"},
2854 {"breturn", "exit"}}));
2855#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
2856 GET_BLOCK(entry);
2857 GET_BLOCK(exit);
2858 GET_BLOCK(breturn);
2859 GET_BLOCK(right);
2860 GET_BLOCK(left);
2861 GET_BLOCK(left_left);
2862 GET_BLOCK(left_right);
2863 GET_BLOCK(left_end);
2864#undef GET_BLOCK
2865 TestOrder order = GetParam();
2866 EnsurePredecessorOrder(breturn, {left_end, right});
2867 EnsurePredecessorOrder(left_end, {left_left, left_right});
2868 HInstruction* param1 = MakeParam(DataType::Type::kBool);
2869 HInstruction* param2 = MakeParam(DataType::Type::kBool);
2870 HInstruction* c12 = graph_->GetIntConstant(12);
2871 HInstruction* c15 = graph_->GetIntConstant(15);
2872
2873 HInstruction* cls1 = MakeClassLoad();
2874 HInstruction* cls2 = MakeClassLoad();
2875 HInstruction* new_inst1 = MakeNewInstance(cls1);
2876 HInstruction* store1 = MakeIFieldSet(new_inst1, c12, MemberOffset(32));
2877 HInstruction* new_inst2 = MakeNewInstance(cls2);
2878 HInstruction* store2 = MakeIFieldSet(new_inst2, c15, MemberOffset(32));
2879 HInstruction* if_inst = new (GetAllocator()) HIf(param1);
2880 entry->AddInstruction(cls1);
2881 entry->AddInstruction(cls2);
2882 entry->AddInstruction(new_inst1);
2883 entry->AddInstruction(store1);
2884 entry->AddInstruction(new_inst2);
2885 entry->AddInstruction(store2);
2886 entry->AddInstruction(if_inst);
2887 ManuallyBuildEnvFor(cls1, {});
2888 cls2->CopyEnvironmentFrom(cls1->GetEnvironment());
2889 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
2890 ManuallyBuildEnvFor(new_inst2, {new_inst1});
2891
2892 HInstruction* first_inst = new_inst1;
2893 HInstruction* second_inst = new_inst2;
2894
2895 if (order == TestOrder::kReverseOfAlloc) {
2896 std::swap(first_inst, second_inst);
2897 }
2898
2899 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { first_inst });
2900 HInstruction* if_left = new (GetAllocator()) HIf(param2);
2901 left->AddInstruction(call_left);
2902 left->AddInstruction(if_left);
2903 call_left->CopyEnvironmentFrom(new_inst2->GetEnvironment());
2904
2905 HInstruction* call_left_left = MakeInvoke(DataType::Type::kVoid, { second_inst });
2906 HInstruction* goto_left_left = new (GetAllocator()) HGoto();
2907 left_left->AddInstruction(call_left_left);
2908 left_left->AddInstruction(goto_left_left);
2909 call_left_left->CopyEnvironmentFrom(new_inst2->GetEnvironment());
2910
2911 left_right->AddInstruction(new (GetAllocator()) HGoto());
2912 left_end->AddInstruction(new (GetAllocator()) HGoto());
2913
2914 right->AddInstruction(new (GetAllocator()) HGoto());
2915
2916 HInstruction* read1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32));
2917 HInstruction* read2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32));
2918 HInstruction* add_return = new (GetAllocator()) HAdd(DataType::Type::kInt32, read1, read2);
2919 HInstruction* return_exit = new (GetAllocator()) HReturn(add_return);
2920 breturn->AddInstruction(read1);
2921 breturn->AddInstruction(read2);
2922 breturn->AddInstruction(add_return);
2923 breturn->AddInstruction(return_exit);
2924
2925 SetupExit(exit);
2926
2927 // PerformLSE expects this to be empty.
2928 graph_->ClearDominanceInformation();
2929 LOG(INFO) << "Pre LSE " << blks;
2930 PerformLSEWithPartial();
2931 LOG(INFO) << "Post LSE " << blks;
2932
2933 HNewInstance* moved_new_inst1;
2934 HInstanceFieldSet* moved_set1;
2935 HNewInstance* moved_new_inst2;
2936 HInstanceFieldSet* moved_set2;
2937 HBasicBlock* first_mat_block = left->GetSinglePredecessor();
2938 HBasicBlock* second_mat_block = left_left->GetSinglePredecessor();
2939 if (order == TestOrder::kReverseOfAlloc) {
2940 std::swap(first_mat_block, second_mat_block);
2941 }
2942 std::tie(moved_new_inst1, moved_set1) =
2943 FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, first_mat_block);
2944 std::tie(moved_new_inst2, moved_set2) =
2945 FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, second_mat_block);
2946 std::vector<HPredicatedInstanceFieldGet*> pred_gets;
2947 std::vector<HPhi*> phis;
2948 std::tie(pred_gets, phis) = FindAllInstructions<HPredicatedInstanceFieldGet, HPhi>(graph_);
2949 EXPECT_NE(moved_new_inst1, nullptr);
2950 EXPECT_NE(moved_new_inst2, nullptr);
2951 EXPECT_NE(moved_set1, nullptr);
2952 EXPECT_NE(moved_set2, nullptr);
2953 EXPECT_INS_EQ(moved_set1->InputAt(1), c12);
2954 EXPECT_INS_EQ(moved_set2->InputAt(1), c15);
2955 EXPECT_INS_RETAINED(call_left);
2956 EXPECT_INS_RETAINED(call_left_left);
2957 EXPECT_INS_REMOVED(store1);
2958 EXPECT_INS_REMOVED(store2);
2959 EXPECT_INS_REMOVED(read1);
2960 EXPECT_INS_REMOVED(read2);
2961 EXPECT_INS_EQ(moved_new_inst2->GetEnvironment()->GetInstructionAt(0),
2962 order == TestOrder::kSameAsAlloc
2963 ? moved_new_inst1
2964 : static_cast<HInstruction*>(graph_->GetNullConstant()));
2965}
2966
2967// // ENTRY
2968// obj1 = new Obj1();
2969// obj2 = new Obj2();
2970// val1 = 3;
2971// val2 = 13;
2972// // The exact order the stores are written affects what the order we perform
2973// // partial LSE on the values
2974// obj1/2.field = val1/2;
2975// obj2/1.field = val2/1;
2976// if (parameter_value) {
2977// // LEFT
2978// escape(obj1);
2979// escape(obj2);
2980// } else {
2981// // RIGHT
2982// // ELIMINATE
2983// obj1.field = 2;
2984// obj2.field = 12;
2985// }
2986// EXIT
2987// predicated-ELIMINATE
2988// return obj1.field + obj2.field
2989TEST_P(OrderDependentTestGroup, FieldSetOrderEnv) {
2990 VariableSizedHandleScope vshs(Thread::Current());
2991 CreateGraph(/*handles=*/&vshs);
2992 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
2993 "exit",
2994 {{"entry", "left"},
2995 {"entry", "right"},
2996 {"left", "breturn"},
2997 {"right", "breturn"},
2998 {"breturn", "exit"}}));
2999#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3000 GET_BLOCK(entry);
3001 GET_BLOCK(exit);
3002 GET_BLOCK(breturn);
3003 GET_BLOCK(left);
3004 GET_BLOCK(right);
3005#undef GET_BLOCK
3006 TestOrder order = GetParam();
3007 EnsurePredecessorOrder(breturn, {left, right});
3008 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
3009 HInstruction* c2 = graph_->GetIntConstant(2);
3010 HInstruction* c3 = graph_->GetIntConstant(3);
3011 HInstruction* c12 = graph_->GetIntConstant(12);
3012 HInstruction* c13 = graph_->GetIntConstant(13);
3013
3014 HInstruction* cls1 = MakeClassLoad();
3015 HInstruction* cls2 = MakeClassLoad();
3016 HInstruction* new_inst1 = MakeNewInstance(cls1);
3017 HInstruction* new_inst2 = MakeNewInstance(cls2);
3018 HInstruction* write_entry1 = MakeIFieldSet(new_inst1, c3, MemberOffset(32));
3019 HInstruction* write_entry2 = MakeIFieldSet(new_inst2, c13, MemberOffset(32));
3020 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
3021 entry->AddInstruction(cls1);
3022 entry->AddInstruction(cls2);
3023 entry->AddInstruction(new_inst1);
3024 entry->AddInstruction(new_inst2);
3025 if (order == TestOrder::kSameAsAlloc) {
3026 entry->AddInstruction(write_entry1);
3027 entry->AddInstruction(write_entry2);
3028 } else {
3029 entry->AddInstruction(write_entry2);
3030 entry->AddInstruction(write_entry1);
3031 }
3032 entry->AddInstruction(if_inst);
3033 ManuallyBuildEnvFor(cls1, {});
3034 cls2->CopyEnvironmentFrom(cls1->GetEnvironment());
3035 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
3036 ManuallyBuildEnvFor(new_inst2, {new_inst1});
3037
3038 HInstruction* call_left1 = MakeInvoke(DataType::Type::kVoid, { new_inst1 });
3039 HInstruction* call_left2 = MakeInvoke(DataType::Type::kVoid, { new_inst2 });
3040 HInstruction* goto_left = new (GetAllocator()) HGoto();
3041 left->AddInstruction(call_left1);
3042 left->AddInstruction(call_left2);
3043 left->AddInstruction(goto_left);
3044 call_left1->CopyEnvironmentFrom(cls1->GetEnvironment());
3045 call_left2->CopyEnvironmentFrom(cls1->GetEnvironment());
3046
3047 HInstruction* write_right1 = MakeIFieldSet(new_inst1, c2, MemberOffset(32));
3048 HInstruction* write_right2 = MakeIFieldSet(new_inst2, c12, MemberOffset(32));
3049 HInstruction* goto_right = new (GetAllocator()) HGoto();
3050 right->AddInstruction(write_right1);
3051 right->AddInstruction(write_right2);
3052 right->AddInstruction(goto_right);
3053
3054 HInstruction* read_bottom1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32));
3055 HInstruction* read_bottom2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32));
3056 HInstruction* combine =
3057 new (GetAllocator()) HAdd(DataType::Type::kInt32, read_bottom1, read_bottom2);
3058 HInstruction* return_exit = new (GetAllocator()) HReturn(combine);
3059 breturn->AddInstruction(read_bottom1);
3060 breturn->AddInstruction(read_bottom2);
3061 breturn->AddInstruction(combine);
3062 breturn->AddInstruction(return_exit);
3063
3064 SetupExit(exit);
3065
3066 // PerformLSE expects this to be empty.
3067 graph_->ClearDominanceInformation();
3068 LOG(INFO) << "Pre LSE " << blks;
3069 PerformLSEWithPartial();
3070 LOG(INFO) << "Post LSE " << blks;
3071
3072 EXPECT_INS_REMOVED(write_entry1);
3073 EXPECT_INS_REMOVED(write_entry2);
3074 EXPECT_INS_REMOVED(read_bottom1);
3075 EXPECT_INS_REMOVED(read_bottom2);
3076 EXPECT_INS_REMOVED(write_right1);
3077 EXPECT_INS_REMOVED(write_right2);
3078 EXPECT_INS_RETAINED(call_left1);
3079 EXPECT_INS_RETAINED(call_left2);
3080 std::vector<HPhi*> merges;
3081 std::vector<HPredicatedInstanceFieldGet*> pred_gets;
3082 std::vector<HNewInstance*> materializations;
3083 std::tie(merges, pred_gets) =
3084 FindAllInstructions<HPhi, HPredicatedInstanceFieldGet>(graph_, breturn);
3085 std::tie(materializations) = FindAllInstructions<HNewInstance>(graph_);
3086 ASSERT_EQ(merges.size(), 4u);
3087 ASSERT_EQ(pred_gets.size(), 2u);
3088 ASSERT_EQ(materializations.size(), 2u);
3089 HPhi* merge_value_return1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
3090 return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c2;
3091 });
3092 HPhi* merge_value_return2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
3093 return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c12;
3094 });
3095 HNewInstance* mat_alloc1 = FindOrNull(materializations.begin(),
3096 materializations.end(),
3097 [&](HNewInstance* n) { return n->InputAt(0) == cls1; });
3098 HNewInstance* mat_alloc2 = FindOrNull(materializations.begin(),
3099 materializations.end(),
3100 [&](HNewInstance* n) { return n->InputAt(0) == cls2; });
3101 ASSERT_NE(mat_alloc1, nullptr);
3102 ASSERT_NE(mat_alloc2, nullptr);
3103 HPhi* merge_alloc1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
3104 return p->GetType() == DataType::Type::kReference && p->InputAt(0) == mat_alloc1;
3105 });
3106 HPhi* merge_alloc2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
3107 return p->GetType() == DataType::Type::kReference && p->InputAt(0) == mat_alloc2;
3108 });
3109 ASSERT_NE(merge_alloc1, nullptr);
3110 HPredicatedInstanceFieldGet* pred_get1 =
3111 FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) {
3112 return pg->GetTarget() == merge_alloc1;
3113 });
3114 ASSERT_NE(merge_alloc2, nullptr);
3115 HPredicatedInstanceFieldGet* pred_get2 =
3116 FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) {
3117 return pg->GetTarget() == merge_alloc2;
3118 });
3119 ASSERT_NE(merge_value_return1, nullptr);
3120 ASSERT_NE(merge_value_return2, nullptr);
3121 EXPECT_INS_EQ(merge_alloc1->InputAt(1), graph_->GetNullConstant());
3122 EXPECT_INS_EQ(merge_alloc2->InputAt(1), graph_->GetNullConstant());
3123 ASSERT_NE(pred_get1, nullptr);
3124 EXPECT_INS_EQ(pred_get1->GetTarget(), merge_alloc1);
3125 EXPECT_INS_EQ(pred_get1->GetDefaultValue(), merge_value_return1)
3126 << " pred-get is: " << *pred_get1;
3127 EXPECT_INS_EQ(merge_value_return1->InputAt(0), graph_->GetIntConstant(0))
3128 << " merge val is: " << *merge_value_return1;
3129 EXPECT_INS_EQ(merge_value_return1->InputAt(1), c2) << " merge val is: " << *merge_value_return1;
3130 ASSERT_NE(pred_get2, nullptr);
3131 EXPECT_INS_EQ(pred_get2->GetTarget(), merge_alloc2);
3132 EXPECT_INS_EQ(pred_get2->GetDefaultValue(), merge_value_return2)
3133 << " pred-get is: " << *pred_get2;
3134 EXPECT_INS_EQ(merge_value_return2->InputAt(0), graph_->GetIntConstant(0))
3135 << " merge val is: " << *merge_value_return1;
3136 EXPECT_INS_EQ(merge_value_return2->InputAt(1), c12) << " merge val is: " << *merge_value_return1;
3137 EXPECT_INS_EQ(mat_alloc2->GetEnvironment()->GetInstructionAt(0), mat_alloc1);
3138}
3139
3140// // TODO We can compile this better if we are better able to understand lifetimes.
3141// // ENTRY
3142// obj1 = new Obj1();
3143// obj2 = new Obj2();
3144// // The exact order the stores are written affects what the order we perform
3145// // partial LSE on the values
3146// obj{1,2}.var = param_obj;
3147// obj{2,1}.var = param_obj;
3148// if (param_1) {
3149// // EARLY_RETURN
3150// return;
3151// }
3152// // escape of obj1
3153// obj2.var = obj1;
3154// if (param_2) {
3155// // escape of obj2 with a materialization that uses obj1
3156// escape(obj2);
3157// }
3158// // EXIT
3159// return;
3160TEST_P(OrderDependentTestGroup, MaterializationMovedUse) {
3161 VariableSizedHandleScope vshs(Thread::Current());
3162 CreateGraph(/*handles=*/&vshs);
3163 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
3164 "exit",
3165 {{"entry", "early_return"},
3166 {"early_return", "exit"},
3167 {"entry", "escape_1"},
3168 {"escape_1", "escape_2"},
3169 {"escape_1", "escape_1_crit_break"},
3170 {"escape_1_crit_break", "exit"},
3171 {"escape_2", "exit"}}));
3172#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3173 GET_BLOCK(entry);
3174 GET_BLOCK(exit);
3175 GET_BLOCK(early_return);
3176 GET_BLOCK(escape_1);
3177 GET_BLOCK(escape_1_crit_break);
3178 GET_BLOCK(escape_2);
3179#undef GET_BLOCK
3180 TestOrder order = GetParam();
3181 HInstruction* param_1 = MakeParam(DataType::Type::kBool);
3182 HInstruction* param_2 = MakeParam(DataType::Type::kBool);
3183 HInstruction* param_obj = MakeParam(DataType::Type::kReference);
3184
3185 HInstruction* cls1 = MakeClassLoad();
3186 HInstruction* cls2 = MakeClassLoad();
3187 HInstruction* new_inst1 = MakeNewInstance(cls1);
3188 HInstruction* new_inst2 = MakeNewInstance(cls2);
3189 HInstruction* write_entry1 = MakeIFieldSet(new_inst1, param_obj, MemberOffset(32));
3190 HInstruction* write_entry2 = MakeIFieldSet(new_inst2, param_obj, MemberOffset(32));
3191 HInstruction* if_inst = new (GetAllocator()) HIf(param_1);
3192 entry->AddInstruction(cls1);
3193 entry->AddInstruction(cls2);
3194 entry->AddInstruction(new_inst1);
3195 entry->AddInstruction(new_inst2);
3196 if (order == TestOrder::kSameAsAlloc) {
3197 entry->AddInstruction(write_entry1);
3198 entry->AddInstruction(write_entry2);
3199 } else {
3200 entry->AddInstruction(write_entry2);
3201 entry->AddInstruction(write_entry1);
3202 }
3203 entry->AddInstruction(if_inst);
3204 ManuallyBuildEnvFor(cls1, {});
3205 cls2->CopyEnvironmentFrom(cls1->GetEnvironment());
3206 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
3207 new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment());
3208
3209 early_return->AddInstruction(new (GetAllocator()) HReturnVoid());
3210
3211 HInstruction* escape_1_set = MakeIFieldSet(new_inst2, new_inst1, MemberOffset(32));
3212 HInstruction* escape_1_if = new (GetAllocator()) HIf(param_2);
3213 escape_1->AddInstruction(escape_1_set);
3214 escape_1->AddInstruction(escape_1_if);
3215
3216 escape_1_crit_break->AddInstruction(new (GetAllocator()) HReturnVoid());
3217
3218 HInstruction* escape_2_call = MakeInvoke(DataType::Type::kVoid, {new_inst2});
3219 HInstruction* escape_2_return = new (GetAllocator()) HReturnVoid();
3220 escape_2->AddInstruction(escape_2_call);
3221 escape_2->AddInstruction(escape_2_return);
3222 escape_2_call->CopyEnvironmentFrom(cls1->GetEnvironment());
3223
3224 SetupExit(exit);
3225
3226 // PerformLSE expects this to be empty.
3227 graph_->ClearDominanceInformation();
3228 LOG(INFO) << "Pre LSE " << blks;
3229 PerformLSEWithPartial();
3230 LOG(INFO) << "Post LSE " << blks;
3231
3232 EXPECT_INS_REMOVED(new_inst1);
3233 EXPECT_INS_REMOVED(new_inst2);
3234 EXPECT_INS_REMOVED(write_entry1);
3235 EXPECT_INS_REMOVED(write_entry2);
3236 EXPECT_INS_REMOVED(escape_1_set);
3237 EXPECT_INS_RETAINED(escape_2_call);
3238
3239 HInstruction* obj1_mat =
3240 FindSingleInstruction<HNewInstance>(graph_, escape_1->GetSinglePredecessor());
3241 HInstruction* obj1_set =
3242 FindSingleInstruction<HInstanceFieldSet>(graph_, escape_1->GetSinglePredecessor());
3243 HInstruction* obj2_mat =
3244 FindSingleInstruction<HNewInstance>(graph_, escape_2->GetSinglePredecessor());
3245 HInstruction* obj2_set =
3246 FindSingleInstruction<HInstanceFieldSet>(graph_, escape_2->GetSinglePredecessor());
3247 ASSERT_TRUE(obj1_mat != nullptr);
3248 ASSERT_TRUE(obj2_mat != nullptr);
3249 ASSERT_TRUE(obj1_set != nullptr);
3250 ASSERT_TRUE(obj2_set != nullptr);
3251 EXPECT_INS_EQ(obj1_set->InputAt(0), obj1_mat);
3252 EXPECT_INS_EQ(obj1_set->InputAt(1), param_obj);
3253 EXPECT_INS_EQ(obj2_set->InputAt(0), obj2_mat);
3254 EXPECT_INS_EQ(obj2_set->InputAt(1), obj1_mat);
3255}
3256
3257INSTANTIATE_TEST_SUITE_P(LoadStoreEliminationTest,
3258 OrderDependentTestGroup,
3259 testing::Values(TestOrder::kSameAsAlloc, TestOrder::kReverseOfAlloc));
3260
3261// // ENTRY
3262// // To be moved
3263// obj = new Obj();
3264// obj.foo = 12;
3265// if (parameter_value) {
3266// // LEFT
3267// escape(obj);
3268// } else {}
3269// EXIT
3270TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc) {
3271 VariableSizedHandleScope vshs(Thread::Current());
3272 CreateGraph(&vshs);
3273 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
3274 "exit",
3275 {{"entry", "left"},
3276 {"entry", "right"},
3277 {"right", "breturn"},
3278 {"left", "breturn"},
3279 {"breturn", "exit"}}));
3280#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3281 GET_BLOCK(entry);
3282 GET_BLOCK(exit);
3283 GET_BLOCK(breturn);
3284 GET_BLOCK(left);
3285 GET_BLOCK(right);
3286#undef GET_BLOCK
3287 EnsurePredecessorOrder(breturn, {left, right});
3288 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
3289 HInstruction* c12 = graph_->GetIntConstant(12);
3290
3291 HInstruction* cls = MakeClassLoad();
3292 HInstruction* new_inst = MakeNewInstance(cls);
3293 HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32));
3294 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
3295 entry->AddInstruction(cls);
3296 entry->AddInstruction(new_inst);
3297 entry->AddInstruction(store);
3298 entry->AddInstruction(if_inst);
3299 ManuallyBuildEnvFor(cls, {});
3300 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
3301
3302 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
3303 HInstruction* goto_left = new (GetAllocator()) HGoto();
3304 left->AddInstruction(call_left);
3305 left->AddInstruction(goto_left);
3306 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
3307
3308 right->AddInstruction(new (GetAllocator()) HGoto());
3309
3310 HInstruction* return_exit = new (GetAllocator()) HReturnVoid();
3311 breturn->AddInstruction(return_exit);
3312
3313 SetupExit(exit);
3314
3315 // PerformLSE expects this to be empty.
3316 graph_->ClearDominanceInformation();
3317 LOG(INFO) << "Pre LSE " << blks;
3318 PerformLSEWithPartial();
3319 LOG(INFO) << "Post LSE " << blks;
3320
3321 HNewInstance* moved_new_inst = nullptr;
3322 HInstanceFieldSet* moved_set = nullptr;
3323 std::tie(moved_new_inst, moved_set) =
3324 FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_);
3325 EXPECT_NE(moved_new_inst, nullptr);
3326 EXPECT_NE(moved_set, nullptr);
3327 EXPECT_INS_RETAINED(call_left);
3328 // store removed or moved.
3329 EXPECT_NE(store->GetBlock(), entry);
3330 // New-inst removed or moved.
3331 EXPECT_NE(new_inst->GetBlock(), entry);
3332 EXPECT_INS_EQ(moved_set->InputAt(0), moved_new_inst);
3333 EXPECT_INS_EQ(moved_set->InputAt(1), c12);
3334}
3335
3336// // ENTRY
3337// // To be moved
3338// obj = new Obj();
3339// obj.foo = 12;
3340// if (parameter_value) {
3341// // LEFT
3342// escape(obj);
3343// }
3344// EXIT
3345// int a = obj.foo;
3346// obj.foo = 13;
3347// noescape();
3348// int b = obj.foo;
3349// obj.foo = 14;
3350// noescape();
3351// int c = obj.foo;
3352// obj.foo = 15;
3353// noescape();
3354// return a + b + c
3355TEST_F(LoadStoreEliminationTest, MutiPartialLoadStore) {
3356 VariableSizedHandleScope vshs(Thread::Current());
3357 CreateGraph(&vshs);
3358 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
3359 "exit",
3360 {{"entry", "left"},
3361 {"entry", "right"},
3362 {"right", "breturn"},
3363 {"left", "breturn"},
3364 {"breturn", "exit"}}));
3365#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3366 GET_BLOCK(entry);
3367 GET_BLOCK(exit);
3368 GET_BLOCK(breturn);
3369 GET_BLOCK(left);
3370 GET_BLOCK(right);
3371#undef GET_BLOCK
3372 EnsurePredecessorOrder(breturn, {left, right});
3373 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
3374 HInstruction* c12 = graph_->GetIntConstant(12);
3375 HInstruction* c13 = graph_->GetIntConstant(13);
3376 HInstruction* c14 = graph_->GetIntConstant(14);
3377 HInstruction* c15 = graph_->GetIntConstant(15);
3378
3379 HInstruction* cls = MakeClassLoad();
3380 HInstruction* new_inst = MakeNewInstance(cls);
3381 HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32));
3382 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
3383 entry->AddInstruction(cls);
3384 entry->AddInstruction(new_inst);
3385 entry->AddInstruction(store);
3386 entry->AddInstruction(if_inst);
3387 ManuallyBuildEnvFor(cls, {});
3388 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
3389
3390 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
3391 HInstruction* goto_left = new (GetAllocator()) HGoto();
3392 left->AddInstruction(call_left);
3393 left->AddInstruction(goto_left);
3394 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
3395
3396 HInstruction* goto_right = new (GetAllocator()) HGoto();
3397 right->AddInstruction(goto_right);
3398
3399 HInstruction* a_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3400 HInstruction* a_reset = MakeIFieldSet(new_inst, c13, MemberOffset(32));
3401 HInstruction* a_noescape = MakeInvoke(DataType::Type::kVoid, {});
3402 HInstruction* b_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3403 HInstruction* b_reset = MakeIFieldSet(new_inst, c14, MemberOffset(32));
3404 HInstruction* b_noescape = MakeInvoke(DataType::Type::kVoid, {});
3405 HInstruction* c_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3406 HInstruction* c_reset = MakeIFieldSet(new_inst, c15, MemberOffset(32));
3407 HInstruction* c_noescape = MakeInvoke(DataType::Type::kVoid, {});
3408 HInstruction* add_1_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, a_val, b_val);
3409 HInstruction* add_2_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, c_val, add_1_exit);
3410 HInstruction* return_exit = new (GetAllocator()) HReturn(add_2_exit);
3411 breturn->AddInstruction(a_val);
3412 breturn->AddInstruction(a_reset);
3413 breturn->AddInstruction(a_noescape);
3414 breturn->AddInstruction(b_val);
3415 breturn->AddInstruction(b_reset);
3416 breturn->AddInstruction(b_noescape);
3417 breturn->AddInstruction(c_val);
3418 breturn->AddInstruction(c_reset);
3419 breturn->AddInstruction(c_noescape);
3420 breturn->AddInstruction(add_1_exit);
3421 breturn->AddInstruction(add_2_exit);
3422 breturn->AddInstruction(return_exit);
3423 ManuallyBuildEnvFor(a_noescape, {new_inst, a_val});
3424 ManuallyBuildEnvFor(b_noescape, {new_inst, a_val, b_val});
3425 ManuallyBuildEnvFor(c_noescape, {new_inst, a_val, b_val, c_val});
3426
3427 SetupExit(exit);
3428
3429 // PerformLSE expects this to be empty.
3430 graph_->ClearDominanceInformation();
3431 LOG(INFO) << "Pre LSE " << blks;
3432 PerformLSEWithPartial();
3433 LOG(INFO) << "Post LSE " << blks;
3434
3435 HNewInstance* moved_new_inst = nullptr;
3436 HInstanceFieldSet* moved_set = nullptr;
3437 std::tie(moved_new_inst, moved_set) =
3438 FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, left->GetSinglePredecessor());
3439 std::vector<HPredicatedInstanceFieldGet*> pred_gets;
3440 std::vector<HInstanceFieldSet*> pred_sets;
3441 std::vector<HPhi*> return_phis;
3442 std::tie(return_phis, pred_gets, pred_sets) =
3443 FindAllInstructions<HPhi, HPredicatedInstanceFieldGet, HInstanceFieldSet>(graph_, breturn);
3444 ASSERT_EQ(return_phis.size(), 2u);
3445 HPhi* inst_phi = return_phis[0];
3446 HPhi* val_phi = return_phis[1];
3447 if (inst_phi->GetType() != DataType::Type::kReference) {
3448 std::swap(inst_phi, val_phi);
3449 }
3450 ASSERT_NE(moved_new_inst, nullptr);
3451 EXPECT_INS_EQ(inst_phi->InputAt(0), moved_new_inst);
3452 EXPECT_INS_EQ(inst_phi->InputAt(1), graph_->GetNullConstant());
3453 EXPECT_INS_EQ(val_phi->InputAt(0), graph_->GetIntConstant(0));
3454 EXPECT_EQ(val_phi->InputAt(1), c12);
3455 ASSERT_EQ(pred_gets.size(), 3u);
3456 ASSERT_EQ(pred_gets.size(), pred_sets.size());
3457 std::vector<HInstruction*> set_values{c13, c14, c15};
3458 std::vector<HInstruction*> get_values{val_phi, c13, c14};
3459 ASSERT_NE(moved_set, nullptr);
3460 EXPECT_INS_EQ(moved_set->InputAt(0), moved_new_inst);
3461 EXPECT_INS_EQ(moved_set->InputAt(1), c12);
3462 EXPECT_INS_RETAINED(call_left);
3463 // store removed or moved.
3464 EXPECT_NE(store->GetBlock(), entry);
3465 // New-inst removed or moved.
3466 EXPECT_NE(new_inst->GetBlock(), entry);
3467 for (auto [get, val] : ZipLeft(MakeIterationRange(pred_gets), MakeIterationRange(get_values))) {
3468 EXPECT_INS_EQ(get->GetDefaultValue(), val);
3469 }
3470 for (auto [set, val] : ZipLeft(MakeIterationRange(pred_sets), MakeIterationRange(set_values))) {
3471 EXPECT_INS_EQ(set->InputAt(1), val);
3472 EXPECT_TRUE(set->GetIsPredicatedSet()) << *set;
3473 }
3474 EXPECT_INS_RETAINED(a_noescape);
3475 EXPECT_INS_RETAINED(b_noescape);
3476 EXPECT_INS_RETAINED(c_noescape);
3477 EXPECT_INS_EQ(add_1_exit->InputAt(0), pred_gets[0]);
3478 EXPECT_INS_EQ(add_1_exit->InputAt(1), pred_gets[1]);
3479 EXPECT_INS_EQ(add_2_exit->InputAt(0), pred_gets[2]);
3480
3481 EXPECT_EQ(a_noescape->GetEnvironment()->Size(), 2u);
3482 EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(0), inst_phi);
3483 EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(1), pred_gets[0]);
3484 EXPECT_EQ(b_noescape->GetEnvironment()->Size(), 3u);
3485 EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(0), inst_phi);
3486 EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(1), pred_gets[0]);
3487 EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(2), pred_gets[1]);
3488 EXPECT_EQ(c_noescape->GetEnvironment()->Size(), 4u);
3489 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(0), inst_phi);
3490 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(1), pred_gets[0]);
3491 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(2), pred_gets[1]);
3492 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(3), pred_gets[2]);
3493}
3494
3495// // ENTRY
3496// // To be moved
3497// obj = new Obj();
3498// obj.foo = 12;
3499// int a = obj.foo;
3500// obj.foo = 13;
3501// noescape();
3502// int b = obj.foo;
3503// obj.foo = 14;
3504// noescape();
3505// int c = obj.foo;
3506// obj.foo = 15;
3507// noescape();
3508// if (parameter_value) {
3509// // LEFT
3510// escape(obj);
3511// }
3512// EXIT
3513// return a + b + c + obj.foo
3514TEST_F(LoadStoreEliminationTest, MutiPartialLoadStore2) {
3515 VariableSizedHandleScope vshs(Thread::Current());
3516 CreateGraph(&vshs);
3517 // Need to have an actual entry block since we check env-layout and the way we
3518 // add constants would screw this up otherwise.
3519 AdjacencyListGraph blks(SetupFromAdjacencyList("start",
3520 "exit",
3521 {{"start", "entry"},
3522 {"entry", "left"},
3523 {"entry", "right"},
3524 {"right", "breturn"},
3525 {"left", "breturn"},
3526 {"breturn", "exit"}}));
3527#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3528 GET_BLOCK(start);
3529 GET_BLOCK(entry);
3530 GET_BLOCK(exit);
3531 GET_BLOCK(breturn);
3532 GET_BLOCK(left);
3533 GET_BLOCK(right);
3534#undef GET_BLOCK
3535 EnsurePredecessorOrder(breturn, {left, right});
3536 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
3537 HInstruction* c12 = graph_->GetIntConstant(12);
3538 HInstruction* c13 = graph_->GetIntConstant(13);
3539 HInstruction* c14 = graph_->GetIntConstant(14);
3540 HInstruction* c15 = graph_->GetIntConstant(15);
3541
3542 HInstruction* start_suspend = new (GetAllocator()) HSuspendCheck();
3543 HInstruction* start_goto = new (GetAllocator()) HGoto();
3544
3545 start->AddInstruction(start_suspend);
3546 start->AddInstruction(start_goto);
3547 ManuallyBuildEnvFor(start_suspend, {});
3548
3549 HInstruction* cls = MakeClassLoad();
3550 HInstruction* new_inst = MakeNewInstance(cls);
3551 HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32));
3552
3553 HInstruction* a_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3554 HInstruction* a_reset = MakeIFieldSet(new_inst, c13, MemberOffset(32));
3555 HInstruction* a_noescape = MakeInvoke(DataType::Type::kVoid, {});
3556 HInstruction* b_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3557 HInstruction* b_reset = MakeIFieldSet(new_inst, c14, MemberOffset(32));
3558 HInstruction* b_noescape = MakeInvoke(DataType::Type::kVoid, {});
3559 HInstruction* c_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3560 HInstruction* c_reset = MakeIFieldSet(new_inst, c15, MemberOffset(32));
3561 HInstruction* c_noescape = MakeInvoke(DataType::Type::kVoid, {});
3562 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
3563 entry->AddInstruction(cls);
3564 entry->AddInstruction(new_inst);
3565 entry->AddInstruction(store);
3566 entry->AddInstruction(a_val);
3567 entry->AddInstruction(a_reset);
3568 entry->AddInstruction(a_noescape);
3569 entry->AddInstruction(b_val);
3570 entry->AddInstruction(b_reset);
3571 entry->AddInstruction(b_noescape);
3572 entry->AddInstruction(c_val);
3573 entry->AddInstruction(c_reset);
3574 entry->AddInstruction(c_noescape);
3575 entry->AddInstruction(if_inst);
3576 ManuallyBuildEnvFor(cls, {});
3577 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
3578 ManuallyBuildEnvFor(a_noescape, {new_inst, a_val});
3579 ManuallyBuildEnvFor(b_noescape, {new_inst, a_val, b_val});
3580 ManuallyBuildEnvFor(c_noescape, {new_inst, a_val, b_val, c_val});
3581
3582 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
3583 HInstruction* goto_left = new (GetAllocator()) HGoto();
3584 left->AddInstruction(call_left);
3585 left->AddInstruction(goto_left);
3586 call_left->CopyEnvironmentFrom(c_noescape->GetEnvironment());
3587
3588 HInstruction* goto_right = new (GetAllocator()) HGoto();
3589 right->AddInstruction(goto_right);
3590
3591 HInstruction* val_exit = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3592 HInstruction* add_1_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, a_val, b_val);
3593 HInstruction* add_2_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, c_val, add_1_exit);
3594 HInstruction* add_3_exit =
3595 new (GetAllocator()) HAdd(DataType::Type::kInt32, val_exit, add_2_exit);
3596 HInstruction* return_exit = new (GetAllocator()) HReturn(add_3_exit);
3597 breturn->AddInstruction(val_exit);
3598 breturn->AddInstruction(add_1_exit);
3599 breturn->AddInstruction(add_2_exit);
3600 breturn->AddInstruction(add_3_exit);
3601 breturn->AddInstruction(return_exit);
3602
3603 SetupExit(exit);
3604
3605 // PerformLSE expects this to be empty.
3606 graph_->ClearDominanceInformation();
3607 LOG(INFO) << "Pre LSE " << blks;
3608 PerformLSEWithPartial();
3609 LOG(INFO) << "Post LSE " << blks;
3610
3611 HNewInstance* moved_new_inst = nullptr;
3612 HInstanceFieldSet* moved_set = nullptr;
3613 std::tie(moved_new_inst, moved_set) =
3614 FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, left->GetSinglePredecessor());
3615 std::vector<HPredicatedInstanceFieldGet*> pred_gets;
3616 std::vector<HInstanceFieldSet*> pred_sets;
3617 std::vector<HPhi*> return_phis;
3618 std::tie(return_phis, pred_gets, pred_sets) =
3619 FindAllInstructions<HPhi, HPredicatedInstanceFieldGet, HInstanceFieldSet>(graph_, breturn);
3620 ASSERT_EQ(return_phis.size(), 2u);
3621 HPhi* inst_phi = return_phis[0];
3622 HPhi* val_phi = return_phis[1];
3623 if (inst_phi->GetType() != DataType::Type::kReference) {
3624 std::swap(inst_phi, val_phi);
3625 }
3626 ASSERT_NE(moved_new_inst, nullptr);
3627 EXPECT_INS_EQ(inst_phi->InputAt(0), moved_new_inst);
3628 EXPECT_INS_EQ(inst_phi->InputAt(1), graph_->GetNullConstant());
3629 EXPECT_INS_EQ(val_phi->InputAt(0), graph_->GetIntConstant(0));
3630 EXPECT_INS_EQ(val_phi->InputAt(1), c15);
3631 ASSERT_EQ(pred_gets.size(), 1u);
3632 ASSERT_EQ(pred_sets.size(), 0u);
3633 ASSERT_NE(moved_set, nullptr);
3634 EXPECT_INS_EQ(moved_set->InputAt(0), moved_new_inst);
3635 EXPECT_INS_EQ(moved_set->InputAt(1), c15);
3636 EXPECT_INS_RETAINED(call_left);
3637 // store removed or moved.
3638 EXPECT_NE(store->GetBlock(), entry);
3639 // New-inst removed or moved.
3640 EXPECT_NE(new_inst->GetBlock(), entry);
3641 EXPECT_INS_REMOVED(a_val);
3642 EXPECT_INS_REMOVED(b_val);
3643 EXPECT_INS_REMOVED(c_val);
3644 EXPECT_INS_RETAINED(a_noescape);
3645 EXPECT_INS_RETAINED(b_noescape);
3646 EXPECT_INS_RETAINED(c_noescape);
3647 EXPECT_INS_EQ(add_1_exit->InputAt(0), c12);
3648 EXPECT_INS_EQ(add_1_exit->InputAt(1), c13);
3649 EXPECT_INS_EQ(add_2_exit->InputAt(0), c14);
3650 EXPECT_INS_EQ(add_2_exit->InputAt(1), add_1_exit);
3651 EXPECT_INS_EQ(add_3_exit->InputAt(0), pred_gets[0]);
3652 EXPECT_INS_EQ(pred_gets[0]->GetDefaultValue(), val_phi);
3653 EXPECT_INS_EQ(add_3_exit->InputAt(1), add_2_exit);
3654 EXPECT_EQ(a_noescape->GetEnvironment()->Size(), 2u);
3655 EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(0), graph_->GetNullConstant());
3656 EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(1), c12);
3657 EXPECT_EQ(b_noescape->GetEnvironment()->Size(), 3u);
3658 EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(0), graph_->GetNullConstant());
3659 EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(1), c12);
3660 EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(2), c13);
3661 EXPECT_EQ(c_noescape->GetEnvironment()->Size(), 4u);
3662 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(0), graph_->GetNullConstant());
3663 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(1), c12);
3664 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(2), c13);
3665 EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(3), c14);
3666}
3667
3668// // ENTRY
3669// // To be moved
3670// obj = new Obj();
3671// // Transforms required for creation non-trivial and unimportant
3672// if (parameter_value) {
3673// obj.foo = 10
3674// } else {
3675// obj.foo = 12;
3676// }
3677// if (parameter_value_2) {
3678// escape(obj);
3679// }
3680// EXIT
3681TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc2) {
3682 VariableSizedHandleScope vshs(Thread::Current());
3683 CreateGraph(&vshs);
3684 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
3685 "exit",
3686 {{"entry", "left_set"},
3687 {"entry", "right_set"},
3688 {"left_set", "merge_crit_break"},
3689 {"right_set", "merge_crit_break"},
3690 {"merge_crit_break", "merge"},
3691 {"merge", "escape"},
3692 {"escape", "breturn"},
3693 {"merge", "breturn"},
3694 {"breturn", "exit"}}));
3695#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3696 GET_BLOCK(entry);
3697 GET_BLOCK(exit);
3698 GET_BLOCK(breturn);
3699 GET_BLOCK(left_set);
3700 GET_BLOCK(right_set);
3701 GET_BLOCK(merge);
3702 GET_BLOCK(merge_crit_break);
3703 GET_BLOCK(escape);
3704#undef GET_BLOCK
3705 EnsurePredecessorOrder(breturn, {merge, escape});
3706 EnsurePredecessorOrder(merge_crit_break, {left_set, right_set});
3707 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
3708 HInstruction* bool_value_2 = MakeParam(DataType::Type::kBool);
3709 HInstruction* c10 = graph_->GetIntConstant(10);
3710 HInstruction* c12 = graph_->GetIntConstant(12);
3711
3712 HInstruction* cls = MakeClassLoad();
3713 HInstruction* new_inst = MakeNewInstance(cls);
3714 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
3715 entry->AddInstruction(cls);
3716 entry->AddInstruction(new_inst);
3717 entry->AddInstruction(if_inst);
3718 ManuallyBuildEnvFor(cls, {});
3719 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
3720
3721 HInstruction* store_left = MakeIFieldSet(new_inst, c10, MemberOffset(32));
3722 HInstruction* goto_left = new (GetAllocator()) HGoto();
3723 left_set->AddInstruction(store_left);
3724 left_set->AddInstruction(goto_left);
3725
3726 HInstruction* store_right = MakeIFieldSet(new_inst, c12, MemberOffset(32));
3727 HInstruction* goto_right = new (GetAllocator()) HGoto();
3728 right_set->AddInstruction(store_right);
3729 right_set->AddInstruction(goto_right);
3730
3731 merge_crit_break->AddInstruction(new (GetAllocator()) HGoto());
3732 HInstruction* if_merge = new (GetAllocator()) HIf(bool_value_2);
3733 merge->AddInstruction(if_merge);
3734
3735 HInstruction* escape_instruction = MakeInvoke(DataType::Type::kVoid, { new_inst });
3736 HInstruction* escape_goto = new (GetAllocator()) HGoto();
3737 escape->AddInstruction(escape_instruction);
3738 escape->AddInstruction(escape_goto);
3739 escape_instruction->CopyEnvironmentFrom(cls->GetEnvironment());
3740
3741 HInstruction* return_exit = new (GetAllocator()) HReturnVoid();
3742 breturn->AddInstruction(return_exit);
3743
3744 SetupExit(exit);
3745
3746 // PerformLSE expects this to be empty.
3747 graph_->ClearDominanceInformation();
3748 LOG(INFO) << "Pre LSE " << blks;
3749 PerformLSEWithPartial();
3750 LOG(INFO) << "Post LSE " << blks;
3751
3752 HNewInstance* moved_new_inst;
3753 HInstanceFieldSet* moved_set;
3754 std::tie(moved_new_inst, moved_set) =
3755 FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_);
3756 HPhi* merge_phi = FindSingleInstruction<HPhi>(graph_, merge_crit_break);
3757 HPhi* alloc_phi = FindSingleInstruction<HPhi>(graph_, breturn);
3758 EXPECT_INS_EQ(moved_new_inst, moved_set->InputAt(0));
3759 ASSERT_NE(alloc_phi, nullptr);
3760 EXPECT_EQ(alloc_phi->InputAt(0), graph_->GetNullConstant())
3761 << alloc_phi->GetBlock()->GetPredecessors()[0]->GetBlockId() << " " << *alloc_phi;
3762 EXPECT_TRUE(alloc_phi->InputAt(1)->IsNewInstance()) << *alloc_phi;
3763 ASSERT_NE(merge_phi, nullptr);
3764 EXPECT_EQ(merge_phi->InputCount(), 2u);
3765 EXPECT_INS_EQ(merge_phi->InputAt(0), c10);
3766 EXPECT_INS_EQ(merge_phi->InputAt(1), c12);
3767 EXPECT_TRUE(merge_phi->GetUses().HasExactlyOneElement());
3768 EXPECT_INS_EQ(merge_phi->GetUses().front().GetUser(), moved_set);
3769 EXPECT_INS_RETAINED(escape_instruction);
3770 EXPECT_INS_EQ(escape_instruction->InputAt(0), moved_new_inst);
3771 // store removed or moved.
3772 EXPECT_NE(store_left->GetBlock(), left_set);
3773 EXPECT_NE(store_right->GetBlock(), left_set);
3774 // New-inst removed or moved.
3775 EXPECT_NE(new_inst->GetBlock(), entry);
3776}
3777
3778// // ENTRY
3779// // To be moved
3780// obj = new Obj();
3781// switch(args) {
3782// default:
3783// return obj.a;
3784// case b:
3785// obj.a = 5; break;
3786// case c:
3787// obj.b = 4; break;
3788// }
3789// escape(obj);
3790// return obj.a;
3791// EXIT
3792TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc3) {
3793 VariableSizedHandleScope vshs(Thread::Current());
3794 CreateGraph(&vshs);
3795 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
3796 "exit",
3797 {{"entry", "early_return"},
3798 {"entry", "set_one"},
3799 {"entry", "set_two"},
3800 {"early_return", "exit"},
3801 {"set_one", "escape"},
3802 {"set_two", "escape"},
3803 {"escape", "exit"}}));
3804#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3805 GET_BLOCK(entry);
3806 GET_BLOCK(exit);
3807 GET_BLOCK(escape);
3808 GET_BLOCK(early_return);
3809 GET_BLOCK(set_one);
3810 GET_BLOCK(set_two);
3811#undef GET_BLOCK
3812 EnsurePredecessorOrder(escape, {set_one, set_two});
3813 HInstruction* int_val = MakeParam(DataType::Type::kInt32);
3814 HInstruction* c0 = graph_->GetIntConstant(0);
3815 HInstruction* c4 = graph_->GetIntConstant(4);
3816 HInstruction* c5 = graph_->GetIntConstant(5);
3817
3818 HInstruction* cls = MakeClassLoad();
3819 HInstruction* new_inst = MakeNewInstance(cls);
3820 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val);
3821 entry->AddInstruction(cls);
3822 entry->AddInstruction(new_inst);
3823 entry->AddInstruction(switch_inst);
3824 ManuallyBuildEnvFor(cls, {});
3825 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
3826
3827 HInstruction* store_one = MakeIFieldSet(new_inst, c4, MemberOffset(32));
3828 HInstruction* goto_one = new (GetAllocator()) HGoto();
3829 set_one->AddInstruction(store_one);
3830 set_one->AddInstruction(goto_one);
3831
3832 HInstruction* store_two = MakeIFieldSet(new_inst, c5, MemberOffset(32));
3833 HInstruction* goto_two = new (GetAllocator()) HGoto();
3834 set_two->AddInstruction(store_two);
3835 set_two->AddInstruction(goto_two);
3836
3837 HInstruction* read_early = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3838 HInstruction* return_early = new (GetAllocator()) HReturn(read_early);
3839 early_return->AddInstruction(read_early);
3840 early_return->AddInstruction(return_early);
3841
3842 HInstruction* escape_instruction = MakeInvoke(DataType::Type::kVoid, { new_inst });
3843 HInstruction* read_escape = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3844 HInstruction* return_escape = new (GetAllocator()) HReturn(read_escape);
3845 escape->AddInstruction(escape_instruction);
3846 escape->AddInstruction(read_escape);
3847 escape->AddInstruction(return_escape);
3848 escape_instruction->CopyEnvironmentFrom(cls->GetEnvironment());
3849
3850 SetupExit(exit);
3851
3852 // PerformLSE expects this to be empty.
3853 graph_->ClearDominanceInformation();
3854 LOG(INFO) << "Pre LSE " << blks;
3855 PerformLSEWithPartial();
3856 LOG(INFO) << "Post LSE " << blks;
3857
3858 // Each escaping switch path gets its own materialization block.
3859 // Blocks:
3860 // early_return(5) -> [exit(4)]
3861 // entry(3) -> [early_return(5), <Unnamed>(9), <Unnamed>(10)]
3862 // escape(8) -> [exit(4)]
3863 // exit(4) -> []
3864 // set_one(6) -> [escape(8)]
3865 // set_two(7) -> [escape(8)]
3866 // <Unnamed>(10) -> [set_two(7)]
3867 // <Unnamed>(9) -> [set_one(6)]
3868 HBasicBlock* materialize_one = set_one->GetSinglePredecessor();
3869 HBasicBlock* materialize_two = set_two->GetSinglePredecessor();
3870 HNewInstance* materialization_ins_one =
3871 FindSingleInstruction<HNewInstance>(graph_, materialize_one);
3872 HNewInstance* materialization_ins_two =
3873 FindSingleInstruction<HNewInstance>(graph_, materialize_two);
3874 HPhi* new_phi = FindSingleInstruction<HPhi>(graph_, escape);
3875 EXPECT_NE(materialization_ins_one, nullptr);
3876 EXPECT_NE(materialization_ins_two, nullptr);
3877 EXPECT_EQ(materialization_ins_one, new_phi->InputAt(0))
3878 << *materialization_ins_one << " vs " << *new_phi;
3879 EXPECT_EQ(materialization_ins_two, new_phi->InputAt(1))
3880 << *materialization_ins_two << " vs " << *new_phi;
3881
3882 EXPECT_INS_RETAINED(escape_instruction);
3883 EXPECT_INS_RETAINED(read_escape);
3884 EXPECT_EQ(read_escape->InputAt(0), new_phi) << *new_phi << " vs " << *read_escape->InputAt(0);
3885 EXPECT_EQ(store_one->InputAt(0), materialization_ins_one);
3886 EXPECT_EQ(store_two->InputAt(0), materialization_ins_two);
3887 EXPECT_EQ(escape_instruction->InputAt(0), new_phi);
3888 EXPECT_INS_REMOVED(read_early);
3889 EXPECT_EQ(return_early->InputAt(0), c0);
3890}
3891
3892// // ENTRY
3893// // To be moved
3894// obj = new Obj();
3895// switch(args) {
3896// case a:
3897// // set_one_and_escape
3898// obj.a = 5;
3899// escape(obj);
3900// // FALLTHROUGH
3901// case c:
3902// // set_two
3903// obj.a = 4; break;
3904// default:
3905// return obj.a;
3906// }
3907// escape(obj);
3908// return obj.a;
3909// EXIT
3910TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc4) {
3911 VariableSizedHandleScope vshs(Thread::Current());
3912 CreateGraph(&vshs);
3913 // Break the critical edge between entry and set_two with the
3914 // set_two_critical_break node. Graph simplification would do this for us if
3915 // we didn't do it manually. This way we have a nice-name for debugging and
3916 // testing.
3917 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
3918 "exit",
3919 {{"entry", "early_return"},
3920 {"entry", "set_one_and_escape"},
3921 {"entry", "set_two_critical_break"},
3922 {"set_two_critical_break", "set_two"},
3923 {"early_return", "exit"},
3924 {"set_one_and_escape", "set_two"},
3925 {"set_two", "escape"},
3926 {"escape", "exit"}}));
3927#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
3928 GET_BLOCK(entry);
3929 GET_BLOCK(exit);
3930 GET_BLOCK(escape);
3931 GET_BLOCK(early_return);
3932 GET_BLOCK(set_one_and_escape);
3933 GET_BLOCK(set_two);
3934 GET_BLOCK(set_two_critical_break);
3935#undef GET_BLOCK
3936 EnsurePredecessorOrder(set_two, {set_one_and_escape, set_two_critical_break});
3937 HInstruction* int_val = MakeParam(DataType::Type::kInt32);
3938 HInstruction* c0 = graph_->GetIntConstant(0);
3939 HInstruction* c4 = graph_->GetIntConstant(4);
3940 HInstruction* c5 = graph_->GetIntConstant(5);
3941
3942 HInstruction* cls = MakeClassLoad();
3943 HInstruction* new_inst = MakeNewInstance(cls);
3944 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val);
3945 entry->AddInstruction(cls);
3946 entry->AddInstruction(new_inst);
3947 entry->AddInstruction(switch_inst);
3948 ManuallyBuildEnvFor(cls, {});
3949 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
3950
3951 HInstruction* store_one = MakeIFieldSet(new_inst, c4, MemberOffset(32));
3952 HInstruction* escape_one = MakeInvoke(DataType::Type::kVoid, { new_inst });
3953 HInstruction* goto_one = new (GetAllocator()) HGoto();
3954 set_one_and_escape->AddInstruction(store_one);
3955 set_one_and_escape->AddInstruction(escape_one);
3956 set_one_and_escape->AddInstruction(goto_one);
3957 escape_one->CopyEnvironmentFrom(cls->GetEnvironment());
3958
3959 HInstruction* goto_crit_break = new (GetAllocator()) HGoto();
3960 set_two_critical_break->AddInstruction(goto_crit_break);
3961
3962 HInstruction* store_two = MakeIFieldSet(new_inst, c5, MemberOffset(32));
3963 HInstruction* goto_two = new (GetAllocator()) HGoto();
3964 set_two->AddInstruction(store_two);
3965 set_two->AddInstruction(goto_two);
3966
3967 HInstruction* read_early = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3968 HInstruction* return_early = new (GetAllocator()) HReturn(read_early);
3969 early_return->AddInstruction(read_early);
3970 early_return->AddInstruction(return_early);
3971
3972 HInstruction* escape_instruction = MakeInvoke(DataType::Type::kVoid, { new_inst });
3973 HInstruction* read_escape = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
3974 HInstruction* return_escape = new (GetAllocator()) HReturn(read_escape);
3975 escape->AddInstruction(escape_instruction);
3976 escape->AddInstruction(read_escape);
3977 escape->AddInstruction(return_escape);
3978 escape_instruction->CopyEnvironmentFrom(cls->GetEnvironment());
3979
3980 SetupExit(exit);
3981
3982 // PerformLSE expects this to be empty.
3983 graph_->ClearDominanceInformation();
3984 LOG(INFO) << "Pre LSE " << blks;
3985 PerformLSEWithPartial();
3986 LOG(INFO) << "Post LSE " << blks;
3987
3988 EXPECT_INS_REMOVED(read_early);
3989 EXPECT_EQ(return_early->InputAt(0), c0);
3990 // Each escaping switch path gets its own materialization block.
3991 // Blocks:
3992 // early_return(5) -> [exit(4)]
3993 // entry(3) -> [early_return(5), <Unnamed>(10), <Unnamed>(11)]
3994 // escape(9) -> [exit(4)]
3995 // exit(4) -> []
3996 // set_one_and_escape(6) -> [set_two(8)]
3997 // set_two(8) -> [escape(9)]
3998 // set_two_critical_break(7) -> [set_two(8)]
3999 // <Unnamed>(11) -> [set_two_critical_break(7)]
4000 // <Unnamed>(10) -> [set_one_and_escape(6)]
4001 HBasicBlock* materialize_one = set_one_and_escape->GetSinglePredecessor();
4002 HBasicBlock* materialize_two = set_two_critical_break->GetSinglePredecessor();
4003 HNewInstance* materialization_ins_one =
4004 FindSingleInstruction<HNewInstance>(graph_, materialize_one);
4005 HNewInstance* materialization_ins_two =
4006 FindSingleInstruction<HNewInstance>(graph_, materialize_two);
4007 HPhi* new_phi = FindSingleInstruction<HPhi>(graph_, set_two);
4008 ASSERT_NE(new_phi, nullptr);
4009 ASSERT_NE(materialization_ins_one, nullptr);
4010 ASSERT_NE(materialization_ins_two, nullptr);
4011 EXPECT_INS_EQ(materialization_ins_one, new_phi->InputAt(0));
4012 EXPECT_INS_EQ(materialization_ins_two, new_phi->InputAt(1));
4013
4014 EXPECT_INS_EQ(store_one->InputAt(0), materialization_ins_one);
4015 EXPECT_INS_EQ(store_two->InputAt(0), new_phi) << *store_two << " vs " << *new_phi;
4016 EXPECT_INS_EQ(escape_instruction->InputAt(0), new_phi);
4017 EXPECT_INS_RETAINED(escape_one);
4018 EXPECT_INS_EQ(escape_one->InputAt(0), materialization_ins_one);
4019 EXPECT_INS_RETAINED(escape_instruction);
4020 EXPECT_INS_RETAINED(read_escape);
4021 EXPECT_EQ(read_escape->InputAt(0), new_phi) << *new_phi << " vs " << *read_escape->InputAt(0);
4022}
4023
4024// // ENTRY
4025// // To be moved
4026// obj = new Obj();
4027// switch(args) {
4028// case a:
4029// // set_one
4030// obj.a = 5;
4031// // nb passthrough
4032// case c:
4033// // set_two_and_escape
4034// obj.a += 4;
4035// escape(obj);
4036// break;
4037// default:
4038// obj.a = 10;
4039// }
4040// return obj.a;
4041// EXIT
4042TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc5) {
4043 VariableSizedHandleScope vshs(Thread::Current());
4044 CreateGraph(&vshs);
4045 // Break the critical edge between entry and set_two with the
4046 // set_two_critical_break node. Graph simplification would do this for us if
4047 // we didn't do it manually. This way we have a nice-name for debugging and
4048 // testing.
4049 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4050 "exit",
4051 {{"entry", "set_noescape"},
4052 {"entry", "set_one"},
4053 {"entry", "set_two_critical_break"},
4054 {"set_two_critical_break", "set_two_and_escape"},
4055 {"set_noescape", "breturn"},
4056 {"set_one", "set_two_and_escape"},
4057 {"set_two_and_escape", "breturn"},
4058 {"breturn", "exit"}}));
4059#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4060 GET_BLOCK(entry);
4061 GET_BLOCK(exit);
4062 GET_BLOCK(breturn);
4063 GET_BLOCK(set_noescape);
4064 GET_BLOCK(set_one);
4065 GET_BLOCK(set_two_and_escape);
4066 GET_BLOCK(set_two_critical_break);
4067#undef GET_BLOCK
4068 EnsurePredecessorOrder(set_two_and_escape, {set_one, set_two_critical_break});
4069 EnsurePredecessorOrder(breturn, {set_two_and_escape, set_noescape});
4070 HInstruction* int_val = MakeParam(DataType::Type::kInt32);
4071 HInstruction* c0 = graph_->GetIntConstant(0);
4072 HInstruction* c4 = graph_->GetIntConstant(4);
4073 HInstruction* c5 = graph_->GetIntConstant(5);
4074 HInstruction* c10 = graph_->GetIntConstant(10);
4075
4076 HInstruction* cls = MakeClassLoad();
4077 HInstruction* new_inst = MakeNewInstance(cls);
4078 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val);
4079 entry->AddInstruction(cls);
4080 entry->AddInstruction(new_inst);
4081 entry->AddInstruction(switch_inst);
4082 ManuallyBuildEnvFor(cls, {});
4083 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4084
4085 HInstruction* store_one = MakeIFieldSet(new_inst, c5, MemberOffset(32));
4086 HInstruction* goto_one = new (GetAllocator()) HGoto();
4087 set_one->AddInstruction(store_one);
4088 set_one->AddInstruction(goto_one);
4089
4090 HInstruction* goto_crit_break = new (GetAllocator()) HGoto();
4091 set_two_critical_break->AddInstruction(goto_crit_break);
4092
4093 HInstruction* get_two = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4094 HInstruction* add_two = new (GetAllocator()) HAdd(DataType::Type::kInt32, get_two, c4);
4095 HInstruction* store_two = MakeIFieldSet(new_inst, add_two, MemberOffset(32));
4096 HInstruction* escape_two = MakeInvoke(DataType::Type::kVoid, {new_inst});
4097 HInstruction* goto_two = new (GetAllocator()) HGoto();
4098 set_two_and_escape->AddInstruction(get_two);
4099 set_two_and_escape->AddInstruction(add_two);
4100 set_two_and_escape->AddInstruction(store_two);
4101 set_two_and_escape->AddInstruction(escape_two);
4102 set_two_and_escape->AddInstruction(goto_two);
4103 escape_two->CopyEnvironmentFrom(cls->GetEnvironment());
4104
4105 HInstruction* store_noescape = MakeIFieldSet(new_inst, c10, MemberOffset(32));
4106 HInstruction* goto_noescape = new (GetAllocator()) HGoto();
4107 set_noescape->AddInstruction(store_noescape);
4108 set_noescape->AddInstruction(goto_noescape);
4109
4110 HInstruction* read_breturn = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4111 HInstruction* return_breturn = new (GetAllocator()) HReturn(read_breturn);
4112 breturn->AddInstruction(read_breturn);
4113 breturn->AddInstruction(return_breturn);
4114
4115 SetupExit(exit);
4116
4117 // PerformLSE expects this to be empty.
4118 graph_->ClearDominanceInformation();
4119 LOG(INFO) << "Pre LSE " << blks;
4120 PerformLSEWithPartial();
4121 LOG(INFO) << "Post LSE " << blks;
4122
4123 // Normal LSE can get rid of these two.
4124 EXPECT_INS_REMOVED(store_one);
4125 EXPECT_INS_REMOVED(get_two);
4126 EXPECT_INS_RETAINED(add_two);
4127 EXPECT_TRUE(add_two->InputAt(0)->IsPhi());
4128 EXPECT_INS_EQ(add_two->InputAt(0)->InputAt(0), c5);
4129 EXPECT_INS_EQ(add_two->InputAt(0)->InputAt(1), c0);
4130 EXPECT_INS_EQ(add_two->InputAt(1), c4);
4131
4132 HBasicBlock* materialize_one = set_one->GetSinglePredecessor();
4133 HBasicBlock* materialize_two = set_two_critical_break->GetSinglePredecessor();
4134 HNewInstance* materialization_ins_one =
4135 FindSingleInstruction<HNewInstance>(graph_, materialize_one);
4136 HNewInstance* materialization_ins_two =
4137 FindSingleInstruction<HNewInstance>(graph_, materialize_two);
4138 std::vector<HPhi*> phis;
4139 std::tie(phis) = FindAllInstructions<HPhi>(graph_, set_two_and_escape);
4140 HPhi* new_phi = FindOrNull(
4141 phis.begin(), phis.end(), [&](auto p) { return p->GetType() == DataType::Type::kReference; });
4142 ASSERT_NE(new_phi, nullptr);
4143 ASSERT_NE(materialization_ins_one, nullptr);
4144 ASSERT_NE(materialization_ins_two, nullptr);
4145 EXPECT_INS_EQ(materialization_ins_one, new_phi->InputAt(0));
4146 EXPECT_INS_EQ(materialization_ins_two, new_phi->InputAt(1));
4147
4148 HPredicatedInstanceFieldGet* pred_get =
4149 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
4150 EXPECT_TRUE(pred_get->GetTarget()->IsPhi());
4151 EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(0), new_phi);
4152 EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(1), graph_->GetNullConstant());
4153
4154 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), c0);
4155 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), c10);
Alex Light86fe9b82020-11-16 16:54:01 +00004156}
4157
4158// // ENTRY
4159// obj = new Obj();
4160// if (parameter_value) {
4161// // LEFT
4162// // DO NOT ELIMINATE
4163// obj.field = 1;
4164// escape(obj);
4165// return obj.field;
4166// } else {
4167// // RIGHT
4168// // ELIMINATE
4169// obj.field = 2;
4170// return obj.field;
4171// }
4172// EXIT
4173TEST_F(LoadStoreEliminationTest, PartialLoadElimination3) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004174 VariableSizedHandleScope vshs(Thread::Current());
4175 CreateGraph(&vshs);
Alex Light86fe9b82020-11-16 16:54:01 +00004176 AdjacencyListGraph blks(SetupFromAdjacencyList(
4177 "entry",
4178 "exit",
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004179 {{"entry", "left"}, {"entry", "right"}, {"left", "exit"}, {"right", "exit"}}));
Alex Light86fe9b82020-11-16 16:54:01 +00004180#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4181 GET_BLOCK(entry);
4182 GET_BLOCK(exit);
4183 GET_BLOCK(left);
4184 GET_BLOCK(right);
4185#undef GET_BLOCK
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004186 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
Alex Light86fe9b82020-11-16 16:54:01 +00004187 HInstruction* c1 = graph_->GetIntConstant(1);
4188 HInstruction* c2 = graph_->GetIntConstant(2);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004189
4190 HInstruction* cls = MakeClassLoad();
4191 HInstruction* new_inst = MakeNewInstance(cls);
Alex Light86fe9b82020-11-16 16:54:01 +00004192 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
Alex Light86fe9b82020-11-16 16:54:01 +00004193 entry->AddInstruction(cls);
4194 entry->AddInstruction(new_inst);
4195 entry->AddInstruction(if_inst);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004196 ManuallyBuildEnvFor(cls, {});
Alex Light86fe9b82020-11-16 16:54:01 +00004197 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4198
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004199 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
4200 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
4201 HInstruction* read_left = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004202 HInstruction* return_left = new (GetAllocator()) HReturn(read_left);
Alex Light86fe9b82020-11-16 16:54:01 +00004203 left->AddInstruction(write_left);
4204 left->AddInstruction(call_left);
4205 left->AddInstruction(read_left);
4206 left->AddInstruction(return_left);
4207 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
4208
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004209 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4210 HInstruction* read_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004211 HInstruction* return_right = new (GetAllocator()) HReturn(read_right);
4212 right->AddInstruction(write_right);
4213 right->AddInstruction(read_right);
4214 right->AddInstruction(return_right);
4215
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004216 SetupExit(exit);
4217
Alex Light86fe9b82020-11-16 16:54:01 +00004218 // PerformLSE expects this to be empty.
4219 graph_->ClearDominanceInformation();
4220 PerformLSE();
4221
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004222 EXPECT_INS_REMOVED(read_right);
4223 EXPECT_INS_REMOVED(write_right);
4224 EXPECT_INS_RETAINED(write_left);
4225 EXPECT_INS_RETAINED(call_left);
4226 EXPECT_INS_RETAINED(read_left);
Alex Light86fe9b82020-11-16 16:54:01 +00004227}
4228
4229// // ENTRY
4230// obj = new Obj();
4231// if (parameter_value) {
4232// // LEFT
4233// // DO NOT ELIMINATE
4234// obj.field = 1;
4235// while (true) {
4236// bool esc = escape(obj);
4237// // DO NOT ELIMINATE
4238// obj.field = 3;
4239// if (esc) break;
4240// }
4241// // ELIMINATE.
4242// return obj.field;
4243// } else {
4244// // RIGHT
4245// // ELIMINATE
4246// obj.field = 2;
4247// return obj.field;
4248// }
4249// EXIT
4250TEST_F(LoadStoreEliminationTest, PartialLoadElimination4) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004251 VariableSizedHandleScope vshs(Thread::Current());
4252 CreateGraph(&vshs);
Alex Light86fe9b82020-11-16 16:54:01 +00004253 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4254 "exit",
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004255 {{"entry", "entry_post"},
4256 {"entry_post", "right"},
4257 {"right", "exit"},
4258 {"entry_post", "left_pre"},
4259 {"left_pre", "left_loop"},
4260 {"left_loop", "left_loop"},
4261 {"left_loop", "left_finish"},
4262 {"left_finish", "exit"}}));
Alex Light86fe9b82020-11-16 16:54:01 +00004263#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4264 GET_BLOCK(entry);
4265 GET_BLOCK(entry_post);
4266 GET_BLOCK(exit);
4267 GET_BLOCK(left_pre);
4268 GET_BLOCK(left_loop);
4269 GET_BLOCK(left_finish);
4270 GET_BLOCK(right);
4271#undef GET_BLOCK
4272 // Left-loops first successor is the break.
4273 if (left_loop->GetSuccessors()[0] != left_finish) {
4274 left_loop->SwapSuccessors();
4275 }
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004276 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
Alex Light86fe9b82020-11-16 16:54:01 +00004277 HInstruction* c1 = graph_->GetIntConstant(1);
4278 HInstruction* c2 = graph_->GetIntConstant(2);
4279 HInstruction* c3 = graph_->GetIntConstant(3);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004280
4281 HInstruction* cls = MakeClassLoad();
4282 HInstruction* new_inst = MakeNewInstance(cls);
Alex Light86fe9b82020-11-16 16:54:01 +00004283 HInstruction* goto_entry = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00004284 entry->AddInstruction(cls);
4285 entry->AddInstruction(new_inst);
4286 entry->AddInstruction(goto_entry);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004287 ManuallyBuildEnvFor(cls, {});
Alex Light86fe9b82020-11-16 16:54:01 +00004288 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4289
4290 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
4291 entry_post->AddInstruction(if_inst);
4292
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004293 HInstruction* write_left_pre = MakeIFieldSet(new_inst, c1, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004294 HInstruction* goto_left_pre = new (GetAllocator()) HGoto();
4295 left_pre->AddInstruction(write_left_pre);
4296 left_pre->AddInstruction(goto_left_pre);
4297
4298 HInstruction* suspend_left_loop = new (GetAllocator()) HSuspendCheck();
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004299 HInstruction* call_left_loop = MakeInvoke(DataType::Type::kBool, { new_inst });
4300 HInstruction* write_left_loop = MakeIFieldSet(new_inst, c3, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004301 HInstruction* if_left_loop = new (GetAllocator()) HIf(call_left_loop);
Alex Light86fe9b82020-11-16 16:54:01 +00004302 left_loop->AddInstruction(suspend_left_loop);
4303 left_loop->AddInstruction(call_left_loop);
4304 left_loop->AddInstruction(write_left_loop);
4305 left_loop->AddInstruction(if_left_loop);
4306 suspend_left_loop->CopyEnvironmentFrom(cls->GetEnvironment());
4307 call_left_loop->CopyEnvironmentFrom(cls->GetEnvironment());
4308
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004309 HInstruction* read_left_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004310 HInstruction* return_left_end = new (GetAllocator()) HReturn(read_left_end);
4311 left_finish->AddInstruction(read_left_end);
4312 left_finish->AddInstruction(return_left_end);
4313
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004314 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4315 HInstruction* read_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004316 HInstruction* return_right = new (GetAllocator()) HReturn(read_right);
4317 right->AddInstruction(write_right);
4318 right->AddInstruction(read_right);
4319 right->AddInstruction(return_right);
4320
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004321 SetupExit(exit);
4322
Alex Light86fe9b82020-11-16 16:54:01 +00004323 // PerformLSE expects this to be empty.
4324 graph_->ClearDominanceInformation();
4325 PerformLSE();
4326
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004327 EXPECT_INS_RETAINED(write_left_pre);
4328 EXPECT_INS_REMOVED(read_right);
4329 EXPECT_INS_REMOVED(write_right);
4330 EXPECT_INS_RETAINED(write_left_loop);
4331 EXPECT_INS_RETAINED(call_left_loop);
4332 EXPECT_INS_REMOVED(read_left_end);
Alex Light86fe9b82020-11-16 16:54:01 +00004333}
4334
4335// // ENTRY
4336// obj = new Obj();
4337// if (parameter_value) {
4338// // LEFT
4339// // DO NOT ELIMINATE
4340// escape(obj);
4341// obj.field = 1;
4342// } else {
4343// // RIGHT
4344// // obj hasn't escaped so it's invisible.
4345// // ELIMINATE
4346// obj.field = 2;
4347// noescape();
4348// }
4349// EXIT
4350// ELIMINATE
4351// return obj.field
4352TEST_F(LoadStoreEliminationTest, PartialLoadElimination5) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004353 VariableSizedHandleScope vshs(Thread::Current());
4354 CreateGraph(&vshs);
Alex Light86fe9b82020-11-16 16:54:01 +00004355 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4356 "exit",
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004357 {{"entry", "left"},
4358 {"entry", "right"},
4359 {"left", "breturn"},
4360 {"right", "breturn"},
4361 {"breturn", "exit"}}));
Alex Light86fe9b82020-11-16 16:54:01 +00004362#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4363 GET_BLOCK(entry);
4364 GET_BLOCK(exit);
4365 GET_BLOCK(breturn);
4366 GET_BLOCK(left);
4367 GET_BLOCK(right);
4368#undef GET_BLOCK
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004369 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
Alex Light86fe9b82020-11-16 16:54:01 +00004370 HInstruction* c1 = graph_->GetIntConstant(1);
4371 HInstruction* c2 = graph_->GetIntConstant(2);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004372
4373 HInstruction* cls = MakeClassLoad();
4374 HInstruction* new_inst = MakeNewInstance(cls);
Alex Light86fe9b82020-11-16 16:54:01 +00004375 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
Alex Light86fe9b82020-11-16 16:54:01 +00004376 entry->AddInstruction(cls);
4377 entry->AddInstruction(new_inst);
4378 entry->AddInstruction(if_inst);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004379 ManuallyBuildEnvFor(cls, {});
Alex Light86fe9b82020-11-16 16:54:01 +00004380 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4381
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004382 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
4383 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004384 HInstruction* goto_left = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00004385 left->AddInstruction(call_left);
4386 left->AddInstruction(write_left);
4387 left->AddInstruction(goto_left);
4388 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
4389
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004390 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4391 HInstruction* call_right = MakeInvoke(DataType::Type::kVoid, {});
Alex Light86fe9b82020-11-16 16:54:01 +00004392 HInstruction* goto_right = new (GetAllocator()) HGoto();
4393 right->AddInstruction(write_right);
4394 right->AddInstruction(call_right);
4395 right->AddInstruction(goto_right);
4396 call_right->CopyEnvironmentFrom(cls->GetEnvironment());
4397
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004398 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004399 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
4400 breturn->AddInstruction(read_bottom);
4401 breturn->AddInstruction(return_exit);
4402
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004403 SetupExit(exit);
4404
Alex Light86fe9b82020-11-16 16:54:01 +00004405 // PerformLSE expects this to be empty.
4406 graph_->ClearDominanceInformation();
4407 PerformLSE();
4408
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004409 EXPECT_INS_REMOVED(read_bottom);
4410 EXPECT_INS_REMOVED(write_right);
4411 EXPECT_INS_RETAINED(write_left);
4412 EXPECT_INS_RETAINED(call_left);
4413 EXPECT_INS_RETAINED(call_right);
Alex Light86fe9b82020-11-16 16:54:01 +00004414}
4415
4416// // ENTRY
4417// obj = new Obj();
4418// // Eliminate this one. Object hasn't escaped yet so it's safe.
4419// obj.field = 3;
4420// noescape();
4421// if (parameter_value) {
4422// // LEFT
4423// // DO NOT ELIMINATE
4424// obj.field = 5;
4425// escape(obj);
4426// obj.field = 1;
4427// } else {
4428// // RIGHT
4429// // ELIMINATE
4430// obj.field = 2;
4431// }
4432// EXIT
4433// ELIMINATE
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004434// return obj.fid
Alex Light86fe9b82020-11-16 16:54:01 +00004435TEST_F(LoadStoreEliminationTest, PartialLoadElimination6) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004436 VariableSizedHandleScope vshs(Thread::Current());
4437 CreateGraph(&vshs);
Alex Light86fe9b82020-11-16 16:54:01 +00004438 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4439 "exit",
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004440 {{"entry", "left"},
4441 {"entry", "right"},
4442 {"left", "breturn"},
4443 {"right", "breturn"},
4444 {"breturn", "exit"}}));
Alex Light86fe9b82020-11-16 16:54:01 +00004445#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4446 GET_BLOCK(entry);
4447 GET_BLOCK(exit);
4448 GET_BLOCK(breturn);
4449 GET_BLOCK(left);
4450 GET_BLOCK(right);
4451#undef GET_BLOCK
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004452 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
Alex Light86fe9b82020-11-16 16:54:01 +00004453 HInstruction* c1 = graph_->GetIntConstant(1);
4454 HInstruction* c2 = graph_->GetIntConstant(2);
4455 HInstruction* c3 = graph_->GetIntConstant(3);
4456 HInstruction* c5 = graph_->GetIntConstant(5);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004457
4458 HInstruction* cls = MakeClassLoad();
4459 HInstruction* new_inst = MakeNewInstance(cls);
4460 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
4461 HInstruction* call_entry = MakeInvoke(DataType::Type::kVoid, {});
Alex Light86fe9b82020-11-16 16:54:01 +00004462 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
Alex Light86fe9b82020-11-16 16:54:01 +00004463 entry->AddInstruction(cls);
4464 entry->AddInstruction(new_inst);
4465 entry->AddInstruction(write_entry);
4466 entry->AddInstruction(call_entry);
4467 entry->AddInstruction(if_inst);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004468 ManuallyBuildEnvFor(cls, {});
Alex Light86fe9b82020-11-16 16:54:01 +00004469 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4470 call_entry->CopyEnvironmentFrom(cls->GetEnvironment());
4471
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004472 HInstruction* write_left_start = MakeIFieldSet(new_inst, c5, MemberOffset(32));
4473 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
4474 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004475 HInstruction* goto_left = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00004476 left->AddInstruction(write_left_start);
4477 left->AddInstruction(call_left);
4478 left->AddInstruction(write_left);
4479 left->AddInstruction(goto_left);
4480 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
4481
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004482 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004483 HInstruction* goto_right = new (GetAllocator()) HGoto();
4484 right->AddInstruction(write_right);
4485 right->AddInstruction(goto_right);
4486
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004487 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004488 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
4489 breturn->AddInstruction(read_bottom);
4490 breturn->AddInstruction(return_exit);
4491
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004492 SetupExit(exit);
4493
Alex Light86fe9b82020-11-16 16:54:01 +00004494 // PerformLSE expects this to be empty.
4495 graph_->ClearDominanceInformation();
4496 PerformLSE();
4497
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004498 EXPECT_INS_REMOVED(read_bottom);
4499 EXPECT_INS_REMOVED(write_right);
4500 EXPECT_INS_REMOVED(write_entry);
4501 EXPECT_INS_RETAINED(write_left_start);
4502 EXPECT_INS_RETAINED(write_left);
4503 EXPECT_INS_RETAINED(call_left);
4504 EXPECT_INS_RETAINED(call_entry);
Alex Light86fe9b82020-11-16 16:54:01 +00004505}
4506
4507// // ENTRY
4508// obj = new Obj();
4509// if (parameter_value) {
4510// // LEFT
4511// // DO NOT ELIMINATE
4512// obj.field = 1;
4513// while (true) {
4514// bool esc = escape(obj);
4515// if (esc) break;
4516// // DO NOT ELIMINATE
4517// obj.field = 3;
4518// }
4519// } else {
4520// // RIGHT
4521// // DO NOT ELIMINATE
4522// obj.field = 2;
4523// }
4524// // DO NOT ELIMINATE
4525// return obj.field;
4526// EXIT
4527TEST_F(LoadStoreEliminationTest, PartialLoadPreserved3) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004528 VariableSizedHandleScope vshs(Thread::Current());
4529 CreateGraph(&vshs);
Alex Light86fe9b82020-11-16 16:54:01 +00004530 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4531 "exit",
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004532 {{"entry", "entry_post"},
4533 {"entry_post", "right"},
4534 {"right", "return_block"},
4535 {"entry_post", "left_pre"},
4536 {"left_pre", "left_loop"},
4537 {"left_loop", "left_loop_post"},
4538 {"left_loop_post", "left_loop"},
4539 {"left_loop", "return_block"},
4540 {"return_block", "exit"}}));
Alex Light86fe9b82020-11-16 16:54:01 +00004541#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4542 GET_BLOCK(entry);
4543 GET_BLOCK(entry_post);
4544 GET_BLOCK(exit);
4545 GET_BLOCK(return_block);
4546 GET_BLOCK(left_pre);
4547 GET_BLOCK(left_loop);
4548 GET_BLOCK(left_loop_post);
4549 GET_BLOCK(right);
4550#undef GET_BLOCK
4551 // Left-loops first successor is the break.
4552 if (left_loop->GetSuccessors()[0] != return_block) {
4553 left_loop->SwapSuccessors();
4554 }
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004555 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
Alex Light86fe9b82020-11-16 16:54:01 +00004556 HInstruction* c1 = graph_->GetIntConstant(1);
4557 HInstruction* c2 = graph_->GetIntConstant(2);
4558 HInstruction* c3 = graph_->GetIntConstant(3);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004559
4560 HInstruction* cls = MakeClassLoad();
4561 HInstruction* new_inst = MakeNewInstance(cls);
Alex Light86fe9b82020-11-16 16:54:01 +00004562 HInstruction* goto_entry = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00004563 entry->AddInstruction(cls);
4564 entry->AddInstruction(new_inst);
4565 entry->AddInstruction(goto_entry);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004566 ManuallyBuildEnvFor(cls, {});
Alex Light86fe9b82020-11-16 16:54:01 +00004567 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4568
4569 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
4570 entry_post->AddInstruction(if_inst);
4571
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004572 HInstruction* write_left_pre = MakeIFieldSet(new_inst, c1, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004573 HInstruction* goto_left_pre = new (GetAllocator()) HGoto();
4574 left_pre->AddInstruction(write_left_pre);
4575 left_pre->AddInstruction(goto_left_pre);
4576
4577 HInstruction* suspend_left_loop = new (GetAllocator()) HSuspendCheck();
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004578 HInstruction* call_left_loop = MakeInvoke(DataType::Type::kBool, { new_inst });
Alex Light86fe9b82020-11-16 16:54:01 +00004579 HInstruction* if_left_loop = new (GetAllocator()) HIf(call_left_loop);
Alex Light86fe9b82020-11-16 16:54:01 +00004580 left_loop->AddInstruction(suspend_left_loop);
4581 left_loop->AddInstruction(call_left_loop);
4582 left_loop->AddInstruction(if_left_loop);
4583 suspend_left_loop->CopyEnvironmentFrom(cls->GetEnvironment());
4584 call_left_loop->CopyEnvironmentFrom(cls->GetEnvironment());
4585
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004586 HInstruction* write_left_loop = MakeIFieldSet(new_inst, c3, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004587 HInstruction* goto_left_loop = new (GetAllocator()) HGoto();
4588 left_loop_post->AddInstruction(write_left_loop);
4589 left_loop_post->AddInstruction(goto_left_loop);
4590
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004591 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004592 HInstruction* goto_right = new (GetAllocator()) HGoto();
4593 right->AddInstruction(write_right);
4594 right->AddInstruction(goto_right);
4595
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004596 HInstruction* read_return = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004597 HInstruction* return_final = new (GetAllocator()) HReturn(read_return);
4598 return_block->AddInstruction(read_return);
4599 return_block->AddInstruction(return_final);
4600
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004601 SetupExit(exit);
4602
Alex Light86fe9b82020-11-16 16:54:01 +00004603 // PerformLSE expects this to be empty.
4604 graph_->ClearDominanceInformation();
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004605 PerformLSENoPartial();
Alex Light86fe9b82020-11-16 16:54:01 +00004606
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004607 EXPECT_INS_RETAINED(write_left_pre) << *write_left_pre;
4608 EXPECT_INS_RETAINED(read_return) << *read_return;
4609 EXPECT_INS_RETAINED(write_right) << *write_right;
4610 EXPECT_INS_RETAINED(write_left_loop) << *write_left_loop;
4611 EXPECT_INS_RETAINED(call_left_loop) << *call_left_loop;
Alex Light86fe9b82020-11-16 16:54:01 +00004612}
4613
4614// // ENTRY
4615// obj = new Obj();
4616// if (parameter_value) {
4617// // LEFT
4618// // ELIMINATE (not visible since always overridden by obj.field = 3)
4619// obj.field = 1;
4620// while (true) {
4621// bool stop = should_stop();
4622// // DO NOT ELIMINATE (visible by read at end)
4623// obj.field = 3;
4624// if (stop) break;
4625// }
4626// } else {
4627// // RIGHT
4628// // DO NOT ELIMINATE
4629// obj.field = 2;
4630// escape(obj);
4631// }
4632// // DO NOT ELIMINATE
4633// return obj.field;
4634// EXIT
4635TEST_F(LoadStoreEliminationTest, PartialLoadPreserved4) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004636 VariableSizedHandleScope vshs(Thread::Current());
4637 CreateGraph(&vshs);
Alex Light86fe9b82020-11-16 16:54:01 +00004638 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4639 "exit",
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004640 {{"entry", "entry_post"},
4641 {"entry_post", "right"},
4642 {"right", "return_block"},
4643 {"entry_post", "left_pre"},
4644 {"left_pre", "left_loop"},
4645 {"left_loop", "left_loop"},
4646 {"left_loop", "return_block"},
4647 {"return_block", "exit"}}));
Alex Light86fe9b82020-11-16 16:54:01 +00004648#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4649 GET_BLOCK(entry);
4650 GET_BLOCK(entry_post);
4651 GET_BLOCK(exit);
4652 GET_BLOCK(return_block);
4653 GET_BLOCK(left_pre);
4654 GET_BLOCK(left_loop);
4655 GET_BLOCK(right);
4656#undef GET_BLOCK
4657 // Left-loops first successor is the break.
4658 if (left_loop->GetSuccessors()[0] != return_block) {
4659 left_loop->SwapSuccessors();
4660 }
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004661 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
Alex Light86fe9b82020-11-16 16:54:01 +00004662 HInstruction* c1 = graph_->GetIntConstant(1);
4663 HInstruction* c2 = graph_->GetIntConstant(2);
4664 HInstruction* c3 = graph_->GetIntConstant(3);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004665
4666 HInstruction* cls = MakeClassLoad();
4667 HInstruction* new_inst = MakeNewInstance(cls);
Alex Light86fe9b82020-11-16 16:54:01 +00004668 HInstruction* goto_entry = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00004669 entry->AddInstruction(cls);
4670 entry->AddInstruction(new_inst);
4671 entry->AddInstruction(goto_entry);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004672 ManuallyBuildEnvFor(cls, {});
Alex Light86fe9b82020-11-16 16:54:01 +00004673 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4674
4675 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
4676 entry_post->AddInstruction(if_inst);
4677
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004678 HInstruction* write_left_pre = MakeIFieldSet(new_inst, c1, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004679 HInstruction* goto_left_pre = new (GetAllocator()) HGoto();
4680 left_pre->AddInstruction(write_left_pre);
4681 left_pre->AddInstruction(goto_left_pre);
4682
4683 HInstruction* suspend_left_loop = new (GetAllocator()) HSuspendCheck();
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004684 HInstruction* call_left_loop = MakeInvoke(DataType::Type::kBool, {});
4685 HInstruction* write_left_loop = MakeIFieldSet(new_inst, c3, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004686 HInstruction* if_left_loop = new (GetAllocator()) HIf(call_left_loop);
4687 left_loop->AddInstruction(suspend_left_loop);
4688 left_loop->AddInstruction(call_left_loop);
4689 left_loop->AddInstruction(write_left_loop);
4690 left_loop->AddInstruction(if_left_loop);
4691 suspend_left_loop->CopyEnvironmentFrom(cls->GetEnvironment());
4692 call_left_loop->CopyEnvironmentFrom(cls->GetEnvironment());
4693
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004694 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4695 HInstruction* call_right = MakeInvoke(DataType::Type::kBool, { new_inst });
Alex Light86fe9b82020-11-16 16:54:01 +00004696 HInstruction* goto_right = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00004697 right->AddInstruction(write_right);
4698 right->AddInstruction(call_right);
4699 right->AddInstruction(goto_right);
4700 call_right->CopyEnvironmentFrom(cls->GetEnvironment());
4701
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004702 HInstruction* read_return = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004703 HInstruction* return_final = new (GetAllocator()) HReturn(read_return);
4704 return_block->AddInstruction(read_return);
4705 return_block->AddInstruction(return_final);
4706
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004707 SetupExit(exit);
4708
Alex Light86fe9b82020-11-16 16:54:01 +00004709 // PerformLSE expects this to be empty.
4710 graph_->ClearDominanceInformation();
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004711 PerformLSENoPartial();
Alex Light86fe9b82020-11-16 16:54:01 +00004712
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004713 EXPECT_INS_RETAINED(read_return);
4714 EXPECT_INS_RETAINED(write_right);
4715 EXPECT_INS_RETAINED(write_left_loop);
4716 EXPECT_INS_RETAINED(call_left_loop);
4717 EXPECT_INS_REMOVED(write_left_pre);
4718 EXPECT_INS_RETAINED(call_right);
Alex Light86fe9b82020-11-16 16:54:01 +00004719}
4720
4721// // ENTRY
4722// obj = new Obj();
4723// if (parameter_value) {
4724// // LEFT
4725// // DO NOT ELIMINATE
4726// escape(obj);
4727// obj.field = 1;
4728// // obj has already escaped so can't use field = 1 for value
4729// noescape();
4730// } else {
4731// // RIGHT
4732// // obj is needed for read since we don't know what the left value is
4733// // DO NOT ELIMINATE
4734// obj.field = 2;
4735// noescape();
4736// }
4737// EXIT
4738// ELIMINATE
4739// return obj.field
4740TEST_F(LoadStoreEliminationTest, PartialLoadPreserved5) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004741 VariableSizedHandleScope vshs(Thread::Current());
4742 CreateGraph(&vshs);
Alex Light86fe9b82020-11-16 16:54:01 +00004743 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4744 "exit",
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004745 {{"entry", "left"},
4746 {"entry", "right"},
4747 {"left", "breturn"},
4748 {"right", "breturn"},
4749 {"breturn", "exit"}}));
Alex Light86fe9b82020-11-16 16:54:01 +00004750#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4751 GET_BLOCK(entry);
4752 GET_BLOCK(exit);
4753 GET_BLOCK(breturn);
4754 GET_BLOCK(left);
4755 GET_BLOCK(right);
4756#undef GET_BLOCK
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004757 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
Alex Light86fe9b82020-11-16 16:54:01 +00004758 HInstruction* c1 = graph_->GetIntConstant(1);
4759 HInstruction* c2 = graph_->GetIntConstant(2);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004760
4761 HInstruction* cls = MakeClassLoad();
4762 HInstruction* new_inst = MakeNewInstance(cls);
Alex Light86fe9b82020-11-16 16:54:01 +00004763 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
Alex Light86fe9b82020-11-16 16:54:01 +00004764 entry->AddInstruction(cls);
4765 entry->AddInstruction(new_inst);
4766 entry->AddInstruction(if_inst);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004767 ManuallyBuildEnvFor(cls, {});
Alex Light86fe9b82020-11-16 16:54:01 +00004768 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4769
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004770 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
4771 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
4772 HInstruction* call2_left = MakeInvoke(DataType::Type::kVoid, {});
Alex Light86fe9b82020-11-16 16:54:01 +00004773 HInstruction* goto_left = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00004774 left->AddInstruction(call_left);
4775 left->AddInstruction(write_left);
4776 left->AddInstruction(call2_left);
4777 left->AddInstruction(goto_left);
4778 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
4779 call2_left->CopyEnvironmentFrom(cls->GetEnvironment());
4780
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004781 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4782 HInstruction* call_right = MakeInvoke(DataType::Type::kVoid, {});
Alex Light86fe9b82020-11-16 16:54:01 +00004783 HInstruction* goto_right = new (GetAllocator()) HGoto();
4784 right->AddInstruction(write_right);
4785 right->AddInstruction(call_right);
4786 right->AddInstruction(goto_right);
4787 call_right->CopyEnvironmentFrom(cls->GetEnvironment());
4788
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004789 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004790 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
4791 breturn->AddInstruction(read_bottom);
4792 breturn->AddInstruction(return_exit);
4793
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004794 SetupExit(exit);
4795
Alex Light86fe9b82020-11-16 16:54:01 +00004796 // PerformLSE expects this to be empty.
4797 graph_->ClearDominanceInformation();
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004798 PerformLSENoPartial();
Alex Light86fe9b82020-11-16 16:54:01 +00004799
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004800 EXPECT_INS_RETAINED(read_bottom);
4801 EXPECT_INS_RETAINED(write_right);
4802 EXPECT_INS_RETAINED(write_left);
4803 EXPECT_INS_RETAINED(call_left);
4804 EXPECT_INS_RETAINED(call_right);
Alex Light86fe9b82020-11-16 16:54:01 +00004805}
4806
4807// // ENTRY
4808// obj = new Obj();
4809// DO NOT ELIMINATE. Kept by escape.
4810// obj.field = 3;
4811// noescape();
4812// if (parameter_value) {
4813// // LEFT
4814// // DO NOT ELIMINATE
4815// escape(obj);
4816// obj.field = 1;
4817// } else {
4818// // RIGHT
4819// // ELIMINATE
4820// obj.field = 2;
4821// }
4822// EXIT
4823// ELIMINATE
4824// return obj.field
4825TEST_F(LoadStoreEliminationTest, PartialLoadPreserved6) {
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004826 CreateGraph();
Alex Light86fe9b82020-11-16 16:54:01 +00004827 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4828 "exit",
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004829 {{"entry", "left"},
4830 {"entry", "right"},
4831 {"left", "breturn"},
4832 {"right", "breturn"},
4833 {"breturn", "exit"}}));
Alex Light86fe9b82020-11-16 16:54:01 +00004834#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4835 GET_BLOCK(entry);
4836 GET_BLOCK(exit);
4837 GET_BLOCK(breturn);
4838 GET_BLOCK(left);
4839 GET_BLOCK(right);
4840#undef GET_BLOCK
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004841 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
Alex Light86fe9b82020-11-16 16:54:01 +00004842 HInstruction* c1 = graph_->GetIntConstant(1);
4843 HInstruction* c2 = graph_->GetIntConstant(2);
4844 HInstruction* c3 = graph_->GetIntConstant(3);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004845
4846 HInstruction* cls = MakeClassLoad();
4847 HInstruction* new_inst = MakeNewInstance(cls);
4848 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
4849 HInstruction* call_entry = MakeInvoke(DataType::Type::kVoid, {});
Alex Light86fe9b82020-11-16 16:54:01 +00004850 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
Alex Light86fe9b82020-11-16 16:54:01 +00004851 entry->AddInstruction(cls);
4852 entry->AddInstruction(new_inst);
4853 entry->AddInstruction(write_entry);
4854 entry->AddInstruction(call_entry);
4855 entry->AddInstruction(if_inst);
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004856 ManuallyBuildEnvFor(cls, {});
Alex Light86fe9b82020-11-16 16:54:01 +00004857 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4858 call_entry->CopyEnvironmentFrom(cls->GetEnvironment());
4859
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004860 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
4861 HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004862 HInstruction* goto_left = new (GetAllocator()) HGoto();
Alex Light86fe9b82020-11-16 16:54:01 +00004863 left->AddInstruction(call_left);
4864 left->AddInstruction(write_left);
4865 left->AddInstruction(goto_left);
4866 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
4867
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004868 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004869 HInstruction* goto_right = new (GetAllocator()) HGoto();
4870 right->AddInstruction(write_right);
4871 right->AddInstruction(goto_right);
4872
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004873 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
Alex Light86fe9b82020-11-16 16:54:01 +00004874 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
4875 breturn->AddInstruction(read_bottom);
4876 breturn->AddInstruction(return_exit);
4877
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004878 SetupExit(exit);
4879
Alex Light86fe9b82020-11-16 16:54:01 +00004880 // PerformLSE expects this to be empty.
4881 graph_->ClearDominanceInformation();
Alex Lightfc1ce4e2021-01-22 14:05:13 +00004882
4883 LOG(INFO) << "Pre LSE " << blks;
4884 PerformLSENoPartial();
4885 LOG(INFO) << "Post LSE " << blks;
4886
4887 EXPECT_INS_REMOVED(read_bottom);
4888 EXPECT_INS_REMOVED(write_right);
4889 EXPECT_INS_RETAINED(write_entry);
4890 EXPECT_INS_RETAINED(write_left);
4891 EXPECT_INS_RETAINED(call_left);
4892 EXPECT_INS_RETAINED(call_entry);
4893}
4894
4895// // ENTRY
4896// // MOVED TO MATERIALIZATION BLOCK
4897// obj = new Obj();
4898// ELIMINATE, moved to materialization block. Kept by escape.
4899// obj.field = 3;
4900// // Make sure this graph isn't broken
4901// if (obj ==/!= (STATIC.VALUE|obj|null)) {
4902// // partial_BLOCK
4903// // REMOVE (either from unreachable or normal PHI creation)
4904// obj.field = 4;
4905// }
4906// if (parameter_value) {
4907// // LEFT
4908// // DO NOT ELIMINATE
4909// escape(obj);
4910// } else {
4911// // RIGHT
4912// // ELIMINATE
4913// obj.field = 2;
4914// }
4915// EXIT
4916// PREDICATED GET
4917// return obj.field
4918TEST_P(PartialComparisonTestGroup, PartialComparisonBeforeCohort) {
4919 VariableSizedHandleScope vshs(Thread::Current());
4920 CreateGraph(/*handles=*/&vshs);
4921 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
4922 "exit",
4923 {{"entry", "critical_break"},
4924 {"entry", "partial"},
4925 {"partial", "merge"},
4926 {"critical_break", "merge"},
4927 {"merge", "left"},
4928 {"merge", "right"},
4929 {"left", "breturn"},
4930 {"right", "breturn"},
4931 {"breturn", "exit"}}));
4932#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
4933 GET_BLOCK(entry);
4934 GET_BLOCK(merge);
4935 GET_BLOCK(partial);
4936 GET_BLOCK(critical_break);
4937 GET_BLOCK(exit);
4938 GET_BLOCK(breturn);
4939 GET_BLOCK(left);
4940 GET_BLOCK(right);
4941#undef GET_BLOCK
4942 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
4943 HInstruction* c2 = graph_->GetIntConstant(2);
4944 HInstruction* c3 = graph_->GetIntConstant(3);
4945 HInstruction* c4 = graph_->GetIntConstant(4);
4946
4947 HInstruction* cls = MakeClassLoad();
4948 HInstruction* new_inst = MakeNewInstance(cls);
4949 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
4950 ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst);
4951 HInstruction* if_inst = new (GetAllocator()) HIf(cmp_instructions.cmp_);
4952 entry->AddInstruction(cls);
4953 entry->AddInstruction(new_inst);
4954 entry->AddInstruction(write_entry);
4955 cmp_instructions.AddSetup(entry);
4956 entry->AddInstruction(cmp_instructions.cmp_);
4957 entry->AddInstruction(if_inst);
4958 ManuallyBuildEnvFor(cls, {});
4959 cmp_instructions.AddEnvironment(cls->GetEnvironment());
4960 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
4961
4962 HInstruction* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32));
4963 HInstruction* goto_partial = new (GetAllocator()) HGoto();
4964 partial->AddInstruction(write_partial);
4965 partial->AddInstruction(goto_partial);
4966
4967 HInstruction* goto_crit_break = new (GetAllocator()) HGoto();
4968 critical_break->AddInstruction(goto_crit_break);
4969
4970 HInstruction* if_merge = new (GetAllocator()) HIf(bool_value);
4971 merge->AddInstruction(if_merge);
4972
4973 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
4974 HInstruction* goto_left = new (GetAllocator()) HGoto();
4975 left->AddInstruction(call_left);
4976 left->AddInstruction(goto_left);
4977 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
4978
4979 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
4980 HInstruction* goto_right = new (GetAllocator()) HGoto();
4981 right->AddInstruction(write_right);
4982 right->AddInstruction(goto_right);
4983
4984 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
4985 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
4986 breturn->AddInstruction(read_bottom);
4987 breturn->AddInstruction(return_exit);
4988
4989 SetupExit(exit);
4990
4991 // PerformLSE expects this to be empty.
4992 graph_->ClearDominanceInformation();
4993
4994 LOG(INFO) << "Pre LSE " << blks;
4995 PerformLSEWithPartial();
4996 LOG(INFO) << "Post LSE " << blks;
4997
4998 std::vector<HPhi*> merges;
4999 HPredicatedInstanceFieldGet* pred_get;
5000 HInstanceFieldSet* init_set;
5001 std::tie(pred_get, init_set) =
5002 FindSingleInstructions<HPredicatedInstanceFieldGet, HInstanceFieldSet>(graph_);
5003 std::tie(merges) = FindAllInstructions<HPhi>(graph_);
5004 ASSERT_EQ(merges.size(), 3u);
5005 HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5006 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn;
5007 });
5008 HPhi* merge_value_top = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5009 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() != breturn;
5010 });
5011 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
5012 return p->GetType() == DataType::Type::kReference;
5013 });
5014 EXPECT_INS_REMOVED(read_bottom);
5015 EXPECT_INS_REMOVED(write_entry);
5016 EXPECT_INS_REMOVED(write_partial);
5017 EXPECT_INS_RETAINED(call_left);
5018 CheckFinalInstruction(if_inst->InputAt(0), ComparisonPlacement::kBeforeEscape);
5019 EXPECT_INS_EQ(init_set->InputAt(1), merge_value_top);
5020 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
5021 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return);
5022}
5023
5024// // ENTRY
5025// // MOVED TO MATERIALIZATION BLOCK
5026// obj = new Obj();
5027// ELIMINATE, moved to materialization block. Kept by escape.
5028// obj.field = 3;
5029// // Make sure this graph isn't broken
5030// if (parameter_value) {
5031// if (obj ==/!= (STATIC.VALUE|obj|null)) {
5032// // partial_BLOCK
5033// obj.field = 4;
5034// }
5035// // LEFT
5036// // DO NOT ELIMINATE
5037// escape(obj);
5038// } else {
5039// // RIGHT
5040// // ELIMINATE
5041// obj.field = 2;
5042// }
5043// EXIT
5044// PREDICATED GET
5045// return obj.field
5046TEST_P(PartialComparisonTestGroup, PartialComparisonInCohortBeforeEscape) {
5047 VariableSizedHandleScope vshs(Thread::Current());
5048 CreateGraph(/*handles=*/&vshs);
5049 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5050 "exit",
5051 {{"entry", "left_begin"},
5052 {"left_begin", "partial"},
5053 {"left_begin", "left_crit_break"},
5054 {"left_crit_break", "left"},
5055 {"partial", "left"},
5056 {"entry", "right"},
5057 {"left", "breturn"},
5058 {"right", "breturn"},
5059 {"breturn", "exit"}}));
5060#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5061 GET_BLOCK(entry);
5062 GET_BLOCK(partial);
5063 GET_BLOCK(left_begin);
5064 GET_BLOCK(exit);
5065 GET_BLOCK(breturn);
5066 GET_BLOCK(left);
5067 GET_BLOCK(left_crit_break);
5068 GET_BLOCK(right);
5069#undef GET_BLOCK
5070 EnsurePredecessorOrder(left, {left_crit_break, partial});
5071 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5072 HInstruction* c2 = graph_->GetIntConstant(2);
5073 HInstruction* c3 = graph_->GetIntConstant(3);
5074 HInstruction* c4 = graph_->GetIntConstant(4);
5075
5076 HInstruction* cls = MakeClassLoad();
5077 HInstruction* new_inst = MakeNewInstance(cls);
5078 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
5079 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5080 entry->AddInstruction(cls);
5081 entry->AddInstruction(new_inst);
5082 entry->AddInstruction(write_entry);
5083 entry->AddInstruction(if_inst);
5084 ManuallyBuildEnvFor(cls, {});
5085 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
5086
5087 ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst);
5088 HInstruction* if_left_begin = new (GetAllocator()) HIf(cmp_instructions.cmp_);
5089 cmp_instructions.AddSetup(left_begin);
5090 left_begin->AddInstruction(cmp_instructions.cmp_);
5091 left_begin->AddInstruction(if_left_begin);
5092 cmp_instructions.AddEnvironment(cls->GetEnvironment());
5093
5094 left_crit_break->AddInstruction(new (GetAllocator()) HGoto());
5095
5096 HInstruction* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32));
5097 HInstruction* goto_partial = new (GetAllocator()) HGoto();
5098 partial->AddInstruction(write_partial);
5099 partial->AddInstruction(goto_partial);
5100
5101 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
5102 HInstruction* goto_left = new (GetAllocator()) HGoto();
5103 left->AddInstruction(call_left);
5104 left->AddInstruction(goto_left);
5105 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
5106
5107 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
5108 HInstruction* goto_right = new (GetAllocator()) HGoto();
5109 right->AddInstruction(write_right);
5110 right->AddInstruction(goto_right);
5111
5112 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
5113 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
5114 breturn->AddInstruction(read_bottom);
5115 breturn->AddInstruction(return_exit);
5116
5117 SetupExit(exit);
5118
5119 // PerformLSE expects this to be empty.
5120 graph_->ClearDominanceInformation();
5121 LOG(INFO) << "Pre LSE " << blks;
5122 PerformLSEWithPartial();
5123 LOG(INFO) << "Post LSE " << blks;
5124
5125 std::vector<HPhi*> merges;
5126 HInstanceFieldSet* init_set =
5127 FindSingleInstruction<HInstanceFieldSet>(graph_, left_begin->GetSinglePredecessor());
5128 HInstanceFieldSet* partial_set = FindSingleInstruction<HInstanceFieldSet>(graph_, partial);
5129 HPredicatedInstanceFieldGet* pred_get =
5130 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_);
5131 std::tie(merges) = FindAllInstructions<HPhi>(graph_);
5132 ASSERT_EQ(merges.size(), 2u);
5133 HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5134 return p->GetType() == DataType::Type::kInt32;
5135 });
5136 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
5137 return p->GetType() == DataType::Type::kReference;
5138 });
5139 EXPECT_EQ(merge_value_return->GetBlock(), breturn)
5140 << blks.GetName(merge_value_return->GetBlock());
5141 EXPECT_INS_REMOVED(read_bottom);
5142 EXPECT_INS_REMOVED(write_entry);
5143 EXPECT_INS_RETAINED(write_partial);
5144 EXPECT_INS_RETAINED(call_left);
5145 CheckFinalInstruction(if_left_begin->InputAt(0), ComparisonPlacement::kInEscape);
5146 EXPECT_INS_EQ(init_set->InputAt(1), c3);
5147 EXPECT_INS_EQ(partial_set->InputAt(0), init_set->InputAt(0));
5148 EXPECT_INS_EQ(partial_set->InputAt(1), c4);
5149 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
5150 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return);
5151}
5152
5153// // ENTRY
5154// // MOVED TO MATERIALIZATION BLOCK
5155// obj = new Obj();
5156// ELIMINATE, moved to materialization block. Kept by escape.
5157// obj.field = 3;
5158// // Make sure this graph isn't broken
5159// if (parameter_value) {
5160// // LEFT
5161// // DO NOT ELIMINATE
5162// escape(obj);
5163// } else {
5164// // RIGHT
5165// // ELIMINATE
5166// obj.field = 2;
5167// }
5168// if (obj ==/!= (STATIC.VALUE|obj|null)) {
5169// // partial_BLOCK
5170// obj.field = 4;
5171// }
5172// EXIT
5173// PREDICATED GET
5174// return obj.field
5175TEST_P(PartialComparisonTestGroup, PartialComparisonAfterCohort) {
5176 VariableSizedHandleScope vshs(Thread::Current());
5177 CreateGraph(/*handles=*/&vshs);
5178 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5179 "exit",
5180 {{"entry", "left"},
5181 {"entry", "right"},
5182 {"left", "merge"},
5183 {"right", "merge"},
5184 {"merge", "critical_break"},
5185 {"critical_break", "breturn"},
5186 {"merge", "partial"},
5187 {"partial", "breturn"},
5188 {"breturn", "exit"}}));
5189#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5190 GET_BLOCK(entry);
5191 GET_BLOCK(partial);
5192 GET_BLOCK(critical_break);
5193 GET_BLOCK(merge);
5194 GET_BLOCK(exit);
5195 GET_BLOCK(breturn);
5196 GET_BLOCK(left);
5197 GET_BLOCK(right);
5198#undef GET_BLOCK
5199 EnsurePredecessorOrder(breturn, {critical_break, partial});
5200 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5201 HInstruction* c2 = graph_->GetIntConstant(2);
5202 HInstruction* c3 = graph_->GetIntConstant(3);
5203 HInstruction* c4 = graph_->GetIntConstant(4);
5204
5205 HInstruction* cls = MakeClassLoad();
5206 HInstruction* new_inst = MakeNewInstance(cls);
5207 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
5208 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5209 entry->AddInstruction(cls);
5210 entry->AddInstruction(new_inst);
5211 entry->AddInstruction(write_entry);
5212 entry->AddInstruction(if_inst);
5213 ManuallyBuildEnvFor(cls, {});
5214 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
5215
5216 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
5217 HInstruction* goto_left = new (GetAllocator()) HGoto();
5218 left->AddInstruction(call_left);
5219 left->AddInstruction(goto_left);
5220 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
5221
5222 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
5223 HInstruction* goto_right = new (GetAllocator()) HGoto();
5224 right->AddInstruction(write_right);
5225 right->AddInstruction(goto_right);
5226
5227 ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst);
5228 HInstruction* if_merge = new (GetAllocator()) HIf(cmp_instructions.cmp_);
5229 cmp_instructions.AddSetup(merge);
5230 merge->AddInstruction(cmp_instructions.cmp_);
5231 merge->AddInstruction(if_merge);
5232 cmp_instructions.AddEnvironment(cls->GetEnvironment());
5233
5234 HInstanceFieldSet* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32));
5235 HInstruction* goto_partial = new (GetAllocator()) HGoto();
5236 partial->AddInstruction(write_partial);
5237 partial->AddInstruction(goto_partial);
5238
5239 HInstruction* goto_crit_break = new (GetAllocator()) HGoto();
5240 critical_break->AddInstruction(goto_crit_break);
5241
5242 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
5243 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
5244 breturn->AddInstruction(read_bottom);
5245 breturn->AddInstruction(return_exit);
5246
5247 SetupExit(exit);
5248
5249 // PerformLSE expects this to be empty.
5250 graph_->ClearDominanceInformation();
5251 LOG(INFO) << "Pre LSE " << blks;
5252 PerformLSEWithPartial();
5253 LOG(INFO) << "Post LSE " << blks;
5254
5255 std::vector<HPhi*> merges;
5256 HInstanceFieldSet* init_set =
5257 FindSingleInstruction<HInstanceFieldSet>(graph_, left->GetSinglePredecessor());
5258 HPredicatedInstanceFieldGet* pred_get =
5259 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_);
5260 std::tie(merges) = FindAllInstructions<HPhi>(graph_);
5261 ASSERT_EQ(merges.size(), 3u);
5262 HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5263 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn;
5264 });
5265 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
5266 return p->GetType() == DataType::Type::kReference;
5267 });
5268 EXPECT_INS_REMOVED(read_bottom);
5269 EXPECT_INS_REMOVED(write_entry);
5270 EXPECT_INS_RETAINED(write_partial);
5271 EXPECT_TRUE(write_partial->GetIsPredicatedSet());
5272 EXPECT_INS_RETAINED(call_left);
5273 CheckFinalInstruction(if_merge->InputAt(0), ComparisonPlacement::kAfterEscape);
5274 EXPECT_INS_EQ(init_set->InputAt(1), c3);
5275 ASSERT_TRUE(write_partial->InputAt(0)->IsPhi());
5276 EXPECT_INS_EQ(write_partial->InputAt(0)->AsPhi()->InputAt(0), init_set->InputAt(0));
5277 EXPECT_INS_EQ(write_partial->InputAt(1), c4);
5278 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
5279 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return);
5280}
5281
5282// // ENTRY
5283// // MOVED TO MATERIALIZATION BLOCK
5284// obj = new Obj();
5285// ELIMINATE, moved to materialization block. Kept by escape.
5286// obj.field = 3;
5287// // Make sure this graph isn't broken
5288// if (parameter_value) {
5289// // LEFT
5290// // DO NOT ELIMINATE
5291// escape(obj);
5292// if (obj ==/!= (STATIC.VALUE|obj|null)) {
5293// // partial_BLOCK
5294// obj.field = 4;
5295// }
5296// } else {
5297// // RIGHT
5298// // ELIMINATE
5299// obj.field = 2;
5300// }
5301// EXIT
5302// PREDICATED GET
5303// return obj.field
5304TEST_P(PartialComparisonTestGroup, PartialComparisonInCohortAfterEscape) {
5305 PartialComparisonKind kind = GetParam();
5306 VariableSizedHandleScope vshs(Thread::Current());
5307 CreateGraph(/*handles=*/&vshs);
5308 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5309 "exit",
5310 {{"entry", "left"},
5311 {"left", "partial"},
5312 {"partial", "left_end"},
5313 {"left", "left_crit_break"},
5314 {"left_crit_break", "left_end"},
5315 {"left_end", "breturn"},
5316 {"entry", "right"},
5317 {"right", "breturn"},
5318 {"breturn", "exit"}}));
5319#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5320 GET_BLOCK(entry);
5321 GET_BLOCK(partial);
5322 GET_BLOCK(left_end);
5323 GET_BLOCK(exit);
5324 GET_BLOCK(breturn);
5325 GET_BLOCK(left);
5326 GET_BLOCK(left_crit_break);
5327 GET_BLOCK(right);
5328#undef GET_BLOCK
5329 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5330 HInstruction* c2 = graph_->GetIntConstant(2);
5331 HInstruction* c3 = graph_->GetIntConstant(3);
5332 HInstruction* c4 = graph_->GetIntConstant(4);
5333
5334 HInstruction* cls = MakeClassLoad();
5335 HInstruction* new_inst = MakeNewInstance(cls);
5336 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
5337 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5338 entry->AddInstruction(cls);
5339 entry->AddInstruction(new_inst);
5340 entry->AddInstruction(write_entry);
5341 entry->AddInstruction(if_inst);
5342 ManuallyBuildEnvFor(cls, {});
5343 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
5344
5345 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
5346 ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst);
5347 HInstruction* if_left = new (GetAllocator()) HIf(cmp_instructions.cmp_);
5348 left->AddInstruction(call_left);
5349 cmp_instructions.AddSetup(left);
5350 left->AddInstruction(cmp_instructions.cmp_);
5351 left->AddInstruction(if_left);
5352 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
5353 cmp_instructions.AddEnvironment(cls->GetEnvironment());
5354 if (if_left->AsIf()->IfTrueSuccessor() != partial) {
5355 left->SwapSuccessors();
5356 }
5357
5358 HInstruction* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32));
5359 HInstruction* goto_partial = new (GetAllocator()) HGoto();
5360 partial->AddInstruction(write_partial);
5361 partial->AddInstruction(goto_partial);
5362
5363 HInstruction* goto_left_crit_break = new (GetAllocator()) HGoto();
5364 left_crit_break->AddInstruction(goto_left_crit_break);
5365
5366 HInstruction* goto_left_end = new (GetAllocator()) HGoto();
5367 left_end->AddInstruction(goto_left_end);
5368
5369 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
5370 HInstruction* goto_right = new (GetAllocator()) HGoto();
5371 right->AddInstruction(write_right);
5372 right->AddInstruction(goto_right);
5373
5374 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
5375 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
5376 breturn->AddInstruction(read_bottom);
5377 breturn->AddInstruction(return_exit);
5378
5379 SetupExit(exit);
5380
5381 // PerformLSE expects this to be empty.
5382 graph_->ClearDominanceInformation();
5383
5384 LOG(INFO) << "Pre LSE " << blks;
5385 PerformLSEWithPartial();
5386 LOG(INFO) << "Post LSE " << blks;
5387
5388 std::vector<HPhi*> merges;
5389 std::vector<HInstanceFieldSet*> sets;
5390 HPredicatedInstanceFieldGet* pred_get =
5391 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_);
5392 std::tie(merges, sets) = FindAllInstructions<HPhi, HInstanceFieldSet>(graph_);
5393 ASSERT_EQ(merges.size(), 2u);
5394 ASSERT_EQ(sets.size(), 2u);
5395 HInstanceFieldSet* init_set = FindOrNull(sets.begin(), sets.end(), [&](HInstanceFieldSet* s) {
5396 return s->GetBlock()->GetSingleSuccessor() == left;
5397 });
5398 EXPECT_INS_EQ(init_set->InputAt(1), c3);
5399 HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5400 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn;
5401 });
5402 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
5403 return p->GetType() == DataType::Type::kReference;
5404 });
5405 EXPECT_INS_REMOVED(read_bottom);
5406 EXPECT_INS_REMOVED(write_entry);
5407 if (kind.IsPossiblyTrue()) {
5408 EXPECT_INS_RETAINED(write_partial);
5409 EXPECT_TRUE(std::find(sets.begin(), sets.end(), write_partial) != sets.end());
5410 }
5411 EXPECT_INS_RETAINED(call_left);
5412 CheckFinalInstruction(if_left->InputAt(0), ComparisonPlacement::kInEscape);
5413 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
5414 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return);
5415}
5416
5417INSTANTIATE_TEST_SUITE_P(
5418 LoadStoreEliminationTest,
5419 PartialComparisonTestGroup,
5420 testing::Values(PartialComparisonKind{PartialComparisonKind::Type::kEquals,
5421 PartialComparisonKind::Target::kNull,
5422 PartialComparisonKind::Position::kLeft},
5423 PartialComparisonKind{PartialComparisonKind::Type::kEquals,
5424 PartialComparisonKind::Target::kNull,
5425 PartialComparisonKind::Position::kRight},
5426 PartialComparisonKind{PartialComparisonKind::Type::kEquals,
5427 PartialComparisonKind::Target::kValue,
5428 PartialComparisonKind::Position::kLeft},
5429 PartialComparisonKind{PartialComparisonKind::Type::kEquals,
5430 PartialComparisonKind::Target::kValue,
5431 PartialComparisonKind::Position::kRight},
5432 PartialComparisonKind{PartialComparisonKind::Type::kEquals,
5433 PartialComparisonKind::Target::kSelf,
5434 PartialComparisonKind::Position::kLeft},
5435 PartialComparisonKind{PartialComparisonKind::Type::kNotEquals,
5436 PartialComparisonKind::Target::kNull,
5437 PartialComparisonKind::Position::kLeft},
5438 PartialComparisonKind{PartialComparisonKind::Type::kNotEquals,
5439 PartialComparisonKind::Target::kNull,
5440 PartialComparisonKind::Position::kRight},
5441 PartialComparisonKind{PartialComparisonKind::Type::kNotEquals,
5442 PartialComparisonKind::Target::kSelf,
5443 PartialComparisonKind::Position::kLeft},
5444 PartialComparisonKind{PartialComparisonKind::Type::kNotEquals,
5445 PartialComparisonKind::Target::kValue,
5446 PartialComparisonKind::Position::kLeft},
5447 PartialComparisonKind{PartialComparisonKind::Type::kNotEquals,
5448 PartialComparisonKind::Target::kValue,
5449 PartialComparisonKind::Position::kRight}));
5450
5451// // ENTRY
5452// obj = new Obj();
5453// if (parameter_value) {
5454// // LEFT
5455// escape(obj);
5456// } else {
5457// // RIGHT
5458// // ELIMINATE
5459// obj.field = 2;
5460// }
5461// EXIT
5462// predicated-ELIMINATE
5463// obj.field = 3;
5464TEST_F(LoadStoreEliminationTest, PredicatedStore1) {
5465 VariableSizedHandleScope vshs(Thread::Current());
5466 InitGraph(/*handles=*/&vshs);
5467 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5468 "exit",
5469 {{"entry", "left"},
5470 {"entry", "right"},
5471 {"left", "breturn"},
5472 {"right", "breturn"},
5473 {"breturn", "exit"}}));
5474#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5475 GET_BLOCK(entry);
5476 GET_BLOCK(exit);
5477 GET_BLOCK(breturn);
5478 GET_BLOCK(left);
5479 GET_BLOCK(right);
5480#undef GET_BLOCK
5481 EnsurePredecessorOrder(breturn, {left, right});
5482 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5483 HInstruction* null_const = graph_->GetNullConstant();
5484 HInstruction* c2 = graph_->GetIntConstant(2);
5485 HInstruction* c3 = graph_->GetIntConstant(3);
5486
5487 HInstruction* cls = MakeClassLoad();
5488 HInstruction* new_inst = MakeNewInstance(cls);
5489 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5490 entry->AddInstruction(cls);
5491 entry->AddInstruction(new_inst);
5492 entry->AddInstruction(if_inst);
5493 ManuallyBuildEnvFor(cls, {});
5494 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
5495
5496 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
5497 HInstruction* goto_left = new (GetAllocator()) HGoto();
5498 left->AddInstruction(call_left);
5499 left->AddInstruction(goto_left);
5500 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
5501
5502 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
5503 HInstruction* goto_right = new (GetAllocator()) HGoto();
5504 right->AddInstruction(write_right);
5505 right->AddInstruction(goto_right);
5506
5507 HInstruction* write_bottom = MakeIFieldSet(new_inst, c3, MemberOffset(32));
5508 HInstruction* return_exit = new (GetAllocator()) HReturnVoid();
5509 breturn->AddInstruction(write_bottom);
5510 breturn->AddInstruction(return_exit);
5511
5512 SetupExit(exit);
5513
5514 // PerformLSE expects this to be empty.
5515 graph_->ClearDominanceInformation();
5516
5517 LOG(INFO) << "Pre LSE " << blks;
5518 PerformLSEWithPartial();
5519 LOG(INFO) << "Post LSE " << blks;
5520
5521 EXPECT_INS_RETAINED(write_bottom);
5522 EXPECT_TRUE(write_bottom->AsInstanceFieldSet()->GetIsPredicatedSet());
5523 EXPECT_INS_REMOVED(write_right);
5524 EXPECT_INS_RETAINED(call_left);
5525 HPhi* merge_alloc = FindSingleInstruction<HPhi>(graph_, breturn);
5526 ASSERT_NE(merge_alloc, nullptr);
5527 EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc;
5528 EXPECT_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) << *merge_alloc << " cls? " << *cls;
5529 EXPECT_EQ(merge_alloc->InputAt(1), null_const);
5530}
5531
5532// // ENTRY
5533// obj = new Obj();
5534// obj.field = 3;
5535// if (parameter_value) {
5536// // LEFT
5537// escape(obj);
5538// } else {
5539// // RIGHT
5540// // ELIMINATE
5541// obj.field = 2;
5542// }
5543// // MERGE
5544// if (second_param) {
5545// // NON_ESCAPE
5546// obj.field = 1;
5547// noescape();
5548// }
5549// EXIT
5550// predicated-ELIMINATE
5551// obj.field = 4;
5552TEST_F(LoadStoreEliminationTest, PredicatedStore2) {
5553 VariableSizedHandleScope vshs(Thread::Current());
5554 CreateGraph(/*handles=*/&vshs);
5555 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5556 "exit",
5557 {{"entry", "left"},
5558 {"entry", "right"},
5559 {"left", "merge"},
5560 {"right", "merge"},
5561 {"merge", "non_escape"},
5562 {"non_escape", "breturn"},
5563 {"merge", "merge_crit_break"},
5564 {"merge_crit_break", "breturn"},
5565 {"breturn", "exit"}}));
5566#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5567 GET_BLOCK(entry);
5568 GET_BLOCK(exit);
5569 GET_BLOCK(breturn);
5570 GET_BLOCK(left);
5571 GET_BLOCK(right);
5572 GET_BLOCK(merge);
5573 GET_BLOCK(merge_crit_break);
5574 GET_BLOCK(non_escape);
5575#undef GET_BLOCK
5576 EnsurePredecessorOrder(merge, {left, right});
5577 EnsurePredecessorOrder(breturn, {merge_crit_break, non_escape});
5578 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5579 HInstruction* bool_value2 = MakeParam(DataType::Type::kBool);
5580 HInstruction* null_const = graph_->GetNullConstant();
5581 HInstruction* c1 = graph_->GetIntConstant(3);
5582 HInstruction* c2 = graph_->GetIntConstant(2);
5583 HInstruction* c3 = graph_->GetIntConstant(3);
5584 HInstruction* c4 = graph_->GetIntConstant(4);
5585
5586 HInstruction* cls = MakeClassLoad();
5587 HInstruction* new_inst = MakeNewInstance(cls);
5588 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
5589 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5590 entry->AddInstruction(cls);
5591 entry->AddInstruction(new_inst);
5592 entry->AddInstruction(write_entry);
5593 entry->AddInstruction(if_inst);
5594 ManuallyBuildEnvFor(cls, {});
5595 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
5596
5597 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
5598 HInstruction* goto_left = new (GetAllocator()) HGoto();
5599 left->AddInstruction(call_left);
5600 left->AddInstruction(goto_left);
5601 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
5602
5603 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
5604 HInstruction* goto_right = new (GetAllocator()) HGoto();
5605 right->AddInstruction(write_right);
5606 right->AddInstruction(goto_right);
5607
5608 HInstruction* merge_if = new (GetAllocator()) HIf(bool_value2);
5609 merge->AddInstruction(merge_if);
5610
5611 merge_crit_break->AddInstruction(new (GetAllocator()) HGoto());
5612
5613 HInstruction* write_non_escape = MakeIFieldSet(new_inst, c1, MemberOffset(32));
5614 HInstruction* non_escape_call = MakeInvoke(DataType::Type::kVoid, {});
5615 HInstruction* non_escape_goto = new (GetAllocator()) HGoto();
5616 non_escape->AddInstruction(write_non_escape);
5617 non_escape->AddInstruction(non_escape_call);
5618 non_escape->AddInstruction(non_escape_goto);
5619 non_escape_call->CopyEnvironmentFrom(cls->GetEnvironment());
5620
5621 HInstruction* write_bottom = MakeIFieldSet(new_inst, c4, MemberOffset(32));
5622 HInstruction* return_exit = new (GetAllocator()) HReturnVoid();
5623 breturn->AddInstruction(write_bottom);
5624 breturn->AddInstruction(return_exit);
5625
5626 SetupExit(exit);
5627
5628 // PerformLSE expects this to be empty.
5629 graph_->ClearDominanceInformation();
5630 LOG(INFO) << "Pre LSE " << blks;
5631 PerformLSEWithPartial();
5632 LOG(INFO) << "Post LSE " << blks;
5633
5634 EXPECT_INS_RETAINED(write_bottom);
5635 EXPECT_TRUE(write_bottom->AsInstanceFieldSet()->GetIsPredicatedSet()) << *write_bottom;
5636 EXPECT_INS_REMOVED(write_right);
5637 EXPECT_INS_RETAINED(call_left);
5638 HInstanceFieldSet* pred_set = FindSingleInstruction<HInstanceFieldSet>(graph_, breturn);
5639 HPhi* merge_alloc = FindSingleInstruction<HPhi>(graph_);
5640 ASSERT_NE(merge_alloc, nullptr);
5641 EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc;
5642 EXPECT_INS_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) << " phi is: " << *merge_alloc;
5643 EXPECT_INS_EQ(merge_alloc->InputAt(1), null_const);
5644 ASSERT_NE(pred_set, nullptr);
5645 EXPECT_TRUE(pred_set->GetIsPredicatedSet()) << *pred_set;
5646 EXPECT_INS_EQ(pred_set->InputAt(0), merge_alloc);
5647}
5648
5649// // ENTRY
5650// obj = new Obj();
5651// obj.field = 3;
5652// if (parameter_value) {
5653// // LEFT
5654// escape(obj);
5655// } else {
5656// // RIGHT
5657// // ELIMINATE
5658// obj.field = 2;
5659// }
5660// EXIT
5661// predicated-ELIMINATE
5662// return obj.field
5663TEST_F(LoadStoreEliminationTest, PredicatedLoad1) {
5664 VariableSizedHandleScope vshs(Thread::Current());
5665 CreateGraph(/*handles=*/&vshs);
5666 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5667 "exit",
5668 {{"entry", "left"},
5669 {"entry", "right"},
5670 {"left", "breturn"},
5671 {"right", "breturn"},
5672 {"breturn", "exit"}}));
5673#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5674 GET_BLOCK(entry);
5675 GET_BLOCK(exit);
5676 GET_BLOCK(breturn);
5677 GET_BLOCK(left);
5678 GET_BLOCK(right);
5679#undef GET_BLOCK
5680 EnsurePredecessorOrder(breturn, {left, right});
5681 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5682 HInstruction* null_const = graph_->GetNullConstant();
5683 HInstruction* c2 = graph_->GetIntConstant(2);
5684 HInstruction* c3 = graph_->GetIntConstant(3);
5685
5686 HInstruction* cls = MakeClassLoad();
5687 HInstruction* new_inst = MakeNewInstance(cls);
5688 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
5689 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5690 entry->AddInstruction(cls);
5691 entry->AddInstruction(new_inst);
5692 entry->AddInstruction(write_entry);
5693 entry->AddInstruction(if_inst);
5694 ManuallyBuildEnvFor(cls, {});
5695 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
5696
5697 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
5698 HInstruction* goto_left = new (GetAllocator()) HGoto();
5699 left->AddInstruction(call_left);
5700 left->AddInstruction(goto_left);
5701 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
5702
5703 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
5704 HInstruction* goto_right = new (GetAllocator()) HGoto();
5705 right->AddInstruction(write_right);
5706 right->AddInstruction(goto_right);
5707
5708 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
5709 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
5710 breturn->AddInstruction(read_bottom);
5711 breturn->AddInstruction(return_exit);
5712
5713 SetupExit(exit);
5714
5715 // PerformLSE expects this to be empty.
5716 graph_->ClearDominanceInformation();
5717 LOG(INFO) << "Pre LSE " << blks;
5718 PerformLSEWithPartial();
5719 LOG(INFO) << "Post LSE " << blks;
5720
5721 EXPECT_INS_REMOVED(read_bottom);
5722 EXPECT_INS_REMOVED(write_right);
5723 EXPECT_INS_RETAINED(call_left);
5724 std::vector<HPhi*> merges;
5725 HPredicatedInstanceFieldGet* pred_get =
5726 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
5727 std::tie(merges) = FindAllInstructions<HPhi>(graph_, breturn);
5728 ASSERT_EQ(merges.size(), 2u);
5729 HPhi* merge_value_return = FindOrNull(
5730 merges.begin(), merges.end(), [](HPhi* p) { return p->GetType() == DataType::Type::kInt32; });
5731 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
5732 return p->GetType() == DataType::Type::kReference;
5733 });
5734 ASSERT_NE(merge_alloc, nullptr);
5735 EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc;
5736 EXPECT_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) << *merge_alloc << " cls? " << *cls;
5737 EXPECT_EQ(merge_alloc->InputAt(1), null_const);
5738 ASSERT_NE(pred_get, nullptr);
5739 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
5740 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return) << " pred-get is: " << *pred_get;
5741 EXPECT_INS_EQ(merge_value_return->InputAt(0), graph_->GetIntConstant(0))
5742 << " merge val is: " << *merge_value_return;
5743 EXPECT_INS_EQ(merge_value_return->InputAt(1), c2) << " merge val is: " << *merge_value_return;
5744}
5745
5746// // ENTRY
5747// obj1 = new Obj1();
5748// obj2 = new Obj2();
5749// obj1.field = 3;
5750// obj2.field = 13;
5751// if (parameter_value) {
5752// // LEFT
5753// escape(obj1);
5754// escape(obj2);
5755// } else {
5756// // RIGHT
5757// // ELIMINATE
5758// obj1.field = 2;
5759// obj2.field = 12;
5760// }
5761// EXIT
5762// predicated-ELIMINATE
5763// return obj1.field + obj2.field
5764TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad1) {
5765 VariableSizedHandleScope vshs(Thread::Current());
5766 CreateGraph(/*handles=*/&vshs);
5767 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5768 "exit",
5769 {{"entry", "left"},
5770 {"entry", "right"},
5771 {"left", "breturn"},
5772 {"right", "breturn"},
5773 {"breturn", "exit"}}));
5774#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5775 GET_BLOCK(entry);
5776 GET_BLOCK(exit);
5777 GET_BLOCK(breturn);
5778 GET_BLOCK(left);
5779 GET_BLOCK(right);
5780#undef GET_BLOCK
5781 EnsurePredecessorOrder(breturn, {left, right});
5782 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5783 HInstruction* c2 = graph_->GetIntConstant(2);
5784 HInstruction* c3 = graph_->GetIntConstant(3);
5785 HInstruction* c12 = graph_->GetIntConstant(12);
5786 HInstruction* c13 = graph_->GetIntConstant(13);
5787
5788 HInstruction* cls1 = MakeClassLoad();
5789 HInstruction* cls2 = MakeClassLoad();
5790 HInstruction* new_inst1 = MakeNewInstance(cls1);
5791 HInstruction* new_inst2 = MakeNewInstance(cls2);
5792 HInstruction* write_entry1 = MakeIFieldSet(new_inst1, c3, MemberOffset(32));
5793 HInstruction* write_entry2 = MakeIFieldSet(new_inst2, c13, MemberOffset(32));
5794 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5795 entry->AddInstruction(cls1);
5796 entry->AddInstruction(cls2);
5797 entry->AddInstruction(new_inst1);
5798 entry->AddInstruction(new_inst2);
5799 entry->AddInstruction(write_entry1);
5800 entry->AddInstruction(write_entry2);
5801 entry->AddInstruction(if_inst);
5802 ManuallyBuildEnvFor(cls1, {});
5803 cls2->CopyEnvironmentFrom(cls1->GetEnvironment());
5804 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
5805 new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment());
5806
5807 HInstruction* call_left1 = MakeInvoke(DataType::Type::kVoid, { new_inst1 });
5808 HInstruction* call_left2 = MakeInvoke(DataType::Type::kVoid, { new_inst2 });
5809 HInstruction* goto_left = new (GetAllocator()) HGoto();
5810 left->AddInstruction(call_left1);
5811 left->AddInstruction(call_left2);
5812 left->AddInstruction(goto_left);
5813 call_left1->CopyEnvironmentFrom(cls1->GetEnvironment());
5814 call_left2->CopyEnvironmentFrom(cls1->GetEnvironment());
5815
5816 HInstruction* write_right1 = MakeIFieldSet(new_inst1, c2, MemberOffset(32));
5817 HInstruction* write_right2 = MakeIFieldSet(new_inst2, c12, MemberOffset(32));
5818 HInstruction* goto_right = new (GetAllocator()) HGoto();
5819 right->AddInstruction(write_right1);
5820 right->AddInstruction(write_right2);
5821 right->AddInstruction(goto_right);
5822
5823 HInstruction* read_bottom1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32));
5824 HInstruction* read_bottom2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32));
5825 HInstruction* combine =
5826 new (GetAllocator()) HAdd(DataType::Type::kInt32, read_bottom1, read_bottom2);
5827 HInstruction* return_exit = new (GetAllocator()) HReturn(combine);
5828 breturn->AddInstruction(read_bottom1);
5829 breturn->AddInstruction(read_bottom2);
5830 breturn->AddInstruction(combine);
5831 breturn->AddInstruction(return_exit);
5832
5833 SetupExit(exit);
5834
5835 // PerformLSE expects this to be empty.
5836 graph_->ClearDominanceInformation();
5837 LOG(INFO) << "Pre LSE " << blks;
5838 PerformLSEWithPartial();
5839 LOG(INFO) << "Post LSE " << blks;
5840
5841 EXPECT_INS_REMOVED(read_bottom1);
5842 EXPECT_INS_REMOVED(read_bottom2);
5843 EXPECT_INS_REMOVED(write_right1);
5844 EXPECT_INS_REMOVED(write_right2);
5845 EXPECT_INS_RETAINED(call_left1);
5846 EXPECT_INS_RETAINED(call_left2);
5847 std::vector<HPhi*> merges;
5848 std::vector<HPredicatedInstanceFieldGet*> pred_gets;
5849 std::tie(merges, pred_gets) =
5850 FindAllInstructions<HPhi, HPredicatedInstanceFieldGet>(graph_, breturn);
5851 ASSERT_EQ(merges.size(), 4u);
5852 ASSERT_EQ(pred_gets.size(), 2u);
5853 HPhi* merge_value_return1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5854 return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c2;
5855 });
5856 HPhi* merge_value_return2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5857 return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c12;
5858 });
5859 HPhi* merge_alloc1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5860 return p->GetType() == DataType::Type::kReference &&
5861 p->InputAt(0)->IsNewInstance() &&
5862 p->InputAt(0)->InputAt(0) == cls1;
5863 });
5864 HPhi* merge_alloc2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
5865 return p->GetType() == DataType::Type::kReference &&
5866 p->InputAt(0)->IsNewInstance() &&
5867 p->InputAt(0)->InputAt(0) == cls2;
5868 });
5869 ASSERT_NE(merge_alloc1, nullptr);
5870 ASSERT_NE(merge_alloc2, nullptr);
5871 EXPECT_EQ(merge_alloc1->InputAt(1), graph_->GetNullConstant());
5872 EXPECT_EQ(merge_alloc2->InputAt(1), graph_->GetNullConstant());
5873 HPredicatedInstanceFieldGet* pred_get1 =
5874 FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) {
5875 return pg->GetTarget() == merge_alloc1;
5876 });
5877 HPredicatedInstanceFieldGet* pred_get2 =
5878 FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) {
5879 return pg->GetTarget() == merge_alloc2;
5880 });
5881 ASSERT_NE(pred_get1, nullptr);
5882 EXPECT_INS_EQ(pred_get1->GetTarget(), merge_alloc1);
5883 EXPECT_INS_EQ(pred_get1->GetDefaultValue(), merge_value_return1)
5884 << " pred-get is: " << *pred_get1;
5885 EXPECT_INS_EQ(merge_value_return1->InputAt(0), graph_->GetIntConstant(0))
5886 << " merge val is: " << *merge_value_return1;
5887 EXPECT_INS_EQ(merge_value_return1->InputAt(1), c2) << " merge val is: " << *merge_value_return1;
5888 ASSERT_NE(pred_get2, nullptr);
5889 EXPECT_INS_EQ(pred_get2->GetTarget(), merge_alloc2);
5890 EXPECT_INS_EQ(pred_get2->GetDefaultValue(), merge_value_return2)
5891 << " pred-get is: " << *pred_get2;
5892 EXPECT_INS_EQ(merge_value_return2->InputAt(0), graph_->GetIntConstant(0))
5893 << " merge val is: " << *merge_value_return1;
5894 EXPECT_INS_EQ(merge_value_return2->InputAt(1), c12) << " merge val is: " << *merge_value_return1;
5895}
5896
5897// // ENTRY
5898// obj1 = new Obj1();
5899// obj2 = new Obj2();
5900// obj1.field = 3;
5901// obj2.field = 13;
5902// if (parameter_value) {
5903// // LEFT
5904// escape(obj1);
5905// // ELIMINATE
5906// obj2.field = 12;
5907// } else {
5908// // RIGHT
5909// // ELIMINATE
5910// obj1.field = 2;
5911// escape(obj2);
5912// }
5913// EXIT
5914// predicated-ELIMINATE
5915// return obj1.field + obj2.field
5916TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad2) {
5917 VariableSizedHandleScope vshs(Thread::Current());
5918 CreateGraph(/*handles=*/&vshs);
5919 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
5920 "exit",
5921 {{"entry", "left"},
5922 {"entry", "right"},
5923 {"left", "breturn"},
5924 {"right", "breturn"},
5925 {"breturn", "exit"}}));
5926#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
5927 GET_BLOCK(entry);
5928 GET_BLOCK(exit);
5929 GET_BLOCK(breturn);
5930 GET_BLOCK(left);
5931 GET_BLOCK(right);
5932#undef GET_BLOCK
5933 EnsurePredecessorOrder(breturn, {left, right});
5934 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
5935 HInstruction* c2 = graph_->GetIntConstant(2);
5936 HInstruction* c3 = graph_->GetIntConstant(3);
5937 HInstruction* c12 = graph_->GetIntConstant(12);
5938 HInstruction* c13 = graph_->GetIntConstant(13);
5939
5940 HInstruction* cls1 = MakeClassLoad();
5941 HInstruction* cls2 = MakeClassLoad();
5942 HInstruction* new_inst1 = MakeNewInstance(cls1);
5943 HInstruction* new_inst2 = MakeNewInstance(cls2);
5944 HInstruction* write_entry1 = MakeIFieldSet(new_inst1, c3, MemberOffset(32));
5945 HInstruction* write_entry2 = MakeIFieldSet(new_inst2, c13, MemberOffset(32));
5946 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
5947 entry->AddInstruction(cls1);
5948 entry->AddInstruction(cls2);
5949 entry->AddInstruction(new_inst1);
5950 entry->AddInstruction(new_inst2);
5951 entry->AddInstruction(write_entry1);
5952 entry->AddInstruction(write_entry2);
5953 entry->AddInstruction(if_inst);
5954 ManuallyBuildEnvFor(cls1, {});
5955 cls2->CopyEnvironmentFrom(cls1->GetEnvironment());
5956 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
5957 new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment());
5958
5959 HInstruction* call_left1 = MakeInvoke(DataType::Type::kVoid, { new_inst1 });
5960 HInstruction* write_left2 = MakeIFieldSet(new_inst2, c12, MemberOffset(32));
5961 HInstruction* goto_left = new (GetAllocator()) HGoto();
5962 left->AddInstruction(call_left1);
5963 left->AddInstruction(write_left2);
5964 left->AddInstruction(goto_left);
5965 call_left1->CopyEnvironmentFrom(cls1->GetEnvironment());
5966
5967 HInstruction* write_right1 = MakeIFieldSet(new_inst1, c2, MemberOffset(32));
5968 HInstruction* call_right2 = MakeInvoke(DataType::Type::kVoid, { new_inst2 });
5969 HInstruction* goto_right = new (GetAllocator()) HGoto();
5970 right->AddInstruction(write_right1);
5971 right->AddInstruction(call_right2);
5972 right->AddInstruction(goto_right);
5973 call_right2->CopyEnvironmentFrom(cls1->GetEnvironment());
5974
5975 HInstruction* read_bottom1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32));
5976 HInstruction* read_bottom2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32));
5977 HInstruction* combine =
5978 new (GetAllocator()) HAdd(DataType::Type::kInt32, read_bottom1, read_bottom2);
5979 HInstruction* return_exit = new (GetAllocator()) HReturn(combine);
5980 breturn->AddInstruction(read_bottom1);
5981 breturn->AddInstruction(read_bottom2);
5982 breturn->AddInstruction(combine);
5983 breturn->AddInstruction(return_exit);
5984
5985 SetupExit(exit);
5986
5987 // PerformLSE expects this to be empty.
5988 graph_->ClearDominanceInformation();
5989 LOG(INFO) << "Pre LSE " << blks;
5990 PerformLSEWithPartial();
5991 LOG(INFO) << "Post LSE " << blks;
5992
5993 EXPECT_INS_REMOVED(read_bottom1);
5994 EXPECT_INS_REMOVED(read_bottom2);
5995 EXPECT_INS_REMOVED(write_right1);
5996 EXPECT_INS_REMOVED(write_left2);
5997 EXPECT_INS_RETAINED(call_left1);
5998 EXPECT_INS_RETAINED(call_right2);
5999 std::vector<HPhi*> merges;
6000 std::vector<HPredicatedInstanceFieldGet*> pred_gets;
6001 std::tie(merges, pred_gets) =
6002 FindAllInstructions<HPhi, HPredicatedInstanceFieldGet>(graph_, breturn);
6003 ASSERT_EQ(merges.size(), 4u);
6004 ASSERT_EQ(pred_gets.size(), 2u);
6005 HPhi* merge_value_return1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
6006 return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c2;
6007 });
6008 HPhi* merge_value_return2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
6009 return p->GetType() == DataType::Type::kInt32 && p->InputAt(0) == c12;
6010 });
6011 HPhi* merge_alloc1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
6012 return p->GetType() == DataType::Type::kReference && p->InputAt(1)->IsNullConstant();
6013 });
6014 HPhi* merge_alloc2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
6015 return p->GetType() == DataType::Type::kReference && p->InputAt(0)->IsNullConstant();
6016 });
6017 ASSERT_NE(merge_alloc1, nullptr);
6018 ASSERT_NE(merge_alloc2, nullptr);
6019 EXPECT_TRUE(merge_alloc1->InputAt(0)->IsNewInstance()) << *merge_alloc1;
6020 EXPECT_INS_EQ(merge_alloc1->InputAt(0)->InputAt(0), cls1) << *merge_alloc1;
6021 EXPECT_INS_EQ(merge_alloc1->InputAt(1), graph_->GetNullConstant());
6022 EXPECT_TRUE(merge_alloc2->InputAt(1)->IsNewInstance()) << *merge_alloc2;
6023 EXPECT_INS_EQ(merge_alloc2->InputAt(1)->InputAt(0), cls2) << *merge_alloc2;
6024 EXPECT_INS_EQ(merge_alloc2->InputAt(0), graph_->GetNullConstant());
6025 HPredicatedInstanceFieldGet* pred_get1 =
6026 FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) {
6027 return pg->GetTarget() == merge_alloc1;
6028 });
6029 HPredicatedInstanceFieldGet* pred_get2 =
6030 FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) {
6031 return pg->GetTarget() == merge_alloc2;
6032 });
6033 ASSERT_NE(pred_get1, nullptr);
6034 EXPECT_INS_EQ(pred_get1->GetTarget(), merge_alloc1);
6035 EXPECT_INS_EQ(pred_get1->GetDefaultValue(), merge_value_return1)
6036 << " pred-get is: " << *pred_get1;
6037 EXPECT_INS_EQ(merge_value_return1->InputAt(0), graph_->GetIntConstant(0))
6038 << " merge val is: " << *merge_value_return1;
6039 EXPECT_INS_EQ(merge_value_return1->InputAt(1), c2) << " merge val is: " << *merge_value_return1;
6040 ASSERT_NE(pred_get2, nullptr);
6041 EXPECT_INS_EQ(pred_get2->GetTarget(), merge_alloc2);
6042 EXPECT_INS_EQ(pred_get2->GetDefaultValue(), merge_value_return2)
6043 << " pred-get is: " << *pred_get2;
6044 EXPECT_INS_EQ(merge_value_return2->InputAt(1), graph_->GetIntConstant(0))
6045 << " merge val is: " << *merge_value_return1;
6046 EXPECT_INS_EQ(merge_value_return2->InputAt(0), c12) << " merge val is: " << *merge_value_return1;
6047}
6048
6049// Based on structure seen in `java.util.List
6050// java.util.Collections.checkedList(java.util.List, java.lang.Class)`
6051// Incorrect accounting would cause attempts to materialize both obj1 and obj2
6052// in each of the materialization blocks.
6053// // ENTRY
6054// Obj obj;
6055// if (param1) {
6056// // needs to be moved after param2 check
6057// obj1 = new Obj1();
6058// obj1.foo = 33;
6059// if (param2) {
6060// return obj1.foo;
6061// }
6062// obj = obj1;
6063// } else {
6064// obj2 = new Obj2();
6065// obj2.foo = 44;
6066// if (param2) {
6067// return obj2.foo;
6068// }
6069// obj = obj2;
6070// }
6071// EXIT
6072// // obj = PHI[obj1, obj2]
6073// // NB The phi acts as an escape for both obj1 and obj2 meaning as far as the
6074// // LSA is concerned the escape frontier is left_crit_break->breturn and
6075// // right_crit_break->breturn for both even though only one of the objects is
6076// // actually live at each edge.
6077// // TODO In the future we really should track liveness through PHIs which would
6078// // allow us to entirely remove the allocation in this test.
6079// return obj.foo;
6080TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad3) {
6081 VariableSizedHandleScope vshs(Thread::Current());
6082 CreateGraph(/*handles=*/&vshs);
6083 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
6084 "exit",
6085 {{"entry", "left"},
6086 {"left", "left_end"},
6087 {"left_end", "breturn"},
6088 {"left", "left_exit_early"},
6089 {"left_exit_early", "exit"},
6090 {"entry", "right"},
6091 {"right", "right_end"},
6092 {"right_end", "breturn"},
6093 {"right", "right_exit_early"},
6094 {"right_exit_early", "exit"},
6095 {"breturn", "exit"}}));
6096#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6097 GET_BLOCK(entry);
6098 GET_BLOCK(exit);
6099 GET_BLOCK(breturn);
6100 GET_BLOCK(left);
6101 GET_BLOCK(left_end);
6102 GET_BLOCK(left_exit_early);
6103 GET_BLOCK(right);
6104 GET_BLOCK(right_end);
6105 GET_BLOCK(right_exit_early);
6106#undef GET_BLOCK
6107 EnsurePredecessorOrder(breturn, {left_end, right_end});
6108 HInstruction* param1 = MakeParam(DataType::Type::kBool);
6109 HInstruction* param2 = MakeParam(DataType::Type::kBool);
6110 HInstruction* c33 = graph_->GetIntConstant(33);
6111 HInstruction* c44 = graph_->GetIntConstant(44);
6112
6113 HInstruction* if_inst = new (GetAllocator()) HIf(param1);
6114 entry->AddInstruction(if_inst);
6115
6116 HInstruction* cls1 = MakeClassLoad();
6117 HInstruction* new_inst1 = MakeNewInstance(cls1);
6118 HInstruction* write1 = MakeIFieldSet(new_inst1, c33, MemberOffset(32));
6119 HInstruction* if_left = new (GetAllocator()) HIf(param2);
6120 left->AddInstruction(cls1);
6121 left->AddInstruction(new_inst1);
6122 left->AddInstruction(write1);
6123 left->AddInstruction(if_left);
6124 ManuallyBuildEnvFor(cls1, {});
6125 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
6126
6127 left_end->AddInstruction(new (GetAllocator()) HGoto());
6128
6129 HInstruction* early_exit_left_read =
6130 MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32));
6131 HInstruction* early_exit_left_return = new (GetAllocator()) HReturn(early_exit_left_read);
6132 left_exit_early->AddInstruction(early_exit_left_read);
6133 left_exit_early->AddInstruction(early_exit_left_return);
6134
6135 HInstruction* cls2 = MakeClassLoad();
6136 HInstruction* new_inst2 = MakeNewInstance(cls2);
6137 HInstruction* write2 = MakeIFieldSet(new_inst2, c44, MemberOffset(32));
6138 HInstruction* if_right = new (GetAllocator()) HIf(param2);
6139 right->AddInstruction(cls2);
6140 right->AddInstruction(new_inst2);
6141 right->AddInstruction(write2);
6142 right->AddInstruction(if_right);
6143 cls2->CopyEnvironmentFrom(cls1->GetEnvironment());
6144 new_inst2->CopyEnvironmentFrom(cls2->GetEnvironment());
6145
6146 right_end->AddInstruction(new (GetAllocator()) HGoto());
6147
6148 HInstruction* early_exit_right_read =
6149 MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32));
6150 HInstruction* early_exit_right_return = new (GetAllocator()) HReturn(early_exit_right_read);
6151 right_exit_early->AddInstruction(early_exit_right_read);
6152 right_exit_early->AddInstruction(early_exit_right_return);
6153
6154 HPhi* bottom_phi = MakePhi({new_inst1, new_inst2});
6155 HInstruction* read_bottom = MakeIFieldGet(bottom_phi, DataType::Type::kInt32, MemberOffset(32));
6156 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
6157 breturn->AddPhi(bottom_phi);
6158 breturn->AddInstruction(read_bottom);
6159 breturn->AddInstruction(return_exit);
6160
6161 SetupExit(exit);
6162
6163 // PerformLSE expects this to be empty.
6164 graph_->ClearDominanceInformation();
6165 LOG(INFO) << "Pre LSE " << blks;
6166 PerformLSEWithPartial();
6167 LOG(INFO) << "Post LSE " << blks;
6168
6169 EXPECT_INS_REMOVED(early_exit_left_read);
6170 EXPECT_INS_REMOVED(early_exit_right_read);
6171 EXPECT_INS_RETAINED(bottom_phi);
6172 EXPECT_INS_RETAINED(read_bottom);
6173 EXPECT_INS_EQ(early_exit_left_return->InputAt(0), c33);
6174 EXPECT_INS_EQ(early_exit_right_return->InputAt(0), c44);
6175 // These assert there is only 1 HNewInstance in the given blocks.
6176 HNewInstance* moved_ni1 =
6177 FindSingleInstruction<HNewInstance>(graph_, left_end->GetSinglePredecessor());
6178 HNewInstance* moved_ni2 =
6179 FindSingleInstruction<HNewInstance>(graph_, right_end->GetSinglePredecessor());
6180 ASSERT_NE(moved_ni1, nullptr);
6181 ASSERT_NE(moved_ni2, nullptr);
6182 EXPECT_INS_EQ(bottom_phi->InputAt(0), moved_ni1);
6183 EXPECT_INS_EQ(bottom_phi->InputAt(1), moved_ni2);
6184}
6185
6186// Based on structure seen in `java.util.Set java.util.Collections$UnmodifiableMap.entrySet()`
6187// We end up having to update a PHI generated by normal LSE.
6188// // ENTRY
6189// Obj obj_init = param_obj.BAR;
6190// if (param1) {
6191// Obj other = new Obj();
6192// other.foo = 42;
6193// if (param2) {
6194// return other.foo;
6195// } else {
6196// param_obj.BAR = other;
6197// }
6198// } else { }
6199// EXIT
6200// LSE Turns this into PHI[obj_init, other]
6201// read_bottom = param_obj.BAR;
6202// // won't be changed. The escape happens with .BAR set so this is in escaping cohort.
6203// return read_bottom.foo;
6204TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad4) {
6205 VariableSizedHandleScope vshs(Thread::Current());
6206 CreateGraph(/*handles=*/&vshs);
6207 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
6208 "exit",
6209 {{"entry", "left"},
6210 {"left", "left_early_return"},
6211 {"left_early_return", "exit"},
6212 {"left", "left_write_escape"},
6213 {"left_write_escape", "breturn"},
6214 {"entry", "right"},
6215 {"right", "breturn"},
6216 {"breturn", "exit"}}));
6217#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6218 GET_BLOCK(entry);
6219 GET_BLOCK(exit);
6220 GET_BLOCK(breturn);
6221 GET_BLOCK(left);
6222 GET_BLOCK(left_early_return);
6223 GET_BLOCK(left_write_escape);
6224 GET_BLOCK(right);
6225#undef GET_BLOCK
6226 MemberOffset foo_offset = MemberOffset(32);
6227 MemberOffset bar_offset = MemberOffset(20);
6228 EnsurePredecessorOrder(breturn, {left_write_escape, right});
6229 HInstruction* c42 = graph_->GetIntConstant(42);
6230 HInstruction* param1 = MakeParam(DataType::Type::kBool);
6231 HInstruction* param2 = MakeParam(DataType::Type::kBool);
6232 HInstruction* param_obj = MakeParam(DataType::Type::kReference);
6233
6234 HInstruction* get_initial = MakeIFieldGet(param_obj, DataType::Type::kReference, bar_offset);
6235 HInstruction* if_inst = new (GetAllocator()) HIf(param1);
6236 entry->AddInstruction(get_initial);
6237 entry->AddInstruction(if_inst);
6238
6239 HInstruction* cls1 = MakeClassLoad();
6240 HInstruction* new_inst1 = MakeNewInstance(cls1);
6241 HInstruction* write1 = MakeIFieldSet(new_inst1, c42, foo_offset);
6242 HInstruction* if_left = new (GetAllocator()) HIf(param2);
6243 left->AddInstruction(cls1);
6244 left->AddInstruction(new_inst1);
6245 left->AddInstruction(write1);
6246 left->AddInstruction(if_left);
6247 ManuallyBuildEnvFor(cls1, {});
6248 new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment());
6249
6250 HInstruction* read_early_return = MakeIFieldGet(new_inst1, DataType::Type::kInt32, foo_offset);
6251 HInstruction* return_early = new (GetAllocator()) HReturn(read_early_return);
6252 left_early_return->AddInstruction(read_early_return);
6253 left_early_return->AddInstruction(return_early);
6254
6255 HInstruction* write_escape = MakeIFieldSet(param_obj, new_inst1, bar_offset);
6256 HInstruction* write_goto = new (GetAllocator()) HGoto();
6257 left_write_escape->AddInstruction(write_escape);
6258 left_write_escape->AddInstruction(write_goto);
6259
6260 right->AddInstruction(new (GetAllocator()) HGoto());
6261
6262 HInstruction* read_bottom = MakeIFieldGet(param_obj, DataType::Type::kReference, bar_offset);
6263 HInstruction* final_read = MakeIFieldGet(read_bottom, DataType::Type::kInt32, foo_offset);
6264 HInstruction* return_exit = new (GetAllocator()) HReturn(final_read);
6265 breturn->AddInstruction(read_bottom);
6266 breturn->AddInstruction(final_read);
6267 breturn->AddInstruction(return_exit);
6268
6269 SetupExit(exit);
6270
6271 // PerformLSE expects this to be empty.
6272 graph_->ClearDominanceInformation();
6273 LOG(INFO) << "Pre LSE " << blks;
6274 PerformLSEWithPartial();
6275 LOG(INFO) << "Post LSE " << blks;
6276
6277 EXPECT_INS_REMOVED(read_bottom);
6278 EXPECT_INS_REMOVED(read_early_return);
6279 EXPECT_INS_EQ(return_early->InputAt(0), c42);
6280 EXPECT_INS_RETAINED(final_read);
6281 HNewInstance* moved_ni =
6282 FindSingleInstruction<HNewInstance>(graph_, left_write_escape->GetSinglePredecessor());
6283 EXPECT_TRUE(final_read->InputAt(0)->IsPhi());
6284 EXPECT_INS_EQ(final_read->InputAt(0)->InputAt(0), moved_ni);
6285 EXPECT_INS_EQ(final_read->InputAt(0)->InputAt(1), get_initial);
6286}
6287
6288// // ENTRY
6289// obj = new Obj();
6290// obj.field = 3;
6291// if (parameter_value) {
6292// // LEFT
6293// escape(obj);
6294// } else {
6295// // RIGHT
6296// // ELIMINATE
6297// obj.field = 2;
6298// }
6299// // MERGE
6300// if (second_param) {
6301// // NON_ESCAPE
6302// obj.field = 1;
6303// noescape();
6304// }
6305// EXIT
6306// predicated-ELIMINATE
6307// return obj.field
6308TEST_F(LoadStoreEliminationTest, PredicatedLoad2) {
6309 VariableSizedHandleScope vshs(Thread::Current());
6310 CreateGraph(/*handles=*/&vshs);
6311 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
6312 "exit",
6313 {{"entry", "left"},
6314 {"entry", "right"},
6315 {"left", "merge"},
6316 {"right", "merge"},
6317 {"merge", "non_escape"},
6318 {"non_escape", "breturn"},
6319 {"merge", "crit_break"},
6320 {"crit_break", "breturn"},
6321 {"breturn", "exit"}}));
6322#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6323 GET_BLOCK(entry);
6324 GET_BLOCK(exit);
6325 GET_BLOCK(breturn);
6326 GET_BLOCK(left);
6327 GET_BLOCK(right);
6328 GET_BLOCK(merge);
6329 GET_BLOCK(non_escape);
6330 GET_BLOCK(crit_break);
6331#undef GET_BLOCK
6332 EnsurePredecessorOrder(merge, {left, right});
6333 EnsurePredecessorOrder(breturn, {crit_break, non_escape});
6334 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
6335 HInstruction* bool_value2 = MakeParam(DataType::Type::kBool);
6336 HInstruction* null_const = graph_->GetNullConstant();
6337 HInstruction* c1 = graph_->GetIntConstant(1);
6338 HInstruction* c2 = graph_->GetIntConstant(2);
6339 HInstruction* c3 = graph_->GetIntConstant(3);
6340
6341 HInstruction* cls = MakeClassLoad();
6342 HInstruction* new_inst = MakeNewInstance(cls);
6343 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
6344 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
6345 entry->AddInstruction(cls);
6346 entry->AddInstruction(new_inst);
6347 entry->AddInstruction(write_entry);
6348 entry->AddInstruction(if_inst);
6349 ManuallyBuildEnvFor(cls, {});
6350 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
6351
6352 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
6353 HInstruction* goto_left = new (GetAllocator()) HGoto();
6354 left->AddInstruction(call_left);
6355 left->AddInstruction(goto_left);
6356 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
6357
6358 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
6359 HInstruction* goto_right = new (GetAllocator()) HGoto();
6360 right->AddInstruction(write_right);
6361 right->AddInstruction(goto_right);
6362
6363 HInstruction* merge_if = new (GetAllocator()) HIf(bool_value2);
6364 merge->AddInstruction(merge_if);
6365
6366 crit_break->AddInstruction(new (GetAllocator()) HGoto());
6367
6368 HInstruction* write_non_escape = MakeIFieldSet(new_inst, c1, MemberOffset(32));
6369 HInstruction* non_escape_call = MakeInvoke(DataType::Type::kVoid, {});
6370 HInstruction* non_escape_goto = new (GetAllocator()) HGoto();
6371 non_escape->AddInstruction(write_non_escape);
6372 non_escape->AddInstruction(non_escape_call);
6373 non_escape->AddInstruction(non_escape_goto);
6374 non_escape_call->CopyEnvironmentFrom(cls->GetEnvironment());
6375
6376 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
6377 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
6378 breturn->AddInstruction(read_bottom);
6379 breturn->AddInstruction(return_exit);
6380
6381 SetupExit(exit);
6382
6383 // PerformLSE expects this to be empty.
6384 graph_->ClearDominanceInformation();
6385 LOG(INFO) << "Pre LSE " << blks;
6386 PerformLSEWithPartial();
6387 LOG(INFO) << "Post LSE " << blks;
6388
6389 EXPECT_INS_REMOVED(read_bottom);
6390 EXPECT_INS_REMOVED(write_right);
6391 EXPECT_INS_RETAINED(call_left);
6392 std::vector<HPhi*> merges;
6393 HPredicatedInstanceFieldGet* pred_get =
6394 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
6395 std::tie(merges) = FindAllInstructions<HPhi>(graph_);
6396 ASSERT_EQ(merges.size(), 3u);
6397 HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
6398 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn;
6399 });
6400 HPhi* merge_value_merge = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
6401 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() != breturn;
6402 });
6403 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
6404 return p->GetType() == DataType::Type::kReference;
6405 });
6406 ASSERT_NE(merge_alloc, nullptr);
6407 EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc;
6408 EXPECT_INS_EQ(merge_alloc->InputAt(0)->InputAt(0), cls)
6409 << " phi is: " << merge_alloc->DumpWithArgs();
6410 EXPECT_INS_EQ(merge_alloc->InputAt(1), null_const);
6411 ASSERT_NE(pred_get, nullptr);
6412 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
6413 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return)
6414 << "get is " << pred_get->DumpWithArgs();
6415 EXPECT_INS_EQ(merge_value_return->InputAt(0), merge_value_merge)
6416 << " phi is: " << *merge_value_return;
6417 EXPECT_INS_EQ(merge_value_return->InputAt(1), c1)
6418 << " phi is: " << merge_value_return->DumpWithArgs();
6419 EXPECT_INS_EQ(merge_value_merge->InputAt(0), graph_->GetIntConstant(0))
6420 << " phi is: " << *merge_value_merge;
6421 EXPECT_INS_EQ(merge_value_merge->InputAt(1), c2)
6422 << " phi is: " << merge_value_merge->DumpWithArgs();
6423}
6424
6425// // ENTRY
6426// obj = new Obj();
6427// obj.field = 3;
6428// if (parameter_value) {
6429// // LEFT
6430// escape(obj);
6431// } else {
6432// // RIGHT
6433// // ELIMINATE
6434// obj.field = 2;
6435// }
6436// // MERGE
6437// if (second_param) {
6438// // NON_ESCAPE
6439// obj.field = 1;
6440// }
6441// noescape();
6442// EXIT
6443// predicated-ELIMINATE
6444// return obj.field
6445TEST_F(LoadStoreEliminationTest, PredicatedLoad3) {
6446 VariableSizedHandleScope vshs(Thread::Current());
6447 CreateGraph(/*handles=*/&vshs);
6448 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
6449 "exit",
6450 {{"entry", "left"},
6451 {"entry", "right"},
6452 {"left", "merge"},
6453 {"right", "merge"},
6454 {"merge", "non_escape"},
6455 {"non_escape", "breturn"},
6456 {"merge", "crit_break"},
6457 {"crit_break", "breturn"},
6458 {"breturn", "exit"}}));
6459#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6460 GET_BLOCK(entry);
6461 GET_BLOCK(exit);
6462 GET_BLOCK(breturn);
6463 GET_BLOCK(left);
6464 GET_BLOCK(right);
6465 GET_BLOCK(merge);
6466 GET_BLOCK(crit_break);
6467 GET_BLOCK(non_escape);
6468#undef GET_BLOCK
6469 EnsurePredecessorOrder(merge, {left, right});
6470 EnsurePredecessorOrder(breturn, {crit_break, non_escape});
6471 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
6472 HInstruction* bool_value2 = MakeParam(DataType::Type::kBool);
6473 HInstruction* null_const = graph_->GetNullConstant();
6474 HInstruction* c1 = graph_->GetIntConstant(1);
6475 HInstruction* c2 = graph_->GetIntConstant(2);
6476 HInstruction* c3 = graph_->GetIntConstant(3);
6477
6478 HInstruction* cls = MakeClassLoad();
6479 HInstruction* new_inst = MakeNewInstance(cls);
6480 HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
6481 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
6482 entry->AddInstruction(cls);
6483 entry->AddInstruction(new_inst);
6484 entry->AddInstruction(write_entry);
6485 entry->AddInstruction(if_inst);
6486 ManuallyBuildEnvFor(cls, {});
6487 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
6488
6489 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
6490 HInstruction* goto_left = new (GetAllocator()) HGoto();
6491 left->AddInstruction(call_left);
6492 left->AddInstruction(goto_left);
6493 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
6494
6495 HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32));
6496 HInstruction* goto_right = new (GetAllocator()) HGoto();
6497 right->AddInstruction(write_right);
6498 right->AddInstruction(goto_right);
6499
6500 HInstruction* merge_if = new (GetAllocator()) HIf(bool_value2);
6501 merge->AddInstruction(merge_if);
6502
6503 HInstruction* write_non_escape = MakeIFieldSet(new_inst, c1, MemberOffset(32));
6504 HInstruction* non_escape_goto = new (GetAllocator()) HGoto();
6505 non_escape->AddInstruction(write_non_escape);
6506 non_escape->AddInstruction(non_escape_goto);
6507
6508 crit_break->AddInstruction(new (GetAllocator()) HGoto());
6509
6510 HInstruction* bottom_call = MakeInvoke(DataType::Type::kVoid, {});
6511 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
6512 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
6513 breturn->AddInstruction(bottom_call);
6514 breturn->AddInstruction(read_bottom);
6515 breturn->AddInstruction(return_exit);
6516 bottom_call->CopyEnvironmentFrom(cls->GetEnvironment());
6517
6518 SetupExit(exit);
6519
6520 // PerformLSE expects this to be empty.
6521 graph_->ClearDominanceInformation();
6522 LOG(INFO) << "Pre LSE " << blks;
6523 PerformLSEWithPartial();
6524 LOG(INFO) << "Post LSE " << blks;
6525
6526 EXPECT_INS_REMOVED(read_bottom);
6527 EXPECT_INS_REMOVED(write_right);
6528 EXPECT_INS_RETAINED(call_left);
6529 std::vector<HPhi*> merges;
6530 HPredicatedInstanceFieldGet* pred_get =
6531 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
6532 std::tie(merges) = FindAllInstructions<HPhi>(graph_);
6533 ASSERT_EQ(merges.size(), 3u);
6534 HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
6535 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn;
6536 });
6537 HPhi* merge_value_merge = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) {
6538 return p->GetType() == DataType::Type::kInt32 && p->GetBlock() != breturn;
6539 });
6540 HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) {
6541 return p->GetType() == DataType::Type::kReference;
6542 });
6543 ASSERT_NE(merge_alloc, nullptr);
6544 EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << merge_alloc->DumpWithArgs();
6545 EXPECT_INS_EQ(merge_alloc->InputAt(0)->InputAt(0), cls)
6546 << " phi is: " << merge_alloc->DumpWithArgs();
6547 EXPECT_INS_EQ(merge_alloc->InputAt(1), null_const);
6548 ASSERT_NE(pred_get, nullptr);
6549 EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc);
6550 EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return)
6551 << "get is " << pred_get->DumpWithArgs();
6552 EXPECT_INS_EQ(merge_value_return->InputAt(0), merge_value_merge)
6553 << " phi is: " << *merge_value_return;
6554 EXPECT_INS_EQ(merge_value_return->InputAt(1), c1) << " phi is: " << *merge_value_return;
6555 EXPECT_INS_EQ(merge_value_merge->InputAt(0), graph_->GetIntConstant(0))
6556 << " phi is: " << *merge_value_merge;
6557 EXPECT_INS_EQ(merge_value_merge->InputAt(1), c2) << " phi is: " << *merge_value_merge;
6558}
6559
6560// // ENTRY
6561// obj = new Obj();
6562// // ALL should be kept
6563// switch (parameter_value) {
6564// case 1:
6565// // Case1
6566// obj.field = 1;
6567// call_func(obj);
6568// break;
6569// case 2:
6570// // Case2
6571// obj.field = 2;
6572// call_func(obj);
6573// break;
6574// default:
6575// // Case3
6576// obj.field = 3;
6577// do {
6578// if (test2()) { } else { obj.field = 5; }
6579// } while (test());
6580// break;
6581// }
6582// EXIT
6583// // predicated-ELIMINATE
6584// return obj.field
6585TEST_F(LoadStoreEliminationTest, PartialLoopPhis1) {
6586 VariableSizedHandleScope vshs(Thread::Current());
6587 CreateGraph(/*handles=*/&vshs);
6588 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
6589 "exit",
6590 {{"entry", "bswitch"},
6591 {"bswitch", "case1"},
6592 {"bswitch", "case2"},
6593 {"bswitch", "case3"},
6594 {"case1", "breturn"},
6595 {"case2", "breturn"},
6596 {"case3", "loop_pre_header"},
6597 {"loop_pre_header", "loop_header"},
6598 {"loop_header", "loop_body"},
6599 {"loop_body", "loop_if_left"},
6600 {"loop_body", "loop_if_right"},
6601 {"loop_if_left", "loop_merge"},
6602 {"loop_if_right", "loop_merge"},
6603 {"loop_merge", "loop_end"},
6604 {"loop_end", "loop_header"},
6605 {"loop_end", "critical_break"},
6606 {"critical_break", "breturn"},
6607 {"breturn", "exit"}}));
6608#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6609 GET_BLOCK(entry);
6610 GET_BLOCK(bswitch);
6611 GET_BLOCK(exit);
6612 GET_BLOCK(breturn);
6613 GET_BLOCK(case1);
6614 GET_BLOCK(case2);
6615 GET_BLOCK(case3);
6616
6617 GET_BLOCK(loop_pre_header);
6618 GET_BLOCK(loop_header);
6619 GET_BLOCK(loop_body);
6620 GET_BLOCK(loop_if_left);
6621 GET_BLOCK(loop_if_right);
6622 GET_BLOCK(loop_merge);
6623 GET_BLOCK(loop_end);
6624 GET_BLOCK(critical_break);
6625#undef GET_BLOCK
6626 EnsurePredecessorOrder(breturn, {case1, case2, critical_break});
6627 EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_end});
6628 EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right});
6629 CHECK_SUBROUTINE_FAILURE();
6630 HInstruction* switch_val = MakeParam(DataType::Type::kInt32);
6631 HInstruction* c1 = graph_->GetIntConstant(1);
6632 HInstruction* c2 = graph_->GetIntConstant(2);
6633 HInstruction* c3 = graph_->GetIntConstant(3);
6634 HInstruction* c5 = graph_->GetIntConstant(5);
6635
6636 HInstruction* cls = MakeClassLoad();
6637 HInstruction* new_inst = MakeNewInstance(cls);
6638 HInstruction* entry_goto = new (GetAllocator()) HGoto();
6639 entry->AddInstruction(cls);
6640 entry->AddInstruction(new_inst);
6641 entry->AddInstruction(entry_goto);
6642 ManuallyBuildEnvFor(cls, {});
6643 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
6644
6645 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, switch_val);
6646 bswitch->AddInstruction(switch_inst);
6647
6648 HInstruction* write_c1 = MakeIFieldSet(new_inst, c1, MemberOffset(32));
6649 HInstruction* call_c1 = MakeInvoke(DataType::Type::kVoid, { new_inst });
6650 HInstruction* goto_c1 = new (GetAllocator()) HGoto();
6651 case1->AddInstruction(write_c1);
6652 case1->AddInstruction(call_c1);
6653 case1->AddInstruction(goto_c1);
6654 call_c1->CopyEnvironmentFrom(cls->GetEnvironment());
6655
6656 HInstruction* write_c2 = MakeIFieldSet(new_inst, c2, MemberOffset(32));
6657 HInstruction* call_c2 = MakeInvoke(DataType::Type::kVoid, { new_inst });
6658 HInstruction* goto_c2 = new (GetAllocator()) HGoto();
6659 case2->AddInstruction(write_c2);
6660 case2->AddInstruction(call_c2);
6661 case2->AddInstruction(goto_c2);
6662 call_c2->CopyEnvironmentFrom(cls->GetEnvironment());
6663
6664 HInstruction* write_c3 = MakeIFieldSet(new_inst, c3, MemberOffset(32));
6665 HInstruction* goto_c3 = new (GetAllocator()) HGoto();
6666 case3->AddInstruction(write_c3);
6667 case3->AddInstruction(goto_c3);
6668
6669 HInstruction* goto_preheader = new (GetAllocator()) HGoto();
6670 loop_pre_header->AddInstruction(goto_preheader);
6671
6672 HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck();
6673 HInstruction* goto_header = new (GetAllocator()) HGoto();
6674 loop_header->AddInstruction(suspend_check_header);
6675 loop_header->AddInstruction(goto_header);
6676 suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment());
6677
6678 HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {});
6679 HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body);
6680 loop_body->AddInstruction(call_loop_body);
6681 loop_body->AddInstruction(if_loop_body);
6682 call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment());
6683
6684 HInstruction* goto_loop_left = new (GetAllocator()) HGoto();
6685 loop_if_left->AddInstruction(goto_loop_left);
6686
6687 HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32));
6688 HInstruction* goto_loop_right = new (GetAllocator()) HGoto();
6689 loop_if_right->AddInstruction(write_loop_right);
6690 loop_if_right->AddInstruction(goto_loop_right);
6691
6692 HInstruction* goto_loop_merge = new (GetAllocator()) HGoto();
6693 loop_merge->AddInstruction(goto_loop_merge);
6694
6695 HInstruction* call_end = MakeInvoke(DataType::Type::kBool, {});
6696 HInstruction* if_end = new (GetAllocator()) HIf(call_end);
6697 loop_end->AddInstruction(call_end);
6698 loop_end->AddInstruction(if_end);
6699 call_end->CopyEnvironmentFrom(cls->GetEnvironment());
6700
6701 HInstruction* goto_critical_break = new (GetAllocator()) HGoto();
6702 critical_break->AddInstruction(goto_critical_break);
6703
6704 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
6705 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
6706 breturn->AddInstruction(read_bottom);
6707 breturn->AddInstruction(return_exit);
6708
6709 SetupExit(exit);
6710
6711 // PerformLSE expects this to be empty.
6712 graph_->ClearDominanceInformation();
6713 LOG(INFO) << "Pre LSE " << blks;
6714 PerformLSEWithPartial();
6715 LOG(INFO) << "Post LSE " << blks;
6716
6717 HPredicatedInstanceFieldGet* pred_get =
6718 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
6719 EXPECT_INS_REMOVED(read_bottom) << *read_bottom;
6720 ASSERT_TRUE(pred_get != nullptr);
6721 HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi();
6722 ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs();
6723 EXPECT_INS_EQ(inst_return_phi->InputAt(0),
6724 FindSingleInstruction<HNewInstance>(graph_, case1->GetSinglePredecessor()));
6725 EXPECT_INS_EQ(inst_return_phi->InputAt(1),
6726 FindSingleInstruction<HNewInstance>(graph_, case2->GetSinglePredecessor()));
6727 EXPECT_INS_EQ(inst_return_phi->InputAt(2), graph_->GetNullConstant());
6728 HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi();
6729 ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs();
6730 EXPECT_INS_EQ(inst_value_phi->InputAt(0), graph_->GetIntConstant(0));
6731 EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0));
6732 HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge);
6733 ASSERT_TRUE(loop_merge_phi != nullptr);
6734 HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header);
6735 ASSERT_TRUE(loop_header_phi != nullptr);
6736 EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3);
6737 EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi);
6738 EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi);
6739 EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5);
6740 EXPECT_INS_EQ(inst_value_phi->InputAt(2), loop_merge_phi);
6741 EXPECT_INS_RETAINED(write_c1) << *write_c1;
6742 EXPECT_INS_RETAINED(write_c2) << *write_c2;
6743 EXPECT_INS_REMOVED(write_c3) << *write_c3;
6744 EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right;
6745}
6746
6747// // ENTRY
6748// obj = new Obj();
6749// switch (parameter_value) {
6750// case 1:
6751// // Case1
6752// obj.field = 1;
6753// call_func(obj);
6754// break;
6755// case 2:
6756// // Case2
6757// obj.field = 2;
6758// call_func(obj);
6759// break;
6760// default:
6761// // Case3
6762// obj.field = 3;
6763// while (!test()) {
6764// if (test2()) { } else { obj.field = 5; }
6765// }
6766// break;
6767// }
6768// EXIT
6769// // predicated-ELIMINATE
6770// return obj.field
6771TEST_F(LoadStoreEliminationTest, PartialLoopPhis2) {
6772 VariableSizedHandleScope vshs(Thread::Current());
6773 CreateGraph(/*handles=*/&vshs);
6774 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
6775 "exit",
6776 {{"entry", "bswitch"},
6777 {"bswitch", "case1"},
6778 {"bswitch", "case2"},
6779 {"bswitch", "case3"},
6780 {"case1", "breturn"},
6781 {"case2", "breturn"},
6782 {"case3", "loop_pre_header"},
6783
6784 {"loop_pre_header", "loop_header"},
6785 {"loop_header", "critical_break"},
6786 {"loop_header", "loop_body"},
6787 {"loop_body", "loop_if_left"},
6788 {"loop_body", "loop_if_right"},
6789 {"loop_if_left", "loop_merge"},
6790 {"loop_if_right", "loop_merge"},
6791 {"loop_merge", "loop_header"},
6792
6793 {"critical_break", "breturn"},
6794 {"breturn", "exit"}}));
6795#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6796 GET_BLOCK(entry);
6797 GET_BLOCK(bswitch);
6798 GET_BLOCK(exit);
6799 GET_BLOCK(breturn);
6800 GET_BLOCK(case1);
6801 GET_BLOCK(case2);
6802 GET_BLOCK(case3);
6803
6804 GET_BLOCK(loop_pre_header);
6805 GET_BLOCK(loop_header);
6806 GET_BLOCK(loop_body);
6807 GET_BLOCK(loop_if_left);
6808 GET_BLOCK(loop_if_right);
6809 GET_BLOCK(loop_merge);
6810 GET_BLOCK(critical_break);
6811#undef GET_BLOCK
6812 EnsurePredecessorOrder(breturn, {case1, case2, critical_break});
6813 EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge});
6814 EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right});
6815 CHECK_SUBROUTINE_FAILURE();
6816 HInstruction* switch_val = MakeParam(DataType::Type::kInt32);
6817 HInstruction* c1 = graph_->GetIntConstant(1);
6818 HInstruction* c2 = graph_->GetIntConstant(2);
6819 HInstruction* c3 = graph_->GetIntConstant(3);
6820 HInstruction* c5 = graph_->GetIntConstant(5);
6821
6822 HInstruction* cls = MakeClassLoad();
6823 HInstruction* new_inst = MakeNewInstance(cls);
6824 HInstruction* entry_goto = new (GetAllocator()) HGoto();
6825 entry->AddInstruction(cls);
6826 entry->AddInstruction(new_inst);
6827 entry->AddInstruction(entry_goto);
6828 ManuallyBuildEnvFor(cls, {});
6829 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
6830
6831 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, switch_val);
6832 bswitch->AddInstruction(switch_inst);
6833
6834 HInstruction* write_c1 = MakeIFieldSet(new_inst, c1, MemberOffset(32));
6835 HInstruction* call_c1 = MakeInvoke(DataType::Type::kVoid, { new_inst });
6836 HInstruction* goto_c1 = new (GetAllocator()) HGoto();
6837 case1->AddInstruction(write_c1);
6838 case1->AddInstruction(call_c1);
6839 case1->AddInstruction(goto_c1);
6840 call_c1->CopyEnvironmentFrom(cls->GetEnvironment());
6841
6842 HInstruction* write_c2 = MakeIFieldSet(new_inst, c2, MemberOffset(32));
6843 HInstruction* call_c2 = MakeInvoke(DataType::Type::kVoid, { new_inst });
6844 HInstruction* goto_c2 = new (GetAllocator()) HGoto();
6845 case2->AddInstruction(write_c2);
6846 case2->AddInstruction(call_c2);
6847 case2->AddInstruction(goto_c2);
6848 call_c2->CopyEnvironmentFrom(cls->GetEnvironment());
6849
6850 HInstruction* write_c3 = MakeIFieldSet(new_inst, c3, MemberOffset(32));
6851 HInstruction* goto_c3 = new (GetAllocator()) HGoto();
6852 case3->AddInstruction(write_c3);
6853 case3->AddInstruction(goto_c3);
6854
6855 HInstruction* goto_preheader = new (GetAllocator()) HGoto();
6856 loop_pre_header->AddInstruction(goto_preheader);
6857
6858 HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck();
6859 HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {});
6860 HInstruction* if_header = new (GetAllocator()) HIf(call_header);
6861 loop_header->AddInstruction(suspend_check_header);
6862 loop_header->AddInstruction(call_header);
6863 loop_header->AddInstruction(if_header);
6864 call_header->CopyEnvironmentFrom(cls->GetEnvironment());
6865 suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment());
6866
6867 HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {});
6868 HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body);
6869 loop_body->AddInstruction(call_loop_body);
6870 loop_body->AddInstruction(if_loop_body);
6871 call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment());
6872
6873 HInstruction* goto_loop_left = new (GetAllocator()) HGoto();
6874 loop_if_left->AddInstruction(goto_loop_left);
6875
6876 HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32));
6877 HInstruction* goto_loop_right = new (GetAllocator()) HGoto();
6878 loop_if_right->AddInstruction(write_loop_right);
6879 loop_if_right->AddInstruction(goto_loop_right);
6880
6881 HInstruction* goto_loop_merge = new (GetAllocator()) HGoto();
6882 loop_merge->AddInstruction(goto_loop_merge);
6883
6884 HInstruction* goto_critical_break = new (GetAllocator()) HGoto();
6885 critical_break->AddInstruction(goto_critical_break);
6886
6887 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
6888 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
6889 breturn->AddInstruction(read_bottom);
6890 breturn->AddInstruction(return_exit);
6891
6892 SetupExit(exit);
6893
6894 // PerformLSE expects this to be empty.
6895 graph_->ClearDominanceInformation();
6896 LOG(INFO) << "Pre LSE " << blks;
6897 PerformLSEWithPartial();
6898 LOG(INFO) << "Post LSE " << blks;
6899
6900 HPredicatedInstanceFieldGet* pred_get =
6901 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
6902 EXPECT_INS_REMOVED(read_bottom) << *read_bottom;
6903 ASSERT_TRUE(pred_get != nullptr);
6904 HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi();
6905 ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs();
6906 EXPECT_INS_EQ(inst_return_phi->InputAt(0),
6907 FindSingleInstruction<HNewInstance>(graph_, case1->GetSinglePredecessor()));
6908 EXPECT_INS_EQ(inst_return_phi->InputAt(1),
6909 FindSingleInstruction<HNewInstance>(graph_, case2->GetSinglePredecessor()));
6910 EXPECT_INS_EQ(inst_return_phi->InputAt(2), graph_->GetNullConstant());
6911 HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi();
6912 ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs();
6913 EXPECT_INS_EQ(inst_value_phi->InputAt(0), graph_->GetIntConstant(0));
6914 EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0));
6915 HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge);
6916 ASSERT_TRUE(loop_merge_phi != nullptr);
6917 HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header);
6918 ASSERT_TRUE(loop_header_phi != nullptr);
6919 EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3);
6920 EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi);
6921 EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi);
6922 EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5);
6923 EXPECT_INS_EQ(inst_value_phi->InputAt(2), loop_header_phi);
6924 EXPECT_INS_RETAINED(write_c1) << *write_c1;
6925 EXPECT_INS_RETAINED(write_c2) << *write_c2;
6926 EXPECT_INS_REMOVED(write_c3) << *write_c3;
6927 EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right;
6928}
6929
6930// // ENTRY
6931// obj = new Obj();
6932// obj.field = 3;
6933// while (!test()) {
6934// if (test2()) { } else { obj.field = 5; }
6935// }
6936// if (parameter_value) {
6937// escape(obj);
6938// }
6939// EXIT
6940// return obj.field
6941TEST_F(LoadStoreEliminationTest, PartialLoopPhis3) {
6942 VariableSizedHandleScope vshs(Thread::Current());
6943 CreateGraph(/*handles=*/&vshs);
6944 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
6945 "exit",
6946 {{"entry", "loop_pre_header"},
6947
6948 {"loop_pre_header", "loop_header"},
6949 {"loop_header", "escape_check"},
6950 {"loop_header", "loop_body"},
6951 {"loop_body", "loop_if_left"},
6952 {"loop_body", "loop_if_right"},
6953 {"loop_if_left", "loop_merge"},
6954 {"loop_if_right", "loop_merge"},
6955 {"loop_merge", "loop_header"},
6956
6957 {"escape_check", "escape"},
6958 {"escape_check", "no_escape"},
6959 {"no_escape", "breturn"},
6960 {"escape", "breturn"},
6961 {"breturn", "exit"}}));
6962#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
6963 GET_BLOCK(entry);
6964 GET_BLOCK(exit);
6965 GET_BLOCK(breturn);
6966 GET_BLOCK(no_escape);
6967 GET_BLOCK(escape);
6968 GET_BLOCK(escape_check);
6969
6970 GET_BLOCK(loop_pre_header);
6971 GET_BLOCK(loop_header);
6972 GET_BLOCK(loop_body);
6973 GET_BLOCK(loop_if_left);
6974 GET_BLOCK(loop_if_right);
6975 GET_BLOCK(loop_merge);
6976#undef GET_BLOCK
6977 EnsurePredecessorOrder(breturn, {no_escape, escape});
6978 EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge});
6979 EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right});
6980 CHECK_SUBROUTINE_FAILURE();
6981 HInstruction* bool_val = MakeParam(DataType::Type::kBool);
6982 HInstruction* c3 = graph_->GetIntConstant(3);
6983 HInstruction* c5 = graph_->GetIntConstant(5);
6984
6985 HInstruction* cls = MakeClassLoad();
6986 HInstruction* new_inst = MakeNewInstance(cls);
6987 HInstruction* entry_goto = new (GetAllocator()) HGoto();
6988 entry->AddInstruction(cls);
6989 entry->AddInstruction(new_inst);
6990 entry->AddInstruction(entry_goto);
6991 ManuallyBuildEnvFor(cls, {});
6992 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
6993
6994 HInstruction* write_pre_header = MakeIFieldSet(new_inst, c3, MemberOffset(32));
6995 HInstruction* goto_preheader = new (GetAllocator()) HGoto();
6996 loop_pre_header->AddInstruction(write_pre_header);
6997 loop_pre_header->AddInstruction(goto_preheader);
6998
6999 HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck();
7000 HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {});
7001 HInstruction* if_header = new (GetAllocator()) HIf(call_header);
7002 loop_header->AddInstruction(suspend_check_header);
7003 loop_header->AddInstruction(call_header);
7004 loop_header->AddInstruction(if_header);
7005 call_header->CopyEnvironmentFrom(cls->GetEnvironment());
7006 suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment());
7007
7008 HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {});
7009 HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body);
7010 loop_body->AddInstruction(call_loop_body);
7011 loop_body->AddInstruction(if_loop_body);
7012 call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment());
7013
7014 HInstruction* goto_loop_left = new (GetAllocator()) HGoto();
7015 loop_if_left->AddInstruction(goto_loop_left);
7016
7017 HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32));
7018 HInstruction* goto_loop_right = new (GetAllocator()) HGoto();
7019 loop_if_right->AddInstruction(write_loop_right);
7020 loop_if_right->AddInstruction(goto_loop_right);
7021
7022 HInstruction* goto_loop_merge = new (GetAllocator()) HGoto();
7023 loop_merge->AddInstruction(goto_loop_merge);
7024
7025 HInstruction* if_esc_check = new (GetAllocator()) HIf(bool_val);
7026 escape_check->AddInstruction(if_esc_check);
7027
7028 HInstruction* call_escape = MakeInvoke(DataType::Type::kVoid, { new_inst });
7029 HInstruction* goto_escape = new (GetAllocator()) HGoto();
7030 escape->AddInstruction(call_escape);
7031 escape->AddInstruction(goto_escape);
7032 call_escape->CopyEnvironmentFrom(cls->GetEnvironment());
7033
7034 HInstruction* goto_no_escape = new (GetAllocator()) HGoto();
7035 no_escape->AddInstruction(goto_no_escape);
7036
7037 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7038 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
7039 breturn->AddInstruction(read_bottom);
7040 breturn->AddInstruction(return_exit);
7041
7042 SetupExit(exit);
7043
7044 // PerformLSE expects this to be empty.
7045 graph_->ClearDominanceInformation();
7046 LOG(INFO) << "Pre LSE " << blks;
7047 PerformLSEWithPartial();
7048 LOG(INFO) << "Post LSE " << blks;
7049
7050 HPredicatedInstanceFieldGet* pred_get =
7051 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7052 EXPECT_INS_REMOVED(read_bottom) << *read_bottom;
7053 ASSERT_TRUE(pred_get != nullptr);
7054 HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi();
7055 ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs();
7056 EXPECT_INS_EQ(inst_return_phi->InputAt(0), graph_->GetNullConstant());
7057 EXPECT_INS_EQ(inst_return_phi->InputAt(1),
7058 FindSingleInstruction<HNewInstance>(graph_, escape->GetSinglePredecessor()));
7059 HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi();
7060 ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs();
7061 HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header);
7062 HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge);
7063 EXPECT_INS_EQ(inst_value_phi->InputAt(0), loop_header_phi);
7064 EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0));
7065 EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3);
7066 EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi);
7067 EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi);
7068 EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5);
7069 HInstanceFieldSet* mat_set =
7070 FindSingleInstruction<HInstanceFieldSet>(graph_, escape->GetSinglePredecessor());
7071 ASSERT_NE(mat_set, nullptr);
7072 EXPECT_INS_EQ(mat_set->InputAt(1), loop_header_phi);
7073 EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right;
7074 EXPECT_INS_REMOVED(write_pre_header) << *write_pre_header;
7075}
7076
7077// // ENTRY
7078// obj = new Obj();
7079// if (parameter_value) {
7080// escape(obj);
7081// }
7082// obj.field = 3;
7083// while (!test()) {
7084// if (test2()) { } else { obj.field = 5; }
7085// }
7086// EXIT
7087// return obj.field
7088TEST_F(LoadStoreEliminationTest, PartialLoopPhis4) {
7089 VariableSizedHandleScope vshs(Thread::Current());
7090 CreateGraph(/*handles=*/&vshs);
7091 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
7092 "exit",
7093 {{"entry", "escape_check"},
7094 {"escape_check", "escape"},
7095 {"escape_check", "no_escape"},
7096 {"no_escape", "loop_pre_header"},
7097 {"escape", "loop_pre_header"},
7098
7099 {"loop_pre_header", "loop_header"},
7100 {"loop_header", "breturn"},
7101 {"loop_header", "loop_body"},
7102 {"loop_body", "loop_if_left"},
7103 {"loop_body", "loop_if_right"},
7104 {"loop_if_left", "loop_merge"},
7105 {"loop_if_right", "loop_merge"},
7106 {"loop_merge", "loop_header"},
7107
7108 {"breturn", "exit"}}));
7109#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
7110 GET_BLOCK(entry);
7111 GET_BLOCK(exit);
7112 GET_BLOCK(breturn);
7113 GET_BLOCK(no_escape);
7114 GET_BLOCK(escape);
7115 GET_BLOCK(escape_check);
7116
7117 GET_BLOCK(loop_pre_header);
7118 GET_BLOCK(loop_header);
7119 GET_BLOCK(loop_body);
7120 GET_BLOCK(loop_if_left);
7121 GET_BLOCK(loop_if_right);
7122 GET_BLOCK(loop_merge);
7123#undef GET_BLOCK
7124 EnsurePredecessorOrder(loop_pre_header, {no_escape, escape});
7125 EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge});
7126 EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right});
7127 CHECK_SUBROUTINE_FAILURE();
7128 HInstruction* bool_val = MakeParam(DataType::Type::kBool);
7129 HInstruction* c3 = graph_->GetIntConstant(3);
7130 HInstruction* c5 = graph_->GetIntConstant(5);
7131
7132 HInstruction* cls = MakeClassLoad();
7133 HInstruction* new_inst = MakeNewInstance(cls);
7134 HInstruction* entry_goto = new (GetAllocator()) HGoto();
7135 entry->AddInstruction(cls);
7136 entry->AddInstruction(new_inst);
7137 entry->AddInstruction(entry_goto);
7138 ManuallyBuildEnvFor(cls, {});
7139 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
7140
7141 HInstruction* if_esc_check = new (GetAllocator()) HIf(bool_val);
7142 escape_check->AddInstruction(if_esc_check);
7143
7144 HInstruction* call_escape = MakeInvoke(DataType::Type::kVoid, { new_inst });
7145 HInstruction* goto_escape = new (GetAllocator()) HGoto();
7146 escape->AddInstruction(call_escape);
7147 escape->AddInstruction(goto_escape);
7148 call_escape->CopyEnvironmentFrom(cls->GetEnvironment());
7149
7150 HInstruction* goto_no_escape = new (GetAllocator()) HGoto();
7151 no_escape->AddInstruction(goto_no_escape);
7152
7153 HInstruction* write_pre_header = MakeIFieldSet(new_inst, c3, MemberOffset(32));
7154 HInstruction* goto_preheader = new (GetAllocator()) HGoto();
7155 loop_pre_header->AddInstruction(write_pre_header);
7156 loop_pre_header->AddInstruction(goto_preheader);
7157
7158 HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck();
7159 HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {});
7160 HInstruction* if_header = new (GetAllocator()) HIf(call_header);
7161 loop_header->AddInstruction(suspend_check_header);
7162 loop_header->AddInstruction(call_header);
7163 loop_header->AddInstruction(if_header);
7164 call_header->CopyEnvironmentFrom(cls->GetEnvironment());
7165 suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment());
7166
7167 HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {});
7168 HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body);
7169 loop_body->AddInstruction(call_loop_body);
7170 loop_body->AddInstruction(if_loop_body);
7171 call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment());
7172
7173 HInstruction* goto_loop_left = new (GetAllocator()) HGoto();
7174 loop_if_left->AddInstruction(goto_loop_left);
7175
7176 HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32));
7177 HInstruction* goto_loop_right = new (GetAllocator()) HGoto();
7178 loop_if_right->AddInstruction(write_loop_right);
7179 loop_if_right->AddInstruction(goto_loop_right);
7180
7181 HInstruction* goto_loop_merge = new (GetAllocator()) HGoto();
7182 loop_merge->AddInstruction(goto_loop_merge);
7183
7184 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7185 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
7186 breturn->AddInstruction(read_bottom);
7187 breturn->AddInstruction(return_exit);
7188
7189 SetupExit(exit);
7190
7191 // PerformLSE expects this to be empty.
7192 graph_->ClearDominanceInformation();
7193 LOG(INFO) << "Pre LSE " << blks;
7194 PerformLSEWithPartial();
7195 LOG(INFO) << "Post LSE " << blks;
7196
7197 HPredicatedInstanceFieldGet* pred_get =
7198 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7199 EXPECT_INS_REMOVED(read_bottom) << *read_bottom;
7200 ASSERT_TRUE(pred_get != nullptr);
7201 HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi();
7202 ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs();
7203 EXPECT_INS_EQ(inst_return_phi->InputAt(0), graph_->GetNullConstant());
7204 EXPECT_INS_EQ(inst_return_phi->InputAt(1),
7205 FindSingleInstruction<HNewInstance>(graph_, escape->GetSinglePredecessor()));
7206 HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi();
7207 ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs();
7208 HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header);
7209 HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge);
7210 EXPECT_INS_EQ(inst_value_phi, loop_header_phi);
7211 EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3);
7212 EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi);
7213 EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi);
7214 EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5);
7215 EXPECT_INS_RETAINED(write_loop_right) << *write_loop_right;
7216 EXPECT_TRUE(write_loop_right->AsInstanceFieldSet()->GetIsPredicatedSet()) << *write_loop_right;
7217 EXPECT_INS_RETAINED(write_pre_header) << *write_pre_header;
7218 EXPECT_TRUE(write_pre_header->AsInstanceFieldSet()->GetIsPredicatedSet()) << *write_pre_header;
7219}
7220
7221// // ENTRY
7222// obj = new Obj();
7223// obj.field = 3;
7224// while (!test()) {
7225// if (test2()) { } else { obj.field += 5; }
7226// }
7227// if (parameter_value) {
7228// escape(obj);
7229// }
7230// EXIT
7231// return obj.field
7232TEST_F(LoadStoreEliminationTest, PartialLoopPhis5) {
7233 VariableSizedHandleScope vshs(Thread::Current());
7234 CreateGraph(/*handles=*/&vshs);
7235 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
7236 "exit",
7237 {{"entry", "loop_pre_header"},
7238 {"loop_pre_header", "loop_header"},
7239 {"loop_header", "escape_check"},
7240 {"loop_header", "loop_body"},
7241 {"loop_body", "loop_if_left"},
7242 {"loop_body", "loop_if_right"},
7243 {"loop_if_left", "loop_merge"},
7244 {"loop_if_right", "loop_merge"},
7245 {"loop_merge", "loop_header"},
7246 {"escape_check", "escape"},
7247 {"escape_check", "no_escape"},
7248 {"no_escape", "breturn"},
7249 {"escape", "breturn"},
7250 {"breturn", "exit"}}));
7251#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
7252 GET_BLOCK(entry);
7253 GET_BLOCK(exit);
7254 GET_BLOCK(breturn);
7255 GET_BLOCK(no_escape);
7256 GET_BLOCK(escape);
7257 GET_BLOCK(escape_check);
7258
7259 GET_BLOCK(loop_pre_header);
7260 GET_BLOCK(loop_header);
7261 GET_BLOCK(loop_body);
7262 GET_BLOCK(loop_if_left);
7263 GET_BLOCK(loop_if_right);
7264 GET_BLOCK(loop_merge);
7265#undef GET_BLOCK
7266 EnsurePredecessorOrder(breturn, {no_escape, escape});
7267 EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge});
7268 EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right});
7269 CHECK_SUBROUTINE_FAILURE();
7270 HInstruction* bool_val = MakeParam(DataType::Type::kBool);
7271 HInstruction* c3 = graph_->GetIntConstant(3);
7272 HInstruction* c5 = graph_->GetIntConstant(5);
7273
7274 HInstruction* cls = MakeClassLoad();
7275 HInstruction* new_inst = MakeNewInstance(cls);
7276 HInstruction* entry_goto = new (GetAllocator()) HGoto();
7277 entry->AddInstruction(cls);
7278 entry->AddInstruction(new_inst);
7279 entry->AddInstruction(entry_goto);
7280 ManuallyBuildEnvFor(cls, {});
7281 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
7282
7283 HInstruction* write_pre_header = MakeIFieldSet(new_inst, c3, MemberOffset(32));
7284 HInstruction* goto_preheader = new (GetAllocator()) HGoto();
7285 loop_pre_header->AddInstruction(write_pre_header);
7286 loop_pre_header->AddInstruction(goto_preheader);
7287
7288 HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck();
7289 HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {});
7290 HInstruction* if_header = new (GetAllocator()) HIf(call_header);
7291 loop_header->AddInstruction(suspend_check_header);
7292 loop_header->AddInstruction(call_header);
7293 loop_header->AddInstruction(if_header);
7294 call_header->CopyEnvironmentFrom(cls->GetEnvironment());
7295 suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment());
7296
7297 HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {});
7298 HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body);
7299 loop_body->AddInstruction(call_loop_body);
7300 loop_body->AddInstruction(if_loop_body);
7301 call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment());
7302
7303 HInstruction* goto_loop_left = new (GetAllocator()) HGoto();
7304 loop_if_left->AddInstruction(goto_loop_left);
7305
7306 HInstruction* read_loop_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7307 HInstruction* add_loop_right =
7308 new (GetAllocator()) HAdd(DataType::Type::kInt32, read_loop_right, c5);
7309 HInstruction* write_loop_right = MakeIFieldSet(new_inst, add_loop_right, MemberOffset(32));
7310 HInstruction* goto_loop_right = new (GetAllocator()) HGoto();
7311 loop_if_right->AddInstruction(read_loop_right);
7312 loop_if_right->AddInstruction(add_loop_right);
7313 loop_if_right->AddInstruction(write_loop_right);
7314 loop_if_right->AddInstruction(goto_loop_right);
7315
7316 HInstruction* goto_loop_merge = new (GetAllocator()) HGoto();
7317 loop_merge->AddInstruction(goto_loop_merge);
7318
7319 HInstruction* if_esc_check = new (GetAllocator()) HIf(bool_val);
7320 escape_check->AddInstruction(if_esc_check);
7321
7322 HInstruction* call_escape = MakeInvoke(DataType::Type::kVoid, { new_inst });
7323 HInstruction* goto_escape = new (GetAllocator()) HGoto();
7324 escape->AddInstruction(call_escape);
7325 escape->AddInstruction(goto_escape);
7326 call_escape->CopyEnvironmentFrom(cls->GetEnvironment());
7327
7328 HInstruction* goto_no_escape = new (GetAllocator()) HGoto();
7329 no_escape->AddInstruction(goto_no_escape);
7330
7331 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7332 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
7333 breturn->AddInstruction(read_bottom);
7334 breturn->AddInstruction(return_exit);
7335
7336 SetupExit(exit);
7337
7338 // PerformLSE expects this to be empty.
7339 graph_->ClearDominanceInformation();
7340 LOG(INFO) << "Pre LSE " << blks;
7341 PerformLSEWithPartial();
7342 LOG(INFO) << "Post LSE " << blks;
7343
7344 HPredicatedInstanceFieldGet* pred_get =
7345 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7346 EXPECT_INS_REMOVED(read_bottom) << *read_bottom;
7347 ASSERT_TRUE(pred_get != nullptr);
7348 HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi();
7349 ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs();
7350 EXPECT_INS_EQ(inst_return_phi->InputAt(0), graph_->GetNullConstant());
7351 EXPECT_INS_EQ(inst_return_phi->InputAt(1),
7352 FindSingleInstruction<HNewInstance>(graph_, escape->GetSinglePredecessor()));
7353 HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi();
7354 ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs();
7355 HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header);
7356 HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge);
7357 EXPECT_INS_EQ(inst_value_phi->InputAt(0), loop_header_phi);
7358 EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0));
7359 EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3);
7360 EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi);
7361 EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi);
7362 EXPECT_INS_EQ(loop_merge_phi->InputAt(1), add_loop_right);
7363 EXPECT_INS_EQ(add_loop_right->InputAt(0), loop_header_phi);
7364 EXPECT_INS_EQ(add_loop_right->InputAt(1), c5);
7365 HInstanceFieldSet* mat_set =
7366 FindSingleInstruction<HInstanceFieldSet>(graph_, escape->GetSinglePredecessor());
7367 ASSERT_NE(mat_set, nullptr);
7368 EXPECT_INS_EQ(mat_set->InputAt(1), loop_header_phi);
7369 EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right;
7370 EXPECT_INS_REMOVED(write_pre_header) << *write_pre_header;
7371}
7372
7373// TODO This should really be in an Instruction simplifier Gtest but (1) that
7374// doesn't exist and (2) we should move this simplification to directly in the
7375// LSE pass since there is more information then.
7376// // ENTRY
7377// obj = new Obj();
7378// obj.field = 3;
7379// if (param) {
7380// escape(obj);
7381// } else {
7382// obj.field = 10;
7383// }
7384// return obj.field;
7385TEST_F(LoadStoreEliminationTest, SimplifyTest) {
7386 VariableSizedHandleScope vshs(Thread::Current());
7387 CreateGraph(&vshs);
7388 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
7389 "exit",
7390 {{"entry", "left"},
7391 {"entry", "right"},
7392 {"left", "breturn"},
7393 {"right", "breturn"},
7394 {"breturn", "exit"}}));
7395#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
7396 GET_BLOCK(entry);
7397 GET_BLOCK(exit);
7398 GET_BLOCK(breturn);
7399 GET_BLOCK(left);
7400 GET_BLOCK(right);
7401#undef GET_BLOCK
7402 EnsurePredecessorOrder(breturn, {left, right});
7403
7404 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
7405 HInstruction* c3 = graph_->GetIntConstant(3);
7406 HInstruction* c10 = graph_->GetIntConstant(10);
7407
7408 HInstruction* cls = MakeClassLoad();
7409 HInstruction* new_inst = MakeNewInstance(cls);
7410 HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32));
7411 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
7412 entry->AddInstruction(cls);
7413 entry->AddInstruction(new_inst);
7414 entry->AddInstruction(write_start);
7415 entry->AddInstruction(if_inst);
7416 ManuallyBuildEnvFor(cls, {});
7417 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
7418
7419 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst });
7420 HInstruction* goto_left = new (GetAllocator()) HGoto();
7421 left->AddInstruction(call_left);
7422 left->AddInstruction(goto_left);
7423 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
7424
7425 HInstruction* write_right = MakeIFieldSet(new_inst, c10, MemberOffset(32));
7426 HInstruction* goto_right = new (GetAllocator()) HGoto();
7427 right->AddInstruction(write_right);
7428 right->AddInstruction(goto_right);
7429
7430 HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7431 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
7432 breturn->AddInstruction(read_end);
7433 breturn->AddInstruction(return_exit);
7434
7435 SetupExit(exit);
7436
7437 // PerformLSE expects this to be empty.
7438 graph_->ClearDominanceInformation();
7439 LOG(INFO) << "Pre LSE " << blks;
Alex Light86fe9b82020-11-16 16:54:01 +00007440 PerformLSE();
7441
Alex Lightfc1ce4e2021-01-22 14:05:13 +00007442 // Run the code-simplifier too
7443 LOG(INFO) << "Pre simplification " << blks;
7444 InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
7445 simp.Run();
7446
7447 LOG(INFO) << "Post LSE " << blks;
7448
7449 EXPECT_INS_REMOVED(write_right);
7450 EXPECT_INS_REMOVED(write_start);
7451 EXPECT_INS_REMOVED(read_end);
7452 EXPECT_INS_RETAINED(call_left);
7453
7454 HPredicatedInstanceFieldGet* pred_get =
7455 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7456 ASSERT_NE(pred_get, nullptr);
7457 EXPECT_INS_EQ(pred_get->GetDefaultValue(), c10);
7458}
7459
7460
7461// TODO This should really be in an Instruction simplifier Gtest but (1) that
7462// doesn't exist and (2) we should move this simplification to directly in the
7463// LSE pass since there is more information then.
7464//
7465// This checks that we don't replace phis when the replacement isn't valid at
7466// that point (i.e. it doesn't dominate)
7467// // ENTRY
7468// obj = new Obj();
7469// obj.field = 3;
7470// if (param) {
7471// escape(obj);
7472// } else {
7473// obj.field = noescape();
7474// }
7475// return obj.field;
7476TEST_F(LoadStoreEliminationTest, SimplifyTest2) {
7477 VariableSizedHandleScope vshs(Thread::Current());
7478 CreateGraph(&vshs);
7479 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
7480 "exit",
7481 {{"entry", "left"},
7482 {"entry", "right"},
7483 {"left", "breturn"},
7484 {"right", "breturn"},
7485 {"breturn", "exit"}}));
7486#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
7487 GET_BLOCK(entry);
7488 GET_BLOCK(exit);
7489 GET_BLOCK(breturn);
7490 GET_BLOCK(left);
7491 GET_BLOCK(right);
7492#undef GET_BLOCK
7493 EnsurePredecessorOrder(breturn, {left, right});
7494
7495 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
7496 HInstruction* c3 = graph_->GetIntConstant(3);
7497
7498 HInstruction* cls = MakeClassLoad();
7499 HInstruction* new_inst = MakeNewInstance(cls);
7500 HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32));
7501 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
7502 entry->AddInstruction(cls);
7503 entry->AddInstruction(new_inst);
7504 entry->AddInstruction(write_start);
7505 entry->AddInstruction(if_inst);
7506 ManuallyBuildEnvFor(cls, {});
7507 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
7508
7509 HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, {new_inst});
7510 HInstruction* goto_left = new (GetAllocator()) HGoto();
7511 left->AddInstruction(call_left);
7512 left->AddInstruction(goto_left);
7513 call_left->CopyEnvironmentFrom(cls->GetEnvironment());
7514
7515 HInstruction* call_right = MakeInvoke(DataType::Type::kInt32, {});
7516 HInstruction* write_right = MakeIFieldSet(new_inst, call_right, MemberOffset(32));
7517 HInstruction* goto_right = new (GetAllocator()) HGoto();
7518 right->AddInstruction(call_right);
7519 right->AddInstruction(write_right);
7520 right->AddInstruction(goto_right);
7521 call_right->CopyEnvironmentFrom(cls->GetEnvironment());
7522
7523 HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7524 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
7525 breturn->AddInstruction(read_end);
7526 breturn->AddInstruction(return_exit);
7527
7528 SetupExit(exit);
7529
7530 // PerformLSE expects this to be empty.
7531 graph_->ClearDominanceInformation();
7532 LOG(INFO) << "Pre LSE " << blks;
7533 PerformLSE();
7534
7535 // Run the code-simplifier too
7536 LOG(INFO) << "Pre simplification " << blks;
7537 InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
7538 simp.Run();
7539
7540 LOG(INFO) << "Post LSE " << blks;
7541
7542 EXPECT_INS_REMOVED(write_right);
7543 EXPECT_INS_REMOVED(write_start);
7544 EXPECT_INS_REMOVED(read_end);
7545 EXPECT_INS_RETAINED(call_left);
7546 EXPECT_INS_RETAINED(call_right);
7547 EXPECT_EQ(call_right->GetBlock(), right);
7548
7549 HPredicatedInstanceFieldGet* pred_get =
7550 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7551 ASSERT_NE(pred_get, nullptr);
7552 EXPECT_TRUE(pred_get->GetDefaultValue()->IsPhi()) << pred_get->DumpWithArgs();
7553 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), graph_->GetIntConstant(0))
7554 << pred_get->DumpWithArgs();
7555 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), call_right) << pred_get->DumpWithArgs();
7556}
7557
7558// TODO This should really be in an Instruction simplifier Gtest but (1) that
7559// doesn't exist and (2) we should move this simplification to directly in the
7560// LSE pass since there is more information then.
7561//
7562// This checks that we replace phis even when there are multiple replacements as
7563// long as they are equal
7564// // ENTRY
7565// obj = new Obj();
7566// obj.field = 3;
7567// switch (param) {
7568// case 1:
7569// escape(obj);
7570// break;
7571// case 2:
7572// obj.field = 10;
7573// break;
7574// case 3:
7575// obj.field = 10;
7576// break;
7577// }
7578// return obj.field;
7579TEST_F(LoadStoreEliminationTest, SimplifyTest3) {
7580 VariableSizedHandleScope vshs(Thread::Current());
7581 CreateGraph(&vshs);
7582 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
7583 "exit",
7584 {{"entry", "case1"},
7585 {"entry", "case2"},
7586 {"entry", "case3"},
7587 {"case1", "breturn"},
7588 {"case2", "breturn"},
7589 {"case3", "breturn"},
7590 {"breturn", "exit"}}));
7591#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
7592 GET_BLOCK(entry);
7593 GET_BLOCK(exit);
7594 GET_BLOCK(breturn);
7595 GET_BLOCK(case1);
7596 GET_BLOCK(case2);
7597 GET_BLOCK(case3);
7598#undef GET_BLOCK
7599 EnsurePredecessorOrder(breturn, {case1, case2, case3});
7600
7601 HInstruction* int_val = MakeParam(DataType::Type::kInt32);
7602 HInstruction* c3 = graph_->GetIntConstant(3);
7603 HInstruction* c10 = graph_->GetIntConstant(10);
7604
7605 HInstruction* cls = MakeClassLoad();
7606 HInstruction* new_inst = MakeNewInstance(cls);
7607 HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32));
7608 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val);
7609 entry->AddInstruction(cls);
7610 entry->AddInstruction(new_inst);
7611 entry->AddInstruction(write_start);
7612 entry->AddInstruction(switch_inst);
7613 ManuallyBuildEnvFor(cls, {});
7614 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
7615
7616 HInstruction* call_case1 = MakeInvoke(DataType::Type::kVoid, {new_inst});
7617 HInstruction* goto_case1 = new (GetAllocator()) HGoto();
7618 case1->AddInstruction(call_case1);
7619 case1->AddInstruction(goto_case1);
7620 call_case1->CopyEnvironmentFrom(cls->GetEnvironment());
7621
7622 HInstruction* write_case2 = MakeIFieldSet(new_inst, c10, MemberOffset(32));
7623 HInstruction* goto_case2 = new (GetAllocator()) HGoto();
7624 case2->AddInstruction(write_case2);
7625 case2->AddInstruction(goto_case2);
7626
7627 HInstruction* write_case3 = MakeIFieldSet(new_inst, c10, MemberOffset(32));
7628 HInstruction* goto_case3 = new (GetAllocator()) HGoto();
7629 case3->AddInstruction(write_case3);
7630 case3->AddInstruction(goto_case3);
7631
7632 HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7633 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
7634 breturn->AddInstruction(read_end);
7635 breturn->AddInstruction(return_exit);
7636
7637 SetupExit(exit);
7638
7639 // PerformLSE expects this to be empty.
7640 graph_->ClearDominanceInformation();
7641 LOG(INFO) << "Pre LSE " << blks;
7642 PerformLSE();
7643
7644 // Run the code-simplifier too
7645 LOG(INFO) << "Pre simplification " << blks;
7646 InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
7647 simp.Run();
7648
7649 LOG(INFO) << "Post LSE " << blks;
7650
7651 EXPECT_INS_REMOVED(write_case2);
7652 EXPECT_INS_REMOVED(write_case3);
7653 EXPECT_INS_REMOVED(write_start);
7654 EXPECT_INS_REMOVED(read_end);
7655 EXPECT_INS_RETAINED(call_case1);
7656
7657 HPredicatedInstanceFieldGet* pred_get =
7658 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7659 ASSERT_NE(pred_get, nullptr);
7660 EXPECT_INS_EQ(pred_get->GetDefaultValue(), c10)
7661 << pred_get->DumpWithArgs();
7662}
7663
7664// TODO This should really be in an Instruction simplifier Gtest but (1) that
7665// doesn't exist and (2) we should move this simplification to directly in the
7666// LSE pass since there is more information then.
7667//
7668// This checks that we don't replace phis even when there are multiple
7669// replacements if they are not equal
7670// // ENTRY
7671// obj = new Obj();
7672// obj.field = 3;
7673// switch (param) {
7674// case 1:
7675// escape(obj);
7676// break;
7677// case 2:
7678// obj.field = 10;
7679// break;
7680// case 3:
7681// obj.field = 20;
7682// break;
7683// }
7684// return obj.field;
7685TEST_F(LoadStoreEliminationTest, SimplifyTest4) {
7686 VariableSizedHandleScope vshs(Thread::Current());
7687 CreateGraph(&vshs);
7688 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
7689 "exit",
7690 {{"entry", "case1"},
7691 {"entry", "case2"},
7692 {"entry", "case3"},
7693 {"case1", "breturn"},
7694 {"case2", "breturn"},
7695 {"case3", "breturn"},
7696 {"breturn", "exit"}}));
7697#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
7698 GET_BLOCK(entry);
7699 GET_BLOCK(exit);
7700 GET_BLOCK(breturn);
7701 GET_BLOCK(case1);
7702 GET_BLOCK(case2);
7703 GET_BLOCK(case3);
7704#undef GET_BLOCK
7705 EnsurePredecessorOrder(breturn, {case1, case2, case3});
7706
7707 HInstruction* int_val = MakeParam(DataType::Type::kInt32);
7708 HInstruction* c3 = graph_->GetIntConstant(3);
7709 HInstruction* c10 = graph_->GetIntConstant(10);
7710 HInstruction* c20 = graph_->GetIntConstant(20);
7711
7712 HInstruction* cls = MakeClassLoad();
7713 HInstruction* new_inst = MakeNewInstance(cls);
7714 HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32));
7715 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val);
7716 entry->AddInstruction(cls);
7717 entry->AddInstruction(new_inst);
7718 entry->AddInstruction(write_start);
7719 entry->AddInstruction(switch_inst);
7720 ManuallyBuildEnvFor(cls, {});
7721 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
7722
7723 HInstruction* call_case1 = MakeInvoke(DataType::Type::kVoid, {new_inst});
7724 HInstruction* goto_case1 = new (GetAllocator()) HGoto();
7725 case1->AddInstruction(call_case1);
7726 case1->AddInstruction(goto_case1);
7727 call_case1->CopyEnvironmentFrom(cls->GetEnvironment());
7728
7729 HInstruction* write_case2 = MakeIFieldSet(new_inst, c10, MemberOffset(32));
7730 HInstruction* goto_case2 = new (GetAllocator()) HGoto();
7731 case2->AddInstruction(write_case2);
7732 case2->AddInstruction(goto_case2);
7733
7734 HInstruction* write_case3 = MakeIFieldSet(new_inst, c20, MemberOffset(32));
7735 HInstruction* goto_case3 = new (GetAllocator()) HGoto();
7736 case3->AddInstruction(write_case3);
7737 case3->AddInstruction(goto_case3);
7738
7739 HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7740 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
7741 breturn->AddInstruction(read_end);
7742 breturn->AddInstruction(return_exit);
7743
7744 SetupExit(exit);
7745
7746 // PerformLSE expects this to be empty.
7747 graph_->ClearDominanceInformation();
7748 LOG(INFO) << "Pre LSE " << blks;
7749 PerformLSE();
7750
7751 // Run the code-simplifier too
7752 LOG(INFO) << "Pre simplification " << blks;
7753 InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
7754 simp.Run();
7755
7756 LOG(INFO) << "Post LSE " << blks;
7757
7758 EXPECT_INS_REMOVED(write_case2);
7759 EXPECT_INS_REMOVED(write_case3);
7760 EXPECT_INS_REMOVED(write_start);
7761 EXPECT_INS_REMOVED(read_end);
7762 EXPECT_INS_RETAINED(call_case1);
7763
7764 HPredicatedInstanceFieldGet* pred_get =
7765 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7766 ASSERT_NE(pred_get, nullptr);
7767 EXPECT_TRUE(pred_get->GetDefaultValue()->IsPhi())
7768 << pred_get->DumpWithArgs();
7769 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), graph_->GetIntConstant(0));
7770 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), c10);
7771 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(2), c20);
7772}
7773
7774// Make sure that irreducible loops don't screw up Partial LSE. We can't pull
7775// phis through them so we need to treat them as escapes.
7776// TODO We should be able to do better than this? Need to do some research.
7777// // ENTRY
7778// obj = new Obj();
7779// obj.foo = 11;
7780// if (param1) {
7781// } else {
7782// // irreducible loop here. NB the objdoesn't actually escape
7783// obj.foo = 33;
7784// if (param2) {
7785// goto inner;
7786// } else {
7787// while (test()) {
7788// if (test()) {
7789// obj.foo = 66;
7790// } else {
7791// }
7792// inner:
7793// }
7794// }
7795// }
7796// return obj.foo;
7797TEST_F(LoadStoreEliminationTest, PartialIrreducibleLoop) {
7798 VariableSizedHandleScope vshs(Thread::Current());
7799 CreateGraph(&vshs);
7800 AdjacencyListGraph blks(SetupFromAdjacencyList("start",
7801 "exit",
7802 {{"start", "entry"},
7803 {"entry", "left"},
7804 {"entry", "right"},
7805 {"left", "breturn"},
7806
7807 {"right", "right_crit_break_loop"},
7808 {"right_crit_break_loop", "loop_header"},
7809 {"right", "right_crit_break_end"},
7810 {"right_crit_break_end", "loop_end"},
7811
7812 {"loop_header", "loop_body"},
7813 {"loop_body", "loop_left"},
7814 {"loop_body", "loop_right"},
7815 {"loop_left", "loop_end"},
7816 {"loop_right", "loop_end"},
7817 {"loop_end", "loop_header"},
7818 {"loop_header", "loop_header_crit_break"},
7819 {"loop_header_crit_break", "breturn"},
7820
7821 {"breturn", "exit"}}));
7822#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
7823 GET_BLOCK(start);
7824 GET_BLOCK(entry);
7825 GET_BLOCK(exit);
7826 GET_BLOCK(breturn);
7827 GET_BLOCK(left);
7828 GET_BLOCK(right);
7829 GET_BLOCK(right_crit_break_end);
7830 GET_BLOCK(right_crit_break_loop);
7831 GET_BLOCK(loop_header);
7832 GET_BLOCK(loop_header_crit_break);
7833 GET_BLOCK(loop_body);
7834 GET_BLOCK(loop_left);
7835 GET_BLOCK(loop_right);
7836 GET_BLOCK(loop_end);
7837#undef GET_BLOCK
7838 EnsurePredecessorOrder(breturn, {left, loop_header_crit_break});
7839 HInstruction* c11 = graph_->GetIntConstant(11);
7840 HInstruction* c33 = graph_->GetIntConstant(33);
7841 HInstruction* c66 = graph_->GetIntConstant(66);
7842 HInstruction* param1 = MakeParam(DataType::Type::kBool);
7843 HInstruction* param2 = MakeParam(DataType::Type::kBool);
7844
7845 HInstruction* suspend = new (GetAllocator()) HSuspendCheck();
7846 HInstruction* start_goto = new (GetAllocator()) HGoto();
7847 start->AddInstruction(suspend);
7848 start->AddInstruction(start_goto);
7849 ManuallyBuildEnvFor(suspend, {});
7850
7851 HInstruction* cls = MakeClassLoad();
7852 HInstruction* new_inst = MakeNewInstance(cls);
7853 HInstruction* write_start = MakeIFieldSet(new_inst, c11, MemberOffset(32));
7854 HInstruction* if_inst = new (GetAllocator()) HIf(param1);
7855 entry->AddInstruction(cls);
7856 entry->AddInstruction(new_inst);
7857 entry->AddInstruction(write_start);
7858 entry->AddInstruction(if_inst);
7859 ManuallyBuildEnvFor(cls, {});
7860 new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
7861
7862 left->AddInstruction(new (GetAllocator()) HGoto());
7863
7864 right->AddInstruction(MakeIFieldSet(new_inst, c33, MemberOffset(32)));
7865 right->AddInstruction(new (GetAllocator()) HIf(param2));
7866
7867 right_crit_break_end->AddInstruction(new (GetAllocator()) HGoto());
7868 right_crit_break_loop->AddInstruction(new (GetAllocator()) HGoto());
7869
7870 HInstruction* header_suspend = new (GetAllocator()) HSuspendCheck();
7871 HInstruction* header_invoke = MakeInvoke(DataType::Type::kBool, {});
7872 HInstruction* header_if = new (GetAllocator()) HIf(header_invoke);
7873 loop_header->AddInstruction(header_suspend);
7874 loop_header->AddInstruction(header_invoke);
7875 loop_header->AddInstruction(header_if);
7876 header_suspend->CopyEnvironmentFrom(cls->GetEnvironment());
7877 header_invoke->CopyEnvironmentFrom(cls->GetEnvironment());
7878
7879 HInstruction* body_invoke = MakeInvoke(DataType::Type::kBool, {});
7880 HInstruction* body_if = new (GetAllocator()) HIf(body_invoke);
7881 loop_body->AddInstruction(body_invoke);
7882 loop_body->AddInstruction(body_if);
7883 body_invoke->CopyEnvironmentFrom(cls->GetEnvironment());
7884
7885 HInstruction* left_set = MakeIFieldSet(new_inst, c66, MemberOffset(32));
7886 HInstruction* left_goto = MakeIFieldSet(new_inst, c66, MemberOffset(32));
7887 loop_left->AddInstruction(left_set);
7888 loop_left->AddInstruction(left_goto);
7889
7890 loop_right->AddInstruction(new (GetAllocator()) HGoto());
7891
7892 loop_end->AddInstruction(new (GetAllocator()) HGoto());
7893
7894 HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
7895 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
7896 breturn->AddInstruction(read_end);
7897 breturn->AddInstruction(return_exit);
7898
7899 SetupExit(exit);
7900
7901 // PerformLSE expects this to be empty.
7902 graph_->ClearDominanceInformation();
7903 LOG(INFO) << "Pre LSE " << blks;
7904 PerformLSE();
7905 LOG(INFO) << "Post LSE " << blks;
7906
7907 EXPECT_TRUE(loop_header->IsLoopHeader());
7908 EXPECT_TRUE(loop_header->GetLoopInformation()->IsIrreducible());
7909
7910 EXPECT_INS_RETAINED(left_set);
7911 EXPECT_INS_REMOVED(write_start);
7912 EXPECT_INS_REMOVED(read_end);
7913
7914 HPredicatedInstanceFieldGet* pred_get =
7915 FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
7916 ASSERT_NE(pred_get, nullptr);
7917 ASSERT_TRUE(pred_get->GetDefaultValue()->IsPhi()) << pred_get->DumpWithArgs();
7918 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), c11);
7919 EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), graph_->GetIntConstant(0));
7920 ASSERT_TRUE(pred_get->GetTarget()->IsPhi()) << pred_get->DumpWithArgs();
7921 EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(0), graph_->GetNullConstant());
7922 HNewInstance* mat = FindSingleInstruction<HNewInstance>(graph_, right->GetSinglePredecessor());
7923 ASSERT_NE(mat, nullptr);
7924 EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(1), mat);
Alex Light86fe9b82020-11-16 16:54:01 +00007925}
7926
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00007927} // namespace art