• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(const AdjacencyListGraph & blks)55   void PerformSimplification(const AdjacencyListGraph& blks) {
56     if (kDebugSimplifierTests) {
57       LOG(INFO) << "Pre simplification " << blks;
58     }
59     graph_->ClearDominanceInformation();
60     graph_->BuildDominatorTree();
61     InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
62     simp.Run();
63     if (kDebugSimplifierTests) {
64       LOG(INFO) << "Post simplify " << blks;
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(VariableSizedHandleScope * vshs)106   std::pair<HLoadClass*, HLoadClass*> GetLoadClasses(VariableSizedHandleScope* vshs) {
107     InstanceOfKind kind = GetParam();
108     ScopedObjectAccess soa(Thread::Current());
109     // New inst always needs to have a valid rti since we dcheck that.
110     HLoadClass* new_inst = MakeClassLoad(
111         /* ti= */ std::nullopt, vshs->NewHandle<mirror::Class>(GetClassRoot<mirror::ClassExt>()));
112     new_inst->SetValidLoadedClassRTI();
113     if (kind == InstanceOfKind::kSelf) {
114       return {new_inst, new_inst};
115     }
116     if (kind == InstanceOfKind::kUnrelatedUnloaded) {
117       HLoadClass* target_class = MakeClassLoad();
118       EXPECT_FALSE(target_class->GetLoadedClassRTI().IsValid());
119       return {new_inst, target_class};
120     }
121     // Force both classes to be a real classes.
122     // For simplicity we use class-roots as the types. The new-inst will always
123     // be a ClassExt, unrelated-loaded will always be Throwable and super will
124     // always be Object
125     HLoadClass* target_class = MakeClassLoad(
126         /* ti= */ std::nullopt,
127         vshs->NewHandle<mirror::Class>(kind == InstanceOfKind::kSupertype ?
128                                            GetClassRoot<mirror::Object>() :
129                                            GetClassRoot<mirror::Throwable>()));
130     target_class->SetValidLoadedClassRTI();
131     EXPECT_TRUE(target_class->GetLoadedClassRTI().IsValid());
132     return {new_inst, target_class};
133   }
134 };
135 
136 // // ENTRY
137 // obj = new Obj();
138 // // Make sure this graph isn't broken
139 // if (obj instanceof <other>) {
140 //   // LEFT
141 // } else {
142 //   // RIGHT
143 // }
144 // EXIT
145 // return obj.field
TEST_P(InstanceOfInstructionSimplifierTestGroup,ExactClassInstanceOfOther)146 TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassInstanceOfOther) {
147   ScopedObjectAccess soa(Thread::Current());
148   VariableSizedHandleScope vshs(soa.Self());
149   InitGraph(/*handles=*/&vshs);
150 
151   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
152                                                  "exit",
153                                                  {{"entry", "left"},
154                                                   {"entry", "right"},
155                                                   {"left", "breturn"},
156                                                   {"right", "breturn"},
157                                                   {"breturn", "exit"}}));
158 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
159   GET_BLOCK(entry);
160   GET_BLOCK(exit);
161   GET_BLOCK(breturn);
162   GET_BLOCK(left);
163   GET_BLOCK(right);
164 #undef GET_BLOCK
165   EnsurePredecessorOrder(breturn, {left, right});
166   HInstruction* test_res = graph_->GetIntConstant(GetConstantResult() ? 1 : 0);
167 
168   auto [new_inst_klass, target_klass] = GetLoadClasses(&vshs);
169   HInstruction* new_inst = MakeNewInstance(new_inst_klass);
170   new_inst->SetReferenceTypeInfo(
171       ReferenceTypeInfo::Create(new_inst_klass->GetClass(), /*is_exact=*/true));
172   HInstanceOf* instance_of = new (GetAllocator()) HInstanceOf(new_inst,
173                                                               target_klass,
174                                                               TypeCheckKind::kClassHierarchyCheck,
175                                                               target_klass->GetClass(),
176                                                               0u,
177                                                               GetAllocator(),
178                                                               nullptr,
179                                                               nullptr);
180   if (target_klass->GetLoadedClassRTI().IsValid()) {
181     instance_of->SetValidTargetClassRTI();
182   }
183   HInstruction* if_inst = new (GetAllocator()) HIf(instance_of);
184   entry->AddInstruction(new_inst_klass);
185   if (new_inst_klass != target_klass) {
186     entry->AddInstruction(target_klass);
187   }
188   entry->AddInstruction(new_inst);
189   entry->AddInstruction(instance_of);
190   entry->AddInstruction(if_inst);
191   ManuallyBuildEnvFor(new_inst_klass, {});
192   if (new_inst_klass != target_klass) {
193     target_klass->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
194   }
195   new_inst->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
196 
197   HInstruction* goto_left = new (GetAllocator()) HGoto();
198   left->AddInstruction(goto_left);
199 
200   HInstruction* goto_right = new (GetAllocator()) HGoto();
201   right->AddInstruction(goto_right);
202 
203   HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
204   HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
205   breturn->AddInstruction(read_bottom);
206   breturn->AddInstruction(return_exit);
207 
208   SetupExit(exit);
209 
210   PerformSimplification(blks);
211 
212   if (!GetConstantResult() || GetParam() == InstanceOfKind::kSelf) {
213     EXPECT_INS_RETAINED(target_klass);
214   } else {
215     EXPECT_INS_REMOVED(target_klass);
216   }
217   EXPECT_INS_REMOVED(instance_of);
218   EXPECT_INS_EQ(if_inst->InputAt(0), test_res);
219 }
220 
221 // // ENTRY
222 // obj = new Obj();
223 // (<other>)obj;
224 // // Make sure this graph isn't broken
225 // EXIT
226 // return obj
TEST_P(InstanceOfInstructionSimplifierTestGroup,ExactClassCheckCastOther)227 TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassCheckCastOther) {
228   ScopedObjectAccess soa(Thread::Current());
229   VariableSizedHandleScope vshs(soa.Self());
230   InitGraph(/*handles=*/&vshs);
231 
232   AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", {{"entry", "exit"}}));
233 #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
234   GET_BLOCK(entry);
235   GET_BLOCK(exit);
236 #undef GET_BLOCK
237 
238   auto [new_inst_klass, target_klass] = GetLoadClasses(&vshs);
239   HInstruction* new_inst = MakeNewInstance(new_inst_klass);
240   new_inst->SetReferenceTypeInfo(
241       ReferenceTypeInfo::Create(new_inst_klass->GetClass(), /*is_exact=*/true));
242   HCheckCast* check_cast = new (GetAllocator()) HCheckCast(new_inst,
243                                                            target_klass,
244                                                            TypeCheckKind::kClassHierarchyCheck,
245                                                            target_klass->GetClass(),
246                                                            0u,
247                                                            GetAllocator(),
248                                                            nullptr,
249                                                            nullptr);
250   if (target_klass->GetLoadedClassRTI().IsValid()) {
251     check_cast->SetValidTargetClassRTI();
252   }
253   HInstruction* entry_return = new (GetAllocator()) HReturn(new_inst);
254   entry->AddInstruction(new_inst_klass);
255   if (new_inst_klass != target_klass) {
256     entry->AddInstruction(target_klass);
257   }
258   entry->AddInstruction(new_inst);
259   entry->AddInstruction(check_cast);
260   entry->AddInstruction(entry_return);
261   ManuallyBuildEnvFor(new_inst_klass, {});
262   if (new_inst_klass != target_klass) {
263     target_klass->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
264   }
265   new_inst->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
266 
267   SetupExit(exit);
268 
269   PerformSimplification(blks);
270 
271   if (!GetConstantResult() || GetParam() == InstanceOfKind::kSelf) {
272     EXPECT_INS_RETAINED(target_klass);
273   } else {
274     EXPECT_INS_REMOVED(target_klass);
275   }
276   if (GetConstantResult()) {
277     EXPECT_INS_REMOVED(check_cast);
278   } else {
279     EXPECT_INS_RETAINED(check_cast);
280   }
281 }
282 
283 INSTANTIATE_TEST_SUITE_P(InstructionSimplifierTest,
284                          InstanceOfInstructionSimplifierTestGroup,
285                          testing::Values(InstanceOfKind::kSelf,
286                                          InstanceOfKind::kUnrelatedLoaded,
287                                          InstanceOfKind::kUnrelatedUnloaded,
288                                          InstanceOfKind::kSupertype));
289 
290 }  // namespace art
291