Commit 0e21a147 authored by Toon Verwaest's avatar Toon Verwaest Committed by Commit Bot

[runtime] Feed back normalization to constructors (behind flag)

When an instance of a constructor goes dictionary mode, this changes the initial map
of that constructor to also be in dictionary mode. This avoids spurious hidden class
creation, that also results in IC misses.

BUG=

Change-Id: I0e70f822ac345d0224f2092ec473621a603d4cc5
Reviewed-on: https://chromium-review.googlesource.com/446361Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43452}
parent 97608517
...@@ -236,22 +236,27 @@ Node* ConstructorBuiltinsAssembler::EmitFastNewObject( ...@@ -236,22 +236,27 @@ Node* ConstructorBuiltinsAssembler::EmitFastNewObject(
LoadObjectField(initial_map, Map::kConstructorOrBackPointerOffset); LoadObjectField(initial_map, Map::kConstructorOrBackPointerOffset);
GotoIf(WordNotEqual(target, new_target_constructor), call_runtime); GotoIf(WordNotEqual(target, new_target_constructor), call_runtime);
Node* instance_size_words = ChangeUint32ToWord(LoadObjectField( Variable properties(this, MachineRepresentation::kTagged);
initial_map, Map::kInstanceSizeOffset, MachineType::Uint8()));
Node* instance_size = Label instantiate_map(this), allocate_properties(this);
WordShl(instance_size_words, IntPtrConstant(kPointerSizeLog2)); GotoIf(IsDictionaryMap(initial_map), &allocate_properties);
{
properties.Bind(EmptyFixedArrayConstant());
Goto(&instantiate_map);
}
Bind(&allocate_properties);
{
properties.Bind(AllocateNameDictionary(NameDictionary::kInitialCapacity));
Goto(&instantiate_map);
}
Node* object = Allocate(instance_size); Bind(&instantiate_map);
StoreMapNoWriteBarrier(object, initial_map);
Node* empty_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
StoreObjectFieldNoWriteBarrier(object, JSObject::kPropertiesOffset,
empty_array);
StoreObjectFieldNoWriteBarrier(object, JSObject::kElementsOffset,
empty_array);
instance_size_words = ChangeUint32ToWord(LoadObjectField( Node* object = AllocateJSObjectFromMap(initial_map, properties.value());
Node* instance_size_words = ChangeUint32ToWord(LoadObjectField(
initial_map, Map::kInstanceSizeOffset, MachineType::Uint8())); initial_map, Map::kInstanceSizeOffset, MachineType::Uint8()));
instance_size = Node* instance_size =
WordShl(instance_size_words, IntPtrConstant(kPointerSizeLog2)); WordShl(instance_size_words, IntPtrConstant(kPointerSizeLog2));
// Perform in-object slack tracking if requested. // Perform in-object slack tracking if requested.
......
...@@ -120,6 +120,7 @@ Node* GetArgumentsFrameState(Node* frame_state) { ...@@ -120,6 +120,7 @@ Node* GetArgumentsFrameState(Node* frame_state) {
bool IsAllocationInlineable(Handle<JSFunction> target, bool IsAllocationInlineable(Handle<JSFunction> target,
Handle<JSFunction> new_target) { Handle<JSFunction> new_target) {
return new_target->has_initial_map() && return new_target->has_initial_map() &&
!new_target->initial_map()->is_dictionary_map() &&
new_target->initial_map()->constructor_or_backpointer() == *target; new_target->initial_map()->constructor_or_backpointer() == *target;
} }
......
...@@ -9744,6 +9744,7 @@ bool HOptimizedGraphBuilder::TryInlineArrayCall(Expression* expression, ...@@ -9744,6 +9744,7 @@ bool HOptimizedGraphBuilder::TryInlineArrayCall(Expression* expression,
static bool IsAllocationInlineable(Handle<JSFunction> constructor) { static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
return constructor->has_initial_map() && return constructor->has_initial_map() &&
!IsDerivedConstructor(constructor->shared()->kind()) && !IsDerivedConstructor(constructor->shared()->kind()) &&
!constructor->initial_map()->is_dictionary_map() &&
constructor->initial_map()->instance_type() == JS_OBJECT_TYPE && constructor->initial_map()->instance_type() == JS_OBJECT_TYPE &&
constructor->initial_map()->instance_size() < constructor->initial_map()->instance_size() <
HAllocate::kMaxInlineSize; HAllocate::kMaxInlineSize;
......
...@@ -293,6 +293,8 @@ DEFINE_BOOL(track_field_types, true, "track field types") ...@@ -293,6 +293,8 @@ DEFINE_BOOL(track_field_types, true, "track field types")
DEFINE_IMPLICATION(track_field_types, track_fields) DEFINE_IMPLICATION(track_field_types, track_fields)
DEFINE_IMPLICATION(track_field_types, track_heap_object_fields) DEFINE_IMPLICATION(track_field_types, track_heap_object_fields)
DEFINE_BOOL(type_profile, false, "collect type information") DEFINE_BOOL(type_profile, false, "collect type information")
DEFINE_BOOL(feedback_normalization, false,
"feed back normalization to constructors")
// Flags for optimization types. // Flags for optimization types.
DEFINE_BOOL(optimize_for_size, false, DEFINE_BOOL(optimize_for_size, false,
......
...@@ -286,7 +286,9 @@ void LookupIterator::ReconfigureDataProperty(Handle<Object> value, ...@@ -286,7 +286,9 @@ void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
kMutable, value); kMutable, value);
JSObject::MigrateToMap(holder, new_map); JSObject::MigrateToMap(holder, new_map);
ReloadPropertyInformation<false>(); ReloadPropertyInformation<false>();
} else { }
if (!IsElement() && !holder->HasFastProperties()) {
PropertyDetails details(kData, attributes, 0, PropertyCellType::kMutable); PropertyDetails details(kData, attributes, 0, PropertyCellType::kMutable);
if (holder->IsJSGlobalObject()) { if (holder->IsJSGlobalObject()) {
Handle<GlobalDictionary> dictionary(holder->global_dictionary()); Handle<GlobalDictionary> dictionary(holder->global_dictionary());
......
...@@ -219,13 +219,23 @@ MapUpdater::State MapUpdater::FindRootMap() { ...@@ -219,13 +219,23 @@ MapUpdater::State MapUpdater::FindRootMap() {
DCHECK_EQ(kInitialized, state_); DCHECK_EQ(kInitialized, state_);
// Check the state of the root map. // Check the state of the root map.
root_map_ = handle(old_map_->FindRootMap(), isolate_); root_map_ = handle(old_map_->FindRootMap(), isolate_);
ElementsKind from_kind = root_map_->elements_kind();
ElementsKind to_kind = new_elements_kind_;
if (root_map_->is_deprecated()) {
state_ = kEnd;
result_map_ = handle(
JSFunction::cast(root_map_->GetConstructor())->initial_map(), isolate_);
if (from_kind != to_kind) {
result_map_ = Map::AsElementsKind(result_map_, to_kind);
}
DCHECK(result_map_->is_dictionary_map());
return state_;
}
int root_nof = root_map_->NumberOfOwnDescriptors(); int root_nof = root_map_->NumberOfOwnDescriptors();
if (!old_map_->EquivalentToForTransition(*root_map_)) { if (!old_map_->EquivalentToForTransition(*root_map_)) {
return CopyGeneralizeAllFields("GenAll_NotEquivalent"); return CopyGeneralizeAllFields("GenAll_NotEquivalent");
} }
ElementsKind from_kind = root_map_->elements_kind();
ElementsKind to_kind = new_elements_kind_;
// TODO(ishell): Add a test for SLOW_SLOPPY_ARGUMENTS_ELEMENTS. // TODO(ishell): Add a test for SLOW_SLOPPY_ARGUMENTS_ELEMENTS.
if (from_kind != to_kind && to_kind != DICTIONARY_ELEMENTS && if (from_kind != to_kind && to_kind != DICTIONARY_ELEMENTS &&
to_kind != SLOW_STRING_WRAPPER_ELEMENTS && to_kind != SLOW_STRING_WRAPPER_ELEMENTS &&
......
...@@ -1274,6 +1274,11 @@ MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor, ...@@ -1274,6 +1274,11 @@ MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor,
JSFunction::GetDerivedMap(isolate, constructor, new_target), JSObject); JSFunction::GetDerivedMap(isolate, constructor, new_target), JSObject);
Handle<JSObject> result = Handle<JSObject> result =
isolate->factory()->NewJSObjectFromMap(initial_map, NOT_TENURED, site); isolate->factory()->NewJSObjectFromMap(initial_map, NOT_TENURED, site);
if (initial_map->is_dictionary_map()) {
Handle<NameDictionary> dictionary =
NameDictionary::New(isolate, NameDictionary::kInitialCapacity);
result->set_properties(*dictionary);
}
isolate->counters()->constructed_objects()->Increment(); isolate->counters()->constructed_objects()->Increment();
isolate->counters()->constructed_objects_runtime()->Increment(); isolate->counters()->constructed_objects_runtime()->Increment();
return result; return result;
...@@ -2867,6 +2872,10 @@ void Map::PrintGeneralization( ...@@ -2867,6 +2872,10 @@ void Map::PrintGeneralization(
void JSObject::PrintInstanceMigration(FILE* file, void JSObject::PrintInstanceMigration(FILE* file,
Map* original_map, Map* original_map,
Map* new_map) { Map* new_map) {
if (new_map->is_dictionary_map()) {
PrintF(file, "[migrating to slow]\n");
return;
}
PrintF(file, "[migrating]"); PrintF(file, "[migrating]");
DescriptorArray* o = original_map->instance_descriptors(); DescriptorArray* o = original_map->instance_descriptors();
DescriptorArray* n = new_map->instance_descriptors(); DescriptorArray* n = new_map->instance_descriptors();
...@@ -2887,6 +2896,10 @@ void JSObject::PrintInstanceMigration(FILE* file, ...@@ -2887,6 +2896,10 @@ void JSObject::PrintInstanceMigration(FILE* file,
PrintF(file, " "); PrintF(file, " ");
} }
} }
if (original_map->elements_kind() != new_map->elements_kind()) {
PrintF(file, "elements_kind[%i->%i]", original_map->elements_kind(),
new_map->elements_kind());
}
PrintF(file, "\n"); PrintF(file, "\n");
} }
...@@ -4108,6 +4121,16 @@ MaybeHandle<Map> Map::TryUpdate(Handle<Map> old_map) { ...@@ -4108,6 +4121,16 @@ MaybeHandle<Map> Map::TryUpdate(Handle<Map> old_map) {
// Check the state of the root map. // Check the state of the root map.
Map* root_map = old_map->FindRootMap(); Map* root_map = old_map->FindRootMap();
if (root_map->is_deprecated()) {
JSFunction* constructor = JSFunction::cast(root_map->GetConstructor());
DCHECK(constructor->has_initial_map());
DCHECK(constructor->initial_map()->is_dictionary_map());
if (constructor->initial_map()->elements_kind() !=
old_map->elements_kind()) {
return MaybeHandle<Map>();
}
return handle(constructor->initial_map());
}
if (!old_map->EquivalentToForTransition(root_map)) return MaybeHandle<Map>(); if (!old_map->EquivalentToForTransition(root_map)) return MaybeHandle<Map>();
ElementsKind from_kind = root_map->elements_kind(); ElementsKind from_kind = root_map->elements_kind();
...@@ -5319,7 +5342,7 @@ bool JSObject::TryMigrateInstance(Handle<JSObject> object) { ...@@ -5319,7 +5342,7 @@ bool JSObject::TryMigrateInstance(Handle<JSObject> object) {
return false; return false;
} }
JSObject::MigrateToMap(object, new_map); JSObject::MigrateToMap(object, new_map);
if (FLAG_trace_migration) { if (FLAG_trace_migration && *original_map != object->map()) {
object->PrintInstanceMigration(stdout, *original_map, object->map()); object->PrintInstanceMigration(stdout, *original_map, object->map());
} }
#if VERIFY_HEAP #if VERIFY_HEAP
...@@ -9159,17 +9182,41 @@ Handle<Map> Map::TransitionToDataProperty(Handle<Map> map, Handle<Name> name, ...@@ -9159,17 +9182,41 @@ Handle<Map> Map::TransitionToDataProperty(Handle<Map> map, Handle<Name> name,
Handle<Map> result; Handle<Map> result;
if (!maybe_map.ToHandle(&result)) { if (!maybe_map.ToHandle(&result)) {
Isolate* isolate = name->GetIsolate();
const char* reason = "TooManyFastProperties";
#if TRACE_MAPS #if TRACE_MAPS
std::unique_ptr<ScopedVector<char>> buffer;
if (FLAG_trace_maps) { if (FLAG_trace_maps) {
Vector<char> name_buffer = Vector<char>::New(100); ScopedVector<char> name_buffer(100);
name->NameShortPrint(name_buffer); name->NameShortPrint(name_buffer);
Vector<char> buffer = Vector<char>::New(128); buffer.reset(new ScopedVector<char>(128));
SNPrintF(buffer, "TooManyFastProperties %s", name_buffer.start()); SNPrintF(*buffer, "TooManyFastProperties %s", name_buffer.start());
return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES, buffer.start()); reason = buffer->start();
} }
#endif #endif
return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES, Handle<Map> result;
"TooManyFastProperties"); Handle<JSFunction> constructor(JSFunction::cast(map->GetConstructor()));
if (FLAG_feedback_normalization && map->new_target_is_base() &&
!constructor->shared()->native()) {
DCHECK_NE(*constructor,
constructor->context()->native_context()->object_function());
Handle<Map> initial_map(constructor->initial_map(), isolate);
result = Map::Normalize(initial_map, CLEAR_INOBJECT_PROPERTIES, reason);
initial_map->DeprecateTransitionTree();
Handle<Object> prototype(result->prototype(), isolate);
JSFunction::SetInitialMap(constructor, result, prototype);
// Deoptimize all code that embeds the previous initial map.
initial_map->dependent_code()->DeoptimizeDependentCodeGroup(
isolate, DependentCode::kInitialMapChangedGroup);
if (!result->EquivalentToForNormalization(*map,
CLEAR_INOBJECT_PROPERTIES)) {
result = Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES, reason);
}
} else {
result = Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES, reason);
}
return result;
} }
return result; return result;
......
...@@ -599,6 +599,9 @@ static void TestClassHierarchy(const std::vector<int>& hierarchy_desc, int n) { ...@@ -599,6 +599,9 @@ static void TestClassHierarchy(const std::vector<int>& hierarchy_desc, int n) {
CHECK(func->has_initial_map()); CHECK(func->has_initial_map());
Handle<Map> initial_map(func->initial_map()); Handle<Map> initial_map(func->initial_map());
// If the object is slow-mode already, bail out.
if (obj->map()->is_dictionary_map()) continue;
// There must be at least some slack. // There must be at least some slack.
CHECK_LT(fields_count, obj->map()->GetInObjectProperties()); CHECK_LT(fields_count, obj->map()->GetInObjectProperties());
......
...@@ -67,7 +67,7 @@ function test(use_new, add_first, set__proto__) { ...@@ -67,7 +67,7 @@ function test(use_new, add_first, set__proto__) {
var proto = use_new ? new Super() : {}; var proto = use_new ? new Super() : {};
// New object is fast. // New object is fast.
assertTrue(%HasFastProperties(proto)); assertTrue(use_new || %HasFastProperties(proto));
if (add_first) { if (add_first) {
AddProps(proto); AddProps(proto);
......
...@@ -25,7 +25,7 @@ function __f_0(__v_1, __v_6) { ...@@ -25,7 +25,7 @@ function __f_0(__v_1, __v_6) {
%DebugPrint(undefined); %DebugPrint(undefined);
function __f_1(__v_4, add_first, __v_6, same_map_as) { function __f_1(__v_4, add_first, __v_6, same_map_as) {
var __v_1 = __v_4 ? new __f_3() : {}; var __v_1 = __v_4 ? new __f_3() : {};
assertTrue(%HasFastProperties(__v_1)); assertTrue(__v_4 || %HasFastProperties(__v_1));
if (add_first) { if (add_first) {
__f_4(__v_1); __f_4(__v_1);
assertFalse(%HasFastProperties(__v_1)); assertFalse(%HasFastProperties(__v_1));
...@@ -33,9 +33,9 @@ function __f_1(__v_4, add_first, __v_6, same_map_as) { ...@@ -33,9 +33,9 @@ function __f_1(__v_4, add_first, __v_6, same_map_as) {
assertFalse(%HasFastProperties(__v_1)); assertFalse(%HasFastProperties(__v_1));
} else { } else {
__f_0(__v_1, __v_6); __f_0(__v_1, __v_6);
assertTrue(%HasFastProperties(__v_1)); assertTrue(__v_4 || %HasFastProperties(__v_1));
__f_4(__v_1); __f_4(__v_1);
assertTrue(%HasFastProperties(__v_1)); assertTrue(__v_4 || %HasFastProperties(__v_1));
} }
} }
gc(); gc();
......
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