blob: f747fc516caaab33bd43abfc826076c3ce2b155c [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
Mathieu Chartiere401d142015-04-22 13:56:20 -070019#include "class_linker-inl.h"
Calin Juravleacf735c2015-02-12 15:25:22 +000020#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
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +010026class RTPVisitor : public HGraphDelegateVisitor {
27 public:
Calin Juravle2e768302015-07-28 14:41:11 +000028 RTPVisitor(HGraph* graph,
29 StackHandleScopeCollection* handles,
30 GrowableArray<HInstruction*>* worklist,
31 ReferenceTypeInfo::TypeHandle object_class_handle,
32 ReferenceTypeInfo::TypeHandle class_class_handle,
33 ReferenceTypeInfo::TypeHandle string_class_handle)
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +010034 : HGraphDelegateVisitor(graph),
Calin Juravle2e768302015-07-28 14:41:11 +000035 handles_(handles),
36 object_class_handle_(object_class_handle),
37 class_class_handle_(class_class_handle),
38 string_class_handle_(string_class_handle),
39 worklist_(worklist) {}
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +010040
Calin Juravle2e768302015-07-28 14:41:11 +000041 void VisitNullConstant(HNullConstant* null_constant) OVERRIDE;
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +010042 void VisitNewInstance(HNewInstance* new_instance) OVERRIDE;
43 void VisitLoadClass(HLoadClass* load_class) OVERRIDE;
Calin Juravle2e768302015-07-28 14:41:11 +000044 void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE;
45 void VisitLoadString(HLoadString* instr) OVERRIDE;
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +010046 void VisitNewArray(HNewArray* instr) OVERRIDE;
Calin Juravle2e768302015-07-28 14:41:11 +000047 void VisitParameterValue(HParameterValue* instr) OVERRIDE;
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +010048 void UpdateFieldAccessTypeInfo(HInstruction* instr, const FieldInfo& info);
49 void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass, bool is_exact);
50 void VisitInstanceFieldGet(HInstanceFieldGet* instr) OVERRIDE;
51 void VisitStaticFieldGet(HStaticFieldGet* instr) OVERRIDE;
52 void VisitInvoke(HInvoke* instr) OVERRIDE;
Guillaume "Vermeille" Sanchez72a5eb52015-06-02 17:39:45 +010053 void VisitArrayGet(HArrayGet* instr) OVERRIDE;
Calin Juravlea5ae3c32015-07-28 14:40:50 +000054 void VisitCheckCast(HCheckCast* instr) OVERRIDE;
Calin Juravle2e768302015-07-28 14:41:11 +000055 void VisitNullCheck(HNullCheck* instr) OVERRIDE;
56 void VisitFakeString(HFakeString* instr) OVERRIDE;
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +010057 void UpdateReferenceTypeInfo(HInstruction* instr,
58 uint16_t type_idx,
59 const DexFile& dex_file,
60 bool is_exact);
61
62 private:
63 StackHandleScopeCollection* handles_;
Calin Juravle2e768302015-07-28 14:41:11 +000064 ReferenceTypeInfo::TypeHandle object_class_handle_;
65 ReferenceTypeInfo::TypeHandle class_class_handle_;
66 ReferenceTypeInfo::TypeHandle string_class_handle_;
67 GrowableArray<HInstruction*>* worklist_;
68
69 static constexpr size_t kDefaultWorklistSize = 8;
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +010070};
71
Calin Juravle2e768302015-07-28 14:41:11 +000072ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
73 StackHandleScopeCollection* handles,
74 const char* name)
75 : HOptimization(graph, name),
76 handles_(handles),
77 worklist_(graph->GetArena(), kDefaultWorklistSize) {
78 ClassLinker* linker = Runtime::Current()->GetClassLinker();
79 object_class_handle_ = handles_->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangObject));
80 string_class_handle_ = handles_->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangString));
81 class_class_handle_ = handles_->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangClass));
82
83 if (kIsDebugBuild) {
84 ScopedObjectAccess soa(Thread::Current());
85 DCHECK(ReferenceTypeInfo::IsValidHandle(object_class_handle_));
86 DCHECK(ReferenceTypeInfo::IsValidHandle(class_class_handle_));
87 DCHECK(ReferenceTypeInfo::IsValidHandle(string_class_handle_));
88 }
89}
90
Calin Juravleacf735c2015-02-12 15:25:22 +000091void ReferenceTypePropagation::Run() {
92 // To properly propagate type info we need to visit in the dominator-based order.
Calin Juravle10e244f2015-01-26 18:54:32 +000093 // Reverse post order guarantees a node's dominators are visited first.
94 // We take advantage of this order in `VisitBasicBlock`.
Calin Juravle6c0c4f22015-06-12 15:40:42 +000095 for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
Calin Juravle10e244f2015-01-26 18:54:32 +000096 VisitBasicBlock(it.Current());
97 }
98 ProcessWorklist();
Calin Juravle2e768302015-07-28 14:41:11 +000099
100 if (kIsDebugBuild) {
101 // TODO: move this to the graph checker.
102 ScopedObjectAccess soa(Thread::Current());
103 for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
104 HBasicBlock* block = it.Current();
105 for (HInstructionIterator iti(block->GetInstructions()); !iti.Done(); iti.Advance()) {
106 HInstruction* instr = iti.Current();
107 if (instr->GetType() == Primitive::kPrimNot) {
108 DCHECK(instr->GetReferenceTypeInfo().IsValid())
109 << "Invalid RTI for instruction: " << instr->DebugName();
110 if (instr->IsBoundType()) {
111 DCHECK(instr->AsBoundType()->GetUpperBound().IsValid());
112 } else if (instr->IsLoadClass()) {
113 DCHECK(instr->AsLoadClass()->GetReferenceTypeInfo().IsExact());
114 DCHECK(instr->AsLoadClass()->GetLoadedClassRTI().IsValid());
115 } else if (instr->IsNullCheck()) {
116 DCHECK(instr->GetReferenceTypeInfo().IsEqual(instr->InputAt(0)->GetReferenceTypeInfo()))
117 << "NullCheck " << instr->GetReferenceTypeInfo()
118 << "Input(0) " << instr->InputAt(0)->GetReferenceTypeInfo();
119 }
120 }
121 }
122 }
123 }
Calin Juravle10e244f2015-01-26 18:54:32 +0000124}
125
Calin Juravleb1498f62015-02-16 13:13:29 +0000126void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
Calin Juravle2e768302015-07-28 14:41:11 +0000127 RTPVisitor visitor(graph_,
128 handles_,
129 &worklist_,
130 object_class_handle_,
131 class_class_handle_,
132 string_class_handle_);
133 // Handle Phis first as there might be instructions in the same block who depend on them.
134 for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
135 VisitPhi(it.Current()->AsPhi());
136 }
Calin Juravlebeba9302015-07-08 15:57:18 +0000137
Calin Juravle2e768302015-07-28 14:41:11 +0000138 // Handle instructions.
Calin Juravleb1498f62015-02-16 13:13:29 +0000139 for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
140 HInstruction* instr = it.Current();
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +0100141 instr->Accept(&visitor);
Calin Juravleb1498f62015-02-16 13:13:29 +0000142 }
143
Calin Juravleb1498f62015-02-16 13:13:29 +0000144 // Add extra nodes to bound types.
Calin Juravle61d544b2015-02-23 16:46:57 +0000145 BoundTypeForIfNotNull(block);
Calin Juravleb1498f62015-02-16 13:13:29 +0000146 BoundTypeForIfInstanceOf(block);
Calin Juravle10e244f2015-01-26 18:54:32 +0000147}
148
Calin Juravlea5ae3c32015-07-28 14:40:50 +0000149// Create a bound type for the given object narrowing the type as much as possible.
150// The BoundType upper values for the super type and can_be_null will be taken from
151// load_class.GetLoadedClassRTI() and upper_can_be_null.
152static HBoundType* CreateBoundType(ArenaAllocator* arena,
153 HInstruction* obj,
154 HLoadClass* load_class,
155 bool upper_can_be_null)
156 SHARED_REQUIRES(Locks::mutator_lock_) {
157 ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo();
158 ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
159 HBoundType* bound_type = new (arena) HBoundType(obj, class_rti, upper_can_be_null);
160 // Narrow the type as much as possible.
Calin Juravle2e768302015-07-28 14:41:11 +0000161 if (class_rti.GetTypeHandle()->IsFinal()) {
Calin Juravlea5ae3c32015-07-28 14:40:50 +0000162 bound_type->SetReferenceTypeInfo(
163 ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ true));
Calin Juravle2e768302015-07-28 14:41:11 +0000164 } else if (obj_rti.IsValid() && class_rti.IsSupertypeOf(obj_rti)) {
Calin Juravlea5ae3c32015-07-28 14:40:50 +0000165 bound_type->SetReferenceTypeInfo(obj_rti);
166 } else {
167 bound_type->SetReferenceTypeInfo(
168 ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ false));
169 }
170 return bound_type;
171}
172
173// Check if we should create a bound type for the given object at the specified
174// position. Because of inlining and the fact we run RTP more than once and we
175// might have a HBoundType already. If we do, we should not create a new one.
176// In this case we also assert that there are no other uses of the object (except
177// the bound type) dominated by the specified dominator_instr or dominator_block.
178static bool ShouldCreateBoundType(HInstruction* position,
179 HInstruction* obj,
180 ReferenceTypeInfo upper_bound,
181 HInstruction* dominator_instr,
182 HBasicBlock* dominator_block)
183 SHARED_REQUIRES(Locks::mutator_lock_) {
184 // If the position where we should insert the bound type is not already a
185 // a bound type then we need to create one.
186 if (position == nullptr || !position->IsBoundType()) {
187 return true;
188 }
189
190 HBoundType* existing_bound_type = position->AsBoundType();
191 if (existing_bound_type->GetUpperBound().IsSupertypeOf(upper_bound)) {
192 if (kIsDebugBuild) {
193 // Check that the existing HBoundType dominates all the uses.
194 for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
195 HInstruction* user = it.Current()->GetUser();
196 if (dominator_instr != nullptr) {
197 DCHECK(!dominator_instr->StrictlyDominates(user)
198 || user == existing_bound_type
199 || existing_bound_type->StrictlyDominates(user));
200 } else if (dominator_block != nullptr) {
201 DCHECK(!dominator_block->Dominates(user->GetBlock())
202 || user == existing_bound_type
203 || existing_bound_type->StrictlyDominates(user));
204 }
205 }
206 }
207 } else {
208 // TODO: if the current bound type is a refinement we could update the
209 // existing_bound_type with the a new upper limit. However, we also need to
210 // update its users and have access to the work list.
211 }
212 return false;
213}
214
Calin Juravle61d544b2015-02-23 16:46:57 +0000215void ReferenceTypePropagation::BoundTypeForIfNotNull(HBasicBlock* block) {
Calin Juravleb3306642015-04-20 18:30:42 +0100216 HIf* ifInstruction = block->GetLastInstruction()->AsIf();
217 if (ifInstruction == nullptr) {
Calin Juravle61d544b2015-02-23 16:46:57 +0000218 return;
219 }
Calin Juravleb3306642015-04-20 18:30:42 +0100220 HInstruction* ifInput = ifInstruction->InputAt(0);
Calin Juravle61d544b2015-02-23 16:46:57 +0000221 if (!ifInput->IsNotEqual() && !ifInput->IsEqual()) {
222 return;
223 }
224 HInstruction* input0 = ifInput->InputAt(0);
225 HInstruction* input1 = ifInput->InputAt(1);
Calin Juravleedad8ad2015-04-23 14:34:33 +0100226 HInstruction* obj = nullptr;
Calin Juravle61d544b2015-02-23 16:46:57 +0000227
Calin Juravleedad8ad2015-04-23 14:34:33 +0100228 if (input1->IsNullConstant()) {
Calin Juravle61d544b2015-02-23 16:46:57 +0000229 obj = input0;
Calin Juravleedad8ad2015-04-23 14:34:33 +0100230 } else if (input0->IsNullConstant()) {
Calin Juravle61d544b2015-02-23 16:46:57 +0000231 obj = input1;
232 } else {
233 return;
234 }
235
Nicolas Geoffray7d5ea032015-07-02 15:48:27 +0100236 if (!obj->CanBeNull() || obj->IsNullConstant()) {
237 // Null check is dead code and will be removed by DCE.
238 return;
239 }
240 DCHECK(!obj->IsLoadClass()) << "We should not replace HLoadClass instructions";
241
Calin Juravleb3306642015-04-20 18:30:42 +0100242 // We only need to bound the type if we have uses in the relevant block.
243 // So start with null and create the HBoundType lazily, only if it's needed.
244 HBoundType* bound_type = nullptr;
Calin Juravle61d544b2015-02-23 16:46:57 +0000245 HBasicBlock* notNullBlock = ifInput->IsNotEqual()
Calin Juravleb3306642015-04-20 18:30:42 +0100246 ? ifInstruction->IfTrueSuccessor()
247 : ifInstruction->IfFalseSuccessor();
248
Calin Juravle61d544b2015-02-23 16:46:57 +0000249 for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
250 HInstruction* user = it.Current()->GetUser();
251 if (notNullBlock->Dominates(user->GetBlock())) {
Calin Juravleb3306642015-04-20 18:30:42 +0100252 if (bound_type == nullptr) {
Calin Juravlea5ae3c32015-07-28 14:40:50 +0000253 ScopedObjectAccess soa(Thread::Current());
254 HInstruction* insert_point = notNullBlock->GetFirstInstruction();
Calin Juravle2e768302015-07-28 14:41:11 +0000255 ReferenceTypeInfo object_rti = ReferenceTypeInfo::Create(
256 object_class_handle_, /* is_exact */ true);
Calin Juravlea5ae3c32015-07-28 14:40:50 +0000257 if (ShouldCreateBoundType(insert_point, obj, object_rti, nullptr, notNullBlock)) {
258 bound_type = new (graph_->GetArena()) HBoundType(
259 obj, object_rti, /* bound_can_be_null */ false);
Calin Juravle2e768302015-07-28 14:41:11 +0000260 if (obj->GetReferenceTypeInfo().IsValid()) {
261 bound_type->SetReferenceTypeInfo(obj->GetReferenceTypeInfo());
262 }
Calin Juravlea5ae3c32015-07-28 14:40:50 +0000263 notNullBlock->InsertInstructionBefore(bound_type, insert_point);
264 } else {
265 // We already have a bound type on the position we would need to insert
266 // the new one. The existing bound type should dominate all the users
267 // (dchecked) so there's no need to continue.
268 break;
269 }
Calin Juravleb3306642015-04-20 18:30:42 +0100270 }
Calin Juravle61d544b2015-02-23 16:46:57 +0000271 user->ReplaceInput(bound_type, it.Current()->GetIndex());
272 }
273 }
274}
275
Calin Juravleb1498f62015-02-16 13:13:29 +0000276// Detects if `block` is the True block for the pattern
277// `if (x instanceof ClassX) { }`
278// If that's the case insert an HBoundType instruction to bound the type of `x`
279// to `ClassX` in the scope of the dominated blocks.
280void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) {
Calin Juravleb3306642015-04-20 18:30:42 +0100281 HIf* ifInstruction = block->GetLastInstruction()->AsIf();
282 if (ifInstruction == nullptr) {
Calin Juravleb1498f62015-02-16 13:13:29 +0000283 return;
284 }
Calin Juravleb3306642015-04-20 18:30:42 +0100285 HInstruction* ifInput = ifInstruction->InputAt(0);
286 HInstruction* instanceOf = nullptr;
287 HBasicBlock* instanceOfTrueBlock = nullptr;
David Brazdil0d13fee2015-04-17 14:52:19 +0100288
Calin Juravleb3306642015-04-20 18:30:42 +0100289 // The instruction simplifier has transformed:
290 // - `if (a instanceof A)` into an HIf with an HInstanceOf input
291 // - `if (!(a instanceof A)` into an HIf with an HBooleanNot input (which in turn
292 // has an HInstanceOf input)
293 // So we should not see the usual HEqual here.
David Brazdil0d13fee2015-04-17 14:52:19 +0100294 if (ifInput->IsInstanceOf()) {
295 instanceOf = ifInput;
Calin Juravleb3306642015-04-20 18:30:42 +0100296 instanceOfTrueBlock = ifInstruction->IfTrueSuccessor();
David Brazdil0d13fee2015-04-17 14:52:19 +0100297 } else if (ifInput->IsBooleanNot() && ifInput->InputAt(0)->IsInstanceOf()) {
298 instanceOf = ifInput->InputAt(0);
Calin Juravleb3306642015-04-20 18:30:42 +0100299 instanceOfTrueBlock = ifInstruction->IfFalseSuccessor();
David Brazdil0d13fee2015-04-17 14:52:19 +0100300 } else {
Calin Juravleb1498f62015-02-16 13:13:29 +0000301 return;
Calin Juravleacf735c2015-02-12 15:25:22 +0000302 }
303
Calin Juravleb3306642015-04-20 18:30:42 +0100304 // We only need to bound the type if we have uses in the relevant block.
305 // So start with null and create the HBoundType lazily, only if it's needed.
306 HBoundType* bound_type = nullptr;
307
Calin Juravleb1498f62015-02-16 13:13:29 +0000308 HInstruction* obj = instanceOf->InputAt(0);
Nicolas Geoffrayf9a19952015-06-29 13:43:54 +0100309 if (obj->GetReferenceTypeInfo().IsExact() && !obj->IsPhi()) {
310 // This method is being called while doing a fixed-point calculation
311 // over phis. Non-phis instruction whose type is already known do
312 // not need to be bound to another type.
313 // Not that this also prevents replacing `HLoadClass` with a `HBoundType`.
314 // `HCheckCast` and `HInstanceOf` expect a `HLoadClass` as a second
315 // input.
316 return;
317 }
Nicolas Geoffray7d5ea032015-07-02 15:48:27 +0100318 DCHECK(!obj->IsLoadClass()) << "We should not replace HLoadClass instructions";
Calin Juravleb1498f62015-02-16 13:13:29 +0000319 for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
320 HInstruction* user = it.Current()->GetUser();
321 if (instanceOfTrueBlock->Dominates(user->GetBlock())) {
Calin Juravleb3306642015-04-20 18:30:42 +0100322 if (bound_type == nullptr) {
Calin Juravlea5ae3c32015-07-28 14:40:50 +0000323 ScopedObjectAccess soa(Thread::Current());
Calin Juravleb3306642015-04-20 18:30:42 +0100324 HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass();
Calin Juravleb3306642015-04-20 18:30:42 +0100325 ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
Calin Juravlea5ae3c32015-07-28 14:40:50 +0000326 HInstruction* insert_point = instanceOfTrueBlock->GetFirstInstruction();
327 if (ShouldCreateBoundType(insert_point, obj, class_rti, nullptr, instanceOfTrueBlock)) {
328 bound_type = CreateBoundType(
329 graph_->GetArena(),
330 obj,
331 load_class,
332 false /* InstanceOf ensures the object is not null. */);
333 instanceOfTrueBlock->InsertInstructionBefore(bound_type, insert_point);
334 } else {
335 // We already have a bound type on the position we would need to insert
336 // the new one. The existing bound type should dominate all the users
337 // (dchecked) so there's no need to continue.
338 break;
Calin Juravleb3306642015-04-20 18:30:42 +0100339 }
Calin Juravleb3306642015-04-20 18:30:42 +0100340 }
Calin Juravleb1498f62015-02-16 13:13:29 +0000341 user->ReplaceInput(bound_type, it.Current()->GetIndex());
342 }
343 }
Calin Juravleacf735c2015-02-12 15:25:22 +0000344}
345
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +0100346void RTPVisitor::SetClassAsTypeInfo(HInstruction* instr,
347 mirror::Class* klass,
348 bool is_exact) {
Calin Juravle2e768302015-07-28 14:41:11 +0000349 if (instr->IsInvokeStaticOrDirect() && instr->AsInvokeStaticOrDirect()->IsStringInit()) {
350 // Calls to String.<init> are replaced with a StringFactory.
351 if (kIsDebugBuild) {
352 ScopedObjectAccess soa(Thread::Current());
353 ClassLinker* cl = Runtime::Current()->GetClassLinker();
354 mirror::DexCache* dex_cache = cl->FindDexCache(instr->AsInvoke()->GetDexFile());
355 ArtMethod* method = dex_cache->GetResolvedMethod(
356 instr->AsInvoke()->GetDexMethodIndex(), cl->GetImagePointerSize());
357 DCHECK(method != nullptr);
358 mirror::Class* declaring_class = method->GetDeclaringClass();
359 DCHECK(declaring_class != nullptr);
360 DCHECK(declaring_class->IsStringClass())
361 << "Expected String class: " << PrettyDescriptor(declaring_class);
362 DCHECK(method->IsConstructor())
363 << "Expected String.<init>: " << PrettyMethod(method);
364 }
365 instr->SetReferenceTypeInfo(
366 ReferenceTypeInfo::Create(string_class_handle_, /* is_exact */ true));
367 } else if (klass != nullptr) {
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100368 ScopedObjectAccess soa(Thread::Current());
Calin Juravle2e768302015-07-28 14:41:11 +0000369 ReferenceTypeInfo::TypeHandle handle = handles_->NewHandle(klass);
Guillaume Sanchez222862c2015-06-09 18:33:02 +0100370 is_exact = is_exact || klass->IsFinal();
371 instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact));
Calin Juravle2e768302015-07-28 14:41:11 +0000372 } else {
373 instr->SetReferenceTypeInfo(
374 ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false));
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100375 }
376}
377
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +0100378void RTPVisitor::UpdateReferenceTypeInfo(HInstruction* instr,
379 uint16_t type_idx,
380 const DexFile& dex_file,
381 bool is_exact) {
Guillaume "Vermeille" Sanchez81d804a2015-05-20 12:42:25 +0100382 DCHECK_EQ(instr->GetType(), Primitive::kPrimNot);
383
Calin Juravleacf735c2015-02-12 15:25:22 +0000384 ScopedObjectAccess soa(Thread::Current());
Guillaume "Vermeille" Sanchez81d804a2015-05-20 12:42:25 +0100385 mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file);
Calin Juravleacf735c2015-02-12 15:25:22 +0000386 // Get type from dex cache assuming it was populated by the verifier.
Guillaume Sanchez222862c2015-06-09 18:33:02 +0100387 SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact);
Calin Juravleacf735c2015-02-12 15:25:22 +0000388}
389
Calin Juravle2e768302015-07-28 14:41:11 +0000390void RTPVisitor::VisitNullConstant(HNullConstant* instr) {
391 // TODO: The null constant could be bound contextually (e.g. based on return statements)
392 // to a more precise type.
393 instr->SetReferenceTypeInfo(
394 ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false));
395}
396
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +0100397void RTPVisitor::VisitNewInstance(HNewInstance* instr) {
Guillaume Sanchez222862c2015-06-09 18:33:02 +0100398 UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
Guillaume "Vermeille" Sanchez81d804a2015-05-20 12:42:25 +0100399}
400
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +0100401void RTPVisitor::VisitNewArray(HNewArray* instr) {
Guillaume Sanchez222862c2015-06-09 18:33:02 +0100402 UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
Guillaume "Vermeille" Sanchez81d804a2015-05-20 12:42:25 +0100403}
404
Calin Juravle2e768302015-07-28 14:41:11 +0000405void RTPVisitor::VisitParameterValue(HParameterValue* instr) {
406 if (instr->GetType() == Primitive::kPrimNot) {
407 // TODO: parse the signature and add precise types for the parameters.
408 SetClassAsTypeInfo(instr, nullptr, /* is_exact */ false);
409 }
410}
411
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +0100412void RTPVisitor::UpdateFieldAccessTypeInfo(HInstruction* instr,
413 const FieldInfo& info) {
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100414 // The field index is unknown only during tests.
415 if (instr->GetType() != Primitive::kPrimNot || info.GetFieldIndex() == kUnknownFieldIndex) {
416 return;
417 }
418
419 ScopedObjectAccess soa(Thread::Current());
420 ClassLinker* cl = Runtime::Current()->GetClassLinker();
421 mirror::DexCache* dex_cache = cl->FindDexCache(info.GetDexFile());
422 ArtField* field = cl->GetResolvedField(info.GetFieldIndex(), dex_cache);
Calin Juravle2e768302015-07-28 14:41:11 +0000423 // TODO: There are certain cases where we can't resolve the field.
424 // b/21914925 is open to keep track of a repro case for this issue.
425 mirror::Class* klass = (field == nullptr) ? nullptr : field->GetType<false>();
426 SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100427}
428
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +0100429void RTPVisitor::VisitInstanceFieldGet(HInstanceFieldGet* instr) {
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100430 UpdateFieldAccessTypeInfo(instr, instr->GetFieldInfo());
431}
432
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +0100433void RTPVisitor::VisitStaticFieldGet(HStaticFieldGet* instr) {
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100434 UpdateFieldAccessTypeInfo(instr, instr->GetFieldInfo());
435}
436
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +0100437void RTPVisitor::VisitLoadClass(HLoadClass* instr) {
Calin Juravleacf735c2015-02-12 15:25:22 +0000438 ScopedObjectAccess soa(Thread::Current());
Nicolas Geoffrayd5111bf2015-05-22 15:37:09 +0100439 mirror::DexCache* dex_cache =
440 Runtime::Current()->GetClassLinker()->FindDexCache(instr->GetDexFile());
Calin Juravleacf735c2015-02-12 15:25:22 +0000441 // Get type from dex cache assuming it was populated by the verifier.
442 mirror::Class* resolved_class = dex_cache->GetResolvedType(instr->GetTypeIndex());
Calin Juravle2e768302015-07-28 14:41:11 +0000443 DCHECK(resolved_class != nullptr);
444 ReferenceTypeInfo::TypeHandle handle = handles_->NewHandle(resolved_class);
445 instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(handle, /* is_exact */ true));
446 instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(class_class_handle_, /* is_exact */ true));
447}
448
449void RTPVisitor::VisitClinitCheck(HClinitCheck* instr) {
450 instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo());
451}
452
453void RTPVisitor::VisitLoadString(HLoadString* instr) {
454 instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(string_class_handle_, /* is_exact */ true));
455}
456
457void RTPVisitor::VisitNullCheck(HNullCheck* instr) {
458 ScopedObjectAccess soa(Thread::Current());
459 ReferenceTypeInfo parent_rti = instr->InputAt(0)->GetReferenceTypeInfo();
460 DCHECK(parent_rti.IsValid());
461 instr->SetReferenceTypeInfo(parent_rti);
462}
463
464void RTPVisitor::VisitFakeString(HFakeString* instr) {
465 instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(string_class_handle_, /* is_exact */ true));
Calin Juravleacf735c2015-02-12 15:25:22 +0000466}
Calin Juravle10e244f2015-01-26 18:54:32 +0000467
Calin Juravlea5ae3c32015-07-28 14:40:50 +0000468void RTPVisitor::VisitCheckCast(HCheckCast* check_cast) {
469 HInstruction* obj = check_cast->InputAt(0);
470 HBoundType* bound_type = nullptr;
471 for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
472 HInstruction* user = it.Current()->GetUser();
473 if (check_cast->StrictlyDominates(user)) {
474 if (bound_type == nullptr) {
475 ScopedObjectAccess soa(Thread::Current());
476 HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
477 ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
478 if (ShouldCreateBoundType(check_cast->GetNext(), obj, class_rti, check_cast, nullptr)) {
479 bound_type = CreateBoundType(
480 GetGraph()->GetArena(),
481 obj,
482 load_class,
483 true /* CheckCast succeeds for nulls. */);
484 check_cast->GetBlock()->InsertInstructionAfter(bound_type, check_cast);
485 } else {
486 // We already have a bound type on the position we would need to insert
487 // the new one. The existing bound type should dominate all the users
488 // (dchecked) so there's no need to continue.
489 break;
490 }
491 }
492 user->ReplaceInput(bound_type, it.Current()->GetIndex());
493 }
494 }
495}
496
Calin Juravleb1498f62015-02-16 13:13:29 +0000497void ReferenceTypePropagation::VisitPhi(HPhi* phi) {
498 if (phi->GetType() != Primitive::kPrimNot) {
499 return;
Calin Juravleacf735c2015-02-12 15:25:22 +0000500 }
Calin Juravleb1498f62015-02-16 13:13:29 +0000501
502 if (phi->GetBlock()->IsLoopHeader()) {
503 // Set the initial type for the phi. Use the non back edge input for reaching
504 // a fixed point faster.
505 AddToWorklist(phi);
506 phi->SetCanBeNull(phi->InputAt(0)->CanBeNull());
507 phi->SetReferenceTypeInfo(phi->InputAt(0)->GetReferenceTypeInfo());
Calin Juravle10e244f2015-01-26 18:54:32 +0000508 } else {
Calin Juravleb1498f62015-02-16 13:13:29 +0000509 // Eagerly compute the type of the phi, for quicker convergence. Note
510 // that we don't need to add users to the worklist because we are
511 // doing a reverse post-order visit, therefore either the phi users are
512 // non-loop phi and will be visited later in the visit, or are loop-phis,
513 // and they are already in the work list.
514 UpdateNullability(phi);
515 UpdateReferenceTypeInfo(phi);
516 }
517}
518
519ReferenceTypeInfo ReferenceTypePropagation::MergeTypes(const ReferenceTypeInfo& a,
520 const ReferenceTypeInfo& b) {
Calin Juravle2e768302015-07-28 14:41:11 +0000521 if (!b.IsValid()) {
522 return a;
523 }
524 if (!a.IsValid()) {
525 return b;
Calin Juravle20e60712015-07-01 18:41:04 +0100526 }
527
Calin Juravle2e768302015-07-28 14:41:11 +0000528 bool is_exact = a.IsExact() && b.IsExact();
529 Handle<mirror::Class> type_handle;
530
531 if (a.GetTypeHandle().Get() == b.GetTypeHandle().Get()) {
532 type_handle = a.GetTypeHandle();
533 } else if (a.IsSupertypeOf(b)) {
534 type_handle = a.GetTypeHandle();
535 is_exact = false;
536 } else if (b.IsSupertypeOf(a)) {
537 type_handle = b.GetTypeHandle();
538 is_exact = false;
539 } else {
540 // TODO: Find the first common super class.
541 type_handle = object_class_handle_;
542 is_exact = false;
543 }
544
545 return ReferenceTypeInfo::Create(type_handle, is_exact);
546}
547
548static void UpdateArrayGet(HArrayGet* instr,
549 StackHandleScopeCollection* handles,
550 ReferenceTypeInfo::TypeHandle object_class_handle)
551 SHARED_REQUIRES(Locks::mutator_lock_) {
552 DCHECK_EQ(Primitive::kPrimNot, instr->GetType());
553
554 ReferenceTypeInfo parent_rti = instr->InputAt(0)->GetReferenceTypeInfo();
555 DCHECK(parent_rti.IsValid());
556
557 Handle<mirror::Class> handle = parent_rti.GetTypeHandle();
558 if (handle->IsObjectArrayClass()) {
559 ReferenceTypeInfo::TypeHandle component_handle = handles->NewHandle(handle->GetComponentType());
560 instr->SetReferenceTypeInfo(
561 ReferenceTypeInfo::Create(component_handle, /* is_exact */ false));
562 } else {
563 // We don't know what the parent actually is, so we fallback to object.
564 instr->SetReferenceTypeInfo(
565 ReferenceTypeInfo::Create(object_class_handle, /* is_exact */ false));
566 }
567
568 return;
Calin Juravleb1498f62015-02-16 13:13:29 +0000569}
570
571bool ReferenceTypePropagation::UpdateReferenceTypeInfo(HInstruction* instr) {
572 ScopedObjectAccess soa(Thread::Current());
573
574 ReferenceTypeInfo previous_rti = instr->GetReferenceTypeInfo();
575 if (instr->IsBoundType()) {
576 UpdateBoundType(instr->AsBoundType());
577 } else if (instr->IsPhi()) {
578 UpdatePhi(instr->AsPhi());
Calin Juravle2e768302015-07-28 14:41:11 +0000579 } else if (instr->IsNullCheck()) {
580 ReferenceTypeInfo parent_rti = instr->InputAt(0)->GetReferenceTypeInfo();
581 if (parent_rti.IsValid()) {
582 instr->SetReferenceTypeInfo(parent_rti);
583 }
584 } else if (instr->IsArrayGet()) {
585 // TODO: consider if it's worth "looking back" and bounding the input object
586 // to an array type.
587 UpdateArrayGet(instr->AsArrayGet(), handles_, object_class_handle_);
Calin Juravleb1498f62015-02-16 13:13:29 +0000588 } else {
589 LOG(FATAL) << "Invalid instruction (should not get here)";
590 }
591
592 return !previous_rti.IsEqual(instr->GetReferenceTypeInfo());
593}
594
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +0100595void RTPVisitor::VisitInvoke(HInvoke* instr) {
596 if (instr->GetType() != Primitive::kPrimNot) {
597 return;
598 }
599
600 ScopedObjectAccess soa(Thread::Current());
601 ClassLinker* cl = Runtime::Current()->GetClassLinker();
602 mirror::DexCache* dex_cache = cl->FindDexCache(instr->GetDexFile());
603 ArtMethod* method = dex_cache->GetResolvedMethod(
604 instr->GetDexMethodIndex(), cl->GetImagePointerSize());
Calin Juravle2e768302015-07-28 14:41:11 +0000605 mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false);
606 SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
Guillaume "Vermeille" Sanchezae09d2d2015-05-29 10:52:55 +0100607}
608
Guillaume "Vermeille" Sanchez72a5eb52015-06-02 17:39:45 +0100609void RTPVisitor::VisitArrayGet(HArrayGet* instr) {
610 if (instr->GetType() != Primitive::kPrimNot) {
611 return;
612 }
Guillaume "Vermeille" Sanchez72a5eb52015-06-02 17:39:45 +0100613 ScopedObjectAccess soa(Thread::Current());
Calin Juravle2e768302015-07-28 14:41:11 +0000614 UpdateArrayGet(instr, handles_, object_class_handle_);
615 if (!instr->GetReferenceTypeInfo().IsValid()) {
616 worklist_->Add(instr);
Guillaume "Vermeille" Sanchez72a5eb52015-06-02 17:39:45 +0100617 }
618}
619
Calin Juravleb1498f62015-02-16 13:13:29 +0000620void ReferenceTypePropagation::UpdateBoundType(HBoundType* instr) {
621 ReferenceTypeInfo new_rti = instr->InputAt(0)->GetReferenceTypeInfo();
Calin Juravle2e768302015-07-28 14:41:11 +0000622 if (!new_rti.IsValid()) {
623 return; // No new info yet.
624 }
625
626 // Make sure that we don't go over the bounded type.
Calin Juravlea5ae3c32015-07-28 14:40:50 +0000627 ReferenceTypeInfo upper_bound_rti = instr->GetUpperBound();
628 if (!upper_bound_rti.IsSupertypeOf(new_rti)) {
629 new_rti = upper_bound_rti;
Calin Juravleb1498f62015-02-16 13:13:29 +0000630 }
631 instr->SetReferenceTypeInfo(new_rti);
632}
633
634void ReferenceTypePropagation::UpdatePhi(HPhi* instr) {
635 ReferenceTypeInfo new_rti = instr->InputAt(0)->GetReferenceTypeInfo();
Calin Juravle2e768302015-07-28 14:41:11 +0000636 if (new_rti.IsValid() && new_rti.IsObjectClass() && !new_rti.IsExact()) {
637 // Early return if we are Object and inexact.
Calin Juravleb1498f62015-02-16 13:13:29 +0000638 instr->SetReferenceTypeInfo(new_rti);
639 return;
640 }
641 for (size_t i = 1; i < instr->InputCount(); i++) {
642 new_rti = MergeTypes(new_rti, instr->InputAt(i)->GetReferenceTypeInfo());
Calin Juravle2e768302015-07-28 14:41:11 +0000643 if (new_rti.IsValid() && new_rti.IsObjectClass()) {
Calin Juravleb1498f62015-02-16 13:13:29 +0000644 if (!new_rti.IsExact()) {
645 break;
646 } else {
647 continue;
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +0000648 }
Calin Juravle10e244f2015-01-26 18:54:32 +0000649 }
650 }
Calin Juravleb1498f62015-02-16 13:13:29 +0000651 instr->SetReferenceTypeInfo(new_rti);
652}
653
654// Re-computes and updates the nullability of the instruction. Returns whether or
655// not the nullability was changed.
656bool ReferenceTypePropagation::UpdateNullability(HInstruction* instr) {
Calin Juravle2e768302015-07-28 14:41:11 +0000657 DCHECK(instr->IsPhi()
658 || instr->IsBoundType()
659 || instr->IsNullCheck()
660 || instr->IsArrayGet());
661
662 if (!instr->IsPhi() && !instr->IsBoundType()) {
663 return false;
664 }
Calin Juravleb1498f62015-02-16 13:13:29 +0000665
Calin Juravlea5ae3c32015-07-28 14:40:50 +0000666 bool existing_can_be_null = instr->CanBeNull();
667 if (instr->IsPhi()) {
668 HPhi* phi = instr->AsPhi();
669 bool new_can_be_null = false;
670 for (size_t i = 0; i < phi->InputCount(); i++) {
671 if (phi->InputAt(i)->CanBeNull()) {
672 new_can_be_null = true;
673 break;
674 }
675 }
676 phi->SetCanBeNull(new_can_be_null);
677 } else if (instr->IsBoundType()) {
678 HBoundType* bound_type = instr->AsBoundType();
679 bound_type->SetCanBeNull(instr->InputAt(0)->CanBeNull() && bound_type->GetUpperCanBeNull());
Calin Juravleb1498f62015-02-16 13:13:29 +0000680 }
Calin Juravlea5ae3c32015-07-28 14:40:50 +0000681 return existing_can_be_null != instr->CanBeNull();
Calin Juravle10e244f2015-01-26 18:54:32 +0000682}
683
684void ReferenceTypePropagation::ProcessWorklist() {
685 while (!worklist_.IsEmpty()) {
Calin Juravleb1498f62015-02-16 13:13:29 +0000686 HInstruction* instruction = worklist_.Pop();
Calin Juravleacf735c2015-02-12 15:25:22 +0000687 if (UpdateNullability(instruction) || UpdateReferenceTypeInfo(instruction)) {
Calin Juravle10e244f2015-01-26 18:54:32 +0000688 AddDependentInstructionsToWorklist(instruction);
689 }
690 }
691}
692
Calin Juravleb1498f62015-02-16 13:13:29 +0000693void ReferenceTypePropagation::AddToWorklist(HInstruction* instruction) {
Calin Juravle2e768302015-07-28 14:41:11 +0000694 DCHECK_EQ(instruction->GetType(), Primitive::kPrimNot)
695 << instruction->DebugName() << ":" << instruction->GetType();
Calin Juravle10e244f2015-01-26 18:54:32 +0000696 worklist_.Add(instruction);
697}
698
Calin Juravleb1498f62015-02-16 13:13:29 +0000699void ReferenceTypePropagation::AddDependentInstructionsToWorklist(HInstruction* instruction) {
Calin Juravle10e244f2015-01-26 18:54:32 +0000700 for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
Calin Juravleb1498f62015-02-16 13:13:29 +0000701 HInstruction* user = it.Current()->GetUser();
Calin Juravle2e768302015-07-28 14:41:11 +0000702 if (user->IsPhi()
703 || user->IsBoundType()
704 || user->IsNullCheck()
705 || (user->IsArrayGet() && (user->GetType() == Primitive::kPrimNot))) {
Calin Juravlebeba9302015-07-08 15:57:18 +0000706 AddToWorklist(user);
Calin Juravle10e244f2015-01-26 18:54:32 +0000707 }
708 }
709}
Calin Juravle10e244f2015-01-26 18:54:32 +0000710} // namespace art