summaryrefslogtreecommitdiff
path: root/compiler/optimizing/load_store_elimination_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing/load_store_elimination_test.cc')
-rw-r--r--compiler/optimizing/load_store_elimination_test.cc382
1 files changed, 381 insertions, 1 deletions
diff --git a/compiler/optimizing/load_store_elimination_test.cc b/compiler/optimizing/load_store_elimination_test.cc
index 56cc769062..14246522b0 100644
--- a/compiler/optimizing/load_store_elimination_test.cc
+++ b/compiler/optimizing/load_store_elimination_test.cc
@@ -8228,7 +8228,7 @@ class UsesOrderDependentTestGroup
// no_escape() // If `obj` escaped, the field value can change. (Avoid non-partial LSE.)
// b = obj.foo;
// return a + b;
-TEST_P(UsesOrderDependentTestGroup, RecordPredicatedReplacements) {
+TEST_P(UsesOrderDependentTestGroup, RecordPredicatedReplacements1) {
VariableSizedHandleScope vshs(Thread::Current());
CreateGraph(&vshs);
AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
@@ -8371,8 +8371,388 @@ TEST_P(UsesOrderDependentTestGroup, RecordPredicatedReplacements) {
ASSERT_INS_EQ(other_input, replacement_middle_read);
}
+// Regression test for a bad DCHECK() found while trying to write a test for b/188188275.
+// // ENTRY
+// obj = new Obj();
+// obj.foo = 11;
+// if (param1) {
+// // LEFT1
+// escape(obj);
+// } else {
+// // RIGHT1
+// }
+// // MIDDLE
+// a = obj.foo;
+// if (param2) {
+// // LEFT2
+// no_escape();
+// } else {
+// // RIGHT2
+// }
+// // BRETURN
+// b = obj.foo;
+// return a + b;
+TEST_P(UsesOrderDependentTestGroup, RecordPredicatedReplacements2) {
+ VariableSizedHandleScope vshs(Thread::Current());
+ CreateGraph(&vshs);
+ AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
+ "exit",
+ {{"entry", "left1"},
+ {"entry", "right1"},
+ {"left1", "middle"},
+ {"right1", "middle"},
+ {"middle", "left2"},
+ {"middle", "right2"},
+ {"left2", "breturn"},
+ {"right2", "breturn"},
+ {"breturn", "exit"}}));
+#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
+ GET_BLOCK(entry);
+ GET_BLOCK(left1);
+ GET_BLOCK(right1);
+ GET_BLOCK(middle);
+ GET_BLOCK(left2);
+ GET_BLOCK(right2);
+ GET_BLOCK(breturn);
+ GET_BLOCK(exit);
+#undef GET_BLOCK
+ EnsurePredecessorOrder(middle, {left1, right1});
+ EnsurePredecessorOrder(breturn, {left2, right2});
+ HInstruction* c0 = graph_->GetIntConstant(0);
+ HInstruction* cnull = graph_->GetNullConstant();
+ HInstruction* c11 = graph_->GetIntConstant(11);
+ HInstruction* param1 = MakeParam(DataType::Type::kBool);
+ HInstruction* param2 = MakeParam(DataType::Type::kBool);
+
+ HInstruction* suspend = new (GetAllocator()) HSuspendCheck();
+ HInstruction* cls = MakeClassLoad();
+ HInstruction* new_inst = MakeNewInstance(cls);
+ HInstruction* entry_write = MakeIFieldSet(new_inst, c11, MemberOffset(32));
+ HInstruction* entry_if = new (GetAllocator()) HIf(param1);
+ entry->AddInstruction(suspend);
+ entry->AddInstruction(cls);
+ entry->AddInstruction(new_inst);
+ entry->AddInstruction(entry_write);
+ entry->AddInstruction(entry_if);
+ ManuallyBuildEnvFor(suspend, {});
+ ManuallyBuildEnvFor(cls, {});
+ ManuallyBuildEnvFor(new_inst, {});
+
+ HInstruction* left1_call = MakeInvoke(DataType::Type::kVoid, { new_inst });
+ HInstruction* left1_goto = new (GetAllocator()) HGoto();
+ left1->AddInstruction(left1_call);
+ left1->AddInstruction(left1_goto);
+ ManuallyBuildEnvFor(left1_call, {});
+
+ HInstruction* right1_goto = new (GetAllocator()) HGoto();
+ right1->AddInstruction(right1_goto);
+
+ HInstruction* middle_read = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
+ HInstruction* middle_if = new (GetAllocator()) HIf(param2);
+ if (GetParam() == UsesOrder::kDefaultOrder) {
+ middle->AddInstruction(middle_read);
+ }
+ middle->AddInstruction(middle_if);
+
+ HInstruction* left2_call = MakeInvoke(DataType::Type::kVoid, {});
+ HInstruction* left2_goto = new (GetAllocator()) HGoto();
+ left2->AddInstruction(left2_call);
+ left2->AddInstruction(left2_goto);
+ ManuallyBuildEnvFor(left2_call, {});
+
+ HInstruction* right2_goto = new (GetAllocator()) HGoto();
+ right2->AddInstruction(right2_goto);
+
+ HInstruction* breturn_read = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
+ HInstruction* breturn_add =
+ new (GetAllocator()) HAdd(DataType::Type::kInt32, middle_read, breturn_read);
+ HInstruction* breturn_return = new (GetAllocator()) HReturn(breturn_add);
+ breturn->AddInstruction(breturn_read);
+ breturn->AddInstruction(breturn_add);
+ breturn->AddInstruction(breturn_return);
+
+ if (GetParam() == UsesOrder::kReverseOrder) {
+ // Insert `middle_read` in the same position as for the `kDefaultOrder` case.
+ // The only difference is the order of entries in `new_inst->GetUses()` which
+ // is used by `HeapReferenceData::CollectReplacements()` and defines the order
+ // of instructions to process for `HeapReferenceData::PredicateInstructions()`.
+ middle->InsertInstructionBefore(middle_read, middle_if);
+ }
+
+ SetupExit(exit);
+
+ // PerformLSE expects this to be empty.
+ graph_->ClearDominanceInformation();
+ LOG(INFO) << "Pre LSE " << blks;
+ PerformLSE();
+ LOG(INFO) << "Post LSE " << blks;
+
+ EXPECT_INS_RETAINED(cls);
+ EXPECT_INS_REMOVED(new_inst);
+ HNewInstance* replacement_new_inst = FindSingleInstruction<HNewInstance>(graph_);
+ ASSERT_NE(replacement_new_inst, nullptr);
+ EXPECT_INS_REMOVED(entry_write);
+ HInstanceFieldSet* replacement_write = FindSingleInstruction<HInstanceFieldSet>(graph_);
+ ASSERT_NE(replacement_write, nullptr);
+ ASSERT_FALSE(replacement_write->GetIsPredicatedSet());
+ ASSERT_INS_EQ(replacement_write->InputAt(0), replacement_new_inst);
+ ASSERT_INS_EQ(replacement_write->InputAt(1), c11);
+
+ EXPECT_INS_RETAINED(left1_call);
+
+ EXPECT_INS_REMOVED(middle_read);
+ HPredicatedInstanceFieldGet* replacement_middle_read =
+ FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, middle);
+ ASSERT_NE(replacement_middle_read, nullptr);
+ ASSERT_TRUE(replacement_middle_read->GetTarget()->IsPhi());
+ ASSERT_EQ(2u, replacement_middle_read->GetTarget()->AsPhi()->InputCount());
+ ASSERT_INS_EQ(replacement_middle_read->GetTarget()->AsPhi()->InputAt(0), replacement_new_inst);
+ ASSERT_INS_EQ(replacement_middle_read->GetTarget()->AsPhi()->InputAt(1), cnull);
+ ASSERT_TRUE(replacement_middle_read->GetDefaultValue()->IsPhi());
+ ASSERT_EQ(2u, replacement_middle_read->GetDefaultValue()->AsPhi()->InputCount());
+ ASSERT_INS_EQ(replacement_middle_read->GetDefaultValue()->AsPhi()->InputAt(0), c0);
+ ASSERT_INS_EQ(replacement_middle_read->GetDefaultValue()->AsPhi()->InputAt(1), c11);
+
+ EXPECT_INS_RETAINED(left2_call);
+
+ EXPECT_INS_REMOVED(breturn_read);
+ HPredicatedInstanceFieldGet* replacement_breturn_read =
+ FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
+ ASSERT_NE(replacement_breturn_read, nullptr);
+ ASSERT_INS_EQ(replacement_breturn_read->GetTarget(), replacement_middle_read->GetTarget());
+ ASSERT_INS_EQ(replacement_breturn_read->GetDefaultValue(), replacement_middle_read);
+}
+
INSTANTIATE_TEST_SUITE_P(LoadStoreEliminationTest,
UsesOrderDependentTestGroup,
testing::Values(UsesOrder::kDefaultOrder, UsesOrder::kReverseOrder));
+// The parameter is the number of times we call `std::next_permutation` (from 0 to 5)
+// so that we test all 6 permutation of three items.
+class UsesOrderDependentTestGroupForThreeItems
+ : public LoadStoreEliminationTestBase<CommonCompilerTestWithParam<size_t>> {};
+
+// Make sure that after we record replacements by predicated loads, we correctly
+// use that predicated load for Phi placeholders that were previously marked as
+// replaced by the now removed unpredicated load. (The fix for bug 183897743 was
+// not good enough.) Bug: 188188275
+// // ENTRY
+// obj = new Obj();
+// obj.foo = 11;
+// if (param1) {
+// // LEFT1
+// escape(obj);
+// } else {
+// // RIGHT1
+// }
+// // MIDDLE1
+// a = obj.foo;
+// if (param2) {
+// // LEFT2
+// no_escape1();
+// } else {
+// // RIGHT2
+// }
+// // MIDDLE2
+// if (param3) {
+// // LEFT3
+// x = obj.foo;
+// no_escape2();
+// } else {
+// // RIGHT3
+// x = 0;
+// }
+// // BRETURN
+// b = obj.foo;
+// return a + b + x;
+TEST_P(UsesOrderDependentTestGroupForThreeItems, RecordPredicatedReplacements3) {
+ VariableSizedHandleScope vshs(Thread::Current());
+ CreateGraph(&vshs);
+ AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
+ "exit",
+ {{"entry", "left1"},
+ {"entry", "right1"},
+ {"left1", "middle1"},
+ {"right1", "middle1"},
+ {"middle1", "left2"},
+ {"middle1", "right2"},
+ {"left2", "middle2"},
+ {"right2", "middle2"},
+ {"middle2", "left3"},
+ {"middle2", "right3"},
+ {"left3", "breturn"},
+ {"right3", "breturn"},
+ {"breturn", "exit"}}));
+#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
+ GET_BLOCK(entry);
+ GET_BLOCK(left1);
+ GET_BLOCK(right1);
+ GET_BLOCK(middle1);
+ GET_BLOCK(left2);
+ GET_BLOCK(right2);
+ GET_BLOCK(middle2);
+ GET_BLOCK(left3);
+ GET_BLOCK(right3);
+ GET_BLOCK(breturn);
+ GET_BLOCK(exit);
+#undef GET_BLOCK
+ EnsurePredecessorOrder(middle1, {left1, right1});
+ EnsurePredecessorOrder(middle2, {left2, right2});
+ EnsurePredecessorOrder(breturn, {left3, right3});
+ HInstruction* c0 = graph_->GetIntConstant(0);
+ HInstruction* cnull = graph_->GetNullConstant();
+ HInstruction* c11 = graph_->GetIntConstant(11);
+ HInstruction* param1 = MakeParam(DataType::Type::kBool);
+ HInstruction* param2 = MakeParam(DataType::Type::kBool);
+ HInstruction* param3 = MakeParam(DataType::Type::kBool);
+
+ HInstruction* suspend = new (GetAllocator()) HSuspendCheck();
+ HInstruction* cls = MakeClassLoad();
+ HInstruction* new_inst = MakeNewInstance(cls);
+ HInstruction* entry_write = MakeIFieldSet(new_inst, c11, MemberOffset(32));
+ HInstruction* entry_if = new (GetAllocator()) HIf(param1);
+ entry->AddInstruction(suspend);
+ entry->AddInstruction(cls);
+ entry->AddInstruction(new_inst);
+ entry->AddInstruction(entry_write);
+ entry->AddInstruction(entry_if);
+ ManuallyBuildEnvFor(suspend, {});
+ ManuallyBuildEnvFor(cls, {});
+ ManuallyBuildEnvFor(new_inst, {});
+
+ HInstruction* left1_call = MakeInvoke(DataType::Type::kVoid, { new_inst });
+ HInstruction* left1_goto = new (GetAllocator()) HGoto();
+ left1->AddInstruction(left1_call);
+ left1->AddInstruction(left1_goto);
+ ManuallyBuildEnvFor(left1_call, {});
+
+ HInstruction* right1_goto = new (GetAllocator()) HGoto();
+ right1->AddInstruction(right1_goto);
+
+ HInstruction* middle1_read = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
+ HInstruction* middle1_if = new (GetAllocator()) HIf(param2);
+ // Delay inserting `middle1_read`, do that later with ordering based on `GetParam()`.
+ middle1->AddInstruction(middle1_if);
+
+ HInstruction* left2_call = MakeInvoke(DataType::Type::kVoid, {});
+ HInstruction* left2_goto = new (GetAllocator()) HGoto();
+ left2->AddInstruction(left2_call);
+ left2->AddInstruction(left2_goto);
+ ManuallyBuildEnvFor(left2_call, {});
+
+ HInstruction* right2_goto = new (GetAllocator()) HGoto();
+ right2->AddInstruction(right2_goto);
+
+ HInstruction* middle2_if = new (GetAllocator()) HIf(param3);
+ middle2->AddInstruction(middle2_if);
+
+ HInstruction* left3_read = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
+ HInstruction* left3_call = MakeInvoke(DataType::Type::kVoid, {});
+ HInstruction* left3_goto = new (GetAllocator()) HGoto();
+ // Delay inserting `left3_read`, do that later with ordering based on `GetParam()`.
+ left3->AddInstruction(left3_call);
+ left3->AddInstruction(left3_goto);
+ ManuallyBuildEnvFor(left3_call, {});
+
+ HInstruction* right3_goto = new (GetAllocator()) HGoto();
+ right3->AddInstruction(right3_goto);
+
+ HPhi* breturn_phi = MakePhi({left3_read, c0});
+ HInstruction* breturn_read = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
+ HInstruction* breturn_add1 =
+ new (GetAllocator()) HAdd(DataType::Type::kInt32, middle1_read, breturn_read);
+ HInstruction* breturn_add2 =
+ new (GetAllocator()) HAdd(DataType::Type::kInt32, breturn_add1, breturn_phi);
+ HInstruction* breturn_return = new (GetAllocator()) HReturn(breturn_add2);
+ breturn->AddPhi(breturn_phi);
+ // Delay inserting `breturn_read`, do that later with ordering based on `GetParam()`.
+ breturn->AddInstruction(breturn_add1);
+ breturn->AddInstruction(breturn_add2);
+ breturn->AddInstruction(breturn_return);
+
+ // Insert reads in the same positions but in different insertion orders.
+ // The only difference is the order of entries in `new_inst->GetUses()` which
+ // is used by `HeapReferenceData::CollectReplacements()` and defines the order
+ // of instructions to process for `HeapReferenceData::PredicateInstructions()`.
+ std::tuple<size_t, HInstruction*, HInstruction*> read_insertions[] = {
+ { 0u, middle1_read, middle1_if },
+ { 1u, left3_read, left3_call },
+ { 2u, breturn_read, breturn_add1 },
+ };
+ for (size_t i = 0, num = GetParam(); i != num; ++i) {
+ std::next_permutation(read_insertions, read_insertions + std::size(read_insertions));
+ }
+ for (auto [order, read, cursor] : read_insertions) {
+ cursor->GetBlock()->InsertInstructionBefore(read, cursor);
+ }
+
+ SetupExit(exit);
+
+ // PerformLSE expects this to be empty.
+ graph_->ClearDominanceInformation();
+ LOG(INFO) << "Pre LSE " << blks;
+ PerformLSE();
+ LOG(INFO) << "Post LSE " << blks;
+
+ EXPECT_INS_RETAINED(cls);
+ EXPECT_INS_REMOVED(new_inst);
+ HNewInstance* replacement_new_inst = FindSingleInstruction<HNewInstance>(graph_);
+ ASSERT_NE(replacement_new_inst, nullptr);
+ EXPECT_INS_REMOVED(entry_write);
+ HInstanceFieldSet* replacement_write = FindSingleInstruction<HInstanceFieldSet>(graph_);
+ ASSERT_NE(replacement_write, nullptr);
+ ASSERT_FALSE(replacement_write->GetIsPredicatedSet());
+ ASSERT_INS_EQ(replacement_write->InputAt(0), replacement_new_inst);
+ ASSERT_INS_EQ(replacement_write->InputAt(1), c11);
+
+ EXPECT_INS_RETAINED(left1_call);
+
+ EXPECT_INS_REMOVED(middle1_read);
+ HPredicatedInstanceFieldGet* replacement_middle1_read =
+ FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, middle1);
+ ASSERT_NE(replacement_middle1_read, nullptr);
+ ASSERT_TRUE(replacement_middle1_read->GetTarget()->IsPhi());
+ ASSERT_EQ(2u, replacement_middle1_read->GetTarget()->AsPhi()->InputCount());
+ ASSERT_INS_EQ(replacement_middle1_read->GetTarget()->AsPhi()->InputAt(0), replacement_new_inst);
+ ASSERT_INS_EQ(replacement_middle1_read->GetTarget()->AsPhi()->InputAt(1), cnull);
+ ASSERT_TRUE(replacement_middle1_read->GetDefaultValue()->IsPhi());
+ ASSERT_EQ(2u, replacement_middle1_read->GetDefaultValue()->AsPhi()->InputCount());
+ ASSERT_INS_EQ(replacement_middle1_read->GetDefaultValue()->AsPhi()->InputAt(0), c0);
+ ASSERT_INS_EQ(replacement_middle1_read->GetDefaultValue()->AsPhi()->InputAt(1), c11);
+
+ EXPECT_INS_RETAINED(left2_call);
+
+ EXPECT_INS_REMOVED(left3_read);
+ HPredicatedInstanceFieldGet* replacement_left3_read =
+ FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, left3);
+ ASSERT_NE(replacement_left3_read, nullptr);
+ ASSERT_TRUE(replacement_left3_read->GetTarget()->IsPhi());
+ ASSERT_INS_EQ(replacement_left3_read->GetTarget(), replacement_middle1_read->GetTarget());
+ ASSERT_INS_EQ(replacement_left3_read->GetDefaultValue(), replacement_middle1_read);
+ EXPECT_INS_RETAINED(left3_call);
+
+ EXPECT_INS_RETAINED(breturn_phi);
+ EXPECT_INS_REMOVED(breturn_read);
+ HPredicatedInstanceFieldGet* replacement_breturn_read =
+ FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
+ ASSERT_NE(replacement_breturn_read, nullptr);
+ ASSERT_INS_EQ(replacement_breturn_read->GetTarget(), replacement_middle1_read->GetTarget());
+ ASSERT_EQ(2u, replacement_breturn_read->GetDefaultValue()->AsPhi()->InputCount());
+ ASSERT_INS_EQ(replacement_breturn_read->GetDefaultValue()->AsPhi()->InputAt(0),
+ replacement_left3_read);
+ ASSERT_INS_EQ(replacement_breturn_read->GetDefaultValue()->AsPhi()->InputAt(1),
+ replacement_middle1_read);
+ EXPECT_INS_RETAINED(breturn_add1);
+ ASSERT_INS_EQ(breturn_add1->InputAt(0), replacement_middle1_read);
+ ASSERT_INS_EQ(breturn_add1->InputAt(1), replacement_breturn_read);
+ EXPECT_INS_RETAINED(breturn_add2);
+ ASSERT_INS_EQ(breturn_add2->InputAt(0), breturn_add1);
+ ASSERT_INS_EQ(breturn_add2->InputAt(1), breturn_phi);
+ EXPECT_INS_RETAINED(breturn_return);
+}
+
+INSTANTIATE_TEST_SUITE_P(LoadStoreEliminationTest,
+ UsesOrderDependentTestGroupForThreeItems,
+ testing::Values(0u, 1u, 2u, 3u, 4u, 5u));
+
} // namespace art