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(
LoadObjectField(initial_map, Map::kConstructorOrBackPointerOffset);
GotoIf(WordNotEqual(target, new_target_constructor), call_runtime);
Node* instance_size_words = ChangeUint32ToWord(LoadObjectField(
initial_map, Map::kInstanceSizeOffset, MachineType::Uint8()));
Node* instance_size =
WordShl(instance_size_words, IntPtrConstant(kPointerSizeLog2));
Variable properties(this, MachineRepresentation::kTagged);
Label instantiate_map(this), allocate_properties(this);
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);
StoreMapNoWriteBarrier(object, initial_map);
Node* empty_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
StoreObjectFieldNoWriteBarrier(object, JSObject::kPropertiesOffset,
empty_array);
StoreObjectFieldNoWriteBarrier(object, JSObject::kElementsOffset,
empty_array);
Bind(&instantiate_map);
instance_size_words = ChangeUint32ToWord(LoadObjectField(
Node* object = AllocateJSObjectFromMap(initial_map, properties.value());
Node* instance_size_words = ChangeUint32ToWord(LoadObjectField(
initial_map, Map::kInstanceSizeOffset, MachineType::Uint8()));
instance_size =
Node* instance_size =
WordShl(instance_size_words, IntPtrConstant(kPointerSizeLog2));
// Perform in-object slack tracking if requested.
......
......@@ -120,6 +120,7 @@ Node* GetArgumentsFrameState(Node* frame_state) {
bool IsAllocationInlineable(Handle<JSFunction> target,
Handle<JSFunction> new_target) {
return new_target->has_initial_map() &&
!new_target->initial_map()->is_dictionary_map() &&
new_target->initial_map()->constructor_or_backpointer() == *target;
}
......
......@@ -9744,6 +9744,7 @@ bool HOptimizedGraphBuilder::TryInlineArrayCall(Expression* expression,
static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
return constructor->has_initial_map() &&
!IsDerivedConstructor(constructor->shared()->kind()) &&
!constructor->initial_map()->is_dictionary_map() &&
constructor->initial_map()->instance_type() == JS_OBJECT_TYPE &&
constructor->initial_map()->instance_size() <
HAllocate::kMaxInlineSize;
......
......@@ -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_heap_object_fields)
DEFINE_BOOL(type_profile, false, "collect type information")
DEFINE_BOOL(feedback_normalization, false,
"feed back normalization to constructors")
// Flags for optimization types.
DEFINE_BOOL(optimize_for_size, false,
......
......@@ -286,7 +286,9 @@ void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
kMutable, value);
JSObject::MigrateToMap(holder, new_map);
ReloadPropertyInformation<false>();
} else {
}
if (!IsElement() && !holder->HasFastProperties()) {
PropertyDetails details(kData, attributes, 0, PropertyCellType::kMutable);
if (holder->IsJSGlobalObject()) {
Handle<GlobalDictionary> dictionary(holder->global_dictionary());
......
......@@ -219,13 +219,23 @@ MapUpdater::State MapUpdater::FindRootMap() {
DCHECK_EQ(kInitialized, state_);
// Check the state of the root map.
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();
if (!old_map_->EquivalentToForTransition(*root_map_)) {
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.
if (from_kind != to_kind && to_kind != DICTIONARY_ELEMENTS &&
to_kind != SLOW_STRING_WRAPPER_ELEMENTS &&
......
......@@ -1274,6 +1274,11 @@ MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor,
JSFunction::GetDerivedMap(isolate, constructor, new_target), JSObject);
Handle<JSObject> result =
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_runtime()->Increment();
return result;
......@@ -2867,6 +2872,10 @@ void Map::PrintGeneralization(
void JSObject::PrintInstanceMigration(FILE* file,
Map* original_map,
Map* new_map) {
if (new_map->is_dictionary_map()) {
PrintF(file, "[migrating to slow]\n");
return;
}
PrintF(file, "[migrating]");
DescriptorArray* o = original_map->instance_descriptors();
DescriptorArray* n = new_map->instance_descriptors();
......@@ -2887,6 +2896,10 @@ void JSObject::PrintInstanceMigration(FILE* 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");
}
......@@ -4108,6 +4121,16 @@ MaybeHandle<Map> Map::TryUpdate(Handle<Map> old_map) {
// Check the state of the root map.
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>();
ElementsKind from_kind = root_map->elements_kind();
......@@ -5319,7 +5342,7 @@ bool JSObject::TryMigrateInstance(Handle<JSObject> object) {
return false;
}
JSObject::MigrateToMap(object, new_map);
if (FLAG_trace_migration) {
if (FLAG_trace_migration && *original_map != object->map()) {
object->PrintInstanceMigration(stdout, *original_map, object->map());
}
#if VERIFY_HEAP
......@@ -9159,17 +9182,41 @@ Handle<Map> Map::TransitionToDataProperty(Handle<Map> map, Handle<Name> name,
Handle<Map> result;
if (!maybe_map.ToHandle(&result)) {
Isolate* isolate = name->GetIsolate();
const char* reason = "TooManyFastProperties";
#if TRACE_MAPS
std::unique_ptr<ScopedVector<char>> buffer;
if (FLAG_trace_maps) {
Vector<char> name_buffer = Vector<char>::New(100);
ScopedVector<char> name_buffer(100);
name->NameShortPrint(name_buffer);
Vector<char> buffer = Vector<char>::New(128);
SNPrintF(buffer, "TooManyFastProperties %s", name_buffer.start());
return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES, buffer.start());
buffer.reset(new ScopedVector<char>(128));
SNPrintF(*buffer, "TooManyFastProperties %s", name_buffer.start());
reason = buffer->start();
}
#endif
return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES,
"TooManyFastProperties");
Handle<Map> result;
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;
......
......@@ -599,6 +599,9 @@ static void TestClassHierarchy(const std::vector<int>& hierarchy_desc, int n) {
CHECK(func->has_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.
CHECK_LT(fields_count, obj->map()->GetInObjectProperties());
......
......@@ -67,7 +67,7 @@ function test(use_new, add_first, set__proto__) {
var proto = use_new ? new Super() : {};
// New object is fast.
assertTrue(%HasFastProperties(proto));
assertTrue(use_new || %HasFastProperties(proto));
if (add_first) {
AddProps(proto);
......
......@@ -25,7 +25,7 @@ function __f_0(__v_1, __v_6) {
%DebugPrint(undefined);
function __f_1(__v_4, add_first, __v_6, same_map_as) {
var __v_1 = __v_4 ? new __f_3() : {};
assertTrue(%HasFastProperties(__v_1));
assertTrue(__v_4 || %HasFastProperties(__v_1));
if (add_first) {
__f_4(__v_1);
assertFalse(%HasFastProperties(__v_1));
......@@ -33,9 +33,9 @@ function __f_1(__v_4, add_first, __v_6, same_map_as) {
assertFalse(%HasFastProperties(__v_1));
} else {
__f_0(__v_1, __v_6);
assertTrue(%HasFastProperties(__v_1));
assertTrue(__v_4 || %HasFastProperties(__v_1));
__f_4(__v_1);
assertTrue(%HasFastProperties(__v_1));
assertTrue(__v_4 || %HasFastProperties(__v_1));
}
}
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