blob: de6941c9838e75bfe3a2b9189c6570d610960e88 [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
38 // (NewArray/Call/Field accesses/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();
43 if (instr->IsNewInstance()) {
44 VisitNewInstance(instr->AsNewInstance());
45 } else if (instr->IsLoadClass()) {
46 VisitLoadClass(instr->AsLoadClass());
47 }
48 }
49
50 // Handle Phis.
51 for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
52 VisitPhi(it.Current()->AsPhi());
53 }
54
55 // Add extra nodes to bound types.
Calin Juravle61d544b2015-02-23 16:46:57 +000056 BoundTypeForIfNotNull(block);
Calin Juravleb1498f62015-02-16 13:13:29 +000057 BoundTypeForIfInstanceOf(block);
Calin Juravle10e244f2015-01-26 18:54:32 +000058}
59
Calin Juravle61d544b2015-02-23 16:46:57 +000060void ReferenceTypePropagation::BoundTypeForIfNotNull(HBasicBlock* block) {
Calin Juravleb3306642015-04-20 18:30:42 +010061 HIf* ifInstruction = block->GetLastInstruction()->AsIf();
62 if (ifInstruction == nullptr) {
Calin Juravle61d544b2015-02-23 16:46:57 +000063 return;
64 }
Calin Juravleb3306642015-04-20 18:30:42 +010065 HInstruction* ifInput = ifInstruction->InputAt(0);
Calin Juravle61d544b2015-02-23 16:46:57 +000066 if (!ifInput->IsNotEqual() && !ifInput->IsEqual()) {
67 return;
68 }
69 HInstruction* input0 = ifInput->InputAt(0);
70 HInstruction* input1 = ifInput->InputAt(1);
71 HInstruction* obj;
72
73 if ((input0->GetType() == Primitive::kPrimNot) && input1->ActAsNullConstant()) {
74 obj = input0;
75 } else if ((input1->GetType() == Primitive::kPrimNot) && input0->ActAsNullConstant()) {
76 obj = input1;
77 } else {
78 return;
79 }
80
Calin Juravleb3306642015-04-20 18:30:42 +010081 // We only need to bound the type if we have uses in the relevant block.
82 // So start with null and create the HBoundType lazily, only if it's needed.
83 HBoundType* bound_type = nullptr;
Calin Juravle61d544b2015-02-23 16:46:57 +000084 HBasicBlock* notNullBlock = ifInput->IsNotEqual()
Calin Juravleb3306642015-04-20 18:30:42 +010085 ? ifInstruction->IfTrueSuccessor()
86 : ifInstruction->IfFalseSuccessor();
87
Calin Juravle61d544b2015-02-23 16:46:57 +000088 for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
89 HInstruction* user = it.Current()->GetUser();
90 if (notNullBlock->Dominates(user->GetBlock())) {
Calin Juravleb3306642015-04-20 18:30:42 +010091 if (bound_type == nullptr) {
92 bound_type = new (graph_->GetArena()) HBoundType(obj, ReferenceTypeInfo::CreateTop(false));
93 notNullBlock->InsertInstructionBefore(bound_type, notNullBlock->GetFirstInstruction());
94 }
Calin Juravle61d544b2015-02-23 16:46:57 +000095 user->ReplaceInput(bound_type, it.Current()->GetIndex());
96 }
97 }
98}
99
Calin Juravleb1498f62015-02-16 13:13:29 +0000100// Detects if `block` is the True block for the pattern
101// `if (x instanceof ClassX) { }`
102// If that's the case insert an HBoundType instruction to bound the type of `x`
103// to `ClassX` in the scope of the dominated blocks.
104void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) {
Calin Juravleb3306642015-04-20 18:30:42 +0100105 HIf* ifInstruction = block->GetLastInstruction()->AsIf();
106 if (ifInstruction == nullptr) {
Calin Juravleb1498f62015-02-16 13:13:29 +0000107 return;
108 }
Calin Juravleb3306642015-04-20 18:30:42 +0100109 HInstruction* ifInput = ifInstruction->InputAt(0);
110 HInstruction* instanceOf = nullptr;
111 HBasicBlock* instanceOfTrueBlock = nullptr;
David Brazdil0d13fee2015-04-17 14:52:19 +0100112
Calin Juravleb3306642015-04-20 18:30:42 +0100113 // The instruction simplifier has transformed:
114 // - `if (a instanceof A)` into an HIf with an HInstanceOf input
115 // - `if (!(a instanceof A)` into an HIf with an HBooleanNot input (which in turn
116 // has an HInstanceOf input)
117 // So we should not see the usual HEqual here.
David Brazdil0d13fee2015-04-17 14:52:19 +0100118 if (ifInput->IsInstanceOf()) {
119 instanceOf = ifInput;
Calin Juravleb3306642015-04-20 18:30:42 +0100120 instanceOfTrueBlock = ifInstruction->IfTrueSuccessor();
David Brazdil0d13fee2015-04-17 14:52:19 +0100121 } else if (ifInput->IsBooleanNot() && ifInput->InputAt(0)->IsInstanceOf()) {
122 instanceOf = ifInput->InputAt(0);
Calin Juravleb3306642015-04-20 18:30:42 +0100123 instanceOfTrueBlock = ifInstruction->IfFalseSuccessor();
David Brazdil0d13fee2015-04-17 14:52:19 +0100124 } else {
Calin Juravleb1498f62015-02-16 13:13:29 +0000125 return;
Calin Juravleacf735c2015-02-12 15:25:22 +0000126 }
127
Calin Juravleb3306642015-04-20 18:30:42 +0100128 // We only need to bound the type if we have uses in the relevant block.
129 // So start with null and create the HBoundType lazily, only if it's needed.
130 HBoundType* bound_type = nullptr;
131
Calin Juravleb1498f62015-02-16 13:13:29 +0000132 HInstruction* obj = instanceOf->InputAt(0);
Calin Juravleb1498f62015-02-16 13:13:29 +0000133 for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
134 HInstruction* user = it.Current()->GetUser();
135 if (instanceOfTrueBlock->Dominates(user->GetBlock())) {
Calin Juravleb3306642015-04-20 18:30:42 +0100136 if (bound_type == nullptr) {
137 HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass();
138
139 ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo();
140 ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
141 bound_type = new (graph_->GetArena()) HBoundType(obj, class_rti);
142
143 // Narrow the type as much as possible.
144 {
145 ScopedObjectAccess soa(Thread::Current());
146 if (!load_class->IsResolved() || class_rti.IsSupertypeOf(obj_rti)) {
147 bound_type->SetReferenceTypeInfo(obj_rti);
148 } else {
149 bound_type->SetReferenceTypeInfo(
150 ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ false));
151 }
152 }
153
154 instanceOfTrueBlock->InsertInstructionBefore(
155 bound_type, instanceOfTrueBlock->GetFirstInstruction());
156 }
Calin Juravleb1498f62015-02-16 13:13:29 +0000157 user->ReplaceInput(bound_type, it.Current()->GetIndex());
158 }
159 }
Calin Juravleacf735c2015-02-12 15:25:22 +0000160}
161
162void ReferenceTypePropagation::VisitNewInstance(HNewInstance* instr) {
163 ScopedObjectAccess soa(Thread::Current());
164 mirror::DexCache* dex_cache = dex_compilation_unit_.GetClassLinker()->FindDexCache(dex_file_);
165 // Get type from dex cache assuming it was populated by the verifier.
166 mirror::Class* resolved_class = dex_cache->GetResolvedType(instr->GetTypeIndex());
167 if (resolved_class != nullptr) {
168 MutableHandle<mirror::Class> handle = handles_->NewHandle(resolved_class);
Calin Juravleb1498f62015-02-16 13:13:29 +0000169 instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, true));
Calin Juravleacf735c2015-02-12 15:25:22 +0000170 }
171}
172
173void ReferenceTypePropagation::VisitLoadClass(HLoadClass* instr) {
174 ScopedObjectAccess soa(Thread::Current());
175 mirror::DexCache* dex_cache = dex_compilation_unit_.GetClassLinker()->FindDexCache(dex_file_);
176 // Get type from dex cache assuming it was populated by the verifier.
177 mirror::Class* resolved_class = dex_cache->GetResolvedType(instr->GetTypeIndex());
178 if (resolved_class != nullptr) {
179 Handle<mirror::Class> handle = handles_->NewHandle(resolved_class);
Calin Juravleb1498f62015-02-16 13:13:29 +0000180 instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(handle, /* is_exact */ true));
Calin Juravleacf735c2015-02-12 15:25:22 +0000181 }
182 Handle<mirror::Class> class_handle = handles_->NewHandle(mirror::Class::GetJavaLangClass());
Calin Juravleb1498f62015-02-16 13:13:29 +0000183 instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(class_handle, /* is_exact */ true));
Calin Juravleacf735c2015-02-12 15:25:22 +0000184}
Calin Juravle10e244f2015-01-26 18:54:32 +0000185
Calin Juravleb1498f62015-02-16 13:13:29 +0000186void ReferenceTypePropagation::VisitPhi(HPhi* phi) {
187 if (phi->GetType() != Primitive::kPrimNot) {
188 return;
Calin Juravleacf735c2015-02-12 15:25:22 +0000189 }
Calin Juravleb1498f62015-02-16 13:13:29 +0000190
191 if (phi->GetBlock()->IsLoopHeader()) {
192 // Set the initial type for the phi. Use the non back edge input for reaching
193 // a fixed point faster.
194 AddToWorklist(phi);
195 phi->SetCanBeNull(phi->InputAt(0)->CanBeNull());
196 phi->SetReferenceTypeInfo(phi->InputAt(0)->GetReferenceTypeInfo());
Calin Juravle10e244f2015-01-26 18:54:32 +0000197 } else {
Calin Juravleb1498f62015-02-16 13:13:29 +0000198 // Eagerly compute the type of the phi, for quicker convergence. Note
199 // that we don't need to add users to the worklist because we are
200 // doing a reverse post-order visit, therefore either the phi users are
201 // non-loop phi and will be visited later in the visit, or are loop-phis,
202 // and they are already in the work list.
203 UpdateNullability(phi);
204 UpdateReferenceTypeInfo(phi);
205 }
206}
207
208ReferenceTypeInfo ReferenceTypePropagation::MergeTypes(const ReferenceTypeInfo& a,
209 const ReferenceTypeInfo& b) {
210 bool is_exact = a.IsExact() && b.IsExact();
211 bool is_top = a.IsTop() || b.IsTop();
212 Handle<mirror::Class> type_handle;
213
214 if (!is_top) {
215 if (a.GetTypeHandle().Get() == b.GetTypeHandle().Get()) {
216 type_handle = a.GetTypeHandle();
217 } else if (a.IsSupertypeOf(b)) {
218 type_handle = a.GetTypeHandle();
219 is_exact = false;
220 } else if (b.IsSupertypeOf(a)) {
221 type_handle = b.GetTypeHandle();
222 is_exact = false;
223 } else {
224 // TODO: Find a common super class.
225 is_top = true;
226 is_exact = false;
227 }
228 }
229
230 return is_top
231 ? ReferenceTypeInfo::CreateTop(is_exact)
232 : ReferenceTypeInfo::Create(type_handle, is_exact);
233}
234
235bool ReferenceTypePropagation::UpdateReferenceTypeInfo(HInstruction* instr) {
236 ScopedObjectAccess soa(Thread::Current());
237
238 ReferenceTypeInfo previous_rti = instr->GetReferenceTypeInfo();
239 if (instr->IsBoundType()) {
240 UpdateBoundType(instr->AsBoundType());
241 } else if (instr->IsPhi()) {
242 UpdatePhi(instr->AsPhi());
243 } else {
244 LOG(FATAL) << "Invalid instruction (should not get here)";
245 }
246
247 return !previous_rti.IsEqual(instr->GetReferenceTypeInfo());
248}
249
250void ReferenceTypePropagation::UpdateBoundType(HBoundType* instr) {
251 ReferenceTypeInfo new_rti = instr->InputAt(0)->GetReferenceTypeInfo();
252 // Be sure that we don't go over the bounded type.
253 ReferenceTypeInfo bound_rti = instr->GetBoundType();
254 if (!bound_rti.IsSupertypeOf(new_rti)) {
255 new_rti = bound_rti;
256 }
257 instr->SetReferenceTypeInfo(new_rti);
258}
259
260void ReferenceTypePropagation::UpdatePhi(HPhi* instr) {
261 ReferenceTypeInfo new_rti = instr->InputAt(0)->GetReferenceTypeInfo();
262 if (new_rti.IsTop() && !new_rti.IsExact()) {
263 // Early return if we are Top and inexact.
264 instr->SetReferenceTypeInfo(new_rti);
265 return;
266 }
267 for (size_t i = 1; i < instr->InputCount(); i++) {
268 new_rti = MergeTypes(new_rti, instr->InputAt(i)->GetReferenceTypeInfo());
269 if (new_rti.IsTop()) {
270 if (!new_rti.IsExact()) {
271 break;
272 } else {
273 continue;
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +0000274 }
Calin Juravle10e244f2015-01-26 18:54:32 +0000275 }
276 }
Calin Juravleb1498f62015-02-16 13:13:29 +0000277 instr->SetReferenceTypeInfo(new_rti);
278}
279
280// Re-computes and updates the nullability of the instruction. Returns whether or
281// not the nullability was changed.
282bool ReferenceTypePropagation::UpdateNullability(HInstruction* instr) {
283 DCHECK(instr->IsPhi() || instr->IsBoundType());
284
285 if (!instr->IsPhi()) {
286 return false;
287 }
288
289 HPhi* phi = instr->AsPhi();
290 bool existing_can_be_null = phi->CanBeNull();
291 bool new_can_be_null = false;
292 for (size_t i = 0; i < phi->InputCount(); i++) {
293 new_can_be_null |= phi->InputAt(i)->CanBeNull();
294 }
295 phi->SetCanBeNull(new_can_be_null);
296
297 return existing_can_be_null != new_can_be_null;
Calin Juravle10e244f2015-01-26 18:54:32 +0000298}
299
300void ReferenceTypePropagation::ProcessWorklist() {
301 while (!worklist_.IsEmpty()) {
Calin Juravleb1498f62015-02-16 13:13:29 +0000302 HInstruction* instruction = worklist_.Pop();
Calin Juravleacf735c2015-02-12 15:25:22 +0000303 if (UpdateNullability(instruction) || UpdateReferenceTypeInfo(instruction)) {
Calin Juravle10e244f2015-01-26 18:54:32 +0000304 AddDependentInstructionsToWorklist(instruction);
305 }
306 }
307}
308
Calin Juravleb1498f62015-02-16 13:13:29 +0000309void ReferenceTypePropagation::AddToWorklist(HInstruction* instruction) {
310 DCHECK_EQ(instruction->GetType(), Primitive::kPrimNot) << instruction->GetType();
Calin Juravle10e244f2015-01-26 18:54:32 +0000311 worklist_.Add(instruction);
312}
313
Calin Juravleb1498f62015-02-16 13:13:29 +0000314void ReferenceTypePropagation::AddDependentInstructionsToWorklist(HInstruction* instruction) {
Calin Juravle10e244f2015-01-26 18:54:32 +0000315 for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
Calin Juravleb1498f62015-02-16 13:13:29 +0000316 HInstruction* user = it.Current()->GetUser();
317 if (user->IsPhi() || user->IsBoundType()) {
318 AddToWorklist(user);
Calin Juravle10e244f2015-01-26 18:54:32 +0000319 }
320 }
321}
Calin Juravle10e244f2015-01-26 18:54:32 +0000322} // namespace art