Commit 750f7c28 authored by Andreas Gampe's avatar Andreas Gampe
Browse files

ART: Change UnresolvedMergedType internal representation

Use a BitVector instead of the tree representation. This
avoids flattening the components and other instances.

Bug: 22881413
Change-Id: Ibf7cfb54443affeb1753bf114c0f306125391c62
parent 77bccdc5
......@@ -16,6 +16,7 @@
#include "reg_type-inl.h"
#include "base/bit_vector-inl.h"
#include "base/casts.h"
#include "class_linker-inl.h"
#include "dex_file-inl.h"
......@@ -309,13 +310,17 @@ PreciseReferenceType::PreciseReferenceType(mirror::Class* klass, const std::stri
std::string UnresolvedMergedType::Dump() const {
std::stringstream result;
std::set<uint16_t> types = GetMergedTypes();
result << "UnresolvedMergedReferences(";
auto it = types.begin();
result << reg_type_cache_->GetFromId(*it).Dump();
for (++it; it != types.end(); ++it) {
result << ", ";
result << reg_type_cache_->GetFromId(*it).Dump();
result << "UnresolvedMergedReferences(" << GetResolvedPart().Dump() << " | ";
const BitVector& types = GetUnresolvedTypes();
bool first = true;
for (uint32_t idx : types.Indexes()) {
if (!first) {
result << ", ";
} else {
first = false;
}
result << reg_type_cache_->GetFromId(idx).Dump();
}
result << ")";
return result.str();
......@@ -492,32 +497,6 @@ bool UnresolvedType::IsNonZeroReferenceTypes() const {
return true;
}
std::set<uint16_t> UnresolvedMergedType::GetMergedTypes() const {
std::pair<uint16_t, uint16_t> refs = GetTopMergedTypes();
const RegType& left = reg_type_cache_->GetFromId(refs.first);
const RegType& right = reg_type_cache_->GetFromId(refs.second);
std::set<uint16_t> types;
if (left.IsUnresolvedMergedReference()) {
types = down_cast<const UnresolvedMergedType*>(&left)->GetMergedTypes();
} else {
types.insert(refs.first);
}
if (right.IsUnresolvedMergedReference()) {
std::set<uint16_t> right_types =
down_cast<const UnresolvedMergedType*>(&right)->GetMergedTypes();
types.insert(right_types.begin(), right_types.end());
} else {
types.insert(refs.second);
}
if (kIsDebugBuild) {
for (const auto& type : types) {
CHECK(!reg_type_cache_->GetFromId(type).IsUnresolvedMergedReference());
}
}
return types;
}
const RegType& RegType::GetSuperClass(RegTypeCache* cache) const {
if (!IsUnresolvedTypes()) {
mirror::Class* super_klass = GetClass()->GetSuperClass();
......@@ -803,12 +782,24 @@ void UnresolvedUninitializedRefType::CheckInvariants() const {
CHECK(klass_.IsNull()) << *this;
}
UnresolvedMergedType::UnresolvedMergedType(const RegType& resolved,
const BitVector& unresolved,
const RegTypeCache* reg_type_cache,
uint16_t cache_id)
: UnresolvedType("", cache_id),
reg_type_cache_(reg_type_cache),
resolved_part_(resolved),
unresolved_types_(unresolved, false, unresolved.GetAllocator()) {
if (kIsDebugBuild) {
CheckInvariants();
}
}
void UnresolvedMergedType::CheckInvariants() const {
// Unresolved merged types: merged types should be defined.
CHECK(descriptor_.empty()) << *this;
CHECK(klass_.IsNull()) << *this;
CHECK_NE(merged_types_.first, 0U) << *this;
CHECK_NE(merged_types_.second, 0U) << *this;
CHECK(resolved_part_.IsReferenceTypes());
CHECK(!resolved_part_.IsUnresolvedTypes());
}
void UnresolvedReferenceType::CheckInvariants() const {
......
......@@ -22,6 +22,7 @@
#include <set>
#include <string>
#include "base/bit_vector.h"
#include "base/macros.h"
#include "base/mutex.h"
#include "gc_root.h"
......@@ -230,6 +231,14 @@ class RegType {
// from another.
const RegType& Merge(const RegType& incoming_type, RegTypeCache* reg_types) const
SHARED_REQUIRES(Locks::mutator_lock_);
// Same as above, but also handles the case where incoming_type == this.
const RegType& SafeMerge(const RegType& incoming_type, RegTypeCache* reg_types) const
SHARED_REQUIRES(Locks::mutator_lock_) {
if (Equals(incoming_type)) {
return *this;
}
return Merge(incoming_type, reg_types);
}
/*
* A basic Join operation on classes. For a pair of types S and T the Join,
......@@ -868,30 +877,23 @@ class UnresolvedSuperClass FINAL : public UnresolvedType {
const RegTypeCache* const reg_type_cache_;
};
// A merge of two unresolved types. If the types were resolved this may be
// Conflict or another
// known ReferenceType.
// A merge of unresolved (and resolved) types. If the types were resolved this may be
// Conflict or another known ReferenceType.
class UnresolvedMergedType FINAL : public UnresolvedType {
public:
UnresolvedMergedType(uint16_t left_id, uint16_t right_id,
// Note: the constructor will copy the unresolved BitVector, not use it directly.
UnresolvedMergedType(const RegType& resolved, const BitVector& unresolved,
const RegTypeCache* reg_type_cache, uint16_t cache_id)
SHARED_REQUIRES(Locks::mutator_lock_)
: UnresolvedType("", cache_id),
reg_type_cache_(reg_type_cache),
merged_types_(left_id, right_id) {
if (kIsDebugBuild) {
CheckInvariants();
}
}
SHARED_REQUIRES(Locks::mutator_lock_);
// The top of a tree of merged types.
std::pair<uint16_t, uint16_t> GetTopMergedTypes() const {
DCHECK(IsUnresolvedMergedReference());
return merged_types_;
// The resolved part. See description below.
const RegType& GetResolvedPart() const {
return resolved_part_;
}
// The unresolved part.
const BitVector& GetUnresolvedTypes() const {
return unresolved_types_;
}
// The complete set of merged types.
std::set<uint16_t> GetMergedTypes() const;
bool IsUnresolvedMergedReference() const OVERRIDE { return true; }
......@@ -903,7 +905,16 @@ class UnresolvedMergedType FINAL : public UnresolvedType {
void CheckInvariants() const SHARED_REQUIRES(Locks::mutator_lock_);
const RegTypeCache* const reg_type_cache_;
const std::pair<uint16_t, uint16_t> merged_types_;
// The original implementation of merged types was a binary tree. Collection of the flattened
// types ("leaves") can be expensive, so we store the expanded list now, as two components:
// 1) A resolved component. We use Zero when there is no resolved component, as that will be
// an identity merge.
// 2) A bitvector of the unresolved reference types. A bitvector was chosen with the assumption
// that there should not be too many types in flight in practice. (We also bias the index
// against the index of Zero, which is one of the later default entries in any cache.)
const RegType& resolved_part_;
const BitVector unresolved_types_;
};
std::ostream& operator<<(std::ostream& os, const RegType& rhs)
......
......@@ -317,39 +317,62 @@ void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() {
}
const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, const RegType& right) {
std::set<uint16_t> types;
BitVector types(1, // Allocate at least a word.
true, // Is expandable.
Allocator::GetMallocAllocator()); // TODO: Arenas in the verifier.
const RegType* left_resolved;
if (left.IsUnresolvedMergedReference()) {
RegType& non_const(const_cast<RegType&>(left));
types = (down_cast<UnresolvedMergedType*>(&non_const))->GetMergedTypes();
const UnresolvedMergedType* left_merge = down_cast<const UnresolvedMergedType*>(&left);
types.Copy(&left_merge->GetUnresolvedTypes());
left_resolved = &left_merge->GetResolvedPart();
} else if (left.IsUnresolvedReference()) {
types.SetBit(left.GetId());
left_resolved = &Zero();
} else {
types.insert(left.GetId());
left_resolved = &left;
}
const RegType* right_resolved;
if (right.IsUnresolvedMergedReference()) {
RegType& non_const(const_cast<RegType&>(right));
std::set<uint16_t> right_types = (down_cast<UnresolvedMergedType*>(&non_const))->GetMergedTypes();
types.insert(right_types.begin(), right_types.end());
const UnresolvedMergedType* right_merge = down_cast<const UnresolvedMergedType*>(&right);
types.Union(&right_merge->GetUnresolvedTypes());
right_resolved = &right_merge->GetResolvedPart();
} else if (right.IsUnresolvedReference()) {
types.SetBit(right.GetId());
right_resolved = &Zero();
} else {
types.insert(right.GetId());
right_resolved = &right;
}
// Merge the resolved parts. Left and right might be equal, so use SafeMerge.
const RegType& resolved_parts_merged = left_resolved->SafeMerge(*right_resolved, this);
// If we get a conflict here, the merge result is a conflict, not an unresolved merge type.
if (resolved_parts_merged.IsConflict()) {
return Conflict();
}
// Check if entry already exists.
for (size_t i = primitive_count_; i < entries_.size(); i++) {
const RegType* cur_entry = entries_[i];
if (cur_entry->IsUnresolvedMergedReference()) {
std::set<uint16_t> cur_entry_types =
(down_cast<const UnresolvedMergedType*>(cur_entry))->GetMergedTypes();
if (cur_entry_types == types) {
const UnresolvedMergedType* cmp_type = down_cast<const UnresolvedMergedType*>(cur_entry);
const RegType& resolved_part = cmp_type->GetResolvedPart();
const BitVector& unresolved_part = cmp_type->GetUnresolvedTypes();
// Use SameBitsSet. "types" is expandable to allow merging in the components, but the
// BitVector in the final RegType will be made non-expandable.
if (&resolved_part == &resolved_parts_merged &&
types.SameBitsSet(&unresolved_part)) {
return *cur_entry;
}
}
}
// Create entry.
RegType* entry = new UnresolvedMergedType(left.GetId(), right.GetId(), this, entries_.size());
RegType* entry = new UnresolvedMergedType(resolved_parts_merged,
types,
this,
entries_.size());
AddEntry(entry);
if (kIsDebugBuild) {
UnresolvedMergedType* tmp_entry = down_cast<UnresolvedMergedType*>(entry);
std::set<uint16_t> check_types = tmp_entry->GetMergedTypes();
CHECK(check_types == types);
}
return *entry;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment