Index: runtime/vm/object.cc |
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc |
index 5414c91da0e22a74fdfdf189ce1f1b3dcb95c36e..b56b1e1997822c70456393e1d91ccbc8f687197e 100644 |
--- a/runtime/vm/object.cc |
+++ b/runtime/vm/object.cc |
@@ -705,6 +705,7 @@ void Object::InitOnce(Isolate* isolate) { |
empty_array_, |
reinterpret_cast<RawArray*>(address + kHeapObjectTag)); |
empty_array_->StoreSmi(&empty_array_->raw_ptr()->length_, Smi::New(0)); |
+ empty_array_->SetCanonical(); |
} |
Smi& smi = Smi::Handle(); |
@@ -718,6 +719,7 @@ void Object::InitOnce(Isolate* isolate) { |
zero_array_->StoreSmi(&zero_array_->raw_ptr()->length_, Smi::New(1)); |
smi = Smi::New(0); |
zero_array_->SetAt(0, smi); |
+ zero_array_->SetCanonical(); |
} |
// Allocate and initialize the canonical empty context scope object. |
@@ -734,6 +736,7 @@ void Object::InitOnce(Isolate* isolate) { |
&empty_context_scope_->raw_ptr()->num_variables_, 0); |
empty_context_scope_->StoreNonPointer( |
&empty_context_scope_->raw_ptr()->is_implicit_, true); |
+ empty_context_scope_->SetCanonical(); |
} |
// Allocate and initialize the canonical empty object pool object. |
@@ -749,6 +752,7 @@ void Object::InitOnce(Isolate* isolate) { |
reinterpret_cast<RawObjectPool*>(address + kHeapObjectTag)); |
empty_object_pool_->StoreNonPointer( |
&empty_object_pool_->raw_ptr()->length_, 0); |
+ empty_object_pool_->SetCanonical(); |
} |
// Allocate and initialize the empty_descriptors instance. |
@@ -762,6 +766,7 @@ void Object::InitOnce(Isolate* isolate) { |
reinterpret_cast<RawPcDescriptors*>(address + kHeapObjectTag)); |
empty_descriptors_->StoreNonPointer(&empty_descriptors_->raw_ptr()->length_, |
0); |
+ empty_descriptors_->SetCanonical(); |
} |
// Allocate and initialize the canonical empty variable descriptor object. |
@@ -777,6 +782,7 @@ void Object::InitOnce(Isolate* isolate) { |
reinterpret_cast<RawLocalVarDescriptors*>(address + kHeapObjectTag)); |
empty_var_descriptors_->StoreNonPointer( |
&empty_var_descriptors_->raw_ptr()->num_entries_, 0); |
+ empty_var_descriptors_->SetCanonical(); |
} |
// Allocate and initialize the canonical empty exception handler info object. |
@@ -794,6 +800,7 @@ void Object::InitOnce(Isolate* isolate) { |
reinterpret_cast<RawExceptionHandlers*>(address + kHeapObjectTag)); |
empty_exception_handlers_->StoreNonPointer( |
&empty_exception_handlers_->raw_ptr()->num_entries_, 0); |
+ empty_exception_handlers_->SetCanonical(); |
} |
// The VM isolate snapshot object table is initialized to an empty array |
@@ -4421,44 +4428,89 @@ RawBigint* Class::LookupCanonicalBigint(Zone* zone, |
} |
+class CanonicalInstanceKey { |
+ public: |
+ explicit CanonicalInstanceKey(const Instance& key) : key_(key) { |
+ ASSERT(!(key.IsString() || key.IsInteger() || key.IsAbstractType())); |
+ } |
+ bool Matches(const Instance& obj) const { |
+ ASSERT(!(obj.IsString() || obj.IsInteger() || obj.IsAbstractType())); |
+ if (key_.CanonicalizeEquals(obj)) { |
+ ASSERT(obj.IsCanonical()); |
+ return true; |
+ } |
+ return false; |
+ } |
+ uword Hash() const { |
+ return key_.ComputeCanonicalTableHash(); |
+ } |
+ const Instance& key_; |
+ |
+ private: |
+ DISALLOW_ALLOCATION(); |
+ DISALLOW_COPY_AND_ASSIGN(CanonicalInstanceKey); |
+}; |
+ |
+ |
+// Traits for looking up Canonical Instances based on a hash of the fields. |
+class CanonicalInstanceTraits { |
+ public: |
+ static const char* Name() { return "CanonicalInstanceTraits"; } |
+ static bool ReportStats() { return false; } |
+ |
+ // Called when growing the table. |
+ static bool IsMatch(const Object& a, const Object& b) { |
+ ASSERT(!(a.IsString() || a.IsInteger() || a.IsAbstractType())); |
+ ASSERT(!(b.IsString() || b.IsInteger() || b.IsAbstractType())); |
+ return a.raw() == b.raw(); |
+ } |
+ static bool IsMatch(const CanonicalInstanceKey& a, const Object& b) { |
+ return a.Matches(Instance::Cast(b)); |
+ } |
+ static uword Hash(const Object& key) { |
+ ASSERT(!(key.IsString() || key.IsNumber() || key.IsAbstractType())); |
+ ASSERT(key.IsInstance()); |
+ return Instance::Cast(key).ComputeCanonicalTableHash(); |
+ } |
+ static uword Hash(const CanonicalInstanceKey& key) { |
+ return key.Hash(); |
+ } |
+ static RawObject* NewKey(const CanonicalInstanceKey& obj) { |
+ return obj.key_.raw(); |
+ } |
+}; |
+typedef UnorderedHashSet<CanonicalInstanceTraits> CanonicalInstancesSet; |
+ |
+ |
RawInstance* Class::LookupCanonicalInstance(Zone* zone, |
- const Instance& value, |
- intptr_t* index) const { |
+ const Instance& value) const { |
ASSERT(this->raw() == value.clazz()); |
- const Array& constants = Array::Handle(zone, this->constants()); |
- const intptr_t constants_len = constants.Length(); |
- // Linear search to see whether this value is already present in the |
- // list of canonicalized constants. |
Instance& canonical_value = Instance::Handle(zone); |
- while (*index < constants_len) { |
- canonical_value ^= constants.At(*index); |
- if (canonical_value.IsNull()) { |
- break; |
- } |
- if (value.CanonicalizeEquals(canonical_value)) { |
- ASSERT(canonical_value.IsCanonical()); |
- return canonical_value.raw(); |
- } |
- *index = *index + 1; |
+ if (this->constants() != Object::empty_array().raw()) { |
+ CanonicalInstancesSet constants(zone, this->constants()); |
+ canonical_value ^= constants.GetOrNull(CanonicalInstanceKey(value)); |
+ this->set_constants(constants.Release()); |
} |
- return Instance::null(); |
+ return canonical_value.raw(); |
} |
-void Class::InsertCanonicalConstant(intptr_t index, |
- const Instance& constant) const { |
- // The constant needs to be added to the list. Grow the list if it is full. |
- Array& canonical_list = Array::Handle(constants()); |
- const intptr_t list_len = canonical_list.Length(); |
- if (index >= list_len) { |
- const intptr_t new_length = (list_len == 0) ? 4 : list_len + 4; |
- const Array& new_canonical_list = |
- Array::Handle(Array::Grow(canonical_list, new_length, Heap::kOld)); |
- set_constants(new_canonical_list); |
- new_canonical_list.SetAt(index, constant); |
+RawInstance* Class::InsertCanonicalConstant(Zone* zone, |
+ const Instance& constant) const { |
+ ASSERT(this->raw() == constant.clazz()); |
+ Instance& canonical_value = Instance::Handle(zone); |
+ if (this->constants() == Object::empty_array().raw()) { |
+ CanonicalInstancesSet constants( |
+ HashTables::New<CanonicalInstancesSet>(128, Heap::kOld)); |
+ canonical_value ^= constants.InsertNewOrGet(CanonicalInstanceKey(constant)); |
+ this->set_constants(constants.Release()); |
} else { |
- canonical_list.SetAt(index, constant); |
+ CanonicalInstancesSet constants(Thread::Current()->zone(), |
+ this->constants()); |
+ canonical_value ^= constants.InsertNewOrGet(CanonicalInstanceKey(constant)); |
+ this->set_constants(constants.Release()); |
} |
+ return canonical_value.raw(); |
} |
@@ -14753,8 +14805,13 @@ bool Instance::CanonicalizeEquals(const Instance& other) const { |
{ |
NoSafepointScope no_safepoint; |
// Raw bits compare. |
- const intptr_t instance_size = Class::Handle(this->clazz()).instance_size(); |
+ const intptr_t instance_size = SizeFromClass(); |
ASSERT(instance_size != 0); |
+ const intptr_t other_instance_size = other.SizeFromClass(); |
+ ASSERT(other_instance_size != 0); |
+ if (instance_size != other_instance_size) { |
+ return false; |
+ } |
uword this_addr = reinterpret_cast<uword>(this->raw_ptr()); |
uword other_addr = reinterpret_cast<uword>(other.raw_ptr()); |
for (intptr_t offset = Instance::NextFieldOffset(); |
@@ -14770,6 +14827,24 @@ bool Instance::CanonicalizeEquals(const Instance& other) const { |
} |
+uword Instance::ComputeCanonicalTableHash() const { |
+ ASSERT(!IsNull()); |
+ NoSafepointScope no_safepoint; |
+ const intptr_t instance_size = SizeFromClass(); |
+ ASSERT(instance_size != 0); |
+ uword hash = instance_size; |
+ uword this_addr = reinterpret_cast<uword>(this->raw_ptr()); |
+ for (intptr_t offset = Instance::NextFieldOffset(); |
+ offset < instance_size; |
+ offset += kWordSize) { |
+ uword value = reinterpret_cast<uword>( |
rmacnak
2016/04/15 18:19:18
This won't work for precompiled or app snapshots,
|
+ *reinterpret_cast<RawObject**>(this_addr + offset)); |
+ hash = CombineHashes(hash, value); |
+ } |
+ return FinalizeHash(hash); |
+} |
+ |
+ |
#if defined(DEBUG) |
class CheckForPointers : public ObjectPointerVisitor { |
public: |
@@ -14792,21 +14867,21 @@ class CheckForPointers : public ObjectPointerVisitor { |
#endif // DEBUG |
-bool Instance::CheckAndCanonicalizeFields(Zone* zone, |
+bool Instance::CheckAndCanonicalizeFields(Thread* thread, |
const char** error_str) const { |
- const Class& cls = Class::Handle(zone, this->clazz()); |
- if (cls.id() >= kNumPredefinedCids) { |
+ if (GetClassId() >= kNumPredefinedCids) { |
// Iterate over all fields, canonicalize numbers and strings, expect all |
// other instances to be canonical otherwise report error (return false). |
+ Zone* zone = thread->zone(); |
Object& obj = Object::Handle(zone); |
- intptr_t end_field_offset = cls.instance_size() - kWordSize; |
+ intptr_t end_field_offset = SizeFromClass() - kWordSize; |
for (intptr_t field_offset = 0; |
field_offset <= end_field_offset; |
field_offset += kWordSize) { |
obj = *this->FieldAddrAtOffset(field_offset); |
if (obj.IsInstance() && !obj.IsSmi() && !obj.IsCanonical()) { |
if (obj.IsNumber() || obj.IsString()) { |
- obj = Instance::Cast(obj).CheckAndCanonicalize(NULL); |
+ obj = Instance::Cast(obj).CheckAndCanonicalize(thread, NULL); |
ASSERT(!obj.IsNull()); |
this->SetFieldAtOffset(field_offset, obj); |
} else { |
@@ -14829,46 +14904,35 @@ bool Instance::CheckAndCanonicalizeFields(Zone* zone, |
} |
-RawInstance* Instance::CheckAndCanonicalize(const char** error_str) const { |
+RawInstance* Instance::CheckAndCanonicalize(Thread* thread, |
+ const char** error_str) const { |
ASSERT(!IsNull()); |
if (this->IsCanonical()) { |
return this->raw(); |
} |
- Thread* thread = Thread::Current(); |
- Zone* zone = thread->zone(); |
- if (!CheckAndCanonicalizeFields(zone, error_str)) { |
+ if (!CheckAndCanonicalizeFields(thread, error_str)) { |
return Instance::null(); |
} |
+ Zone* zone = thread->zone(); |
Isolate* isolate = thread->isolate(); |
Instance& result = Instance::Handle(zone); |
const Class& cls = Class::Handle(zone, this->clazz()); |
- intptr_t index = 0; |
- result ^= cls.LookupCanonicalInstance(zone, *this, &index); |
- if (!result.IsNull()) { |
- return result.raw(); |
- } |
{ |
SafepointMutexLocker ml(isolate->constant_canonicalization_mutex()); |
- // Retry lookup. |
- { |
- result ^= cls.LookupCanonicalInstance(zone, *this, &index); |
+ if (IsNew()) { |
+ result ^= cls.LookupCanonicalInstance(zone, *this); |
if (!result.IsNull()) { |
return result.raw(); |
} |
- } |
- |
- // The value needs to be added to the list. Grow the list if |
- // it is full. |
- result ^= this->raw(); |
- ASSERT((isolate == Dart::vm_isolate()) || !result.InVMHeap()); |
- if (result.IsNew()) { |
+ ASSERT((isolate == Dart::vm_isolate()) || !InVMHeap()); |
// Create a canonical object in old space. |
- result ^= Object::Clone(result, Heap::kOld); |
+ result ^= Object::Clone(*this, Heap::kOld); |
+ } else { |
+ result ^= this->raw(); |
} |
ASSERT(result.IsOld()); |
result.SetCanonical(); |
- cls.InsertCanonicalConstant(index, result); |
- return result.raw(); |
+ return cls.InsertCanonicalConstant(zone, result); |
} |
} |
@@ -17487,7 +17551,8 @@ RawMixinAppType* MixinAppType::New(const AbstractType& super_type, |
} |
-RawInstance* Number::CheckAndCanonicalize(const char** error_str) const { |
+RawInstance* Number::CheckAndCanonicalize(Thread* thread, |
+ const char** error_str) const { |
intptr_t cid = GetClassId(); |
switch (cid) { |
case kSmiCid: |
@@ -17497,10 +17562,9 @@ RawInstance* Number::CheckAndCanonicalize(const char** error_str) const { |
case kDoubleCid: |
return Double::NewCanonical(Double::Cast(*this).value()); |
case kBigintCid: { |
- Thread* thread = Thread::Current(); |
Zone* zone = thread->zone(); |
Isolate* isolate = thread->isolate(); |
- if (!CheckAndCanonicalizeFields(zone, error_str)) { |
+ if (!CheckAndCanonicalizeFields(thread, error_str)) { |
return Instance::null(); |
} |
Bigint& result = Bigint::Handle(zone); |
@@ -18253,15 +18317,16 @@ bool Bigint::Equals(const Instance& other) const { |
} |
-bool Bigint::CheckAndCanonicalizeFields(Zone* zone, |
+bool Bigint::CheckAndCanonicalizeFields(Thread* thread, |
const char** error_str) const { |
+ Zone* zone = thread->zone(); |
// Bool field neg should always be canonical. |
ASSERT(Bool::Handle(zone, neg()).IsCanonical()); |
// Smi field used is canonical by definition. |
if (Used() > 0) { |
// Canonicalize TypedData field digits. |
TypedData& digits_ = TypedData::Handle(zone, digits()); |
- digits_ ^= digits_.CheckAndCanonicalize(NULL); |
+ digits_ ^= digits_.CheckAndCanonicalize(thread, NULL); |
ASSERT(!digits_.IsNull()); |
set_digits(digits_); |
} else { |
@@ -19233,7 +19298,8 @@ bool String::StartsWith(const String& other) const { |
} |
-RawInstance* String::CheckAndCanonicalize(const char** error_str) const { |
+RawInstance* String::CheckAndCanonicalize(Thread* thread, |
+ const char** error_str) const { |
if (IsCanonical()) { |
return this->raw(); |
} |
@@ -20690,6 +20756,9 @@ bool Array::CanonicalizeEquals(const Instance& other) const { |
} |
// Now check if both arrays have the same type arguments. |
+ if (GetTypeArguments() == other.GetTypeArguments()) { |
+ return true; |
+ } |
const TypeArguments& type_args = TypeArguments::Handle(GetTypeArguments()); |
const TypeArguments& other_type_args = TypeArguments::Handle( |
other.GetTypeArguments()); |
@@ -20700,6 +20769,20 @@ bool Array::CanonicalizeEquals(const Instance& other) const { |
} |
+uword Array::ComputeCanonicalTableHash() const { |
+ ASSERT(!IsNull()); |
+ intptr_t len = Length(); |
+ uword hash = len; |
+ uword value = reinterpret_cast<uword>(GetTypeArguments()); |
+ hash = CombineHashes(hash, value); |
+ for (intptr_t i = 0; i < len; i++) { |
+ value = reinterpret_cast<uword>(At(i)); |
+ hash = CombineHashes(hash, value); |
+ } |
+ return FinalizeHash(hash); |
+} |
+ |
+ |
RawArray* Array::New(intptr_t len, Heap::Space space) { |
ASSERT(Isolate::Current()->object_store()->array_class() != Class::null()); |
return New(kClassId, len, space); |
@@ -20841,24 +20924,28 @@ RawArray* Array::MakeArray(const GrowableObjectArray& growable_array) { |
} |
-bool Array::CheckAndCanonicalizeFields(Zone* zone, |
+bool Array::CheckAndCanonicalizeFields(Thread* thread, |
const char** error_str) const { |
- Object& obj = Object::Handle(zone); |
- // Iterate over all elements, canonicalize numbers and strings, expect all |
- // other instances to be canonical otherwise report error (return false). |
- for (intptr_t i = 0; i < Length(); i++) { |
- obj = At(i); |
- if (obj.IsInstance() && !obj.IsSmi() && !obj.IsCanonical()) { |
- if (obj.IsNumber() || obj.IsString()) { |
- obj = Instance::Cast(obj).CheckAndCanonicalize(NULL); |
- ASSERT(!obj.IsNull()); |
- this->SetAt(i, obj); |
- } else { |
- ASSERT(error_str != NULL); |
- char* chars = OS::SCreate(Thread::Current()->zone(), |
- "element at index %" Pd ": %s\n", i, obj.ToCString()); |
- *error_str = chars; |
- return false; |
+ intptr_t len = Length(); |
+ if (len > 0) { |
+ Zone* zone = thread->zone(); |
+ Object& obj = Object::Handle(zone); |
+ // Iterate over all elements, canonicalize numbers and strings, expect all |
+ // other instances to be canonical otherwise report error (return false). |
+ for (intptr_t i = 0; i < len; i++) { |
+ obj = At(i); |
+ if (obj.IsInstance() && !obj.IsSmi() && !obj.IsCanonical()) { |
+ if (obj.IsNumber() || obj.IsString()) { |
+ obj = Instance::Cast(obj).CheckAndCanonicalize(thread, NULL); |
+ ASSERT(!obj.IsNull()); |
+ this->SetAt(i, obj); |
+ } else { |
+ ASSERT(error_str != NULL); |
+ char* chars = OS::SCreate( |
+ zone, "element at index %" Pd ": %s\n", i, obj.ToCString()); |
+ *error_str = chars; |
+ return false; |
+ } |
} |
} |
} |
@@ -21342,6 +21429,17 @@ bool TypedData::CanonicalizeEquals(const Instance& other) const { |
} |
+uword TypedData::ComputeCanonicalTableHash() const { |
+ const intptr_t len = this->LengthInBytes(); |
+ ASSERT(len != 0); |
+ uword hash = len; |
+ for (intptr_t i = 0; i < len; i++) { |
+ hash = CombineHashes(len, GetUint8(i)); |
+ } |
+ return FinalizeHash(hash); |
+} |
+ |
+ |
RawTypedData* TypedData::New(intptr_t class_id, |
intptr_t len, |
Heap::Space space) { |