Chromium Code Reviews| Index: runtime/vm/object.cc |
| diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc |
| index 5b358b5f2f5d96834d7a794622d46730f361069a..42e22beda686ac99372ffb070520c3610f4db4f1 100644 |
| --- a/runtime/vm/object.cc |
| +++ b/runtime/vm/object.cc |
| @@ -64,6 +64,7 @@ DEFINE_FLAG(bool, throw_on_javascript_int_overflow, false, |
| "fit into a javascript integer."); |
| DEFINE_FLAG(bool, use_field_guards, true, "Guard field cids."); |
| DEFINE_FLAG(bool, use_lib_cache, true, "Use library name cache"); |
| +DEFINE_FLAG(bool, trace_field_guards, false, "Trace changes in field's cids."); |
| DECLARE_FLAG(bool, enable_type_checks); |
| DECLARE_FLAG(bool, error_on_bad_override); |
| @@ -6774,6 +6775,7 @@ RawField* Field::New(const String& name, |
| result.set_is_unboxing_candidate(true); |
| result.set_guarded_cid(FLAG_use_field_guards ? kIllegalCid : kDynamicCid); |
| result.set_is_nullable(FLAG_use_field_guards ? false : true); |
| + result.set_guarded_list_length_in_object_offset(-1); |
|
Florian Schneider
2014/05/28 14:57:36
s/-1/kUnknownLengthOffset/
Vyacheslav Egorov (Google)
2014/05/29 17:36:51
Done.
|
| // Presently, we only attempt to remember the list length for final fields. |
| if (is_final && FLAG_use_field_guards) { |
| result.set_guarded_list_length(Field::kUnknownFixedLength); |
| @@ -6822,6 +6824,17 @@ void Field::set_guarded_list_length(intptr_t list_length) const { |
| } |
| +intptr_t Field::guarded_list_length_in_object_offset() const { |
| + return raw_ptr()->guarded_list_length_in_object_offset_; |
| +} |
| + |
| + |
| +void Field::set_guarded_list_length_in_object_offset( |
| + intptr_t list_length_offset) const { |
| + raw_ptr()->guarded_list_length_in_object_offset_ = list_length_offset; |
| +} |
| + |
| + |
| bool Field::IsUnboxedField() const { |
| bool valid_class = (FlowGraphCompiler::SupportsUnboxedDoubles() && |
| (guarded_cid() == kDoubleCid)) || |
| @@ -6983,9 +6996,6 @@ bool Field::IsUninitialized() const { |
| static intptr_t GetListLength(const Object& value) { |
| - const intptr_t cid = value.GetClassId(); |
| - ASSERT(RawObject::IsBuiltinListClassId(cid)); |
| - // Extract list length. |
| if (value.IsTypedData()) { |
| const TypedData& list = TypedData::Cast(value); |
| return list.Length(); |
| @@ -6998,41 +7008,98 @@ static intptr_t GetListLength(const Object& value) { |
| } else if (value.IsExternalTypedData()) { |
| // TODO(johnmccutchan): Enable for external typed data. |
| return Field::kNoFixedLength; |
| - } else if (RawObject::IsTypedDataViewClassId(cid)) { |
| + } else if (RawObject::IsTypedDataViewClassId(value.GetClassId())) { |
| // TODO(johnmccutchan): Enable for typed data views. |
| return Field::kNoFixedLength; |
| } |
| - UNIMPLEMENTED(); |
| return Field::kNoFixedLength; |
| } |
| -bool Field::UpdateGuardedCidAndLength(const Object& value) const { |
| - const intptr_t cid = value.GetClassId(); |
| - bool deoptimize = UpdateCid(cid); |
| - intptr_t list_length = Field::kNoFixedLength; |
| - if ((guarded_cid() != kDynamicCid) && |
| - is_final() && RawObject::IsBuiltinListClassId(cid)) { |
| - list_length = GetListLength(value); |
| - } |
| - deoptimize = UpdateLength(list_length) || deoptimize; |
| - if (deoptimize) { |
| - DeoptimizeDependentCode(); |
| +static intptr_t GetListLengthOffset(const Object& value) { |
| + if (value.IsTypedData()) { |
| + return TypedData::length_offset(); |
| + } else if (value.IsArray()) { |
| + return Array::length_offset(); |
| + } else if (value.IsGrowableObjectArray()) { |
| + // List length is variable. |
| + return Field::kUnknownLengthOffset; |
| + } else if (value.IsExternalTypedData()) { |
| + // TODO(johnmccutchan): Enable for external typed data. |
| + return Field::kUnknownLengthOffset; |
| + } else if (RawObject::IsTypedDataViewClassId(value.GetClassId())) { |
| + // TODO(johnmccutchan): Enable for typed data views. |
| + return Field::kUnknownLengthOffset; |
| } |
| - return deoptimize; |
| + return Field::kUnknownLengthOffset; |
| } |
| -bool Field::UpdateCid(intptr_t cid) const { |
| +const char* Field::GuardedPropertiesAsCString() const { |
| + if (guarded_cid() == kIllegalCid) { |
| + return "<?>"; |
| + } else if (guarded_cid() == kDynamicCid) { |
| + return "<*>"; |
| + } |
| + |
| + const Class& cls = Class::Handle( |
| + Isolate::Current()->class_table()->At(guarded_cid())); |
| + const char* class_name = String::Handle(cls.Name()).ToCString(); |
| + |
| + if (RawObject::IsBuiltinListClassId(guarded_cid()) && |
| + !is_nullable() && |
| + is_final()) { |
| + ASSERT(guarded_list_length() != kUnknownFixedLength); |
| + if (guarded_list_length() == kNoFixedLength) { |
| + return Isolate::Current()->current_zone()->PrintToString( |
| + "<%s [*]>", class_name); |
| + } else { |
| + return Isolate::Current()->current_zone()->PrintToString("<%s [%d @%d]>", |
| + class_name, |
| + guarded_list_length(), |
| + guarded_list_length_in_object_offset()); |
| + } |
| + } |
| + |
| + return Isolate::Current()->current_zone()->PrintToString("<%s %s>", |
| + is_nullable() ? "nullable" : "not-nullable", |
| + class_name); |
| +} |
| + |
| + |
| +bool Field::UpdateGuardedCidAndLength(const Object& value) const { |
| + const intptr_t cid = value.GetClassId(); |
| + |
| if (guarded_cid() == kIllegalCid) { |
| // Field is assigned first time. |
| set_guarded_cid(cid); |
| set_is_nullable(cid == kNullCid); |
| + |
| + // Start tracking length if needed. |
| + ASSERT((guarded_list_length() == Field::kUnknownFixedLength) || |
| + (guarded_list_length() == Field::kNoFixedLength)); |
| + if (needs_length_check()) { |
| + ASSERT(guarded_list_length() == Field::kUnknownFixedLength); |
| + set_guarded_list_length(GetListLength(value)); |
| + set_guarded_list_length_in_object_offset(GetListLengthOffset(value)); |
| + } |
| + |
| return false; |
| } |
| if ((cid == guarded_cid()) || ((cid == kNullCid) && is_nullable())) { |
| // Class id of the assigned value matches expected class id and nullability. |
| + |
| + // If we are tracking length check if it has matches. |
| + if (needs_length_check() && |
| + (guarded_list_length() != GetListLength(value))) { |
| + ASSERT(guarded_list_length() != Field::kUnknownFixedLength); |
| + set_guarded_list_length(Field::kNoFixedLength); |
| + set_guarded_list_length_in_object_offset(-1); |
|
Florian Schneider
2014/05/28 14:57:36
s/-1/kUnknownLengthOffset/
Vyacheslav Egorov (Google)
2014/05/29 17:36:51
Done.
|
| + return true; |
| + } |
| + |
| + // Everything matches. |
| return false; |
| } |
| @@ -7051,38 +7118,33 @@ bool Field::UpdateCid(intptr_t cid) const { |
| set_is_nullable(true); |
| } |
| + // If we were tracking length drop collected feedback. |
| + if (needs_length_check()) { |
| + ASSERT(guarded_list_length() != Field::kUnknownFixedLength); |
| + set_guarded_list_length(Field::kNoFixedLength); |
| + set_guarded_list_length_in_object_offset(-1); |
|
Florian Schneider
2014/05/28 14:57:36
s/-1/kUnknownLengthOffset/
Vyacheslav Egorov (Google)
2014/05/29 17:36:51
Done.
|
| + } |
| + |
| // Expected class id or nullability of the field changed. |
| return true; |
| } |
| -bool Field::UpdateLength(intptr_t list_length) const { |
| - ASSERT(is_final() || (!is_final() && |
| - (list_length < Field::kUnknownFixedLength))); |
| - ASSERT((list_length == Field::kNoFixedLength) || |
| - (list_length > Field::kUnknownFixedLength)); |
| - ASSERT(guarded_cid() != kIllegalCid); |
| - |
| - const bool force_invalidate = (guarded_cid() == kDynamicCid) && |
| - (list_length != Field::kNoFixedLength); |
| - |
| - const bool list_length_unknown = |
| - (guarded_list_length() == Field::kUnknownFixedLength); |
| - const bool list_length_changed = (guarded_list_length() != list_length); |
| - |
| - if (list_length_unknown && list_length_changed && !force_invalidate) { |
| - // List length set for first time. |
| - set_guarded_list_length(list_length); |
| - return false; |
| +void Field::RecordStore(const Object& value) const { |
| + if (FLAG_trace_field_guards) { |
| + OS::Print("Store %s %s <- %s\n", |
| + ToCString(), |
| + GuardedPropertiesAsCString(), |
| + value.ToCString()); |
| } |
| - if (!list_length_changed && !force_invalidate) { |
| - // List length unchanged. |
| - return false; |
| + if (UpdateGuardedCidAndLength(value)) { |
| + if (FLAG_trace_field_guards) { |
| + OS::Print(" => %s\n", GuardedPropertiesAsCString()); |
| + } |
| + |
| + DeoptimizeDependentCode(); |
| } |
| - // Multiple list lengths assigned here, stop tracking length. |
| - set_guarded_list_length(Field::kNoFixedLength); |
| - return true; |
| } |