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