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