1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2021 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker *
4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker *
8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker *
10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker */
16*795d594fSAndroid Build Coastguard Worker
17*795d594fSAndroid Build Coastguard Worker #include "instruction_simplifier.h"
18*795d594fSAndroid Build Coastguard Worker
19*795d594fSAndroid Build Coastguard Worker #include <initializer_list>
20*795d594fSAndroid Build Coastguard Worker #include <tuple>
21*795d594fSAndroid Build Coastguard Worker
22*795d594fSAndroid Build Coastguard Worker #include "gtest/gtest.h"
23*795d594fSAndroid Build Coastguard Worker
24*795d594fSAndroid Build Coastguard Worker #include "class_root-inl.h"
25*795d594fSAndroid Build Coastguard Worker #include "nodes.h"
26*795d594fSAndroid Build Coastguard Worker #include "optimizing/data_type.h"
27*795d594fSAndroid Build Coastguard Worker #include "optimizing_unit_test.h"
28*795d594fSAndroid Build Coastguard Worker
29*795d594fSAndroid Build Coastguard Worker namespace art HIDDEN {
30*795d594fSAndroid Build Coastguard Worker
31*795d594fSAndroid Build Coastguard Worker namespace mirror {
32*795d594fSAndroid Build Coastguard Worker class ClassExt;
33*795d594fSAndroid Build Coastguard Worker class Throwable;
34*795d594fSAndroid Build Coastguard Worker } // namespace mirror
35*795d594fSAndroid Build Coastguard Worker
36*795d594fSAndroid Build Coastguard Worker static constexpr bool kDebugSimplifierTests = false;
37*795d594fSAndroid Build Coastguard Worker
38*795d594fSAndroid Build Coastguard Worker template<typename SuperClass>
39*795d594fSAndroid Build Coastguard Worker class InstructionSimplifierTestBase : public SuperClass, public OptimizingUnitTestHelper {
40*795d594fSAndroid Build Coastguard Worker public:
InstructionSimplifierTestBase()41*795d594fSAndroid Build Coastguard Worker InstructionSimplifierTestBase() {
42*795d594fSAndroid Build Coastguard Worker this->use_boot_image_ = true; // Make the Runtime creation cheaper.
43*795d594fSAndroid Build Coastguard Worker }
44*795d594fSAndroid Build Coastguard Worker
SetUp()45*795d594fSAndroid Build Coastguard Worker void SetUp() override {
46*795d594fSAndroid Build Coastguard Worker SuperClass::SetUp();
47*795d594fSAndroid Build Coastguard Worker gLogVerbosity.compiler = true;
48*795d594fSAndroid Build Coastguard Worker }
49*795d594fSAndroid Build Coastguard Worker
TearDown()50*795d594fSAndroid Build Coastguard Worker void TearDown() override {
51*795d594fSAndroid Build Coastguard Worker SuperClass::TearDown();
52*795d594fSAndroid Build Coastguard Worker gLogVerbosity.compiler = false;
53*795d594fSAndroid Build Coastguard Worker }
54*795d594fSAndroid Build Coastguard Worker
PerformSimplification()55*795d594fSAndroid Build Coastguard Worker void PerformSimplification() {
56*795d594fSAndroid Build Coastguard Worker if (kDebugSimplifierTests) {
57*795d594fSAndroid Build Coastguard Worker graph_->Dump(LOG_STREAM(INFO) << "Pre simplification ", /* codegen_= */ nullptr);
58*795d594fSAndroid Build Coastguard Worker }
59*795d594fSAndroid Build Coastguard Worker graph_->ClearDominanceInformation();
60*795d594fSAndroid Build Coastguard Worker graph_->BuildDominatorTree();
61*795d594fSAndroid Build Coastguard Worker InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
62*795d594fSAndroid Build Coastguard Worker simp.Run();
63*795d594fSAndroid Build Coastguard Worker if (kDebugSimplifierTests) {
64*795d594fSAndroid Build Coastguard Worker graph_->Dump(LOG_STREAM(INFO) << "Post simplify ", /* codegen_= */ nullptr);
65*795d594fSAndroid Build Coastguard Worker }
66*795d594fSAndroid Build Coastguard Worker }
67*795d594fSAndroid Build Coastguard Worker };
68*795d594fSAndroid Build Coastguard Worker
69*795d594fSAndroid Build Coastguard Worker class InstructionSimplifierTest : public InstructionSimplifierTestBase<CommonCompilerTest> {};
70*795d594fSAndroid Build Coastguard Worker
71*795d594fSAndroid Build Coastguard Worker // Various configs we can use for testing. Currently used in PartialComparison tests.
72*795d594fSAndroid Build Coastguard Worker enum class InstanceOfKind {
73*795d594fSAndroid Build Coastguard Worker kSelf,
74*795d594fSAndroid Build Coastguard Worker kUnrelatedLoaded,
75*795d594fSAndroid Build Coastguard Worker kUnrelatedUnloaded,
76*795d594fSAndroid Build Coastguard Worker kSupertype,
77*795d594fSAndroid Build Coastguard Worker };
78*795d594fSAndroid Build Coastguard Worker
operator <<(std::ostream & os,const InstanceOfKind & comp)79*795d594fSAndroid Build Coastguard Worker std::ostream& operator<<(std::ostream& os, const InstanceOfKind& comp) {
80*795d594fSAndroid Build Coastguard Worker switch (comp) {
81*795d594fSAndroid Build Coastguard Worker case InstanceOfKind::kSupertype:
82*795d594fSAndroid Build Coastguard Worker return os << "kSupertype";
83*795d594fSAndroid Build Coastguard Worker case InstanceOfKind::kSelf:
84*795d594fSAndroid Build Coastguard Worker return os << "kSelf";
85*795d594fSAndroid Build Coastguard Worker case InstanceOfKind::kUnrelatedLoaded:
86*795d594fSAndroid Build Coastguard Worker return os << "kUnrelatedLoaded";
87*795d594fSAndroid Build Coastguard Worker case InstanceOfKind::kUnrelatedUnloaded:
88*795d594fSAndroid Build Coastguard Worker return os << "kUnrelatedUnloaded";
89*795d594fSAndroid Build Coastguard Worker }
90*795d594fSAndroid Build Coastguard Worker }
91*795d594fSAndroid Build Coastguard Worker
92*795d594fSAndroid Build Coastguard Worker class InstanceOfInstructionSimplifierTestGroup
93*795d594fSAndroid Build Coastguard Worker : public InstructionSimplifierTestBase<CommonCompilerTestWithParam<InstanceOfKind>> {
94*795d594fSAndroid Build Coastguard Worker public:
GetConstantResult() const95*795d594fSAndroid Build Coastguard Worker bool GetConstantResult() const {
96*795d594fSAndroid Build Coastguard Worker switch (GetParam()) {
97*795d594fSAndroid Build Coastguard Worker case InstanceOfKind::kSupertype:
98*795d594fSAndroid Build Coastguard Worker case InstanceOfKind::kSelf:
99*795d594fSAndroid Build Coastguard Worker return true;
100*795d594fSAndroid Build Coastguard Worker case InstanceOfKind::kUnrelatedLoaded:
101*795d594fSAndroid Build Coastguard Worker case InstanceOfKind::kUnrelatedUnloaded:
102*795d594fSAndroid Build Coastguard Worker return false;
103*795d594fSAndroid Build Coastguard Worker }
104*795d594fSAndroid Build Coastguard Worker }
105*795d594fSAndroid Build Coastguard Worker
GetLoadClasses(HBasicBlock * block,VariableSizedHandleScope * vshs)106*795d594fSAndroid Build Coastguard Worker std::pair<HLoadClass*, HLoadClass*> GetLoadClasses(HBasicBlock* block,
107*795d594fSAndroid Build Coastguard Worker VariableSizedHandleScope* vshs)
108*795d594fSAndroid Build Coastguard Worker REQUIRES_SHARED(Locks::mutator_lock_) {
109*795d594fSAndroid Build Coastguard Worker InstanceOfKind kind = GetParam();
110*795d594fSAndroid Build Coastguard Worker // New inst always needs to have a valid rti since we dcheck that.
111*795d594fSAndroid Build Coastguard Worker HLoadClass* new_inst = MakeLoadClass(
112*795d594fSAndroid Build Coastguard Worker block,
113*795d594fSAndroid Build Coastguard Worker /* ti= */ std::nullopt,
114*795d594fSAndroid Build Coastguard Worker vshs->NewHandle<mirror::Class>(GetClassRoot<mirror::ClassExt>()));
115*795d594fSAndroid Build Coastguard Worker new_inst->SetValidLoadedClassRTI();
116*795d594fSAndroid Build Coastguard Worker if (kind == InstanceOfKind::kSelf) {
117*795d594fSAndroid Build Coastguard Worker return {new_inst, new_inst};
118*795d594fSAndroid Build Coastguard Worker }
119*795d594fSAndroid Build Coastguard Worker if (kind == InstanceOfKind::kUnrelatedUnloaded) {
120*795d594fSAndroid Build Coastguard Worker HLoadClass* target_class = MakeLoadClass(block);
121*795d594fSAndroid Build Coastguard Worker EXPECT_FALSE(target_class->GetLoadedClassRTI().IsValid());
122*795d594fSAndroid Build Coastguard Worker return {new_inst, target_class};
123*795d594fSAndroid Build Coastguard Worker }
124*795d594fSAndroid Build Coastguard Worker // Force both classes to be a real classes.
125*795d594fSAndroid Build Coastguard Worker // For simplicity we use class-roots as the types. The new-inst will always
126*795d594fSAndroid Build Coastguard Worker // be a ClassExt, unrelated-loaded will always be Throwable and super will
127*795d594fSAndroid Build Coastguard Worker // always be Object
128*795d594fSAndroid Build Coastguard Worker HLoadClass* target_class = MakeLoadClass(
129*795d594fSAndroid Build Coastguard Worker block,
130*795d594fSAndroid Build Coastguard Worker /* ti= */ std::nullopt,
131*795d594fSAndroid Build Coastguard Worker vshs->NewHandle<mirror::Class>(kind == InstanceOfKind::kSupertype ?
132*795d594fSAndroid Build Coastguard Worker GetClassRoot<mirror::Object>() :
133*795d594fSAndroid Build Coastguard Worker GetClassRoot<mirror::Throwable>()));
134*795d594fSAndroid Build Coastguard Worker target_class->SetValidLoadedClassRTI();
135*795d594fSAndroid Build Coastguard Worker EXPECT_TRUE(target_class->GetLoadedClassRTI().IsValid());
136*795d594fSAndroid Build Coastguard Worker return {new_inst, target_class};
137*795d594fSAndroid Build Coastguard Worker }
138*795d594fSAndroid Build Coastguard Worker };
139*795d594fSAndroid Build Coastguard Worker
140*795d594fSAndroid Build Coastguard Worker // // ENTRY
141*795d594fSAndroid Build Coastguard Worker // obj = new Obj();
142*795d594fSAndroid Build Coastguard Worker // // Make sure this graph isn't broken
143*795d594fSAndroid Build Coastguard Worker // if (obj instanceof <other>) {
144*795d594fSAndroid Build Coastguard Worker // // LEFT
145*795d594fSAndroid Build Coastguard Worker // } else {
146*795d594fSAndroid Build Coastguard Worker // // RIGHT
147*795d594fSAndroid Build Coastguard Worker // }
148*795d594fSAndroid Build Coastguard Worker // EXIT
149*795d594fSAndroid Build Coastguard Worker // return obj.field
TEST_P(InstanceOfInstructionSimplifierTestGroup,ExactClassInstanceOfOther)150*795d594fSAndroid Build Coastguard Worker TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassInstanceOfOther) {
151*795d594fSAndroid Build Coastguard Worker ScopedObjectAccess soa(Thread::Current());
152*795d594fSAndroid Build Coastguard Worker VariableSizedHandleScope vshs(soa.Self());
153*795d594fSAndroid Build Coastguard Worker HBasicBlock* breturn = InitEntryMainExitGraph(/*handles=*/&vshs);
154*795d594fSAndroid Build Coastguard Worker auto [if_block, left, right] = CreateDiamondPattern(breturn);
155*795d594fSAndroid Build Coastguard Worker EnsurePredecessorOrder(breturn, {left, right});
156*795d594fSAndroid Build Coastguard Worker
157*795d594fSAndroid Build Coastguard Worker HInstruction* test_res = graph_->GetIntConstant(GetConstantResult() ? 1 : 0);
158*795d594fSAndroid Build Coastguard Worker
159*795d594fSAndroid Build Coastguard Worker auto [new_inst_klass, target_klass] = GetLoadClasses(if_block, &vshs);
160*795d594fSAndroid Build Coastguard Worker HInstruction* new_inst = MakeNewInstance(if_block, new_inst_klass);
161*795d594fSAndroid Build Coastguard Worker new_inst->SetReferenceTypeInfo(
162*795d594fSAndroid Build Coastguard Worker ReferenceTypeInfo::Create(new_inst_klass->GetClass(), /*is_exact=*/true));
163*795d594fSAndroid Build Coastguard Worker HInstanceOf* instance_of = new (GetAllocator()) HInstanceOf(new_inst,
164*795d594fSAndroid Build Coastguard Worker target_klass,
165*795d594fSAndroid Build Coastguard Worker TypeCheckKind::kClassHierarchyCheck,
166*795d594fSAndroid Build Coastguard Worker target_klass->GetClass(),
167*795d594fSAndroid Build Coastguard Worker 0u,
168*795d594fSAndroid Build Coastguard Worker GetAllocator(),
169*795d594fSAndroid Build Coastguard Worker nullptr,
170*795d594fSAndroid Build Coastguard Worker nullptr);
171*795d594fSAndroid Build Coastguard Worker if (target_klass->GetLoadedClassRTI().IsValid()) {
172*795d594fSAndroid Build Coastguard Worker instance_of->SetValidTargetClassRTI();
173*795d594fSAndroid Build Coastguard Worker }
174*795d594fSAndroid Build Coastguard Worker if_block->AddInstruction(instance_of);
175*795d594fSAndroid Build Coastguard Worker HIf* if_inst = MakeIf(if_block, instance_of);
176*795d594fSAndroid Build Coastguard Worker
177*795d594fSAndroid Build Coastguard Worker HInstruction* read_bottom =
178*795d594fSAndroid Build Coastguard Worker MakeIFieldGet(breturn, new_inst, DataType::Type::kInt32, MemberOffset(32));
179*795d594fSAndroid Build Coastguard Worker MakeReturn(breturn, read_bottom);
180*795d594fSAndroid Build Coastguard Worker
181*795d594fSAndroid Build Coastguard Worker PerformSimplification();
182*795d594fSAndroid Build Coastguard Worker
183*795d594fSAndroid Build Coastguard Worker if (!GetConstantResult() || GetParam() == InstanceOfKind::kSelf) {
184*795d594fSAndroid Build Coastguard Worker EXPECT_INS_RETAINED(target_klass);
185*795d594fSAndroid Build Coastguard Worker } else {
186*795d594fSAndroid Build Coastguard Worker EXPECT_INS_REMOVED(target_klass);
187*795d594fSAndroid Build Coastguard Worker }
188*795d594fSAndroid Build Coastguard Worker EXPECT_INS_REMOVED(instance_of);
189*795d594fSAndroid Build Coastguard Worker EXPECT_INS_EQ(if_inst->InputAt(0), test_res);
190*795d594fSAndroid Build Coastguard Worker }
191*795d594fSAndroid Build Coastguard Worker
192*795d594fSAndroid Build Coastguard Worker // // ENTRY
193*795d594fSAndroid Build Coastguard Worker // obj = new Obj();
194*795d594fSAndroid Build Coastguard Worker // (<other>)obj;
195*795d594fSAndroid Build Coastguard Worker // // Make sure this graph isn't broken
196*795d594fSAndroid Build Coastguard Worker // EXIT
197*795d594fSAndroid Build Coastguard Worker // return obj
TEST_P(InstanceOfInstructionSimplifierTestGroup,ExactClassCheckCastOther)198*795d594fSAndroid Build Coastguard Worker TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassCheckCastOther) {
199*795d594fSAndroid Build Coastguard Worker ScopedObjectAccess soa(Thread::Current());
200*795d594fSAndroid Build Coastguard Worker VariableSizedHandleScope vshs(soa.Self());
201*795d594fSAndroid Build Coastguard Worker HBasicBlock* main = InitEntryMainExitGraph(/*handles=*/&vshs);
202*795d594fSAndroid Build Coastguard Worker
203*795d594fSAndroid Build Coastguard Worker auto [new_inst_klass, target_klass] = GetLoadClasses(main, &vshs);
204*795d594fSAndroid Build Coastguard Worker HInstruction* new_inst = MakeNewInstance(main, new_inst_klass);
205*795d594fSAndroid Build Coastguard Worker new_inst->SetReferenceTypeInfo(
206*795d594fSAndroid Build Coastguard Worker ReferenceTypeInfo::Create(new_inst_klass->GetClass(), /*is_exact=*/true));
207*795d594fSAndroid Build Coastguard Worker HCheckCast* check_cast = new (GetAllocator()) HCheckCast(new_inst,
208*795d594fSAndroid Build Coastguard Worker target_klass,
209*795d594fSAndroid Build Coastguard Worker TypeCheckKind::kClassHierarchyCheck,
210*795d594fSAndroid Build Coastguard Worker target_klass->GetClass(),
211*795d594fSAndroid Build Coastguard Worker 0u,
212*795d594fSAndroid Build Coastguard Worker GetAllocator(),
213*795d594fSAndroid Build Coastguard Worker nullptr,
214*795d594fSAndroid Build Coastguard Worker nullptr);
215*795d594fSAndroid Build Coastguard Worker if (target_klass->GetLoadedClassRTI().IsValid()) {
216*795d594fSAndroid Build Coastguard Worker check_cast->SetValidTargetClassRTI();
217*795d594fSAndroid Build Coastguard Worker }
218*795d594fSAndroid Build Coastguard Worker main->AddInstruction(check_cast);
219*795d594fSAndroid Build Coastguard Worker MakeReturn(main, new_inst);
220*795d594fSAndroid Build Coastguard Worker
221*795d594fSAndroid Build Coastguard Worker PerformSimplification();
222*795d594fSAndroid Build Coastguard Worker
223*795d594fSAndroid Build Coastguard Worker if (!GetConstantResult() || GetParam() == InstanceOfKind::kSelf) {
224*795d594fSAndroid Build Coastguard Worker EXPECT_INS_RETAINED(target_klass);
225*795d594fSAndroid Build Coastguard Worker } else {
226*795d594fSAndroid Build Coastguard Worker EXPECT_INS_REMOVED(target_klass);
227*795d594fSAndroid Build Coastguard Worker }
228*795d594fSAndroid Build Coastguard Worker if (GetConstantResult()) {
229*795d594fSAndroid Build Coastguard Worker EXPECT_INS_REMOVED(check_cast);
230*795d594fSAndroid Build Coastguard Worker } else {
231*795d594fSAndroid Build Coastguard Worker EXPECT_INS_RETAINED(check_cast);
232*795d594fSAndroid Build Coastguard Worker }
233*795d594fSAndroid Build Coastguard Worker }
234*795d594fSAndroid Build Coastguard Worker
235*795d594fSAndroid Build Coastguard Worker INSTANTIATE_TEST_SUITE_P(InstructionSimplifierTest,
236*795d594fSAndroid Build Coastguard Worker InstanceOfInstructionSimplifierTestGroup,
237*795d594fSAndroid Build Coastguard Worker testing::Values(InstanceOfKind::kSelf,
238*795d594fSAndroid Build Coastguard Worker InstanceOfKind::kUnrelatedLoaded,
239*795d594fSAndroid Build Coastguard Worker InstanceOfKind::kUnrelatedUnloaded,
240*795d594fSAndroid Build Coastguard Worker InstanceOfKind::kSupertype));
241*795d594fSAndroid Build Coastguard Worker
242*795d594fSAndroid Build Coastguard Worker } // namespace art
243