Commit f3d0bda8 authored by Camillo Bruni's avatar Camillo Bruni

[builtins] For-in fast path for empty dict receivers

Change-Id: I58fc4ad8104f9a334a24de181168122f215a0505

BUG=chromium:678427

Change-Id: I58fc4ad8104f9a334a24de181168122f215a0505
Reviewed-on: https://chromium-review.googlesource.com/447980Reviewed-by: 's avatarMythri Alle <mythria@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43628}
parent aa894aff
......@@ -992,6 +992,8 @@ v8_source_set("v8_base") {
"src/builtins/builtins-date.cc",
"src/builtins/builtins-debug.cc",
"src/builtins/builtins-error.cc",
"src/builtins/builtins-forin.cc",
"src/builtins/builtins-forin.h",
"src/builtins/builtins-function.cc",
"src/builtins/builtins-generator.cc",
"src/builtins/builtins-global.cc",
......@@ -1003,7 +1005,6 @@ v8_source_set("v8_base") {
"src/builtins/builtins-math.cc",
"src/builtins/builtins-number.cc",
"src/builtins/builtins-object.cc",
"src/builtins/builtins-object.h",
"src/builtins/builtins-promise.cc",
"src/builtins/builtins-promise.h",
"src/builtins/builtins-proxy.cc",
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/builtins/builtins-forin.h"
#include "src/builtins/builtins-utils.h"
#include "src/builtins/builtins.h"
#include "src/code-factory.h"
#include "src/code-stub-assembler.h"
#include "src/counters.h"
#include "src/keys.h"
#include "src/lookup.h"
#include "src/objects-inl.h"
#include "src/property-descriptor.h"
namespace v8 {
namespace internal {
typedef compiler::Node Node;
Node* ForInBuiltinsAssembler::ForInFilter(Node* key, Node* object,
Node* context) {
Label return_undefined(this, Label::kDeferred), return_to_name(this),
end(this);
Variable var_result(this, MachineRepresentation::kTagged);
Node* has_property =
HasProperty(object, key, context, Runtime::kForInHasProperty);
Branch(WordEqual(has_property, BooleanConstant(true)), &return_to_name,
&return_undefined);
Bind(&return_to_name);
{
var_result.Bind(ToName(context, key));
Goto(&end);
}
Bind(&return_undefined);
{
var_result.Bind(UndefinedConstant());
Goto(&end);
}
Bind(&end);
return var_result.value();
}
std::tuple<Node*, Node*, Node*> ForInBuiltinsAssembler::EmitForInPrepare(
Node* object, Node* context, Label* call_runtime,
Label* nothing_to_iterate) {
Label use_cache(this);
CSA_ASSERT(this, IsJSReceiver(object));
CheckEnumCache(object, &use_cache, nothing_to_iterate, call_runtime);
Bind(&use_cache);
Node* map = LoadMap(object);
Node* enum_length = EnumLength(map);
GotoIf(WordEqual(enum_length, SmiConstant(0)), nothing_to_iterate);
Node* descriptors = LoadMapDescriptors(map);
Node* cache_offset =
LoadObjectField(descriptors, DescriptorArray::kEnumCacheOffset);
Node* enum_cache = LoadObjectField(
cache_offset, DescriptorArray::kEnumCacheBridgeCacheOffset);
return std::make_tuple(map, enum_cache, enum_length);
}
Node* ForInBuiltinsAssembler::EnumLength(Node* map) {
CSA_ASSERT(this, IsMap(map));
Node* bitfield_3 = LoadMapBitField3(map);
Node* enum_length = DecodeWordFromWord32<Map::EnumLengthBits>(bitfield_3);
return SmiTag(enum_length);
}
void ForInBuiltinsAssembler::CheckPrototypeEnumCache(Node* receiver, Node* map,
Label* use_cache,
Label* use_runtime) {
Variable current_js_object(this, MachineRepresentation::kTagged, receiver);
Variable current_map(this, MachineRepresentation::kTagged, map);
// These variables are updated in the loop below.
Variable* loop_vars[2] = {&current_js_object, &current_map};
Label loop(this, 2, loop_vars), next(this);
Goto(&loop);
// Check that there are no elements. |current_js_object| contains
// the current JS object we've reached through the prototype chain.
Bind(&loop);
{
Label if_elements(this), if_no_elements(this);
Node* elements = LoadElements(current_js_object.value());
Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
// Check that there are no elements.
Branch(WordEqual(elements, empty_fixed_array), &if_no_elements,
&if_elements);
Bind(&if_elements);
{
// Second chance, the object may be using the empty slow element
// dictionary.
Node* slow_empty_dictionary =
LoadRoot(Heap::kEmptySlowElementDictionaryRootIndex);
Branch(WordNotEqual(elements, slow_empty_dictionary), use_runtime,
&if_no_elements);
}
Bind(&if_no_elements);
{
// Update map prototype.
current_js_object.Bind(LoadMapPrototype(current_map.value()));
Branch(WordEqual(current_js_object.value(), NullConstant()), use_cache,
&next);
}
}
Bind(&next);
{
// For all objects but the receiver, check that the cache is empty.
current_map.Bind(LoadMap(current_js_object.value()));
Node* enum_length = EnumLength(current_map.value());
Node* zero_constant = SmiConstant(Smi::kZero);
Branch(WordEqual(enum_length, zero_constant), &loop, use_runtime);
}
}
void ForInBuiltinsAssembler::CheckEnumCache(Node* receiver, Label* use_cache,
Label* nothing_to_iterate,
Label* use_runtime) {
Node* map = LoadMap(receiver);
Label check_empty_prototype(this),
check_dict_receiver(this, Label::kDeferred);
// Check if the enum length field is properly initialized, indicating that
// there is an enum cache.
{
Node* invalid_enum_cache_sentinel =
SmiConstant(Smi::FromInt(kInvalidEnumCacheSentinel));
Node* enum_length = EnumLength(map);
Branch(WordEqual(enum_length, invalid_enum_cache_sentinel),
&check_dict_receiver, &check_empty_prototype);
}
// Check that there are no elements on the fast |receiver| and its prototype
// chain.
Bind(&check_empty_prototype);
CheckPrototypeEnumCache(receiver, map, use_cache, use_runtime);
Label dict_loop(this);
Bind(&check_dict_receiver);
{
// Avoid runtime-call for empty dictionary receivers.
GotoIfNot(IsDictionaryMap(map), use_runtime);
Node* properties = LoadProperties(receiver);
Node* length = LoadFixedArrayElement(
properties, NameDictionary::kNumberOfElementsIndex);
GotoIfNot(WordEqual(length, SmiConstant(0)), use_runtime);
// Check that there are no elements on the |receiver| and its prototype
// chain. Given that we do not create an EnumCache for dict-mode objects,
// directly jump to |nothing_to_iterate| if there are no elements and no
// properties on the |receiver|.
CheckPrototypeEnumCache(receiver, map, nothing_to_iterate, use_runtime);
}
}
TF_BUILTIN(ForInFilter, ForInBuiltinsAssembler) {
typedef ForInFilterDescriptor Descriptor;
Node* key = Parameter(Descriptor::kKey);
Node* object = Parameter(Descriptor::kObject);
Node* context = Parameter(Descriptor::kContext);
Return(ForInFilter(key, object, context));
}
TF_BUILTIN(ForInNext, ForInBuiltinsAssembler) {
typedef ForInNextDescriptor Descriptor;
Label filter(this);
Node* object = Parameter(Descriptor::kObject);
Node* cache_array = Parameter(Descriptor::kCacheArray);
Node* cache_type = Parameter(Descriptor::kCacheType);
Node* index = Parameter(Descriptor::kIndex);
Node* context = Parameter(Descriptor::kContext);
Node* key = LoadFixedArrayElement(cache_array, SmiUntag(index));
Node* map = LoadMap(object);
GotoIfNot(WordEqual(map, cache_type), &filter);
Return(key);
Bind(&filter);
Return(ForInFilter(key, object, context));
}
TF_BUILTIN(ForInPrepare, ForInBuiltinsAssembler) {
typedef ForInPrepareDescriptor Descriptor;
Label call_runtime(this), nothing_to_iterate(this);
Node* object = Parameter(Descriptor::kObject);
Node* context = Parameter(Descriptor::kContext);
Node* cache_type;
Node* cache_array;
Node* cache_length;
std::tie(cache_type, cache_array, cache_length) =
EmitForInPrepare(object, context, &call_runtime, &nothing_to_iterate);
Return(cache_type, cache_array, cache_length);
Bind(&call_runtime);
TailCallRuntime(Runtime::kForInPrepare, context, object);
Bind(&nothing_to_iterate);
{
Node* zero = SmiConstant(0);
Return(zero, zero, zero);
}
}
} // namespace internal
} // namespace v8
......@@ -8,18 +8,26 @@
namespace v8 {
namespace internal {
class ObjectBuiltinsAssembler : public CodeStubAssembler {
class ForInBuiltinsAssembler : public CodeStubAssembler {
public:
explicit ObjectBuiltinsAssembler(compiler::CodeAssemblerState* state)
explicit ForInBuiltinsAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
std::tuple<Node*, Node*, Node*> EmitForInPrepare(Node* object, Node* context,
Label* call_runtime,
Label* nothing_to_iterate);
protected:
void IsString(Node* object, Label* if_string, Label* if_notstring);
void ReturnToStringFormat(Node* context, Node* string);
Node* ForInFilter(Node* key, Node* object, Node* context);
private:
// Get the enumerable length from |map| and return the result as a Smi.
Node* EnumLength(Node* map);
void CheckPrototypeEnumCache(Node* receiver, Node* map, Label* use_cache,
Label* use_runtime);
// Check the cache validity for |receiver|. Branch to |use_cache| if
// the cache is valid, otherwise branch to |use_runtime|.
void CheckEnumCache(Node* receiver, Label* use_cache,
Label* nothing_to_iterate, Label* use_runtime);
};
} // namespace internal
......
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/builtins/builtins-object.h"
#include "src/builtins/builtins-utils.h"
#include "src/builtins/builtins.h"
#include "src/code-factory.h"
......@@ -18,25 +17,41 @@ namespace internal {
typedef compiler::Node Node;
std::tuple<Node*, Node*, Node*> ObjectBuiltinsAssembler::EmitForInPrepare(
Node* object, Node* context, Label* call_runtime,
Label* nothing_to_iterate) {
Label use_cache(this);
CSA_ASSERT(this, IsJSReceiver(object));
class ObjectBuiltinsAssembler : public CodeStubAssembler {
public:
explicit ObjectBuiltinsAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
CheckEnumCache(object, &use_cache, call_runtime);
Bind(&use_cache);
Node* map = LoadMap(object);
Node* enum_length = EnumLength(map);
GotoIf(WordEqual(enum_length, SmiConstant(0)), nothing_to_iterate);
Node* descriptors = LoadMapDescriptors(map);
Node* cache_offset =
LoadObjectField(descriptors, DescriptorArray::kEnumCacheOffset);
Node* enum_cache = LoadObjectField(
cache_offset, DescriptorArray::kEnumCacheBridgeCacheOffset);
protected:
void IsString(Node* object, Label* if_string, Label* if_notstring);
void ReturnToStringFormat(Node* context, Node* string);
};
void ObjectBuiltinsAssembler::IsString(Node* object, Label* if_string,
Label* if_notstring) {
Label if_notsmi(this);
Branch(TaggedIsSmi(object), if_notstring, &if_notsmi);
Bind(&if_notsmi);
{
Node* instance_type = LoadInstanceType(object);
Branch(IsStringInstanceType(instance_type), if_string, if_notstring);
}
}
void ObjectBuiltinsAssembler::ReturnToStringFormat(Node* context,
Node* string) {
Node* lhs = HeapConstant(factory()->NewStringFromStaticChars("[object "));
Node* rhs = HeapConstant(factory()->NewStringFromStaticChars("]"));
return std::make_tuple(map, enum_cache, enum_length);
Callable callable =
CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
Return(CallStub(callable, context, CallStub(callable, context, lhs, string),
rhs));
}
// -----------------------------------------------------------------------------
// ES6 section 19.1 Object Objects
......@@ -122,31 +137,6 @@ BUILTIN(ObjectPrototypePropertyIsEnumerable) {
return isolate->heap()->ToBoolean((maybe.FromJust() & DONT_ENUM) == 0);
}
void ObjectBuiltinsAssembler::IsString(Node* object, Label* if_string,
Label* if_notstring) {
Label if_notsmi(this);
Branch(TaggedIsSmi(object), if_notstring, &if_notsmi);
Bind(&if_notsmi);
{
Node* instance_type = LoadInstanceType(object);
Branch(IsStringInstanceType(instance_type), if_string, if_notstring);
}
}
void ObjectBuiltinsAssembler::ReturnToStringFormat(Node* context,
Node* string) {
Node* lhs = HeapConstant(factory()->NewStringFromStaticChars("[object "));
Node* rhs = HeapConstant(factory()->NewStringFromStaticChars("]"));
Callable callable =
CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
Return(CallStub(callable, context, CallStub(callable, context, lhs, string),
rhs));
}
// ES6 section 19.1.3.6 Object.prototype.toString
TF_BUILTIN(ObjectProtoToString, ObjectBuiltinsAssembler) {
Label return_undefined(this, Label::kDeferred),
......@@ -927,59 +917,6 @@ TF_BUILTIN(HasProperty, ObjectBuiltinsAssembler) {
Return(HasProperty(object, key, context, Runtime::kHasProperty));
}
TF_BUILTIN(ForInFilter, ObjectBuiltinsAssembler) {
typedef ForInFilterDescriptor Descriptor;
Node* key = Parameter(Descriptor::kKey);
Node* object = Parameter(Descriptor::kObject);
Node* context = Parameter(Descriptor::kContext);
Return(ForInFilter(key, object, context));
}
TF_BUILTIN(ForInNext, ObjectBuiltinsAssembler) {
typedef ForInNextDescriptor Descriptor;
Label filter(this);
Node* object = Parameter(Descriptor::kObject);
Node* cache_array = Parameter(Descriptor::kCacheArray);
Node* cache_type = Parameter(Descriptor::kCacheType);
Node* index = Parameter(Descriptor::kIndex);
Node* context = Parameter(Descriptor::kContext);
Node* key = LoadFixedArrayElement(cache_array, SmiUntag(index));
Node* map = LoadMap(object);
GotoIfNot(WordEqual(map, cache_type), &filter);
Return(key);
Bind(&filter);
Return(ForInFilter(key, object, context));
}
TF_BUILTIN(ForInPrepare, ObjectBuiltinsAssembler) {
typedef ForInPrepareDescriptor Descriptor;
Label call_runtime(this), nothing_to_iterate(this);
Node* object = Parameter(Descriptor::kObject);
Node* context = Parameter(Descriptor::kContext);
Node* cache_type;
Node* cache_array;
Node* cache_length;
std::tie(cache_type, cache_array, cache_length) =
EmitForInPrepare(object, context, &call_runtime, &nothing_to_iterate);
Return(cache_type, cache_array, cache_length);
Bind(&call_runtime);
TailCallRuntime(Runtime::kForInPrepare, context, object);
Bind(&nothing_to_iterate);
{
Node* zero = SmiConstant(0);
Return(zero, zero, zero);
}
}
TF_BUILTIN(InstanceOf, ObjectBuiltinsAssembler) {
typedef CompareDescriptor Descriptor;
......
......@@ -6158,73 +6158,6 @@ Node* CodeStubAssembler::PageFromAddress(Node* address) {
return WordAnd(address, IntPtrConstant(~Page::kPageAlignmentMask));
}
Node* CodeStubAssembler::EnumLength(Node* map) {
CSA_ASSERT(this, IsMap(map));
Node* bitfield_3 = LoadMapBitField3(map);
Node* enum_length = DecodeWordFromWord32<Map::EnumLengthBits>(bitfield_3);
return SmiTag(enum_length);
}
void CodeStubAssembler::CheckEnumCache(Node* receiver, Label* use_cache,
Label* use_runtime) {
Variable current_js_object(this, MachineRepresentation::kTagged, receiver);
Variable current_map(this, MachineRepresentation::kTagged,
LoadMap(current_js_object.value()));
// These variables are updated in the loop below.
Variable* loop_vars[2] = {&current_js_object, &current_map};
Label loop(this, 2, loop_vars), next(this);
// Check if the enum length field is properly initialized, indicating that
// there is an enum cache.
{
Node* invalid_enum_cache_sentinel =
SmiConstant(Smi::FromInt(kInvalidEnumCacheSentinel));
Node* enum_length = EnumLength(current_map.value());
Branch(WordEqual(enum_length, invalid_enum_cache_sentinel), use_runtime,
&loop);
}
// Check that there are no elements. |current_js_object| contains
// the current JS object we've reached through the prototype chain.
Bind(&loop);
{
Label if_elements(this), if_no_elements(this);
Node* elements = LoadElements(current_js_object.value());
Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
// Check that there are no elements.
Branch(WordEqual(elements, empty_fixed_array), &if_no_elements,
&if_elements);
Bind(&if_elements);
{
// Second chance, the object may be using the empty slow element
// dictionary.
Node* slow_empty_dictionary =
LoadRoot(Heap::kEmptySlowElementDictionaryRootIndex);
Branch(WordNotEqual(elements, slow_empty_dictionary), use_runtime,
&if_no_elements);
}
Bind(&if_no_elements);
{
// Update map prototype.
current_js_object.Bind(LoadMapPrototype(current_map.value()));
Branch(WordEqual(current_js_object.value(), NullConstant()), use_cache,
&next);
}
}
Bind(&next);
{
// For all objects but the receiver, check that the cache is empty.
current_map.Bind(LoadMap(current_js_object.value()));
Node* enum_length = EnumLength(current_map.value());
Node* zero_constant = SmiConstant(Smi::kZero);
Branch(WordEqual(enum_length, zero_constant), &loop, use_runtime);
}
}
Node* CodeStubAssembler::CreateAllocationSiteInFeedbackVector(
Node* feedback_vector, Node* slot) {
Node* size = IntPtrConstant(AllocationSite::kSize);
......@@ -7625,34 +7558,6 @@ Node* CodeStubAssembler::SameValue(Node* lhs, Node* rhs, Node* context) {
return var_result.value();
}
Node* CodeStubAssembler::ForInFilter(Node* key, Node* object, Node* context) {
Label return_undefined(this, Label::kDeferred), return_to_name(this),
end(this);
Variable var_result(this, MachineRepresentation::kTagged);
Node* has_property =
HasProperty(object, key, context, Runtime::kForInHasProperty);
Branch(WordEqual(has_property, BooleanConstant(true)), &return_to_name,
&return_undefined);
Bind(&return_to_name);
{
var_result.Bind(ToName(context, key));
Goto(&end);
}
Bind(&return_undefined);
{
var_result.Bind(UndefinedConstant());
Goto(&end);
}
Bind(&end);
return var_result.value();
}
Node* CodeStubAssembler::HasProperty(
Node* object, Node* key, Node* context,
Runtime::FunctionId fallback_runtime_function_id) {
......
......@@ -1120,14 +1120,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* PageFromAddress(Node* address);
// Get the enumerable length from |map| and return the result as a Smi.
Node* EnumLength(Node* map);
// Check the cache validity for |receiver|. Branch to |use_cache| if
// the cache is valid, otherwise branch to |use_runtime|.
void CheckEnumCache(Node* receiver, CodeStubAssembler::Label* use_cache,
CodeStubAssembler::Label* use_runtime);
// Create a new weak cell with a specified value and install it into a
// feedback vector.
Node* CreateWeakCellInFeedbackVector(Node* feedback_vector, Node* slot,
......@@ -1223,7 +1215,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* HasProperty(
Node* object, Node* key, Node* context,
Runtime::FunctionId fallback_runtime_function_id = Runtime::kHasProperty);
Node* ForInFilter(Node* key, Node* object, Node* context);
Node* ClassOf(Node* object);
......
......@@ -10,7 +10,7 @@
#include "src/ast/prettyprinter.h"
#include "src/builtins/builtins-arguments.h"
#include "src/builtins/builtins-constructor.h"
#include "src/builtins/builtins-object.h"
#include "src/builtins/builtins-forin.h"
#include "src/code-factory.h"
#include "src/compilation-info.h"
#include "src/compiler.h"
......@@ -3167,10 +3167,10 @@ void Interpreter::DoForInPrepare(InterpreterAssembler* assembler) {
Label call_runtime(assembler, Label::kDeferred),
nothing_to_iterate(assembler, Label::kDeferred);
ObjectBuiltinsAssembler object_assembler(assembler->state());
ForInBuiltinsAssembler forin_assembler(assembler->state());
std::tie(cache_type, cache_array, cache_length) =
object_assembler.EmitForInPrepare(receiver, context, &call_runtime,
&nothing_to_iterate);
forin_assembler.EmitForInPrepare(receiver, context, &call_runtime,
&nothing_to_iterate);
BuildForInPrepareResult(output_register, cache_type, cache_array,
cache_length, assembler);
......
......@@ -494,6 +494,8 @@
'builtins/builtins-date.cc',
'builtins/builtins-debug.cc',
'builtins/builtins-error.cc',
'builtins/builtins-forin.cc',
'builtins/builtins-forin.h',
'builtins/builtins-function.cc',
'builtins/builtins-generator.cc',
'builtins/builtins-global.cc',
......@@ -505,7 +507,6 @@
'builtins/builtins-math.cc',
'builtins/builtins-number.cc',
'builtins/builtins-object.cc',
'builtins/builtins-object.h',
'builtins/builtins-promise.cc',
'builtins/builtins-promise.h',
'builtins/builtins-proxy.cc',
......
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