Reference type propagation
- propagate reference types between instructions
- remove checked casts when possible
- add StackHandleScopeCollection to manage an arbitrary number of stack
handles (see comments)
Change-Id: I31200067c5e7375a5ea8e2f873c4374ebdb5ee60
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 4f17b6f..18c0564 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -16,12 +16,34 @@
#include "reference_type_propagation.h"
+#include "class_linker.h"
+#include "mirror/class-inl.h"
+#include "mirror/dex_cache.h"
+#include "scoped_thread_state_change.h"
+
namespace art {
-void ReferenceTypePropagation::Run() {
- // Compute null status for instructions.
+// TODO: Only do the analysis on reference types. We currently have to handle
+// the `null` constant, that is represented as a `HIntConstant` and therefore
+// has the Primitive::kPrimInt type.
- // To properly propagate not-null info we need to visit in the dominator-based order.
+// TODO: handle:
+// public Main ifNullTest(int count, Main a) {
+// Main m = new Main();
+// if (a == null) {
+// a = m;
+// }
+// return a.g();
+// }
+// public Main ifNotNullTest(Main a) {
+// if (a != null) {
+// return a.g();
+// }
+// return new Main();
+// }
+
+void ReferenceTypePropagation::Run() {
+ // To properly propagate type info we need to visit in the dominator-based order.
// Reverse post order guarantees a node's dominators are visited first.
// We take advantage of this order in `VisitBasicBlock`.
for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
@@ -43,8 +65,89 @@
return existing_can_be_null != new_can_be_null;
}
+bool ReferenceTypePropagation::UpdateReferenceTypeInfo(HPhi* phi) {
+ ScopedObjectAccess soa(Thread::Current());
+
+ ReferenceTypeInfo existing_rti = phi->GetReferenceTypeInfo();
+ ReferenceTypeInfo new_rti = phi->InputAt(0)->GetReferenceTypeInfo();
+
+ if (new_rti.IsTop() && !new_rti.IsExact()) {
+ // Early return if we are Top and inexact.
+ phi->SetReferenceTypeInfo(new_rti);
+ return !new_rti.IsEqual(existing_rti);;
+ }
+
+ for (size_t i = 1; i < phi->InputCount(); i++) {
+ ReferenceTypeInfo input_rti = phi->InputAt(i)->GetReferenceTypeInfo();
+
+ if (!input_rti.IsExact()) {
+ new_rti.SetInexact();
+ }
+
+ if (input_rti.IsTop()) {
+ new_rti.SetTop();
+ }
+
+ if (new_rti.IsTop()) {
+ if (!new_rti.IsExact()) {
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ if (new_rti.GetTypeHandle().Get() == input_rti.GetTypeHandle().Get()) {
+ // nothing to do if we have the same type
+ } else if (input_rti.IsSupertypeOf(new_rti)) {
+ new_rti.SetTypeHandle(input_rti.GetTypeHandle(), false);
+ } else if (new_rti.IsSupertypeOf(input_rti)) {
+ new_rti.SetInexact();
+ } else {
+ // TODO: Find common parent.
+ new_rti.SetTop();
+ new_rti.SetInexact();
+ }
+ }
+
+ phi->SetReferenceTypeInfo(new_rti);
+ return !new_rti.IsEqual(existing_rti);
+}
+
+void ReferenceTypePropagation::VisitNewInstance(HNewInstance* instr) {
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::DexCache* dex_cache = dex_compilation_unit_.GetClassLinker()->FindDexCache(dex_file_);
+ // Get type from dex cache assuming it was populated by the verifier.
+ mirror::Class* resolved_class = dex_cache->GetResolvedType(instr->GetTypeIndex());
+ if (resolved_class != nullptr) {
+ MutableHandle<mirror::Class> handle = handles_->NewHandle(resolved_class);
+ instr->SetReferenceTypeInfo(ReferenceTypeInfo(handle, true));
+ }
+}
+
+void ReferenceTypePropagation::VisitLoadClass(HLoadClass* instr) {
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::DexCache* dex_cache = dex_compilation_unit_.GetClassLinker()->FindDexCache(dex_file_);
+ // Get type from dex cache assuming it was populated by the verifier.
+ mirror::Class* resolved_class = dex_cache->GetResolvedType(instr->GetTypeIndex());
+ if (resolved_class != nullptr) {
+ Handle<mirror::Class> handle = handles_->NewHandle(resolved_class);
+ instr->SetLoadedClassRTI(ReferenceTypeInfo(handle, true));
+ }
+ Handle<mirror::Class> class_handle = handles_->NewHandle(mirror::Class::GetJavaLangClass());
+ instr->SetReferenceTypeInfo(ReferenceTypeInfo(class_handle, true));
+}
void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
+ // TODO: handle other instructions that give type info
+ // (NewArray/Call/Field accesses/array accesses)
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instr = it.Current();
+ if (instr->IsNewInstance()) {
+ VisitNewInstance(instr->AsNewInstance());
+ } else if (instr->IsLoadClass()) {
+ VisitLoadClass(instr->AsLoadClass());
+ }
+ }
if (block->IsLoopHeader()) {
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
// Set the initial type for the phi. Use the non back edge input for reaching
@@ -53,6 +156,7 @@
if (phi->GetType() == Primitive::kPrimNot) {
AddToWorklist(phi);
phi->SetCanBeNull(phi->InputAt(0)->CanBeNull());
+ phi->SetReferenceTypeInfo(phi->InputAt(0)->GetReferenceTypeInfo());
}
}
} else {
@@ -65,6 +169,7 @@
HPhi* phi = it.Current()->AsPhi();
if (phi->GetType() == Primitive::kPrimNot) {
UpdateNullability(phi);
+ UpdateReferenceTypeInfo(phi);
}
}
}
@@ -73,7 +178,7 @@
void ReferenceTypePropagation::ProcessWorklist() {
while (!worklist_.IsEmpty()) {
HPhi* instruction = worklist_.Pop();
- if (UpdateNullability(instruction)) {
+ if (UpdateNullability(instruction) || UpdateReferenceTypeInfo(instruction)) {
AddDependentInstructionsToWorklist(instruction);
}
}