Commit d09f9c42 authored by Ulan Degenbaev's avatar Ulan Degenbaev Committed by Commit Bot

[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: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47275}
parent 80423e89
......@@ -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 {
......
......@@ -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");
......
......@@ -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();
......
......@@ -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);
......
......@@ -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;
......
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