blob: f7d2ed12402713cff20d8e12e54f231a5eb9662c [file] [log] [blame]
Calin Juravle10e244f2015-01-26 18:54:32 +00001/*
2 * Copyright (C) 2015 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 "reference_type_propagation.h"
18
Calin Juravleacf735c2015-02-12 15:25:22 +000019#include "class_linker.h"
20#include "mirror/class-inl.h"
21#include "mirror/dex_cache.h"
22#include "scoped_thread_state_change.h"
23
Calin Juravle10e244f2015-01-26 18:54:32 +000024namespace art {
25
Calin Juravleacf735c2015-02-12 15:25:22 +000026void ReferenceTypePropagation::Run() {
27 // To properly propagate type info we need to visit in the dominator-based order.
Calin Juravle10e244f2015-01-26 18:54:32 +000028 // Reverse post order guarantees a node's dominators are visited first.
29 // We take advantage of this order in `VisitBasicBlock`.
30 for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
31 VisitBasicBlock(it.Current());
32 }
33 ProcessWorklist();
34}
35
Calin Juravleb1498f62015-02-16 13:13:29 +000036void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
37 // TODO: handle other instructions that give type info
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +010038 // (Call/array accesses)
Calin Juravle10e244f2015-01-26 18:54:32 +000039
Calin Juravleb1498f62015-02-16 13:13:29 +000040 // Initialize exact types first for faster convergence.
41 for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
42 HInstruction* instr = it.Current();
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +010043 // TODO: Make ReferenceTypePropagation a visitor or create a new one.
Calin Juravleb1498f62015-02-16 13:13:29 +000044 if (instr->IsNewInstance()) {
45 VisitNewInstance(instr->AsNewInstance());
46 } else if (instr->IsLoadClass()) {
47 VisitLoadClass(instr->AsLoadClass());
Guillaume "Vermeille" Sanchez81d804a2015-05-20 12:42:25 +010048 } else if (instr->IsNewArray()) {
49 VisitNewArray(instr->AsNewArray());
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +010050 } else if (instr->IsInstanceFieldGet()) {
51 VisitInstanceFieldGet(instr->AsInstanceFieldGet());
52 } else if (instr->IsStaticFieldGet()) {
53 VisitStaticFieldGet(instr->AsStaticFieldGet());
Calin Juravleb1498f62015-02-16 13:13:29 +000054 }
55 }
56
57 // Handle Phis.
58 for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
59 VisitPhi(it.Current()->AsPhi());
60 }
61
62 // Add extra nodes to bound types.
Calin Juravle61d544b2015-02-23 16:46:57 +000063 BoundTypeForIfNotNull(block);
Calin Juravleb1498f62015-02-16 13:13:29 +000064 BoundTypeForIfInstanceOf(block);
Calin Juravle10e244f2015-01-26 18:54:32 +000065}
66
Calin Juravle61d544b2015-02-23 16:46:57 +000067void ReferenceTypePropagation::BoundTypeForIfNotNull(HBasicBlock* block) {
Calin Juravleb3306642015-04-20 18:30:42 +010068 HIf* ifInstruction = block->GetLastInstruction()->AsIf();
69 if (ifInstruction == nullptr) {
Calin Juravle61d544b2015-02-23 16:46:57 +000070 return;
71 }
Calin Juravleb3306642015-04-20 18:30:42 +010072 HInstruction* ifInput = ifInstruction->InputAt(0);
Calin Juravle61d544b2015-02-23 16:46:57 +000073 if (!ifInput->IsNotEqual() && !ifInput->IsEqual()) {
74 return;
75 }
76 HInstruction* input0 = ifInput->InputAt(0);
77 HInstruction* input1 = ifInput->InputAt(1);
Calin Juravleedad8ad2015-04-23 14:34:33 +010078 HInstruction* obj = nullptr;
Calin Juravle61d544b2015-02-23 16:46:57 +000079
Calin Juravleedad8ad2015-04-23 14:34:33 +010080 if (input1->IsNullConstant()) {
Calin Juravle61d544b2015-02-23 16:46:57 +000081 obj = input0;
Calin Juravleedad8ad2015-04-23 14:34:33 +010082 } else if (input0->IsNullConstant()) {
Calin Juravle61d544b2015-02-23 16:46:57 +000083 obj = input1;
84 } else {
85 return;
86 }
87
Calin Juravleb3306642015-04-20 18:30:42 +010088 // We only need to bound the type if we have uses in the relevant block.
89 // So start with null and create the HBoundType lazily, only if it's needed.
90 HBoundType* bound_type = nullptr;
Calin Juravle61d544b2015-02-23 16:46:57 +000091 HBasicBlock* notNullBlock = ifInput->IsNotEqual()
Calin Juravleb3306642015-04-20 18:30:42 +010092 ? ifInstruction->IfTrueSuccessor()
93 : ifInstruction->IfFalseSuccessor();
94
Calin Juravle61d544b2015-02-23 16:46:57 +000095 for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
96 HInstruction* user = it.Current()->GetUser();
97 if (notNullBlock->Dominates(user->GetBlock())) {
Calin Juravleb3306642015-04-20 18:30:42 +010098 if (bound_type == nullptr) {
99 bound_type = new (graph_->GetArena()) HBoundType(obj, ReferenceTypeInfo::CreateTop(false));
100 notNullBlock->InsertInstructionBefore(bound_type, notNullBlock->GetFirstInstruction());
101 }
Calin Juravle61d544b2015-02-23 16:46:57 +0000102 user->ReplaceInput(bound_type, it.Current()->GetIndex());
103 }
104 }
105}
106
Calin Juravleb1498f62015-02-16 13:13:29 +0000107// Detects if `block` is the True block for the pattern
108// `if (x instanceof ClassX) { }`
109// If that's the case insert an HBoundType instruction to bound the type of `x`
110// to `ClassX` in the scope of the dominated blocks.
111void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) {
Calin Juravleb3306642015-04-20 18:30:42 +0100112 HIf* ifInstruction = block->GetLastInstruction()->AsIf();
113 if (ifInstruction == nullptr) {
Calin Juravleb1498f62015-02-16 13:13:29 +0000114 return;
115 }
Calin Juravleb3306642015-04-20 18:30:42 +0100116 HInstruction* ifInput = ifInstruction->InputAt(0);
117 HInstruction* instanceOf = nullptr;
118 HBasicBlock* instanceOfTrueBlock = nullptr;
David Brazdil0d13fee2015-04-17 14:52:19 +0100119
Calin Juravleb3306642015-04-20 18:30:42 +0100120 // The instruction simplifier has transformed:
121 // - `if (a instanceof A)` into an HIf with an HInstanceOf input
122 // - `if (!(a instanceof A)` into an HIf with an HBooleanNot input (which in turn
123 // has an HInstanceOf input)
124 // So we should not see the usual HEqual here.
David Brazdil0d13fee2015-04-17 14:52:19 +0100125 if (ifInput->IsInstanceOf()) {
126 instanceOf = ifInput;
Calin Juravleb3306642015-04-20 18:30:42 +0100127 instanceOfTrueBlock = ifInstruction->IfTrueSuccessor();
David Brazdil0d13fee2015-04-17 14:52:19 +0100128 } else if (ifInput->IsBooleanNot() && ifInput->InputAt(0)->IsInstanceOf()) {
129 instanceOf = ifInput->InputAt(0);
Calin Juravleb3306642015-04-20 18:30:42 +0100130 instanceOfTrueBlock = ifInstruction->IfFalseSuccessor();
David Brazdil0d13fee2015-04-17 14:52:19 +0100131 } else {
Calin Juravleb1498f62015-02-16 13:13:29 +0000132 return;
Calin Juravleacf735c2015-02-12 15:25:22 +0000133 }
134
Calin Juravleb3306642015-04-20 18:30:42 +0100135 // We only need to bound the type if we have uses in the relevant block.
136 // So start with null and create the HBoundType lazily, only if it's needed.
137 HBoundType* bound_type = nullptr;
138
Calin Juravleb1498f62015-02-16 13:13:29 +0000139 HInstruction* obj = instanceOf->InputAt(0);
Calin Juravleb1498f62015-02-16 13:13:29 +0000140 for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
141 HInstruction* user = it.Current()->GetUser();
142 if (instanceOfTrueBlock->Dominates(user->GetBlock())) {
Calin Juravleb3306642015-04-20 18:30:42 +0100143 if (bound_type == nullptr) {
144 HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass();
145
146 ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo();
147 ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
148 bound_type = new (graph_->GetArena()) HBoundType(obj, class_rti);
149
150 // Narrow the type as much as possible.
151 {
152 ScopedObjectAccess soa(Thread::Current());
153 if (!load_class->IsResolved() || class_rti.IsSupertypeOf(obj_rti)) {
154 bound_type->SetReferenceTypeInfo(obj_rti);
155 } else {
156 bound_type->SetReferenceTypeInfo(
157 ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ false));
158 }
159 }
160
161 instanceOfTrueBlock->InsertInstructionBefore(
162 bound_type, instanceOfTrueBlock->GetFirstInstruction());
163 }
Calin Juravleb1498f62015-02-16 13:13:29 +0000164 user->ReplaceInput(bound_type, it.Current()->GetIndex());
165 }
166 }
Calin Juravleacf735c2015-02-12 15:25:22 +0000167}
168
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100169void ReferenceTypePropagation::SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass) {
170 if (klass != nullptr) {
171 ScopedObjectAccess soa(Thread::Current());
172 MutableHandle<mirror::Class> handle = handles_->NewHandle(klass);
173 instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, true));
174 }
175}
176
Guillaume "Vermeille" Sanchez81d804a2015-05-20 12:42:25 +0100177void ReferenceTypePropagation::UpdateReferenceTypeInfo(HInstruction* instr,
178 uint16_t type_idx,
179 const DexFile& dex_file) {
180 DCHECK_EQ(instr->GetType(), Primitive::kPrimNot);
181
Calin Juravleacf735c2015-02-12 15:25:22 +0000182 ScopedObjectAccess soa(Thread::Current());
Guillaume "Vermeille" Sanchez81d804a2015-05-20 12:42:25 +0100183 mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file);
Calin Juravleacf735c2015-02-12 15:25:22 +0000184 // Get type from dex cache assuming it was populated by the verifier.
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100185 SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx));
Calin Juravleacf735c2015-02-12 15:25:22 +0000186}
187
Guillaume "Vermeille" Sanchez81d804a2015-05-20 12:42:25 +0100188void ReferenceTypePropagation::VisitNewInstance(HNewInstance* instr) {
189 UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile());
190}
191
192void ReferenceTypePropagation::VisitNewArray(HNewArray* instr) {
193 UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile());
194}
195
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100196void ReferenceTypePropagation::UpdateFieldAccessTypeInfo(HInstruction* instr,
197 const FieldInfo& info) {
198 // The field index is unknown only during tests.
199 if (instr->GetType() != Primitive::kPrimNot || info.GetFieldIndex() == kUnknownFieldIndex) {
200 return;
201 }
202
203 ScopedObjectAccess soa(Thread::Current());
204 ClassLinker* cl = Runtime::Current()->GetClassLinker();
205 mirror::DexCache* dex_cache = cl->FindDexCache(info.GetDexFile());
206 ArtField* field = cl->GetResolvedField(info.GetFieldIndex(), dex_cache);
207 DCHECK(field != nullptr);
208 mirror::Class* klass = field->GetType<false>();
209 SetClassAsTypeInfo(instr, klass);
210}
211
212void ReferenceTypePropagation::VisitInstanceFieldGet(HInstanceFieldGet* instr) {
213 UpdateFieldAccessTypeInfo(instr, instr->GetFieldInfo());
214}
215
216void ReferenceTypePropagation::VisitStaticFieldGet(HStaticFieldGet* instr) {
217 UpdateFieldAccessTypeInfo(instr, instr->GetFieldInfo());
218}
219
Calin Juravleacf735c2015-02-12 15:25:22 +0000220void ReferenceTypePropagation::VisitLoadClass(HLoadClass* instr) {
221 ScopedObjectAccess soa(Thread::Current());
Nicolas Geoffrayd5111bf2015-05-22 15:37:09 +0100222 mirror::DexCache* dex_cache =
223 Runtime::Current()->GetClassLinker()->FindDexCache(instr->GetDexFile());
Calin Juravleacf735c2015-02-12 15:25:22 +0000224 // Get type from dex cache assuming it was populated by the verifier.
225 mirror::Class* resolved_class = dex_cache->GetResolvedType(instr->GetTypeIndex());
226 if (resolved_class != nullptr) {
227 Handle<mirror::Class> handle = handles_->NewHandle(resolved_class);
Calin Juravleb1498f62015-02-16 13:13:29 +0000228 instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(handle, /* is_exact */ true));
Calin Juravleacf735c2015-02-12 15:25:22 +0000229 }
230 Handle<mirror::Class> class_handle = handles_->NewHandle(mirror::Class::GetJavaLangClass());
Calin Juravleb1498f62015-02-16 13:13:29 +0000231 instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(class_handle, /* is_exact */ true));
Calin Juravleacf735c2015-02-12 15:25:22 +0000232}
Calin Juravle10e244f2015-01-26 18:54:32 +0000233
Calin Juravleb1498f62015-02-16 13:13:29 +0000234void ReferenceTypePropagation::VisitPhi(HPhi* phi) {
235 if (phi->GetType() != Primitive::kPrimNot) {
236 return;
Calin Juravleacf735c2015-02-12 15:25:22 +0000237 }
Calin Juravleb1498f62015-02-16 13:13:29 +0000238
239 if (phi->GetBlock()->IsLoopHeader()) {
240 // Set the initial type for the phi. Use the non back edge input for reaching
241 // a fixed point faster.
242 AddToWorklist(phi);
243 phi->SetCanBeNull(phi->InputAt(0)->CanBeNull());
244 phi->SetReferenceTypeInfo(phi->InputAt(0)->GetReferenceTypeInfo());
Calin Juravle10e244f2015-01-26 18:54:32 +0000245 } else {
Calin Juravleb1498f62015-02-16 13:13:29 +0000246 // Eagerly compute the type of the phi, for quicker convergence. Note
247 // that we don't need to add users to the worklist because we are
248 // doing a reverse post-order visit, therefore either the phi users are
249 // non-loop phi and will be visited later in the visit, or are loop-phis,
250 // and they are already in the work list.
251 UpdateNullability(phi);
252 UpdateReferenceTypeInfo(phi);
253 }
254}
255
256ReferenceTypeInfo ReferenceTypePropagation::MergeTypes(const ReferenceTypeInfo& a,
257 const ReferenceTypeInfo& b) {
258 bool is_exact = a.IsExact() && b.IsExact();
259 bool is_top = a.IsTop() || b.IsTop();
260 Handle<mirror::Class> type_handle;
261
262 if (!is_top) {
263 if (a.GetTypeHandle().Get() == b.GetTypeHandle().Get()) {
264 type_handle = a.GetTypeHandle();
265 } else if (a.IsSupertypeOf(b)) {
266 type_handle = a.GetTypeHandle();
267 is_exact = false;
268 } else if (b.IsSupertypeOf(a)) {
269 type_handle = b.GetTypeHandle();
270 is_exact = false;
271 } else {
272 // TODO: Find a common super class.
273 is_top = true;
274 is_exact = false;
275 }
276 }
277
278 return is_top
279 ? ReferenceTypeInfo::CreateTop(is_exact)
280 : ReferenceTypeInfo::Create(type_handle, is_exact);
281}
282
283bool ReferenceTypePropagation::UpdateReferenceTypeInfo(HInstruction* instr) {
284 ScopedObjectAccess soa(Thread::Current());
285
286 ReferenceTypeInfo previous_rti = instr->GetReferenceTypeInfo();
287 if (instr->IsBoundType()) {
288 UpdateBoundType(instr->AsBoundType());
289 } else if (instr->IsPhi()) {
290 UpdatePhi(instr->AsPhi());
291 } else {
292 LOG(FATAL) << "Invalid instruction (should not get here)";
293 }
294
295 return !previous_rti.IsEqual(instr->GetReferenceTypeInfo());
296}
297
298void ReferenceTypePropagation::UpdateBoundType(HBoundType* instr) {
299 ReferenceTypeInfo new_rti = instr->InputAt(0)->GetReferenceTypeInfo();
300 // Be sure that we don't go over the bounded type.
301 ReferenceTypeInfo bound_rti = instr->GetBoundType();
302 if (!bound_rti.IsSupertypeOf(new_rti)) {
303 new_rti = bound_rti;
304 }
305 instr->SetReferenceTypeInfo(new_rti);
306}
307
308void ReferenceTypePropagation::UpdatePhi(HPhi* instr) {
309 ReferenceTypeInfo new_rti = instr->InputAt(0)->GetReferenceTypeInfo();
310 if (new_rti.IsTop() && !new_rti.IsExact()) {
311 // Early return if we are Top and inexact.
312 instr->SetReferenceTypeInfo(new_rti);
313 return;
314 }
315 for (size_t i = 1; i < instr->InputCount(); i++) {
316 new_rti = MergeTypes(new_rti, instr->InputAt(i)->GetReferenceTypeInfo());
317 if (new_rti.IsTop()) {
318 if (!new_rti.IsExact()) {
319 break;
320 } else {
321 continue;
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +0000322 }
Calin Juravle10e244f2015-01-26 18:54:32 +0000323 }
324 }
Calin Juravleb1498f62015-02-16 13:13:29 +0000325 instr->SetReferenceTypeInfo(new_rti);
326}
327
328// Re-computes and updates the nullability of the instruction. Returns whether or
329// not the nullability was changed.
330bool ReferenceTypePropagation::UpdateNullability(HInstruction* instr) {
331 DCHECK(instr->IsPhi() || instr->IsBoundType());
332
333 if (!instr->IsPhi()) {
334 return false;
335 }
336
337 HPhi* phi = instr->AsPhi();
338 bool existing_can_be_null = phi->CanBeNull();
339 bool new_can_be_null = false;
340 for (size_t i = 0; i < phi->InputCount(); i++) {
341 new_can_be_null |= phi->InputAt(i)->CanBeNull();
342 }
343 phi->SetCanBeNull(new_can_be_null);
344
345 return existing_can_be_null != new_can_be_null;
Calin Juravle10e244f2015-01-26 18:54:32 +0000346}
347
348void ReferenceTypePropagation::ProcessWorklist() {
349 while (!worklist_.IsEmpty()) {
Calin Juravleb1498f62015-02-16 13:13:29 +0000350 HInstruction* instruction = worklist_.Pop();
Calin Juravleacf735c2015-02-12 15:25:22 +0000351 if (UpdateNullability(instruction) || UpdateReferenceTypeInfo(instruction)) {
Calin Juravle10e244f2015-01-26 18:54:32 +0000352 AddDependentInstructionsToWorklist(instruction);
353 }
354 }
355}
356
Calin Juravleb1498f62015-02-16 13:13:29 +0000357void ReferenceTypePropagation::AddToWorklist(HInstruction* instruction) {
358 DCHECK_EQ(instruction->GetType(), Primitive::kPrimNot) << instruction->GetType();
Calin Juravle10e244f2015-01-26 18:54:32 +0000359 worklist_.Add(instruction);
360}
361
Calin Juravleb1498f62015-02-16 13:13:29 +0000362void ReferenceTypePropagation::AddDependentInstructionsToWorklist(HInstruction* instruction) {
Calin Juravle10e244f2015-01-26 18:54:32 +0000363 for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
Calin Juravleb1498f62015-02-16 13:13:29 +0000364 HInstruction* user = it.Current()->GetUser();
365 if (user->IsPhi() || user->IsBoundType()) {
366 AddToWorklist(user);
Calin Juravle10e244f2015-01-26 18:54:32 +0000367 }
368 }
369}
Calin Juravle10e244f2015-01-26 18:54:32 +0000370} // namespace art