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