blob: 966f5b91cfc596150da1112ddea2af35a9124ac3 [file] [log] [blame]
Alex Light74328052021-03-29 18:11:23 -07001/*
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"
Alex Lightbb550e42021-04-21 17:04:13 -070023
24#include "class_root-inl.h"
Alex Light74328052021-03-29 18:11:23 -070025#include "nodes.h"
26#include "optimizing/data_type.h"
27#include "optimizing_unit_test.h"
28
VladimĂ­r Marko434d9682022-11-04 14:04:17 +000029namespace art HIDDEN {
Alex Light74328052021-03-29 18:11:23 -070030
Alex Lightbb550e42021-04-21 17:04:13 -070031namespace mirror {
32class ClassExt;
33class Throwable;
34} // namespace mirror
35
Vladimir Markob2a164c2023-01-17 17:12:30 +000036static constexpr bool kDebugSimplifierTests = false;
37
Alex Lightbb550e42021-04-21 17:04:13 -070038template<typename SuperClass>
39class InstructionSimplifierTestBase : public SuperClass, public OptimizingUnitTestHelper {
Alex Light74328052021-03-29 18:11:23 -070040 public:
Vladimir Marko483c41a2021-11-12 12:45:23 +000041 InstructionSimplifierTestBase() {
42 this->use_boot_image_ = true; // Make the Runtime creation cheaper.
43 }
44
Alex Light74328052021-03-29 18:11:23 -070045 void SetUp() override {
Alex Lightbb550e42021-04-21 17:04:13 -070046 SuperClass::SetUp();
Alex Light74328052021-03-29 18:11:23 -070047 gLogVerbosity.compiler = true;
48 }
49
50 void TearDown() override {
Alex Lightbb550e42021-04-21 17:04:13 -070051 SuperClass::TearDown();
Alex Light74328052021-03-29 18:11:23 -070052 gLogVerbosity.compiler = false;
53 }
Vladimir Markob2a164c2023-01-17 17:12:30 +000054
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 }
Alex Light74328052021-03-29 18:11:23 -070067};
68
Alex Lightbb550e42021-04-21 17:04:13 -070069class InstructionSimplifierTest : public InstructionSimplifierTestBase<CommonCompilerTest> {};
70
71// Various configs we can use for testing. Currently used in PartialComparison tests.
72enum class InstanceOfKind {
73 kSelf,
74 kUnrelatedLoaded,
75 kUnrelatedUnloaded,
76 kSupertype,
77};
78
79std::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
92class InstanceOfInstructionSimplifierTestGroup
93 : public InstructionSimplifierTestBase<CommonCompilerTestWithParam<InstanceOfKind>> {
94 public:
95 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
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
Alex Light74328052021-03-29 18:11:23 -0700136// // ENTRY
137// switch (param) {
138// case 1:
139// obj1 = param2; break;
140// case 2:
141// obj1 = param3; break;
142// default:
143// obj2 = new Obj();
144// }
145// val_phi = PHI[3,4,10]
146// target_phi = PHI[param2, param3, obj2]
147// return PredFieldGet[val_phi, target_phi] => PredFieldGet[val_phi, target_phi]
148TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetNoMerge) {
Vladimir Marko1d326f92021-06-01 09:26:55 +0100149 ScopedObjectAccess soa(Thread::Current());
150 VariableSizedHandleScope vshs(soa.Self());
Alex Light74328052021-03-29 18:11:23 -0700151 CreateGraph(&vshs);
152 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
153 "exit",
154 {{"entry", "case1"},
155 {"entry", "case2"},
156 {"entry", "case3"},
157 {"case1", "breturn"},
158 {"case2", "breturn"},
159 {"case3", "breturn"},
160 {"breturn", "exit"}}));
161#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
162 GET_BLOCK(entry);
163 GET_BLOCK(exit);
164 GET_BLOCK(case1);
165 GET_BLOCK(case2);
166 GET_BLOCK(case3);
167 GET_BLOCK(breturn);
168#undef GET_BLOCK
169
170 HInstruction* bool_value = MakeParam(DataType::Type::kInt32);
171 HInstruction* obj1_param = MakeParam(DataType::Type::kReference);
172 HInstruction* obj2_param = MakeParam(DataType::Type::kReference);
173 HInstruction* c3 = graph_->GetIntConstant(3);
174 HInstruction* c4 = graph_->GetIntConstant(4);
175 HInstruction* c10 = graph_->GetIntConstant(10);
176
177 HInstruction* cls = MakeClassLoad();
178 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, bool_value);
179 entry->AddInstruction(cls);
180 entry->AddInstruction(switch_inst);
181 ManuallyBuildEnvFor(cls, {});
182
183 HInstruction* goto_c1 = new (GetAllocator()) HGoto();
184 case1->AddInstruction(goto_c1);
185
186 HInstruction* goto_c2 = new (GetAllocator()) HGoto();
187 case2->AddInstruction(goto_c2);
188
189 HInstruction* obj3 = MakeNewInstance(cls);
190 HInstruction* goto_c3 = new (GetAllocator()) HGoto();
191 case3->AddInstruction(obj3);
192 case3->AddInstruction(goto_c3);
193
194 HPhi* val_phi = MakePhi({c3, c4, c10});
195 HPhi* obj_phi = MakePhi({obj1_param, obj2_param, obj3});
196 HPredicatedInstanceFieldGet* read_end =
197 new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi,
198 nullptr,
199 val_phi,
200 val_phi->GetType(),
201 MemberOffset(10),
202 false,
203 42,
204 0,
205 graph_->GetDexFile(),
206 0);
207 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
208 breturn->AddPhi(val_phi);
209 breturn->AddPhi(obj_phi);
210 breturn->AddInstruction(read_end);
211 breturn->AddInstruction(return_exit);
212
213 SetupExit(exit);
214
Vladimir Markob2a164c2023-01-17 17:12:30 +0000215 PerformSimplification(blks);
Alex Light74328052021-03-29 18:11:23 -0700216
217 EXPECT_INS_RETAINED(read_end);
218
219 EXPECT_INS_EQ(read_end->GetTarget(), obj_phi);
220 EXPECT_INS_EQ(read_end->GetDefaultValue(), val_phi);
221}
222
223// // ENTRY
224// switch (param) {
225// case 1:
226// obj1 = param2; break;
227// case 2:
228// obj1 = param3; break;
229// default:
230// obj2 = new Obj();
231// }
232// val_phi = PHI[3,3,10]
233// target_phi = PHI[param2, param3, obj2]
234// return PredFieldGet[val_phi, target_phi] => PredFieldGet[3, target_phi]
235TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetMerge) {
Vladimir Marko1d326f92021-06-01 09:26:55 +0100236 ScopedObjectAccess soa(Thread::Current());
237 VariableSizedHandleScope vshs(soa.Self());
Alex Light74328052021-03-29 18:11:23 -0700238 CreateGraph(&vshs);
239 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
240 "exit",
241 {{"entry", "case1"},
242 {"entry", "case2"},
243 {"entry", "case3"},
244 {"case1", "breturn"},
245 {"case2", "breturn"},
246 {"case3", "breturn"},
247 {"breturn", "exit"}}));
248#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
249 GET_BLOCK(entry);
250 GET_BLOCK(exit);
251 GET_BLOCK(case1);
252 GET_BLOCK(case2);
253 GET_BLOCK(case3);
254 GET_BLOCK(breturn);
255#undef GET_BLOCK
256
257 HInstruction* bool_value = MakeParam(DataType::Type::kInt32);
258 HInstruction* obj1_param = MakeParam(DataType::Type::kReference);
259 HInstruction* obj2_param = MakeParam(DataType::Type::kReference);
260 HInstruction* c3 = graph_->GetIntConstant(3);
261 HInstruction* c10 = graph_->GetIntConstant(10);
262
263 HInstruction* cls = MakeClassLoad();
264 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, bool_value);
265 entry->AddInstruction(cls);
266 entry->AddInstruction(switch_inst);
267 ManuallyBuildEnvFor(cls, {});
268
269 HInstruction* goto_c1 = new (GetAllocator()) HGoto();
270 case1->AddInstruction(goto_c1);
271
272 HInstruction* goto_c2 = new (GetAllocator()) HGoto();
273 case2->AddInstruction(goto_c2);
274
275 HInstruction* obj3 = MakeNewInstance(cls);
276 HInstruction* goto_c3 = new (GetAllocator()) HGoto();
277 case3->AddInstruction(obj3);
278 case3->AddInstruction(goto_c3);
279
280 HPhi* val_phi = MakePhi({c3, c3, c10});
281 HPhi* obj_phi = MakePhi({obj1_param, obj2_param, obj3});
282 HPredicatedInstanceFieldGet* read_end =
283 new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi,
284 nullptr,
285 val_phi,
286 val_phi->GetType(),
287 MemberOffset(10),
288 false,
289 42,
290 0,
291 graph_->GetDexFile(),
292 0);
293 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
294 breturn->AddPhi(val_phi);
295 breturn->AddPhi(obj_phi);
296 breturn->AddInstruction(read_end);
297 breturn->AddInstruction(return_exit);
298
299 SetupExit(exit);
300
Vladimir Markob2a164c2023-01-17 17:12:30 +0000301 PerformSimplification(blks);
Alex Light74328052021-03-29 18:11:23 -0700302
303 EXPECT_FALSE(obj3->CanBeNull());
304 EXPECT_INS_RETAINED(read_end);
305
306 EXPECT_INS_EQ(read_end->GetTarget(), obj_phi);
307 EXPECT_INS_EQ(read_end->GetDefaultValue(), c3);
308}
309
310// // ENTRY
311// if (param) {
312// obj1 = new Obj();
313// } else {
314// obj2 = new Obj();
315// }
316// val_phi = PHI[3,10]
317// target_phi = PHI[obj1, obj2]
318// return PredFieldGet[val_phi, target_phi] => FieldGet[target_phi]
319TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetNoNull) {
Vladimir Marko1d326f92021-06-01 09:26:55 +0100320 ScopedObjectAccess soa(Thread::Current());
321 VariableSizedHandleScope vshs(soa.Self());
Alex Light74328052021-03-29 18:11:23 -0700322 CreateGraph(&vshs);
323 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
324 "exit",
325 {{"entry", "left"},
326 {"entry", "right"},
327 {"left", "breturn"},
328 {"right", "breturn"},
329 {"breturn", "exit"}}));
330#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
331 GET_BLOCK(entry);
332 GET_BLOCK(exit);
333 GET_BLOCK(left);
334 GET_BLOCK(right);
335 GET_BLOCK(breturn);
336#undef GET_BLOCK
337
338 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
339 HInstruction* c3 = graph_->GetIntConstant(3);
340 HInstruction* c10 = graph_->GetIntConstant(10);
341
342 HInstruction* cls = MakeClassLoad();
343 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
344 entry->AddInstruction(cls);
345 entry->AddInstruction(if_inst);
346 ManuallyBuildEnvFor(cls, {});
347
348 HInstruction* obj1 = MakeNewInstance(cls);
349 HInstruction* goto_left = new (GetAllocator()) HGoto();
350 left->AddInstruction(obj1);
351 left->AddInstruction(goto_left);
352
353 HInstruction* obj2 = MakeNewInstance(cls);
354 HInstruction* goto_right = new (GetAllocator()) HGoto();
355 right->AddInstruction(obj2);
356 right->AddInstruction(goto_right);
357
358 HPhi* val_phi = MakePhi({c3, c10});
359 HPhi* obj_phi = MakePhi({obj1, obj2});
Alex Lightbb550e42021-04-21 17:04:13 -0700360 obj_phi->SetCanBeNull(false);
Alex Light74328052021-03-29 18:11:23 -0700361 HInstruction* read_end = new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi,
362 nullptr,
363 val_phi,
364 val_phi->GetType(),
365 MemberOffset(10),
366 false,
367 42,
368 0,
369 graph_->GetDexFile(),
370 0);
371 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
372 breturn->AddPhi(val_phi);
373 breturn->AddPhi(obj_phi);
374 breturn->AddInstruction(read_end);
375 breturn->AddInstruction(return_exit);
376
377 SetupExit(exit);
378
Vladimir Markob2a164c2023-01-17 17:12:30 +0000379 PerformSimplification(blks);
Alex Light74328052021-03-29 18:11:23 -0700380
381 EXPECT_FALSE(obj1->CanBeNull());
382 EXPECT_FALSE(obj2->CanBeNull());
383 EXPECT_INS_REMOVED(read_end);
384
385 HInstanceFieldGet* ifget = FindSingleInstruction<HInstanceFieldGet>(graph_, breturn);
386 ASSERT_NE(ifget, nullptr);
387 EXPECT_INS_EQ(ifget->InputAt(0), obj_phi);
388}
389
Alex Lightbb550e42021-04-21 17:04:13 -0700390// // ENTRY
391// obj = new Obj();
392// // Make sure this graph isn't broken
393// if (obj instanceof <other>) {
394// // LEFT
395// } else {
396// // RIGHT
397// }
398// EXIT
399// return obj.field
400TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassInstanceOfOther) {
Vladimir Marko1d326f92021-06-01 09:26:55 +0100401 ScopedObjectAccess soa(Thread::Current());
402 VariableSizedHandleScope vshs(soa.Self());
Alex Lightbb550e42021-04-21 17:04:13 -0700403 InitGraph(/*handles=*/&vshs);
404
405 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
406 "exit",
407 {{"entry", "left"},
408 {"entry", "right"},
409 {"left", "breturn"},
410 {"right", "breturn"},
411 {"breturn", "exit"}}));
412#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
413 GET_BLOCK(entry);
414 GET_BLOCK(exit);
415 GET_BLOCK(breturn);
416 GET_BLOCK(left);
417 GET_BLOCK(right);
418#undef GET_BLOCK
419 EnsurePredecessorOrder(breturn, {left, right});
420 HInstruction* test_res = graph_->GetIntConstant(GetConstantResult() ? 1 : 0);
421
422 auto [new_inst_klass, target_klass] = GetLoadClasses(&vshs);
423 HInstruction* new_inst = MakeNewInstance(new_inst_klass);
424 new_inst->SetReferenceTypeInfo(
425 ReferenceTypeInfo::Create(new_inst_klass->GetClass(), /*is_exact=*/true));
426 HInstanceOf* instance_of = new (GetAllocator()) HInstanceOf(new_inst,
427 target_klass,
428 TypeCheckKind::kClassHierarchyCheck,
429 target_klass->GetClass(),
430 0u,
431 GetAllocator(),
432 nullptr,
433 nullptr);
434 if (target_klass->GetLoadedClassRTI().IsValid()) {
435 instance_of->SetValidTargetClassRTI();
436 }
437 HInstruction* if_inst = new (GetAllocator()) HIf(instance_of);
438 entry->AddInstruction(new_inst_klass);
439 if (new_inst_klass != target_klass) {
440 entry->AddInstruction(target_klass);
441 }
442 entry->AddInstruction(new_inst);
443 entry->AddInstruction(instance_of);
444 entry->AddInstruction(if_inst);
445 ManuallyBuildEnvFor(new_inst_klass, {});
446 if (new_inst_klass != target_klass) {
447 target_klass->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
448 }
449 new_inst->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
450
451 HInstruction* goto_left = new (GetAllocator()) HGoto();
452 left->AddInstruction(goto_left);
453
454 HInstruction* goto_right = new (GetAllocator()) HGoto();
455 right->AddInstruction(goto_right);
456
457 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
458 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
459 breturn->AddInstruction(read_bottom);
460 breturn->AddInstruction(return_exit);
461
462 SetupExit(exit);
463
Vladimir Markob2a164c2023-01-17 17:12:30 +0000464 PerformSimplification(blks);
Alex Lightbb550e42021-04-21 17:04:13 -0700465
466 if (!GetConstantResult() || GetParam() == InstanceOfKind::kSelf) {
467 EXPECT_INS_RETAINED(target_klass);
468 } else {
469 EXPECT_INS_REMOVED(target_klass);
470 }
471 EXPECT_INS_REMOVED(instance_of);
472 EXPECT_INS_EQ(if_inst->InputAt(0), test_res);
473}
474
475// // ENTRY
476// obj = new Obj();
477// (<other>)obj;
478// // Make sure this graph isn't broken
479// EXIT
480// return obj
481TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassCheckCastOther) {
Vladimir Marko1d326f92021-06-01 09:26:55 +0100482 ScopedObjectAccess soa(Thread::Current());
483 VariableSizedHandleScope vshs(soa.Self());
Alex Lightbb550e42021-04-21 17:04:13 -0700484 InitGraph(/*handles=*/&vshs);
485
486 AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", {{"entry", "exit"}}));
487#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
488 GET_BLOCK(entry);
489 GET_BLOCK(exit);
490#undef GET_BLOCK
491
492 auto [new_inst_klass, target_klass] = GetLoadClasses(&vshs);
493 HInstruction* new_inst = MakeNewInstance(new_inst_klass);
494 new_inst->SetReferenceTypeInfo(
495 ReferenceTypeInfo::Create(new_inst_klass->GetClass(), /*is_exact=*/true));
496 HCheckCast* check_cast = new (GetAllocator()) HCheckCast(new_inst,
497 target_klass,
498 TypeCheckKind::kClassHierarchyCheck,
499 target_klass->GetClass(),
500 0u,
501 GetAllocator(),
502 nullptr,
503 nullptr);
504 if (target_klass->GetLoadedClassRTI().IsValid()) {
505 check_cast->SetValidTargetClassRTI();
506 }
507 HInstruction* entry_return = new (GetAllocator()) HReturn(new_inst);
508 entry->AddInstruction(new_inst_klass);
509 if (new_inst_klass != target_klass) {
510 entry->AddInstruction(target_klass);
511 }
512 entry->AddInstruction(new_inst);
513 entry->AddInstruction(check_cast);
514 entry->AddInstruction(entry_return);
515 ManuallyBuildEnvFor(new_inst_klass, {});
516 if (new_inst_klass != target_klass) {
517 target_klass->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
518 }
519 new_inst->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
520
521 SetupExit(exit);
522
Vladimir Markob2a164c2023-01-17 17:12:30 +0000523 PerformSimplification(blks);
Alex Lightbb550e42021-04-21 17:04:13 -0700524
525 if (!GetConstantResult() || GetParam() == InstanceOfKind::kSelf) {
526 EXPECT_INS_RETAINED(target_klass);
527 } else {
528 EXPECT_INS_REMOVED(target_klass);
529 }
530 if (GetConstantResult()) {
531 EXPECT_INS_REMOVED(check_cast);
532 } else {
533 EXPECT_INS_RETAINED(check_cast);
534 }
535}
536
537INSTANTIATE_TEST_SUITE_P(InstructionSimplifierTest,
538 InstanceOfInstructionSimplifierTestGroup,
539 testing::Values(InstanceOfKind::kSelf,
540 InstanceOfKind::kUnrelatedLoaded,
541 InstanceOfKind::kUnrelatedUnloaded,
542 InstanceOfKind::kSupertype));
543
Alex Light74328052021-03-29 18:11:23 -0700544} // namespace art