Commit c60ec4e1 authored by cbruni's avatar cbruni Committed by Commit bot

[compiler] Support Object.create(null) inlining in TF

In the ideal case, this will speed up Object.create(null) by ~10x.

Drive-by-fix: Spread usage of new IsSpecialReceiverMap() and
              IsSpecialReceiverInstanceType(InstanceType) helpers.

BUG=v8:5788

Review-Url: https://codereview.chromium.org/2622723003
Cr-Commit-Position: refs/heads/master@{#42336}
parent 936f79d9
......@@ -84,6 +84,15 @@ FieldAccess AccessBuilder::ForJSObjectInObjectProperty(Handle<Map> map,
return access;
}
// static
FieldAccess AccessBuilder::ForJSObjectOffset(
int offset, WriteBarrierKind write_barrier_kind) {
FieldAccess access = {kTaggedBase, offset,
MaybeHandle<Name>(), MaybeHandle<Map>(),
Type::NonInternal(), MachineType::AnyTagged(),
write_barrier_kind};
return access;
}
// static
FieldAccess AccessBuilder::ForJSFunctionPrototypeOrInitialMap() {
......@@ -688,12 +697,13 @@ FieldAccess AccessBuilder::ForArgumentsCallee() {
// static
FieldAccess AccessBuilder::ForFixedArraySlot(size_t index) {
FieldAccess AccessBuilder::ForFixedArraySlot(
size_t index, WriteBarrierKind write_barrier_kind) {
int offset = FixedArray::OffsetOfElementAt(static_cast<int>(index));
FieldAccess access = {kTaggedBase, offset,
Handle<Name>(), MaybeHandle<Map>(),
Type::NonInternal(), MachineType::AnyTagged(),
kFullWriteBarrier};
write_barrier_kind};
return access;
}
......@@ -843,6 +853,68 @@ ElementAccess AccessBuilder::ForTypedArrayElement(ExternalArrayType type,
return access;
}
// static
FieldAccess AccessBuilder::ForHashTableBaseNumberOfElements() {
FieldAccess access = {
kTaggedBase,
FixedArray::OffsetOfElementAt(HashTableBase::kNumberOfElementsIndex),
MaybeHandle<Name>(),
MaybeHandle<Map>(),
Type::SignedSmall(),
MachineType::TaggedSigned(),
kNoWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForHashTableBaseNumberOfDeletedElement() {
FieldAccess access = {
kTaggedBase, FixedArray::OffsetOfElementAt(
HashTableBase::kNumberOfDeletedElementsIndex),
MaybeHandle<Name>(), MaybeHandle<Map>(), Type::SignedSmall(),
MachineType::TaggedSigned(), kNoWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForHashTableBaseCapacity() {
FieldAccess access = {
kTaggedBase,
FixedArray::OffsetOfElementAt(HashTableBase::kCapacityIndex),
MaybeHandle<Name>(),
MaybeHandle<Map>(),
Type::SignedSmall(),
MachineType::TaggedSigned(),
kNoWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForDictionaryMaxNumberKey() {
FieldAccess access = {
kTaggedBase,
FixedArray::OffsetOfElementAt(NameDictionary::kMaxNumberKeyIndex),
MaybeHandle<Name>(),
MaybeHandle<Map>(),
Type::Any(),
MachineType::AnyTagged(),
kNoWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForDictionaryNextEnumerationIndex() {
FieldAccess access = {
kTaggedBase,
FixedArray::OffsetOfElementAt(NameDictionary::kNextEnumerationIndexIndex),
MaybeHandle<Name>(),
MaybeHandle<Map>(),
Type::SignedSmall(),
MachineType::TaggedSigned(),
kNoWriteBarrier};
return access;
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -46,6 +46,8 @@ class V8_EXPORT_PRIVATE AccessBuilder final
// Provides access to JSObject inobject property fields.
static FieldAccess ForJSObjectInObjectProperty(Handle<Map> map, int index);
static FieldAccess ForJSObjectOffset(
int offset, WriteBarrierKind write_barrier_kind = kFullWriteBarrier);
// Provides access to JSFunction::prototype_or_initial_map() field.
static FieldAccess ForJSFunctionPrototypeOrInitialMap();
......@@ -221,7 +223,8 @@ class V8_EXPORT_PRIVATE AccessBuilder final
static FieldAccess ForArgumentsCallee();
// Provides access to FixedArray slots.
static FieldAccess ForFixedArraySlot(size_t index);
static FieldAccess ForFixedArraySlot(
size_t index, WriteBarrierKind write_barrier_kind = kFullWriteBarrier);
// Provides access to Context slots.
static FieldAccess ForContextSlot(size_t index);
......@@ -241,6 +244,15 @@ class V8_EXPORT_PRIVATE AccessBuilder final
static ElementAccess ForTypedArrayElement(ExternalArrayType type,
bool is_external);
// Provides access to HashTable fields.
static FieldAccess ForHashTableBaseNumberOfElements();
static FieldAccess ForHashTableBaseNumberOfDeletedElement();
static FieldAccess ForHashTableBaseCapacity();
// Provides access to Dictionary fields.
static FieldAccess ForDictionaryMaxNumberKey();
static FieldAccess ForDictionaryNextEnumerationIndex();
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(AccessBuilder);
};
......
......@@ -4,6 +4,7 @@
#include "src/compiler/js-builtin-reducer.h"
#include "src/base/bits.h"
#include "src/compilation-dependencies.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/js-graph.h"
......@@ -1485,6 +1486,117 @@ Reduction JSBuiltinReducer::ReduceNumberParseInt(Node* node) {
return NoChange();
}
// ES6 section #sec-object.create Object.create(proto, properties)
Reduction JSBuiltinReducer::ReduceObjectCreate(Node* node) {
// We need exactly target, receiver and value parameters.
int arg_count = node->op()->ValueInputCount();
if (arg_count != 3) return NoChange();
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* prototype = NodeProperties::GetValueInput(node, 2);
Type* prototype_type = NodeProperties::GetType(prototype);
Handle<Map> instance_map;
if (!prototype_type->IsHeapConstant()) return NoChange();
Handle<HeapObject> prototype_const =
prototype_type->AsHeapConstant()->Value();
if (!prototype_const->IsNull(isolate()) && !prototype_const->IsJSReceiver()) {
return NoChange();
}
instance_map = Map::GetObjectCreateMap(prototype_const);
Node* properties = jsgraph()->EmptyFixedArrayConstant();
if (instance_map->is_dictionary_map()) {
// Allocated an empty NameDictionary as backing store for the properties.
Handle<Map> map(isolate()->heap()->hash_table_map(), isolate());
int capacity =
NameDictionary::ComputeCapacity(NameDictionary::kInitialCapacity);
DCHECK(base::bits::IsPowerOfTwo32(capacity));
int length = NameDictionary::EntryToIndex(capacity);
int size = NameDictionary::SizeFor(length);
effect = graph()->NewNode(
common()->BeginRegion(RegionObservability::kNotObservable), effect);
Node* value = effect =
graph()->NewNode(simplified()->Allocate(NOT_TENURED),
jsgraph()->Constant(size), effect, control);
effect =
graph()->NewNode(simplified()->StoreField(AccessBuilder::ForMap()),
value, jsgraph()->HeapConstant(map), effect, control);
// Initialize FixedArray fields.
effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForFixedArrayLength()), value,
jsgraph()->SmiConstant(length), effect, control);
// Initialize HashTable fields.
effect =
graph()->NewNode(simplified()->StoreField(
AccessBuilder::ForHashTableBaseNumberOfElements()),
value, jsgraph()->SmiConstant(0), effect, control);
effect = graph()->NewNode(
simplified()->StoreField(
AccessBuilder::ForHashTableBaseNumberOfDeletedElement()),
value, jsgraph()->SmiConstant(0), effect, control);
effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForHashTableBaseCapacity()),
value, jsgraph()->SmiConstant(capacity), effect, control);
// Initialize Dictionary fields.
Node* undefined = jsgraph()->UndefinedConstant();
effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForDictionaryMaxNumberKey()),
value, undefined, effect, control);
effect = graph()->NewNode(
simplified()->StoreField(
AccessBuilder::ForDictionaryNextEnumerationIndex()),
value, jsgraph()->SmiConstant(PropertyDetails::kInitialIndex), effect,
control);
// Initialize hte Properties fields.
for (int index = NameDictionary::kNextEnumerationIndexIndex + 1;
index < length; index++) {
effect = graph()->NewNode(
simplified()->StoreField(
AccessBuilder::ForFixedArraySlot(index, kNoWriteBarrier)),
value, undefined, effect, control);
}
properties = effect =
graph()->NewNode(common()->FinishRegion(), value, effect);
}
int const instance_size = instance_map->instance_size();
if (instance_size > kMaxRegularHeapObjectSize) return NoChange();
dependencies()->AssumeInitialMapCantChange(instance_map);
// Emit code to allocate the JSObject instance for the given
// {instance_map}.
effect = graph()->NewNode(
common()->BeginRegion(RegionObservability::kNotObservable), effect);
Node* value = effect =
graph()->NewNode(simplified()->Allocate(NOT_TENURED),
jsgraph()->Constant(instance_size), effect, control);
effect =
graph()->NewNode(simplified()->StoreField(AccessBuilder::ForMap()), value,
jsgraph()->HeapConstant(instance_map), effect, control);
effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForJSObjectProperties()), value,
properties, effect, control);
effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForJSObjectElements()), value,
jsgraph()->EmptyFixedArrayConstant(), effect, control);
// Initialize Object fields.
Node* undefined = jsgraph()->UndefinedConstant();
for (int offset = JSObject::kHeaderSize; offset < instance_size;
offset += kPointerSize) {
effect = graph()->NewNode(
simplified()->StoreField(
AccessBuilder::ForJSObjectOffset(offset, kNoWriteBarrier)),
value, undefined, effect, control);
}
value = effect = graph()->NewNode(common()->FinishRegion(), value, effect);
// replace it
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
// ES6 section 21.1.2.1 String.fromCharCode ( ...codeUnits )
Reduction JSBuiltinReducer::ReduceStringFromCharCode(Node* node) {
JSCallReduction r(node);
......@@ -1990,6 +2102,9 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
case kNumberParseInt:
reduction = ReduceNumberParseInt(node);
break;
case kObjectCreate:
reduction = ReduceObjectCreate(node);
break;
case kStringFromCharCode:
reduction = ReduceStringFromCharCode(node);
break;
......
......@@ -99,6 +99,7 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
Reduction ReduceNumberIsNaN(Node* node);
Reduction ReduceNumberIsSafeInteger(Node* node);
Reduction ReduceNumberParseInt(Node* node);
Reduction ReduceObjectCreate(Node* node);
Reduction ReduceStringCharAt(Node* node);
Reduction ReduceStringCharCodeAt(Node* node);
Reduction ReduceStringFromCharCode(Node* node);
......
......@@ -73,7 +73,7 @@ void LookupIterator::Next() {
JSReceiver* holder = *holder_;
Map* map = holder->map();
if (map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
if (map->IsSpecialReceiverMap()) {
state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
: LookupInSpecialHolder<false>(map, holder);
if (IsFound()) return;
......
......@@ -288,7 +288,7 @@ class V8_EXPORT_PRIVATE LookupIterator final BASE_EMBEDDED {
void NextInternal(Map* map, JSReceiver* holder);
template <bool is_element>
inline State LookupInHolder(Map* map, JSReceiver* holder) {
return map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE
return map->IsSpecialReceiverMap()
? LookupInSpecialHolder<is_element>(map, holder)
: LookupInRegularHolder<is_element>(map, holder);
}
......
......@@ -2219,6 +2219,9 @@ int JSObject::GetHeaderSize(InstanceType type) {
}
}
inline bool IsSpecialReceiverInstanceType(InstanceType instance_type) {
return instance_type <= LAST_SPECIAL_RECEIVER_TYPE;
}
int JSObject::GetInternalFieldCount(Map* map) {
int instance_size = map->instance_size();
......@@ -4919,6 +4922,12 @@ bool Map::IsJSGlobalObjectMap() {
bool Map::IsJSTypedArrayMap() { return instance_type() == JS_TYPED_ARRAY_TYPE; }
bool Map::IsJSDataViewMap() { return instance_type() == JS_DATA_VIEW_TYPE; }
bool Map::IsSpecialReceiverMap() {
bool result = IsSpecialReceiverInstanceType(instance_type());
DCHECK_IMPLIES(!result,
!has_named_interceptor() && !is_access_check_needed());
return result;
}
bool Map::CanOmitMapChecks() {
return is_stable() && FLAG_omit_map_checks_for_leaf_maps;
......
......@@ -4708,6 +4708,36 @@ void Map::EnsureDescriptorSlack(Handle<Map> map, int slack) {
map->UpdateDescriptors(*new_descriptors, layout_descriptor);
}
// static
Handle<Map> Map::GetObjectCreateMap(Handle<HeapObject> prototype) {
Isolate* isolate = prototype->GetIsolate();
Handle<Map> map(isolate->native_context()->object_function()->initial_map(),
isolate);
if (map->prototype() == *prototype) return map;
if (prototype->IsNull(isolate)) {
return isolate->slow_object_with_null_prototype_map();
}
if (prototype->IsJSObject()) {
Handle<JSObject> js_prototype = Handle<JSObject>::cast(prototype);
if (!js_prototype->map()->is_prototype_map()) {
JSObject::OptimizeAsPrototype(js_prototype, FAST_PROTOTYPE);
}
Handle<PrototypeInfo> info =
Map::GetOrCreatePrototypeInfo(js_prototype, isolate);
// TODO(verwaest): Use inobject slack tracking for this map.
if (info->HasObjectCreateMap()) {
map = handle(info->ObjectCreateMap(), isolate);
} else {
map = Map::CopyInitialMap(map);
Map::SetPrototype(map, prototype, FAST_PROTOTYPE);
PrototypeInfo::SetObjectCreateMap(info, map);
}
return map;
}
return Map::TransitionToPrototype(map, prototype, REGULAR_PROTOTYPE);
}
template <class T>
static int AppendUniqueCallbacks(Handle<TemplateList> callbacks,
Handle<typename T::Array> array,
......@@ -8210,8 +8240,8 @@ bool Map::OnlyHasSimpleProperties() {
// Wrapped string elements aren't explicitly stored in the elements backing
// store, but are loaded indirectly from the underlying string.
return !IsStringWrapperElementsKind(elements_kind()) &&
instance_type() > LAST_SPECIAL_RECEIVER_TYPE &&
!has_hidden_prototype() && !is_dictionary_map();
!IsSpecialReceiverMap() && !has_hidden_prototype() &&
!is_dictionary_map();
}
MUST_USE_RESULT Maybe<bool> FastGetOwnValuesOrEntries(
......
......@@ -6283,6 +6283,8 @@ class Map: public HeapObject {
Code* LookupInCodeCache(Name* name, Code::Flags code);
static Handle<Map> GetObjectCreateMap(Handle<HeapObject> prototype);
// Computes a hash value for this map, to be used in HashTables and such.
int Hash();
......@@ -6307,6 +6309,8 @@ class Map: public HeapObject {
inline bool IsJSTypedArrayMap();
inline bool IsJSDataViewMap();
inline bool IsSpecialReceiverMap();
inline bool CanOmitMapChecks();
static void AddDependentCode(Handle<Map> map,
......@@ -7090,7 +7094,8 @@ class Script: public Struct {
V(Number, isSafeInteger, NumberIsSafeInteger) \
V(Number, parseFloat, NumberParseFloat) \
V(Number, parseInt, NumberParseInt) \
V(Number.prototype, toString, NumberToString)
V(Number.prototype, toString, NumberToString) \
V(Object, create, ObjectCreate)
#define ATOMIC_FUNCTIONS_WITH_ID_LIST(V) \
V(Atomics, load, AtomicsLoad) \
......
......@@ -496,8 +496,7 @@ RUNTIME_FUNCTION(Runtime_ArrayIncludes_Slow) {
// If the receiver is not a special receiver type, and the length is a valid
// element index, perform fast operation tailored to specific ElementsKinds.
if (object->map()->instance_type() > LAST_SPECIAL_RECEIVER_TYPE &&
len < kMaxUInt32 &&
if (!object->map()->IsSpecialReceiverMap() && len < kMaxUInt32 &&
JSObject::PrototypeHasNoElements(isolate, JSObject::cast(*object))) {
Handle<JSObject> obj = Handle<JSObject>::cast(object);
ElementsAccessor* elements = obj->GetElementsAccessor();
......@@ -595,8 +594,7 @@ RUNTIME_FUNCTION(Runtime_ArrayIndexOf) {
// If the receiver is not a special receiver type, and the length is a valid
// element index, perform fast operation tailored to specific ElementsKinds.
if (object->map()->instance_type() > LAST_SPECIAL_RECEIVER_TYPE &&
len < kMaxUInt32 &&
if (!object->map()->IsSpecialReceiverMap() && len < kMaxUInt32 &&
JSObject::PrototypeHasNoElements(isolate, JSObject::cast(*object))) {
Handle<JSObject> obj = Handle<JSObject>::cast(object);
ElementsAccessor* elements = obj->GetElementsAccessor();
......
......@@ -222,30 +222,8 @@ RUNTIME_FUNCTION(Runtime_ObjectCreate) {
// function's initial map from the current native context.
// TODO(bmeurer): Use a dedicated cache for Object.create; think about
// slack tracking for Object.create.
Handle<Map> map(isolate->native_context()->object_function()->initial_map(),
isolate);
if (map->prototype() != *prototype) {
if (prototype->IsNull(isolate)) {
map = isolate->slow_object_with_null_prototype_map();
} else if (prototype->IsJSObject()) {
Handle<JSObject> js_prototype = Handle<JSObject>::cast(prototype);
if (!js_prototype->map()->is_prototype_map()) {
JSObject::OptimizeAsPrototype(js_prototype, FAST_PROTOTYPE);
}
Handle<PrototypeInfo> info =
Map::GetOrCreatePrototypeInfo(js_prototype, isolate);
// TODO(verwaest): Use inobject slack tracking for this map.
if (info->HasObjectCreateMap()) {
map = handle(info->ObjectCreateMap(), isolate);
} else {
map = Map::CopyInitialMap(map);
Map::SetPrototype(map, prototype, FAST_PROTOTYPE);
PrototypeInfo::SetObjectCreateMap(info, map);
}
} else {
map = Map::TransitionToPrototype(map, prototype, REGULAR_PROTOTYPE);
}
}
Handle<Map> map =
Map::GetObjectCreateMap(Handle<HeapObject>::cast(prototype));
bool is_dictionary_map = map->is_dictionary_map();
Handle<FixedArray> object_properties;
......
......@@ -401,7 +401,7 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) {
// Eliminate callable and exotic objects, which should not be serialized.
InstanceType instance_type = receiver->map()->instance_type();
if (receiver->IsCallable() || (instance_type <= LAST_SPECIAL_RECEIVER_TYPE &&
if (receiver->IsCallable() || (IsSpecialReceiverInstanceType(instance_type) &&
instance_type != JS_SPECIAL_API_OBJECT_TYPE)) {
ThrowDataCloneError(MessageTemplate::kDataCloneError, receiver);
return Nothing<bool>();
......
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