[runtime] Do not clear prototype map descriptors.

Mutating the descriptor array and the layout descriptor of a map
races with the concurrent marking. This patch simply transfers
ownership of the descriptor array without mutating the map.

Since the old map is not going to be used anymore and there are
not transitions from the old map, this should be safe for trimming
the descriptor arrays during GC.

This patch also adds checks in IC code avoid caching of dummy
transitions from the abandoned prototype map.

Bug: chromium:752461
Change-Id: I7b44ba7c369199bdb3ff48235226fe504c7eb4a5
Reviewed-on: https://chromium-review.googlesource.com/602210
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47275}
This commit is contained in:
Ulan Degenbaev 2017-08-10 10:12:27 +02:00 committed by Commit Bot
parent 80423e89b1
commit d09f9c424f
5 changed files with 30 additions and 12 deletions

View File

@ -2890,6 +2890,8 @@ void MarkCompactCollector::ClearSimpleMapTransition(
void MarkCompactCollector::ClearSimpleMapTransition(Map* map,
Map* dead_target) {
DCHECK(!map->is_prototype_map());
DCHECK(!dead_target->is_prototype_map());
// Clear the useless weak cell pointer, and take ownership of the descriptor
// array.
map->set_raw_transitions(Smi::kZero);
@ -2931,6 +2933,7 @@ void MarkCompactCollector::ClearFullMapTransitions() {
bool MarkCompactCollector::CompactTransitionArray(
Map* map, TransitionArray* transitions, DescriptorArray* descriptors) {
DCHECK(!map->is_prototype_map());
int num_transitions = transitions->number_of_entries();
bool descriptors_owner_died = false;
int transition_index = 0;
@ -2941,6 +2944,7 @@ bool MarkCompactCollector::CompactTransitionArray(
if (ObjectMarking::IsWhite(target, MarkingState::Internal(target))) {
if (descriptors != nullptr &&
target->instance_descriptors() == descriptors) {
DCHECK(!target->is_prototype_map());
descriptors_owner_died = true;
}
} else {

View File

@ -713,8 +713,9 @@ void IC::CopyICToMegamorphicCache(Handle<Name> name) {
bool IC::IsTransitionOfMonomorphicTarget(Map* source_map, Map* target_map) {
if (source_map == NULL) return true;
if (target_map == NULL) return false;
if (source_map == nullptr) return true;
if (target_map == nullptr) return false;
if (source_map->is_abandoned_prototype_map()) return false;
ElementsKind target_elements_kind = target_map->elements_kind();
bool more_general_transition = IsMoreGeneralElementsKindTransition(
source_map->elements_kind(), target_elements_kind);
@ -2331,11 +2332,14 @@ MaybeHandle<Object> KeyedStoreIC::Store(Handle<Object> object,
if (is_arguments) {
TRACE_GENERIC_IC("arguments receiver");
} else if (key_is_valid_index) {
// We should go generic if receiver isn't a dictionary, but our
// prototype chain does have dictionary elements. This ensures that
// other non-dictionary receivers in the polymorphic case benefit
// from fast path keyed stores.
if (!old_receiver_map->DictionaryElementsInPrototypeChainOnly()) {
if (old_receiver_map->is_abandoned_prototype_map()) {
TRACE_GENERIC_IC("receiver with prototype map");
} else if (!old_receiver_map
->DictionaryElementsInPrototypeChainOnly()) {
// We should go generic if receiver isn't a dictionary, but our
// prototype chain does have dictionary elements. This ensures that
// other non-dictionary receivers in the polymorphic case benefit
// from fast path keyed stores.
UpdateStoreElement(old_receiver_map, store_mode);
} else {
TRACE_GENERIC_IC("dictionary or proxy prototype");

View File

@ -3403,6 +3403,10 @@ bool Map::is_prototype_map() const {
return IsPrototypeMapBits::decode(bit_field2());
}
bool Map::is_abandoned_prototype_map() const {
return is_prototype_map() && !owns_descriptors();
}
bool Map::should_be_fast_prototype_map() const {
if (!prototype_info()->IsPrototypeInfo()) return false;
return PrototypeInfo::cast(prototype_info())->should_be_fast_map();

View File

@ -4177,14 +4177,17 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
if (old_map->is_prototype_map()) {
DCHECK(!old_map->is_stable());
DCHECK(new_map->is_stable());
// Clear out the old descriptor array to avoid problems to sharing
// the descriptor array without using an explicit.
old_map->InitializeDescriptors(
old_map->GetHeap()->empty_descriptor_array(),
LayoutDescriptor::FastPointerLayout());
DCHECK(new_map->owns_descriptors());
DCHECK(old_map->owns_descriptors());
// Transfer ownership to the new map. Keep the descriptor pointer of the
// old map intact because the concurrent marker might be iterating the
// object with the old map.
old_map->set_owns_descriptors(false);
DCHECK(old_map->is_abandoned_prototype_map());
// Ensure that no transition was inserted for prototype migrations.
DCHECK_EQ(0, TransitionsAccessor(old_map).NumberOfTransitions());
DCHECK(new_map->GetBackPointer()->IsUndefined(new_map->GetIsolate()));
DCHECK(object->map() != *old_map);
}
} else {
MigrateFastToSlow(object, new_map, expected_additional_properties);
@ -5210,6 +5213,8 @@ Map* Map::FindElementsKindTransitionedMap(MapHandles const& candidates) {
DisallowHeapAllocation no_allocation;
DisallowDeoptimization no_deoptimization(GetIsolate());
if (is_prototype_map()) return nullptr;
ElementsKind kind = elements_kind();
bool packed = IsFastPackedElementsKind(kind);

View File

@ -258,6 +258,7 @@ class Map : public HeapObject {
inline bool is_extensible() const;
inline void set_is_prototype_map(bool value);
inline bool is_prototype_map() const;
inline bool is_abandoned_prototype_map() const;
inline void set_elements_kind(ElementsKind elements_kind);
inline ElementsKind elements_kind() const;