/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "instruction_simplifier.h" #include #include #include "gtest/gtest.h" #include "nodes.h" #include "optimizing/data_type.h" #include "optimizing_unit_test.h" namespace art { class InstructionSimplifierTest : public CommonCompilerTest, public OptimizingUnitTestHelper { public: void SetUp() override { CommonCompilerTest::SetUp(); gLogVerbosity.compiler = true; } void TearDown() override { CommonCompilerTest::TearDown(); gLogVerbosity.compiler = false; } }; // // ENTRY // switch (param) { // case 1: // obj1 = param2; break; // case 2: // obj1 = param3; break; // default: // obj2 = new Obj(); // } // val_phi = PHI[3,4,10] // target_phi = PHI[param2, param3, obj2] // return PredFieldGet[val_phi, target_phi] => PredFieldGet[val_phi, target_phi] TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetNoMerge) { VariableSizedHandleScope vshs(Thread::Current()); CreateGraph(&vshs); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", {{"entry", "case1"}, {"entry", "case2"}, {"entry", "case3"}, {"case1", "breturn"}, {"case2", "breturn"}, {"case3", "breturn"}, {"breturn", "exit"}})); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); GET_BLOCK(case1); GET_BLOCK(case2); GET_BLOCK(case3); GET_BLOCK(breturn); #undef GET_BLOCK HInstruction* bool_value = MakeParam(DataType::Type::kInt32); HInstruction* obj1_param = MakeParam(DataType::Type::kReference); HInstruction* obj2_param = MakeParam(DataType::Type::kReference); HInstruction* c3 = graph_->GetIntConstant(3); HInstruction* c4 = graph_->GetIntConstant(4); HInstruction* c10 = graph_->GetIntConstant(10); HInstruction* cls = MakeClassLoad(); HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, bool_value); entry->AddInstruction(cls); entry->AddInstruction(switch_inst); ManuallyBuildEnvFor(cls, {}); HInstruction* goto_c1 = new (GetAllocator()) HGoto(); case1->AddInstruction(goto_c1); HInstruction* goto_c2 = new (GetAllocator()) HGoto(); case2->AddInstruction(goto_c2); HInstruction* obj3 = MakeNewInstance(cls); HInstruction* goto_c3 = new (GetAllocator()) HGoto(); case3->AddInstruction(obj3); case3->AddInstruction(goto_c3); HPhi* val_phi = MakePhi({c3, c4, c10}); HPhi* obj_phi = MakePhi({obj1_param, obj2_param, obj3}); HPredicatedInstanceFieldGet* read_end = new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi, nullptr, val_phi, val_phi->GetType(), MemberOffset(10), false, 42, 0, graph_->GetDexFile(), 0); HInstruction* return_exit = new (GetAllocator()) HReturn(read_end); breturn->AddPhi(val_phi); breturn->AddPhi(obj_phi); breturn->AddInstruction(read_end); breturn->AddInstruction(return_exit); SetupExit(exit); LOG(INFO) << "Pre simplification " << blks; graph_->ClearDominanceInformation(); graph_->BuildDominatorTree(); InstructionSimplifier simp(graph_, /*codegen=*/nullptr); simp.Run(); LOG(INFO) << "Post simplify " << blks; EXPECT_INS_RETAINED(read_end); EXPECT_INS_EQ(read_end->GetTarget(), obj_phi); EXPECT_INS_EQ(read_end->GetDefaultValue(), val_phi); } // // ENTRY // switch (param) { // case 1: // obj1 = param2; break; // case 2: // obj1 = param3; break; // default: // obj2 = new Obj(); // } // val_phi = PHI[3,3,10] // target_phi = PHI[param2, param3, obj2] // return PredFieldGet[val_phi, target_phi] => PredFieldGet[3, target_phi] TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetMerge) { VariableSizedHandleScope vshs(Thread::Current()); CreateGraph(&vshs); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", {{"entry", "case1"}, {"entry", "case2"}, {"entry", "case3"}, {"case1", "breturn"}, {"case2", "breturn"}, {"case3", "breturn"}, {"breturn", "exit"}})); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); GET_BLOCK(case1); GET_BLOCK(case2); GET_BLOCK(case3); GET_BLOCK(breturn); #undef GET_BLOCK HInstruction* bool_value = MakeParam(DataType::Type::kInt32); HInstruction* obj1_param = MakeParam(DataType::Type::kReference); HInstruction* obj2_param = MakeParam(DataType::Type::kReference); HInstruction* c3 = graph_->GetIntConstant(3); HInstruction* c10 = graph_->GetIntConstant(10); HInstruction* cls = MakeClassLoad(); HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, bool_value); entry->AddInstruction(cls); entry->AddInstruction(switch_inst); ManuallyBuildEnvFor(cls, {}); HInstruction* goto_c1 = new (GetAllocator()) HGoto(); case1->AddInstruction(goto_c1); HInstruction* goto_c2 = new (GetAllocator()) HGoto(); case2->AddInstruction(goto_c2); HInstruction* obj3 = MakeNewInstance(cls); HInstruction* goto_c3 = new (GetAllocator()) HGoto(); case3->AddInstruction(obj3); case3->AddInstruction(goto_c3); HPhi* val_phi = MakePhi({c3, c3, c10}); HPhi* obj_phi = MakePhi({obj1_param, obj2_param, obj3}); HPredicatedInstanceFieldGet* read_end = new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi, nullptr, val_phi, val_phi->GetType(), MemberOffset(10), false, 42, 0, graph_->GetDexFile(), 0); HInstruction* return_exit = new (GetAllocator()) HReturn(read_end); breturn->AddPhi(val_phi); breturn->AddPhi(obj_phi); breturn->AddInstruction(read_end); breturn->AddInstruction(return_exit); SetupExit(exit); LOG(INFO) << "Pre simplification " << blks; graph_->ClearDominanceInformation(); graph_->BuildDominatorTree(); InstructionSimplifier simp(graph_, /*codegen=*/nullptr); simp.Run(); LOG(INFO) << "Post simplify " << blks; EXPECT_FALSE(obj3->CanBeNull()); EXPECT_INS_RETAINED(read_end); EXPECT_INS_EQ(read_end->GetTarget(), obj_phi); EXPECT_INS_EQ(read_end->GetDefaultValue(), c3); } // // ENTRY // if (param) { // obj1 = new Obj(); // } else { // obj2 = new Obj(); // } // val_phi = PHI[3,10] // target_phi = PHI[obj1, obj2] // return PredFieldGet[val_phi, target_phi] => FieldGet[target_phi] TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetNoNull) { VariableSizedHandleScope vshs(Thread::Current()); CreateGraph(&vshs); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", {{"entry", "left"}, {"entry", "right"}, {"left", "breturn"}, {"right", "breturn"}, {"breturn", "exit"}})); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); GET_BLOCK(left); GET_BLOCK(right); GET_BLOCK(breturn); #undef GET_BLOCK HInstruction* bool_value = MakeParam(DataType::Type::kBool); HInstruction* c3 = graph_->GetIntConstant(3); HInstruction* c10 = graph_->GetIntConstant(10); HInstruction* cls = MakeClassLoad(); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); entry->AddInstruction(cls); entry->AddInstruction(if_inst); ManuallyBuildEnvFor(cls, {}); HInstruction* obj1 = MakeNewInstance(cls); HInstruction* goto_left = new (GetAllocator()) HGoto(); left->AddInstruction(obj1); left->AddInstruction(goto_left); HInstruction* obj2 = MakeNewInstance(cls); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(obj2); right->AddInstruction(goto_right); HPhi* val_phi = MakePhi({c3, c10}); HPhi* obj_phi = MakePhi({obj1, obj2}); HInstruction* read_end = new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi, nullptr, val_phi, val_phi->GetType(), MemberOffset(10), false, 42, 0, graph_->GetDexFile(), 0); HInstruction* return_exit = new (GetAllocator()) HReturn(read_end); breturn->AddPhi(val_phi); breturn->AddPhi(obj_phi); breturn->AddInstruction(read_end); breturn->AddInstruction(return_exit); SetupExit(exit); LOG(INFO) << "Pre simplification " << blks; graph_->ClearDominanceInformation(); graph_->BuildDominatorTree(); InstructionSimplifier simp(graph_, /*codegen=*/nullptr); simp.Run(); LOG(INFO) << "Post simplify " << blks; EXPECT_FALSE(obj1->CanBeNull()); EXPECT_FALSE(obj2->CanBeNull()); EXPECT_INS_REMOVED(read_end); HInstanceFieldGet* ifget = FindSingleInstruction(graph_, breturn); ASSERT_NE(ifget, nullptr); EXPECT_INS_EQ(ifget->InputAt(0), obj_phi); } } // namespace art