blob: ac0bdb93c3d2da4a4eb228b19be92916f6f628fb [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
29namespace art {
30
Alex Lightbb550e42021-04-21 17:04:13 -070031namespace mirror {
32class ClassExt;
33class Throwable;
34} // namespace mirror
35
36template<typename SuperClass>
37class InstructionSimplifierTestBase : public SuperClass, public OptimizingUnitTestHelper {
Alex Light74328052021-03-29 18:11:23 -070038 public:
39 void SetUp() override {
Alex Lightbb550e42021-04-21 17:04:13 -070040 SuperClass::SetUp();
Alex Light74328052021-03-29 18:11:23 -070041 gLogVerbosity.compiler = true;
42 }
43
44 void TearDown() override {
Alex Lightbb550e42021-04-21 17:04:13 -070045 SuperClass::TearDown();
Alex Light74328052021-03-29 18:11:23 -070046 gLogVerbosity.compiler = false;
47 }
48};
49
Alex Lightbb550e42021-04-21 17:04:13 -070050class InstructionSimplifierTest : public InstructionSimplifierTestBase<CommonCompilerTest> {};
51
52// Various configs we can use for testing. Currently used in PartialComparison tests.
53enum class InstanceOfKind {
54 kSelf,
55 kUnrelatedLoaded,
56 kUnrelatedUnloaded,
57 kSupertype,
58};
59
60std::ostream& operator<<(std::ostream& os, const InstanceOfKind& comp) {
61 switch (comp) {
62 case InstanceOfKind::kSupertype:
63 return os << "kSupertype";
64 case InstanceOfKind::kSelf:
65 return os << "kSelf";
66 case InstanceOfKind::kUnrelatedLoaded:
67 return os << "kUnrelatedLoaded";
68 case InstanceOfKind::kUnrelatedUnloaded:
69 return os << "kUnrelatedUnloaded";
70 }
71}
72
73class InstanceOfInstructionSimplifierTestGroup
74 : public InstructionSimplifierTestBase<CommonCompilerTestWithParam<InstanceOfKind>> {
75 public:
76 bool GetConstantResult() const {
77 switch (GetParam()) {
78 case InstanceOfKind::kSupertype:
79 case InstanceOfKind::kSelf:
80 return true;
81 case InstanceOfKind::kUnrelatedLoaded:
82 case InstanceOfKind::kUnrelatedUnloaded:
83 return false;
84 }
85 }
86
87 std::pair<HLoadClass*, HLoadClass*> GetLoadClasses(VariableSizedHandleScope* vshs) {
88 InstanceOfKind kind = GetParam();
89 ScopedObjectAccess soa(Thread::Current());
90 // New inst always needs to have a valid rti since we dcheck that.
91 HLoadClass* new_inst = MakeClassLoad(
92 /* ti= */ std::nullopt, vshs->NewHandle<mirror::Class>(GetClassRoot<mirror::ClassExt>()));
93 new_inst->SetValidLoadedClassRTI();
94 if (kind == InstanceOfKind::kSelf) {
95 return {new_inst, new_inst};
96 }
97 if (kind == InstanceOfKind::kUnrelatedUnloaded) {
98 HLoadClass* target_class = MakeClassLoad();
99 EXPECT_FALSE(target_class->GetLoadedClassRTI().IsValid());
100 return {new_inst, target_class};
101 }
102 // Force both classes to be a real classes.
103 // For simplicity we use class-roots as the types. The new-inst will always
104 // be a ClassExt, unrelated-loaded will always be Throwable and super will
105 // always be Object
106 HLoadClass* target_class = MakeClassLoad(
107 /* ti= */ std::nullopt,
108 vshs->NewHandle<mirror::Class>(kind == InstanceOfKind::kSupertype ?
109 GetClassRoot<mirror::Object>() :
110 GetClassRoot<mirror::Throwable>()));
111 target_class->SetValidLoadedClassRTI();
112 EXPECT_TRUE(target_class->GetLoadedClassRTI().IsValid());
113 return {new_inst, target_class};
114 }
115};
116
Alex Light74328052021-03-29 18:11:23 -0700117// // ENTRY
118// switch (param) {
119// case 1:
120// obj1 = param2; break;
121// case 2:
122// obj1 = param3; break;
123// default:
124// obj2 = new Obj();
125// }
126// val_phi = PHI[3,4,10]
127// target_phi = PHI[param2, param3, obj2]
128// return PredFieldGet[val_phi, target_phi] => PredFieldGet[val_phi, target_phi]
129TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetNoMerge) {
130 VariableSizedHandleScope vshs(Thread::Current());
131 CreateGraph(&vshs);
132 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
133 "exit",
134 {{"entry", "case1"},
135 {"entry", "case2"},
136 {"entry", "case3"},
137 {"case1", "breturn"},
138 {"case2", "breturn"},
139 {"case3", "breturn"},
140 {"breturn", "exit"}}));
141#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
142 GET_BLOCK(entry);
143 GET_BLOCK(exit);
144 GET_BLOCK(case1);
145 GET_BLOCK(case2);
146 GET_BLOCK(case3);
147 GET_BLOCK(breturn);
148#undef GET_BLOCK
149
150 HInstruction* bool_value = MakeParam(DataType::Type::kInt32);
151 HInstruction* obj1_param = MakeParam(DataType::Type::kReference);
152 HInstruction* obj2_param = MakeParam(DataType::Type::kReference);
153 HInstruction* c3 = graph_->GetIntConstant(3);
154 HInstruction* c4 = graph_->GetIntConstant(4);
155 HInstruction* c10 = graph_->GetIntConstant(10);
156
157 HInstruction* cls = MakeClassLoad();
158 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, bool_value);
159 entry->AddInstruction(cls);
160 entry->AddInstruction(switch_inst);
161 ManuallyBuildEnvFor(cls, {});
162
163 HInstruction* goto_c1 = new (GetAllocator()) HGoto();
164 case1->AddInstruction(goto_c1);
165
166 HInstruction* goto_c2 = new (GetAllocator()) HGoto();
167 case2->AddInstruction(goto_c2);
168
169 HInstruction* obj3 = MakeNewInstance(cls);
170 HInstruction* goto_c3 = new (GetAllocator()) HGoto();
171 case3->AddInstruction(obj3);
172 case3->AddInstruction(goto_c3);
173
174 HPhi* val_phi = MakePhi({c3, c4, c10});
175 HPhi* obj_phi = MakePhi({obj1_param, obj2_param, obj3});
176 HPredicatedInstanceFieldGet* read_end =
177 new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi,
178 nullptr,
179 val_phi,
180 val_phi->GetType(),
181 MemberOffset(10),
182 false,
183 42,
184 0,
185 graph_->GetDexFile(),
186 0);
187 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
188 breturn->AddPhi(val_phi);
189 breturn->AddPhi(obj_phi);
190 breturn->AddInstruction(read_end);
191 breturn->AddInstruction(return_exit);
192
193 SetupExit(exit);
194
195 LOG(INFO) << "Pre simplification " << blks;
196 graph_->ClearDominanceInformation();
197 graph_->BuildDominatorTree();
198 InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
199 simp.Run();
200
201 LOG(INFO) << "Post simplify " << blks;
202
203 EXPECT_INS_RETAINED(read_end);
204
205 EXPECT_INS_EQ(read_end->GetTarget(), obj_phi);
206 EXPECT_INS_EQ(read_end->GetDefaultValue(), val_phi);
207}
208
209// // ENTRY
210// switch (param) {
211// case 1:
212// obj1 = param2; break;
213// case 2:
214// obj1 = param3; break;
215// default:
216// obj2 = new Obj();
217// }
218// val_phi = PHI[3,3,10]
219// target_phi = PHI[param2, param3, obj2]
220// return PredFieldGet[val_phi, target_phi] => PredFieldGet[3, target_phi]
221TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetMerge) {
222 VariableSizedHandleScope vshs(Thread::Current());
223 CreateGraph(&vshs);
224 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
225 "exit",
226 {{"entry", "case1"},
227 {"entry", "case2"},
228 {"entry", "case3"},
229 {"case1", "breturn"},
230 {"case2", "breturn"},
231 {"case3", "breturn"},
232 {"breturn", "exit"}}));
233#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
234 GET_BLOCK(entry);
235 GET_BLOCK(exit);
236 GET_BLOCK(case1);
237 GET_BLOCK(case2);
238 GET_BLOCK(case3);
239 GET_BLOCK(breturn);
240#undef GET_BLOCK
241
242 HInstruction* bool_value = MakeParam(DataType::Type::kInt32);
243 HInstruction* obj1_param = MakeParam(DataType::Type::kReference);
244 HInstruction* obj2_param = MakeParam(DataType::Type::kReference);
245 HInstruction* c3 = graph_->GetIntConstant(3);
246 HInstruction* c10 = graph_->GetIntConstant(10);
247
248 HInstruction* cls = MakeClassLoad();
249 HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, bool_value);
250 entry->AddInstruction(cls);
251 entry->AddInstruction(switch_inst);
252 ManuallyBuildEnvFor(cls, {});
253
254 HInstruction* goto_c1 = new (GetAllocator()) HGoto();
255 case1->AddInstruction(goto_c1);
256
257 HInstruction* goto_c2 = new (GetAllocator()) HGoto();
258 case2->AddInstruction(goto_c2);
259
260 HInstruction* obj3 = MakeNewInstance(cls);
261 HInstruction* goto_c3 = new (GetAllocator()) HGoto();
262 case3->AddInstruction(obj3);
263 case3->AddInstruction(goto_c3);
264
265 HPhi* val_phi = MakePhi({c3, c3, c10});
266 HPhi* obj_phi = MakePhi({obj1_param, obj2_param, obj3});
267 HPredicatedInstanceFieldGet* read_end =
268 new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi,
269 nullptr,
270 val_phi,
271 val_phi->GetType(),
272 MemberOffset(10),
273 false,
274 42,
275 0,
276 graph_->GetDexFile(),
277 0);
278 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
279 breturn->AddPhi(val_phi);
280 breturn->AddPhi(obj_phi);
281 breturn->AddInstruction(read_end);
282 breturn->AddInstruction(return_exit);
283
284 SetupExit(exit);
285
286 LOG(INFO) << "Pre simplification " << blks;
287 graph_->ClearDominanceInformation();
288 graph_->BuildDominatorTree();
289 InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
290 simp.Run();
291
292 LOG(INFO) << "Post simplify " << blks;
293
294 EXPECT_FALSE(obj3->CanBeNull());
295 EXPECT_INS_RETAINED(read_end);
296
297 EXPECT_INS_EQ(read_end->GetTarget(), obj_phi);
298 EXPECT_INS_EQ(read_end->GetDefaultValue(), c3);
299}
300
301// // ENTRY
302// if (param) {
303// obj1 = new Obj();
304// } else {
305// obj2 = new Obj();
306// }
307// val_phi = PHI[3,10]
308// target_phi = PHI[obj1, obj2]
309// return PredFieldGet[val_phi, target_phi] => FieldGet[target_phi]
310TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetNoNull) {
311 VariableSizedHandleScope vshs(Thread::Current());
312 CreateGraph(&vshs);
313 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
314 "exit",
315 {{"entry", "left"},
316 {"entry", "right"},
317 {"left", "breturn"},
318 {"right", "breturn"},
319 {"breturn", "exit"}}));
320#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
321 GET_BLOCK(entry);
322 GET_BLOCK(exit);
323 GET_BLOCK(left);
324 GET_BLOCK(right);
325 GET_BLOCK(breturn);
326#undef GET_BLOCK
327
328 HInstruction* bool_value = MakeParam(DataType::Type::kBool);
329 HInstruction* c3 = graph_->GetIntConstant(3);
330 HInstruction* c10 = graph_->GetIntConstant(10);
331
332 HInstruction* cls = MakeClassLoad();
333 HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
334 entry->AddInstruction(cls);
335 entry->AddInstruction(if_inst);
336 ManuallyBuildEnvFor(cls, {});
337
338 HInstruction* obj1 = MakeNewInstance(cls);
339 HInstruction* goto_left = new (GetAllocator()) HGoto();
340 left->AddInstruction(obj1);
341 left->AddInstruction(goto_left);
342
343 HInstruction* obj2 = MakeNewInstance(cls);
344 HInstruction* goto_right = new (GetAllocator()) HGoto();
345 right->AddInstruction(obj2);
346 right->AddInstruction(goto_right);
347
348 HPhi* val_phi = MakePhi({c3, c10});
349 HPhi* obj_phi = MakePhi({obj1, obj2});
Alex Lightbb550e42021-04-21 17:04:13 -0700350 obj_phi->SetCanBeNull(false);
Alex Light74328052021-03-29 18:11:23 -0700351 HInstruction* read_end = new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi,
352 nullptr,
353 val_phi,
354 val_phi->GetType(),
355 MemberOffset(10),
356 false,
357 42,
358 0,
359 graph_->GetDexFile(),
360 0);
361 HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
362 breturn->AddPhi(val_phi);
363 breturn->AddPhi(obj_phi);
364 breturn->AddInstruction(read_end);
365 breturn->AddInstruction(return_exit);
366
367 SetupExit(exit);
368
369 LOG(INFO) << "Pre simplification " << blks;
370 graph_->ClearDominanceInformation();
371 graph_->BuildDominatorTree();
372 InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
373 simp.Run();
374
375 LOG(INFO) << "Post simplify " << blks;
376
377 EXPECT_FALSE(obj1->CanBeNull());
378 EXPECT_FALSE(obj2->CanBeNull());
379 EXPECT_INS_REMOVED(read_end);
380
381 HInstanceFieldGet* ifget = FindSingleInstruction<HInstanceFieldGet>(graph_, breturn);
382 ASSERT_NE(ifget, nullptr);
383 EXPECT_INS_EQ(ifget->InputAt(0), obj_phi);
384}
385
Alex Lightbb550e42021-04-21 17:04:13 -0700386// // ENTRY
387// obj = new Obj();
388// // Make sure this graph isn't broken
389// if (obj instanceof <other>) {
390// // LEFT
391// } else {
392// // RIGHT
393// }
394// EXIT
395// return obj.field
396TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassInstanceOfOther) {
397 VariableSizedHandleScope vshs(Thread::Current());
398 InitGraph(/*handles=*/&vshs);
399
400 AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
401 "exit",
402 {{"entry", "left"},
403 {"entry", "right"},
404 {"left", "breturn"},
405 {"right", "breturn"},
406 {"breturn", "exit"}}));
407#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
408 GET_BLOCK(entry);
409 GET_BLOCK(exit);
410 GET_BLOCK(breturn);
411 GET_BLOCK(left);
412 GET_BLOCK(right);
413#undef GET_BLOCK
414 EnsurePredecessorOrder(breturn, {left, right});
415 HInstruction* test_res = graph_->GetIntConstant(GetConstantResult() ? 1 : 0);
416
417 auto [new_inst_klass, target_klass] = GetLoadClasses(&vshs);
418 HInstruction* new_inst = MakeNewInstance(new_inst_klass);
419 new_inst->SetReferenceTypeInfo(
420 ReferenceTypeInfo::Create(new_inst_klass->GetClass(), /*is_exact=*/true));
421 HInstanceOf* instance_of = new (GetAllocator()) HInstanceOf(new_inst,
422 target_klass,
423 TypeCheckKind::kClassHierarchyCheck,
424 target_klass->GetClass(),
425 0u,
426 GetAllocator(),
427 nullptr,
428 nullptr);
429 if (target_klass->GetLoadedClassRTI().IsValid()) {
430 instance_of->SetValidTargetClassRTI();
431 }
432 HInstruction* if_inst = new (GetAllocator()) HIf(instance_of);
433 entry->AddInstruction(new_inst_klass);
434 if (new_inst_klass != target_klass) {
435 entry->AddInstruction(target_klass);
436 }
437 entry->AddInstruction(new_inst);
438 entry->AddInstruction(instance_of);
439 entry->AddInstruction(if_inst);
440 ManuallyBuildEnvFor(new_inst_klass, {});
441 if (new_inst_klass != target_klass) {
442 target_klass->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
443 }
444 new_inst->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
445
446 HInstruction* goto_left = new (GetAllocator()) HGoto();
447 left->AddInstruction(goto_left);
448
449 HInstruction* goto_right = new (GetAllocator()) HGoto();
450 right->AddInstruction(goto_right);
451
452 HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
453 HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
454 breturn->AddInstruction(read_bottom);
455 breturn->AddInstruction(return_exit);
456
457 SetupExit(exit);
458
459 // PerformLSE expects this to be empty.
460 graph_->ClearDominanceInformation();
461
462 LOG(INFO) << "Pre simplification " << blks;
463 graph_->ClearDominanceInformation();
464 graph_->BuildDominatorTree();
465 InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
466 simp.Run();
467
468 LOG(INFO) << "Post simplify " << blks;
469
470 if (!GetConstantResult() || GetParam() == InstanceOfKind::kSelf) {
471 EXPECT_INS_RETAINED(target_klass);
472 } else {
473 EXPECT_INS_REMOVED(target_klass);
474 }
475 EXPECT_INS_REMOVED(instance_of);
476 EXPECT_INS_EQ(if_inst->InputAt(0), test_res);
477}
478
479// // ENTRY
480// obj = new Obj();
481// (<other>)obj;
482// // Make sure this graph isn't broken
483// EXIT
484// return obj
485TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassCheckCastOther) {
486 VariableSizedHandleScope vshs(Thread::Current());
487 InitGraph(/*handles=*/&vshs);
488
489 AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", {{"entry", "exit"}}));
490#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
491 GET_BLOCK(entry);
492 GET_BLOCK(exit);
493#undef GET_BLOCK
494
495 auto [new_inst_klass, target_klass] = GetLoadClasses(&vshs);
496 HInstruction* new_inst = MakeNewInstance(new_inst_klass);
497 new_inst->SetReferenceTypeInfo(
498 ReferenceTypeInfo::Create(new_inst_klass->GetClass(), /*is_exact=*/true));
499 HCheckCast* check_cast = new (GetAllocator()) HCheckCast(new_inst,
500 target_klass,
501 TypeCheckKind::kClassHierarchyCheck,
502 target_klass->GetClass(),
503 0u,
504 GetAllocator(),
505 nullptr,
506 nullptr);
507 if (target_klass->GetLoadedClassRTI().IsValid()) {
508 check_cast->SetValidTargetClassRTI();
509 }
510 HInstruction* entry_return = new (GetAllocator()) HReturn(new_inst);
511 entry->AddInstruction(new_inst_klass);
512 if (new_inst_klass != target_klass) {
513 entry->AddInstruction(target_klass);
514 }
515 entry->AddInstruction(new_inst);
516 entry->AddInstruction(check_cast);
517 entry->AddInstruction(entry_return);
518 ManuallyBuildEnvFor(new_inst_klass, {});
519 if (new_inst_klass != target_klass) {
520 target_klass->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
521 }
522 new_inst->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
523
524 SetupExit(exit);
525
526 // PerformLSE expects this to be empty.
527 graph_->ClearDominanceInformation();
528
529 LOG(INFO) << "Pre simplification " << blks;
530 graph_->ClearDominanceInformation();
531 graph_->BuildDominatorTree();
532 InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
533 simp.Run();
534
535 LOG(INFO) << "Post simplify " << blks;
536
537 if (!GetConstantResult() || GetParam() == InstanceOfKind::kSelf) {
538 EXPECT_INS_RETAINED(target_klass);
539 } else {
540 EXPECT_INS_REMOVED(target_klass);
541 }
542 if (GetConstantResult()) {
543 EXPECT_INS_REMOVED(check_cast);
544 } else {
545 EXPECT_INS_RETAINED(check_cast);
546 }
547}
548
549INSTANTIATE_TEST_SUITE_P(InstructionSimplifierTest,
550 InstanceOfInstructionSimplifierTestGroup,
551 testing::Values(InstanceOfKind::kSelf,
552 InstanceOfKind::kUnrelatedLoaded,
553 InstanceOfKind::kUnrelatedUnloaded,
554 InstanceOfKind::kSupertype));
555
Alex Light74328052021-03-29 18:11:23 -0700556} // namespace art