Use the object class as top in reference type propagation
This properly types all instructions, making it safe to query the type
at any time.
Change-Id: I3ee2f0f79253cdf45b10ddab37ecb473345ca53a
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index c41574c..1411f6e 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -461,19 +461,14 @@
if (instruction->IsLoadClass()) {
ReferenceTypeInfo info = instruction->AsLoadClass()->GetLoadedClassRTI();
ScopedObjectAccess soa(Thread::Current());
- if (info.GetTypeHandle().GetReference() != nullptr) {
- StartAttributeStream("klass") << PrettyClass(info.GetTypeHandle().Get());
- } else {
- StartAttributeStream("klass") << "unresolved";
- }
+ DCHECK(info.IsValid()) << "Invalid RTI for " << instruction->DebugName();
+ StartAttributeStream("klass") << PrettyClass(info.GetTypeHandle().Get());
+ StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
} else {
ReferenceTypeInfo info = instruction->GetReferenceTypeInfo();
- if (info.IsTop()) {
- StartAttributeStream("klass") << "java.lang.Object";
- } else {
- ScopedObjectAccess soa(Thread::Current());
- StartAttributeStream("klass") << PrettyClass(info.GetTypeHandle().Get());
- }
+ ScopedObjectAccess soa(Thread::Current());
+ DCHECK(info.IsValid()) << "Invalid RTI for " << instruction->DebugName();
+ StartAttributeStream("klass") << PrettyClass(info.GetTypeHandle().Get());
StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
}
}
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 3efe7c7..860b199 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -109,10 +109,8 @@
receiver = receiver->InputAt(0);
}
ReferenceTypeInfo info = receiver->GetReferenceTypeInfo();
- if (info.IsTop()) {
- // We have no information on the receiver.
- return nullptr;
- } else if (!info.IsExact()) {
+ DCHECK(info.IsValid()) << "Invalid RTI for " << receiver->DebugName();
+ if (!info.IsExact()) {
// We currently only support inlining with known receivers.
// TODO: Remove this check, we should be able to inline final methods
// on unknown receivers.
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 881f9ec..c8ce837 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1636,11 +1636,25 @@
}
}
+void HInstruction::SetReferenceTypeInfo(ReferenceTypeInfo rti) {
+ if (kIsDebugBuild) {
+ DCHECK_EQ(GetType(), Primitive::kPrimNot);
+ ScopedObjectAccess soa(Thread::Current());
+ DCHECK(rti.IsValid()) << "Invalid RTI for " << DebugName();
+ if (IsBoundType()) {
+ // Having the test here spares us from making the method virtual just for
+ // the sake of a DCHECK.
+ DCHECK(AsBoundType()->GetBoundType().IsSupertypeOf(rti));
+ }
+ }
+ reference_type_info_ = rti;
+}
+
std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs) {
ScopedObjectAccess soa(Thread::Current());
os << "["
- << " is_top=" << rhs.IsTop()
- << " type=" << (rhs.IsTop() ? "?" : PrettyClass(rhs.GetTypeHandle().Get()))
+ << " is_valid=" << rhs.IsValid()
+ << " type=" << (!rhs.IsValid() ? "?" : PrettyClass(rhs.GetTypeHandle().Get()))
<< " is_exact=" << rhs.IsExact()
<< " ]";
return os;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 95ea966..42c1fc1 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -28,6 +28,7 @@
#include "mirror/class.h"
#include "offsets.h"
#include "primitive.h"
+#include "scoped_thread_state_change.h"
#include "utils/arena_bit_vector.h"
#include "utils/growable_array.h"
@@ -1347,79 +1348,70 @@
public:
typedef Handle<mirror::Class> TypeHandle;
- static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (type_handle->IsObjectClass()) {
- // Override the type handle to be consistent with the case when we get to
- // Top but don't have the Object class available. It avoids having to guess
- // what value the type_handle has when it's Top.
- return ReferenceTypeInfo(TypeHandle(), is_exact, true);
- } else {
- return ReferenceTypeInfo(type_handle, is_exact, false);
- }
+ static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact) {
+ // The constructor will check that the type_handle is valid.
+ return ReferenceTypeInfo(type_handle, is_exact);
}
- static ReferenceTypeInfo CreateTop(bool is_exact) {
- return ReferenceTypeInfo(TypeHandle(), is_exact, true);
+ static ReferenceTypeInfo CreateInvalid() { return ReferenceTypeInfo(); }
+
+ static bool IsValidHandle(TypeHandle handle) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return handle.GetReference() != nullptr;
}
+ bool IsValid() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return IsValidHandle(type_handle_);
+ }
bool IsExact() const { return is_exact_; }
- bool IsTop() const { return is_top_; }
+ bool IsObjectClass() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(IsValid());
+ return GetTypeHandle()->IsObjectClass();
+ }
bool IsInterface() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return !IsTop() && GetTypeHandle()->IsInterface();
+ DCHECK(IsValid());
+ return GetTypeHandle()->IsInterface();
}
Handle<mirror::Class> GetTypeHandle() const { return type_handle_; }
bool IsSupertypeOf(ReferenceTypeInfo rti) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (IsTop()) {
- // Top (equivalent for java.lang.Object) is supertype of anything.
- return true;
- }
- if (rti.IsTop()) {
- // If we get here `this` is not Top() so it can't be a supertype.
- return false;
- }
+ DCHECK(IsValid());
+ DCHECK(rti.IsValid());
return GetTypeHandle()->IsAssignableFrom(rti.GetTypeHandle().Get());
}
// Returns true if the type information provide the same amount of details.
// Note that it does not mean that the instructions have the same actual type
- // (e.g. tops are equal but they can be the result of a merge).
+ // (because the type can be the result of a merge).
bool IsEqual(ReferenceTypeInfo rti) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (IsExact() != rti.IsExact()) {
- return false;
- }
- if (IsTop() && rti.IsTop()) {
- // `Top` means java.lang.Object, so the types are equivalent.
+ if (!IsValid() && !rti.IsValid()) {
+ // Invalid types are equal.
return true;
}
- if (IsTop() || rti.IsTop()) {
- // If only one is top or object than they are not equivalent.
- // NB: We need this extra check because the type_handle of `Top` is invalid
- // and we cannot inspect its reference.
+ if (!IsValid() || !rti.IsValid()) {
+ // One is valid, the other not.
return false;
}
-
- // Finally check the types.
- return GetTypeHandle().Get() == rti.GetTypeHandle().Get();
+ return IsExact() == rti.IsExact()
+ && GetTypeHandle().Get() == rti.GetTypeHandle().Get();
}
private:
- ReferenceTypeInfo() : ReferenceTypeInfo(TypeHandle(), false, true) {}
- ReferenceTypeInfo(TypeHandle type_handle, bool is_exact, bool is_top)
- : type_handle_(type_handle), is_exact_(is_exact), is_top_(is_top) {}
+ ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {}
+
+ ReferenceTypeInfo(TypeHandle type_handle, bool is_exact)
+ : type_handle_(type_handle), is_exact_(is_exact) {
+ if (kIsDebugBuild) {
+ ScopedObjectAccess soa(Thread::Current());
+ DCHECK(IsValidHandle(type_handle));
+ }
+ }
// The class of the object.
TypeHandle type_handle_;
// Whether or not the type is exact or a superclass of the actual type.
// Whether or not we have any information about this type.
bool is_exact_;
- // A true value here means that the object type should be java.lang.Object.
- // We don't have access to the corresponding mirror object every time so this
- // flag acts as a substitute. When true, the TypeHandle refers to a null
- // pointer and should not be used.
- bool is_top_;
};
std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs);
@@ -1437,7 +1429,7 @@
live_interval_(nullptr),
lifetime_position_(kNoLifetime),
side_effects_(side_effects),
- reference_type_info_(ReferenceTypeInfo::CreateTop(/* is_exact */ false)) {}
+ reference_type_info_(ReferenceTypeInfo::CreateInvalid()) {}
virtual ~HInstruction() {}
@@ -1492,10 +1484,7 @@
return false;
}
- void SetReferenceTypeInfo(ReferenceTypeInfo reference_type_info) {
- DCHECK_EQ(GetType(), Primitive::kPrimNot);
- reference_type_info_ = reference_type_info;
- }
+ void SetReferenceTypeInfo(ReferenceTypeInfo rti);
ReferenceTypeInfo GetReferenceTypeInfo() const {
DCHECK_EQ(GetType(), Primitive::kPrimNot);
@@ -3697,7 +3686,7 @@
is_referrers_class_(is_referrers_class),
dex_pc_(dex_pc),
generate_clinit_check_(false),
- loaded_class_rti_(ReferenceTypeInfo::CreateTop(/* is_exact */ false)) {
+ loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) {
SetRawInputAt(0, current_method);
}
@@ -3741,7 +3730,7 @@
return loaded_class_rti_;
}
- void SetLoadedClassRTI(ReferenceTypeInfo rti) {
+ void SetLoadedClassRTI(ReferenceTypeInfo rti) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Make sure we only set exact types (the loaded class should never be merged).
DCHECK(rti.IsExact());
loaded_class_rti_ = rti;
@@ -3993,6 +3982,7 @@
bound_type_(bound_type) {
DCHECK_EQ(input->GetType(), Primitive::kPrimNot);
SetRawInputAt(0, input);
+ SetReferenceTypeInfo(bound_type_);
}
const ReferenceTypeInfo& GetBoundType() const { return bound_type_; }
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 3d6606b..e022d94 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -16,6 +16,7 @@
#include "reference_type_propagation.h"
+#include "class_linker.h"
#include "class_linker-inl.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache.h"
@@ -25,19 +26,34 @@
class RTPVisitor : public HGraphDelegateVisitor {
public:
- RTPVisitor(HGraph* graph, StackHandleScopeCollection* handles)
+ RTPVisitor(HGraph* graph,
+ StackHandleScopeCollection* handles,
+ GrowableArray<HInstruction*>* worklist,
+ ReferenceTypeInfo::TypeHandle object_class_handle,
+ ReferenceTypeInfo::TypeHandle class_class_handle,
+ ReferenceTypeInfo::TypeHandle string_class_handle)
: HGraphDelegateVisitor(graph),
- handles_(handles) {}
+ handles_(handles),
+ object_class_handle_(object_class_handle),
+ class_class_handle_(class_class_handle),
+ string_class_handle_(string_class_handle),
+ worklist_(worklist) {}
+ void VisitNullConstant(HNullConstant* null_constant) OVERRIDE;
void VisitNewInstance(HNewInstance* new_instance) OVERRIDE;
void VisitLoadClass(HLoadClass* load_class) OVERRIDE;
+ void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE;
+ void VisitLoadString(HLoadString* instr) OVERRIDE;
void VisitNewArray(HNewArray* instr) OVERRIDE;
+ void VisitParameterValue(HParameterValue* instr) OVERRIDE;
void UpdateFieldAccessTypeInfo(HInstruction* instr, const FieldInfo& info);
void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass, bool is_exact);
void VisitInstanceFieldGet(HInstanceFieldGet* instr) OVERRIDE;
void VisitStaticFieldGet(HStaticFieldGet* instr) OVERRIDE;
void VisitInvoke(HInvoke* instr) OVERRIDE;
void VisitArrayGet(HArrayGet* instr) OVERRIDE;
+ void VisitNullCheck(HNullCheck* instr) OVERRIDE;
+
void UpdateReferenceTypeInfo(HInstruction* instr,
uint16_t type_idx,
const DexFile& dex_file,
@@ -45,8 +61,32 @@
private:
StackHandleScopeCollection* handles_;
+ ReferenceTypeInfo::TypeHandle object_class_handle_;
+ ReferenceTypeInfo::TypeHandle class_class_handle_;
+ ReferenceTypeInfo::TypeHandle string_class_handle_;
+ GrowableArray<HInstruction*>* worklist_;
+
+ static constexpr size_t kDefaultWorklistSize = 8;
};
+ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
+ StackHandleScopeCollection* handles)
+ : HOptimization(graph, kReferenceTypePropagationPassName),
+ handles_(handles),
+ worklist_(graph->GetArena(), kDefaultWorklistSize) {
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ object_class_handle_ = handles_->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangObject));
+ string_class_handle_ = handles_->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangString));
+ class_class_handle_ = handles_->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangClass));
+
+ if (kIsDebugBuild) {
+ ScopedObjectAccess soa(Thread::Current());
+ DCHECK(ReferenceTypeInfo::IsValidHandle(object_class_handle_));
+ DCHECK(ReferenceTypeInfo::IsValidHandle(class_class_handle_));
+ DCHECK(ReferenceTypeInfo::IsValidHandle(string_class_handle_));
+ }
+}
+
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.
@@ -55,13 +95,30 @@
VisitBasicBlock(it.Current());
}
ProcessWorklist();
+
+ if (kIsDebugBuild) {
+ // TODO: move this to the graph checker.
+ ScopedObjectAccess soa(Thread::Current());
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ for (HInstructionIterator iti(block->GetInstructions()); !iti.Done(); iti.Advance()) {
+ HInstruction* instr = iti.Current();
+ if (instr->GetType() == Primitive::kPrimNot) {
+ DCHECK(instr->GetReferenceTypeInfo().IsValid())
+ << "Invalid RTI for instruction: " << instr->DebugName();
+ }
+ }
+ }
+ }
}
void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
- // TODO: handle other instructions that give type info
- // (array accesses)
-
- RTPVisitor visitor(graph_, handles_);
+ RTPVisitor visitor(graph_,
+ handles_,
+ &worklist_,
+ object_class_handle_,
+ class_class_handle_,
+ string_class_handle_);
// Initialize exact types first for faster convergence.
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* instr = it.Current();
@@ -110,7 +167,7 @@
HInstruction* user = it.Current()->GetUser();
if (notNullBlock->Dominates(user->GetBlock())) {
if (bound_type == nullptr) {
- bound_type = new (graph_->GetArena()) HBoundType(obj, ReferenceTypeInfo::CreateTop(false));
+ bound_type = new (graph_->GetArena()) HBoundType(obj, obj->GetReferenceTypeInfo());
notNullBlock->InsertInstructionBefore(bound_type, notNullBlock->GetFirstInstruction());
}
user->ReplaceInput(bound_type, it.Current()->GetIndex());
@@ -192,11 +249,32 @@
void RTPVisitor::SetClassAsTypeInfo(HInstruction* instr,
mirror::Class* klass,
bool is_exact) {
- if (klass != nullptr) {
+ if (instr->IsInvokeStaticOrDirect() && instr->AsInvokeStaticOrDirect()->IsStringInit()) {
+ // Calls to String.<init> are replaced with a StringFactory.
+ if (kIsDebugBuild) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ mirror::DexCache* dex_cache = cl->FindDexCache(instr->AsInvoke()->GetDexFile());
+ ArtMethod* method = dex_cache->GetResolvedMethod(
+ instr->AsInvoke()->GetDexMethodIndex(), cl->GetImagePointerSize());
+ DCHECK(method != nullptr);
+ mirror::Class* declaring_class = method->GetDeclaringClass();
+ DCHECK(declaring_class != nullptr);
+ DCHECK(declaring_class->IsStringClass())
+ << "Expected String class: " << PrettyDescriptor(declaring_class);
+ DCHECK(method->IsConstructor())
+ << "Expected String.<init>: " << PrettyMethod(method);
+ }
+ instr->SetReferenceTypeInfo(
+ ReferenceTypeInfo::Create(string_class_handle_, /* is_exact */ true));
+ } else if (klass != nullptr) {
ScopedObjectAccess soa(Thread::Current());
- MutableHandle<mirror::Class> handle = handles_->NewHandle(klass);
+ ReferenceTypeInfo::TypeHandle handle = handles_->NewHandle(klass);
is_exact = is_exact || klass->IsFinal();
instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact));
+ } else {
+ instr->SetReferenceTypeInfo(
+ ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false));
}
}
@@ -212,6 +290,11 @@
SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact);
}
+void RTPVisitor::VisitNullConstant(HNullConstant* instr) {
+ instr->SetReferenceTypeInfo(
+ ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false));
+}
+
void RTPVisitor::VisitNewInstance(HNewInstance* instr) {
UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
}
@@ -220,6 +303,13 @@
UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
}
+void RTPVisitor::VisitParameterValue(HParameterValue* instr) {
+ if (instr->GetType() == Primitive::kPrimNot) {
+ // TODO: parse the signature and add precise types for the parameters.
+ SetClassAsTypeInfo(instr, nullptr, /* is_exact */ false);
+ }
+}
+
void RTPVisitor::UpdateFieldAccessTypeInfo(HInstruction* instr,
const FieldInfo& info) {
// The field index is unknown only during tests.
@@ -231,10 +321,10 @@
ClassLinker* cl = Runtime::Current()->GetClassLinker();
mirror::DexCache* dex_cache = cl->FindDexCache(info.GetDexFile());
ArtField* field = cl->GetResolvedField(info.GetFieldIndex(), dex_cache);
- if (field != nullptr) {
- mirror::Class* klass = field->GetType<false>();
- SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
- }
+ // TODO: There are certain cases where we can't resolve the field.
+ // b/21914925 is open to keep track of a repro case for this issue.
+ mirror::Class* klass = (field == nullptr) ? nullptr : field->GetType<false>();
+ SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
}
void RTPVisitor::VisitInstanceFieldGet(HInstanceFieldGet* instr) {
@@ -251,12 +341,33 @@
Runtime::Current()->GetClassLinker()->FindDexCache(instr->GetDexFile());
// 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::Create(handle, /* is_exact */ true));
+ DCHECK(resolved_class != nullptr);
+ ReferenceTypeInfo::TypeHandle handle = handles_->NewHandle(resolved_class);
+ instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(handle, /* is_exact */ true));
+ instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(class_class_handle_, /* is_exact */ true));
+}
+
+void RTPVisitor::VisitClinitCheck(HClinitCheck* instr) {
+ instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo());
+}
+
+void RTPVisitor::VisitLoadString(HLoadString* instr) {
+ instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(string_class_handle_, /* is_exact */ true));
+}
+
+void RTPVisitor::VisitNullCheck(HNullCheck* instr) {
+ ScopedObjectAccess soa(Thread::Current());
+
+ HInstruction* parent = instr->InputAt(0);
+ ReferenceTypeInfo parent_rti = parent->GetReferenceTypeInfo();
+ if (!parent_rti.IsValid()) {
+ // Parent could be a Phi or an ArrayGet and we might not have any valid
+ // information on them at this point.
+ DCHECK(parent->IsPhi() || parent->IsArrayGet()) << parent->DebugName();
+ worklist_->Add(instr);
+ return;
}
- Handle<mirror::Class> class_handle = handles_->NewHandle(mirror::Class::GetJavaLangClass());
- instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(class_handle, /* is_exact */ true));
+ instr->SetReferenceTypeInfo(parent->GetReferenceTypeInfo());
}
void ReferenceTypePropagation::VisitPhi(HPhi* phi) {
@@ -283,29 +394,60 @@
ReferenceTypeInfo ReferenceTypePropagation::MergeTypes(const ReferenceTypeInfo& a,
const ReferenceTypeInfo& b) {
- bool is_exact = a.IsExact() && b.IsExact();
- bool is_top = a.IsTop() || b.IsTop();
- Handle<mirror::Class> type_handle;
-
- if (!is_top) {
- if (a.GetTypeHandle().Get() == b.GetTypeHandle().Get()) {
- type_handle = a.GetTypeHandle();
- } else if (a.IsSupertypeOf(b)) {
- type_handle = a.GetTypeHandle();
- is_exact = false;
- } else if (b.IsSupertypeOf(a)) {
- type_handle = b.GetTypeHandle();
- is_exact = false;
- } else {
- // TODO: Find a common super class.
- is_top = true;
- is_exact = false;
- }
+ if (!b.IsValid()) {
+ return a;
+ }
+ if (!a.IsValid()) {
+ return b;
}
- return is_top
- ? ReferenceTypeInfo::CreateTop(is_exact)
- : ReferenceTypeInfo::Create(type_handle, is_exact);
+ bool is_exact = a.IsExact() && b.IsExact();
+ Handle<mirror::Class> type_handle;
+
+ if (a.GetTypeHandle().Get() == b.GetTypeHandle().Get()) {
+ type_handle = a.GetTypeHandle();
+ } else if (a.IsSupertypeOf(b)) {
+ type_handle = a.GetTypeHandle();
+ is_exact = false;
+ } else if (b.IsSupertypeOf(a)) {
+ type_handle = b.GetTypeHandle();
+ is_exact = false;
+ } else {
+ // TODO: Find the first common super class.
+ type_handle = object_class_handle_;
+ is_exact = false;
+ }
+
+ return ReferenceTypeInfo::Create(type_handle, is_exact);
+}
+
+static void UpdateArrayGet(HArrayGet* instr,
+ StackHandleScopeCollection* handles,
+ ReferenceTypeInfo::TypeHandle object_class_handle)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK_EQ(Primitive::kPrimNot, instr->GetType());
+
+ HInstruction* array = instr->InputAt(0);
+ ReferenceTypeInfo parent_rti = array->GetReferenceTypeInfo();
+ if (!parent_rti.IsValid()) {
+ DCHECK(array->IsPhi() || array->IsNullCheck() || array->IsArrayGet());
+ // The array could be a Phi/NullCheck/ArrayGet for which we might have not have
+ // valid information at this point.
+ return;
+ }
+
+ Handle<mirror::Class> handle = parent_rti.GetTypeHandle();
+ if (handle->IsObjectArrayClass()) {
+ ReferenceTypeInfo::TypeHandle component_handle = handles->NewHandle(handle->GetComponentType());
+ instr->SetReferenceTypeInfo(
+ ReferenceTypeInfo::Create(component_handle, /* is_exact */ false));
+ } else {
+ // We don't know what the parent actually is, so we fallback to object.
+ instr->SetReferenceTypeInfo(
+ ReferenceTypeInfo::Create(object_class_handle, /* is_exact */ false));
+ }
+
+ return;
}
bool ReferenceTypePropagation::UpdateReferenceTypeInfo(HInstruction* instr) {
@@ -316,6 +458,15 @@
UpdateBoundType(instr->AsBoundType());
} else if (instr->IsPhi()) {
UpdatePhi(instr->AsPhi());
+ } else if (instr->IsNullCheck()) {
+ ReferenceTypeInfo parent_rti = instr->InputAt(0)->GetReferenceTypeInfo();
+ if (parent_rti.IsValid()) {
+ instr->SetReferenceTypeInfo(parent_rti);
+ }
+ } else if (instr->IsArrayGet()) {
+ // TODO: consider if it's worth "looking back" and bounding the input object
+ // to an array type.
+ UpdateArrayGet(instr->AsArrayGet(), handles_, object_class_handle_);
} else {
LOG(FATAL) << "Invalid instruction (should not get here)";
}
@@ -333,22 +484,21 @@
mirror::DexCache* dex_cache = cl->FindDexCache(instr->GetDexFile());
ArtMethod* method = dex_cache->GetResolvedMethod(
instr->GetDexMethodIndex(), cl->GetImagePointerSize());
- if (method != nullptr) {
- mirror::Class* klass = method->GetReturnType(false);
- SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
- }
+ mirror::Class* klass = method == nullptr ? nullptr : method->GetReturnType(false);
+ SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
}
void RTPVisitor::VisitArrayGet(HArrayGet* instr) {
if (instr->GetType() != Primitive::kPrimNot) {
return;
}
-
- HInstruction* parent = instr->InputAt(0);
ScopedObjectAccess soa(Thread::Current());
- Handle<mirror::Class> handle = parent->GetReferenceTypeInfo().GetTypeHandle();
- if (handle.GetReference() != nullptr && handle->IsObjectArrayClass()) {
- SetClassAsTypeInfo(instr, handle->GetComponentType(), /* is_exact */ false);
+ UpdateArrayGet(instr, handles_, object_class_handle_);
+ if (!instr->GetReferenceTypeInfo().IsValid()) {
+ // If the RTI is not valid it means it depends on instruction for which we
+ // don't have the final information before the fix point iteration. So we
+ // need to add it to the worklist.
+ worklist_->Add(instr);
}
}
@@ -364,14 +514,14 @@
void ReferenceTypePropagation::UpdatePhi(HPhi* instr) {
ReferenceTypeInfo new_rti = instr->InputAt(0)->GetReferenceTypeInfo();
- if (new_rti.IsTop() && !new_rti.IsExact()) {
- // Early return if we are Top and inexact.
+ if (new_rti.IsObjectClass() && !new_rti.IsExact()) {
+ // Early return if we are Object and inexact.
instr->SetReferenceTypeInfo(new_rti);
return;
}
for (size_t i = 1; i < instr->InputCount(); i++) {
new_rti = MergeTypes(new_rti, instr->InputAt(i)->GetReferenceTypeInfo());
- if (new_rti.IsTop()) {
+ if (new_rti.IsObjectClass()) {
if (!new_rti.IsExact()) {
break;
} else {
@@ -385,7 +535,10 @@
// Re-computes and updates the nullability of the instruction. Returns whether or
// not the nullability was changed.
bool ReferenceTypePropagation::UpdateNullability(HInstruction* instr) {
- DCHECK(instr->IsPhi() || instr->IsBoundType());
+ DCHECK(instr->IsPhi()
+ || instr->IsBoundType()
+ || instr->IsNullCheck()
+ || instr->IsArrayGet());
if (!instr->IsPhi()) {
return false;
@@ -412,15 +565,19 @@
}
void ReferenceTypePropagation::AddToWorklist(HInstruction* instruction) {
- DCHECK_EQ(instruction->GetType(), Primitive::kPrimNot) << instruction->GetType();
+ DCHECK_EQ(instruction->GetType(), Primitive::kPrimNot)
+ << instruction->DebugName() << ":" << instruction->GetType();
worklist_.Add(instruction);
}
void ReferenceTypePropagation::AddDependentInstructionsToWorklist(HInstruction* instruction) {
for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
HInstruction* user = it.Current()->GetUser();
- if (user->IsPhi() || user->IsBoundType()) {
- AddToWorklist(user);
+ if (user->IsPhi()
+ || user->IsBoundType()
+ || user->IsNullCheck()
+ || (user->IsArrayGet() && (user->GetType() == Primitive::kPrimNot))) {
+ AddToWorklist(user);
}
}
}
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 17cfed4..91a7afb 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -30,10 +30,7 @@
*/
class ReferenceTypePropagation : public HOptimization {
public:
- ReferenceTypePropagation(HGraph* graph, StackHandleScopeCollection* handles)
- : HOptimization(graph, kReferenceTypePropagationPassName),
- handles_(handles),
- worklist_(graph->GetArena(), kDefaultWorklistSize) {}
+ ReferenceTypePropagation(HGraph* graph, StackHandleScopeCollection* handles);
void Run() OVERRIDE;
@@ -60,6 +57,10 @@
GrowableArray<HInstruction*> worklist_;
+ ReferenceTypeInfo::TypeHandle object_class_handle_;
+ ReferenceTypeInfo::TypeHandle class_class_handle_;
+ ReferenceTypeInfo::TypeHandle string_class_handle_;
+
static constexpr size_t kDefaultWorklistSize = 8;
DISALLOW_COPY_AND_ASSIGN(ReferenceTypePropagation);