Commit 30599649 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[turbofan] Move Object.create inlining to JSCallReducer

This also adds a javascript operator JSCreateObject and an
associated TFS stub that handles Object.create in cases
where only a prototype, but no additional properties are
provided.

Bug: v8:7250
Change-Id: Ib1fd529a10a553c3718222356319bd6ccffbdf30
Reviewed-on: https://chromium-review.googlesource.com/1013576
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52680}
parent 0cb1ee7f
......@@ -768,6 +768,7 @@ namespace internal {
TFJ(ObjectAssign, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES #sec-object.create */ \
TFJ(ObjectCreate, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
TFS(CreateObjectWithoutProperties, kPrototypeArg) \
CPP(ObjectDefineGetter) \
CPP(ObjectDefineProperties) \
CPP(ObjectDefineProperty) \
......
......@@ -1124,6 +1124,69 @@ TF_BUILTIN(ObjectPrototypeValueOf, CodeStubAssembler) {
Return(ToObject(context, receiver));
}
// ES #sec-object.create
TF_BUILTIN(CreateObjectWithoutProperties, ObjectBuiltinsAssembler) {
Node* const prototype = Parameter(Descriptor::kPrototypeArg);
Node* const context = Parameter(Descriptor::kContext);
Node* const native_context = LoadNativeContext(context);
Label call_runtime(this, Label::kDeferred), prototype_null(this),
prototype_jsreceiver(this);
{
Comment("Argument check: prototype");
GotoIf(IsNull(prototype), &prototype_null);
BranchIfJSReceiver(prototype, &prototype_jsreceiver, &call_runtime);
}
VARIABLE(map, MachineRepresentation::kTagged);
VARIABLE(properties, MachineRepresentation::kTagged);
Label instantiate_map(this);
BIND(&prototype_null);
{
Comment("Prototype is null");
map.Bind(LoadContextElement(native_context,
Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP));
properties.Bind(AllocateNameDictionary(NameDictionary::kInitialCapacity));
Goto(&instantiate_map);
}
BIND(&prototype_jsreceiver);
{
Comment("Prototype is JSReceiver");
properties.Bind(EmptyFixedArrayConstant());
Node* object_function =
LoadContextElement(native_context, Context::OBJECT_FUNCTION_INDEX);
Node* object_function_map = LoadObjectField(
object_function, JSFunction::kPrototypeOrInitialMapOffset);
map.Bind(object_function_map);
GotoIf(WordEqual(prototype, LoadMapPrototype(map.value())),
&instantiate_map);
Comment("Try loading the prototype info");
Node* prototype_info =
LoadMapPrototypeInfo(LoadMap(prototype), &call_runtime);
Node* weak_cell =
LoadObjectField(prototype_info, PrototypeInfo::kObjectCreateMap);
GotoIf(IsUndefined(weak_cell), &call_runtime);
map.Bind(LoadWeakCellValue(weak_cell, &call_runtime));
Goto(&instantiate_map);
}
BIND(&instantiate_map);
{
Comment("Instantiate map");
Node* instance = AllocateJSObjectFromMap(map.value(), properties.value());
Return(instance);
}
BIND(&call_runtime);
{
Comment("Call Runtime (prototype is not null/jsreceiver)");
Node* result = CallRuntime(Runtime::kObjectCreate, context, prototype,
UndefinedConstant());
Return(result);
}
}
// ES #sec-object.create
TF_BUILTIN(ObjectCreate, ObjectBuiltinsAssembler) {
int const kPrototypeArg = 0;
......
......@@ -292,88 +292,6 @@ 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);
if (!prototype_type->IsHeapConstant()) return NoChange();
Handle<HeapObject> prototype_const =
prototype_type->AsHeapConstant()->Value();
Handle<Map> instance_map;
MaybeHandle<Map> maybe_instance_map =
Map::TryGetObjectCreateMap(prototype_const);
if (!maybe_instance_map.ToHandle(&instance_map)) return NoChange();
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()->name_dictionary_map(), isolate());
int capacity =
NameDictionary::ComputeCapacity(NameDictionary::kInitialCapacity);
DCHECK(base::bits::IsPowerOfTwo(capacity));
int length = NameDictionary::EntryToIndex(capacity);
int size = NameDictionary::SizeFor(length);
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(size, NOT_TENURED, Type::Any());
a.Store(AccessBuilder::ForMap(), map);
// Initialize FixedArray fields.
a.Store(AccessBuilder::ForFixedArrayLength(),
jsgraph()->SmiConstant(length));
// Initialize HashTable fields.
a.Store(AccessBuilder::ForHashTableBaseNumberOfElements(),
jsgraph()->SmiConstant(0));
a.Store(AccessBuilder::ForHashTableBaseNumberOfDeletedElement(),
jsgraph()->SmiConstant(0));
a.Store(AccessBuilder::ForHashTableBaseCapacity(),
jsgraph()->SmiConstant(capacity));
// Initialize Dictionary fields.
a.Store(AccessBuilder::ForDictionaryNextEnumerationIndex(),
jsgraph()->SmiConstant(PropertyDetails::kInitialIndex));
a.Store(AccessBuilder::ForDictionaryObjectHashIndex(),
jsgraph()->SmiConstant(PropertyArray::kNoHashSentinel));
// Initialize the Properties fields.
Node* undefined = jsgraph()->UndefinedConstant();
STATIC_ASSERT(NameDictionary::kElementsStartIndex ==
NameDictionary::kObjectHashIndex + 1);
for (int index = NameDictionary::kElementsStartIndex; index < length;
index++) {
a.Store(AccessBuilder::ForFixedArraySlot(index, kNoWriteBarrier),
undefined);
}
properties = effect = a.Finish();
}
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}.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(instance_size, NOT_TENURED, Type::Any());
a.Store(AccessBuilder::ForMap(), instance_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
// Initialize Object fields.
Node* undefined = jsgraph()->UndefinedConstant();
for (int offset = JSObject::kHeaderSize; offset < instance_size;
offset += kPointerSize) {
a.Store(AccessBuilder::ForJSObjectOffset(offset, kNoWriteBarrier),
undefined);
}
Node* value = effect = a.Finish();
// replace it
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
Reduction JSBuiltinReducer::Reduce(Node* node) {
Reduction reduction = NoChange();
JSCallReduction r(node);
......@@ -397,9 +315,6 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
case kNumberParseInt:
reduction = ReduceNumberParseInt(node);
break;
case kObjectCreate:
reduction = ReduceObjectCreate(node);
break;
default:
break;
}
......
......@@ -46,7 +46,6 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
Reduction ReduceGlobalIsFinite(Node* node);
Reduction ReduceGlobalIsNaN(Node* node);
Reduction ReduceNumberParseInt(Node* node);
Reduction ReduceObjectCreate(Node* node);
Node* ToNumber(Node* value);
Node* ToUint32(Node* value);
......
......@@ -794,6 +794,29 @@ Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) {
return ReduceObjectGetPrototype(node, target);
}
// ES6 section #sec-object.create Object.create(proto, properties)
Reduction JSCallReducer::ReduceObjectCreate(Node* node) {
int arg_count = node->op()->ValueInputCount();
Node* properties = arg_count >= 4 ? NodeProperties::GetValueInput(node, 3)
: jsgraph()->UndefinedConstant();
if (properties != jsgraph()->UndefinedConstant()) return NoChange();
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* prototype = arg_count >= 3 ? NodeProperties::GetValueInput(node, 2)
: jsgraph()->UndefinedConstant();
node->ReplaceInput(0, prototype);
node->ReplaceInput(1, context);
node->ReplaceInput(2, frame_state);
node->ReplaceInput(3, effect);
node->ReplaceInput(4, control);
node->TrimInputCount(5);
NodeProperties::ChangeOp(node, javascript()->CreateObject());
return Changed(node);
}
// ES section #sec-reflect.get
Reduction JSCallReducer::ReduceReflectGet(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
......@@ -3303,6 +3326,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
return ReduceFunctionPrototypeHasInstance(node);
case Builtins::kObjectConstructor:
return ReduceObjectConstructor(node);
case Builtins::kObjectCreate:
return ReduceObjectCreate(node);
case Builtins::kObjectGetPrototypeOf:
return ReduceObjectGetPrototypeOf(node);
case Builtins::kObjectIs:
......
......@@ -69,6 +69,7 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
Reduction ReduceObjectPrototypeGetProto(Node* node);
Reduction ReduceObjectPrototypeHasOwnProperty(Node* node);
Reduction ReduceObjectPrototypeIsPrototypeOf(Node* node);
Reduction ReduceObjectCreate(Node* node);
Reduction ReduceReflectApply(Node* node);
Reduction ReduceReflectConstruct(Node* node);
Reduction ReduceReflectGet(Node* node);
......
......@@ -13,6 +13,7 @@
#include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/node.h"
#include "src/compiler/operator-properties.h"
......@@ -172,6 +173,8 @@ Reduction JSCreateLowering::Reduce(Node* node) {
return ReduceJSCreateBlockContext(node);
case IrOpcode::kJSCreateGeneratorObject:
return ReduceJSCreateGeneratorObject(node);
case IrOpcode::kJSCreateObject:
return ReduceJSCreateObject(node);
default:
break;
}
......@@ -1420,6 +1423,84 @@ Reduction JSCreateLowering::ReduceJSCreateBlockContext(Node* node) {
return NoChange();
}
Reduction JSCreateLowering::ReduceJSCreateObject(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateObject, node->opcode());
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* prototype = NodeProperties::GetValueInput(node, 0);
HeapObjectMatcher m(prototype);
if (!m.IsHeapConstant()) return NoChange();
Handle<HeapObject> prototype_const = m.Value();
Handle<Map> instance_map;
MaybeHandle<Map> maybe_instance_map =
Map::TryGetObjectCreateMap(prototype_const);
if (!maybe_instance_map.ToHandle(&instance_map)) return NoChange();
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()->name_dictionary_map(), isolate());
int capacity =
NameDictionary::ComputeCapacity(NameDictionary::kInitialCapacity);
DCHECK(base::bits::IsPowerOfTwo(capacity));
int length = NameDictionary::EntryToIndex(capacity);
int size = NameDictionary::SizeFor(length);
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(size, NOT_TENURED, Type::Any());
a.Store(AccessBuilder::ForMap(), map);
// Initialize FixedArray fields.
a.Store(AccessBuilder::ForFixedArrayLength(),
jsgraph()->SmiConstant(length));
// Initialize HashTable fields.
a.Store(AccessBuilder::ForHashTableBaseNumberOfElements(),
jsgraph()->SmiConstant(0));
a.Store(AccessBuilder::ForHashTableBaseNumberOfDeletedElement(),
jsgraph()->SmiConstant(0));
a.Store(AccessBuilder::ForHashTableBaseCapacity(),
jsgraph()->SmiConstant(capacity));
// Initialize Dictionary fields.
a.Store(AccessBuilder::ForDictionaryNextEnumerationIndex(),
jsgraph()->SmiConstant(PropertyDetails::kInitialIndex));
a.Store(AccessBuilder::ForDictionaryObjectHashIndex(),
jsgraph()->SmiConstant(PropertyArray::kNoHashSentinel));
// Initialize the Properties fields.
Node* undefined = jsgraph()->UndefinedConstant();
STATIC_ASSERT(NameDictionary::kElementsStartIndex ==
NameDictionary::kObjectHashIndex + 1);
for (int index = NameDictionary::kElementsStartIndex; index < length;
index++) {
a.Store(AccessBuilder::ForFixedArraySlot(index, kNoWriteBarrier),
undefined);
}
properties = effect = a.Finish();
}
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}.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(instance_size, NOT_TENURED, Type::Any());
a.Store(AccessBuilder::ForMap(), instance_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
// Initialize Object fields.
Node* undefined = jsgraph()->UndefinedConstant();
for (int offset = JSObject::kHeaderSize; offset < instance_size;
offset += kPointerSize) {
a.Store(AccessBuilder::ForJSObjectOffset(offset, kNoWriteBarrier),
undefined);
}
Node* value = effect = a.Finish();
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
// Helper that allocates a FixedArray holding argument values recorded in the
// given {frame_state}. Serves as backing store for JSCreateArguments nodes.
Node* JSCreateLowering::AllocateArguments(Node* effect, Node* control,
......
......@@ -73,6 +73,7 @@ class V8_EXPORT_PRIVATE JSCreateLowering final
Handle<Map> initial_map, PretenureFlag pretenure);
Reduction ReduceNewArray(Node* node, std::vector<Node*> values,
Handle<Map> initial_map, PretenureFlag pretenure);
Reduction ReduceJSCreateObject(Node* node);
Node* AllocateArguments(Node* effect, Node* control, Node* frame_state);
Node* AllocateRestArguments(Node* effect, Node* control, Node* frame_state,
......
......@@ -389,6 +389,13 @@ void JSGenericLowering::LowerJSCreateBoundFunction(Node* node) {
UNREACHABLE(); // Eliminated in typed lowering.
}
void JSGenericLowering::LowerJSCreateObject(Node* node) {
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
Callable callable = Builtins::CallableFor(
isolate(), Builtins::kCreateObjectWithoutProperties);
ReplaceWithStubCall(node, callable, flags);
}
void JSGenericLowering::LowerJSCreateClosure(Node* node) {
CreateClosureParameters const& p = CreateClosureParametersOf(node->op());
Handle<SharedFunctionInfo> const shared_info = p.shared_info();
......
......@@ -626,6 +626,7 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
V(CreateKeyValueArray, Operator::kEliminatable, 2, 1) \
V(CreatePromise, Operator::kEliminatable, 0, 1) \
V(CreateTypedArray, Operator::kNoProperties, 5, 1) \
V(CreateObject, Operator::kNoWrite, 1, 1) \
V(HasProperty, Operator::kNoProperties, 2, 1) \
V(HasInPrototypeChain, Operator::kNoProperties, 2, 1) \
V(OrdinaryHasInstance, Operator::kNoProperties, 2, 1) \
......
......@@ -727,6 +727,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* CreateIterResultObject();
const Operator* CreateStringIterator();
const Operator* CreateKeyValueArray();
const Operator* CreateObject();
const Operator* CreatePromise();
const Operator* CreateTypedArray();
const Operator* CreateLiteralArray(Handle<ConstantElementsPair> constant,
......
......@@ -144,6 +144,7 @@
V(JSCreateIterResultObject) \
V(JSCreateStringIterator) \
V(JSCreateKeyValueArray) \
V(JSCreateObject) \
V(JSCreatePromise) \
V(JSCreateTypedArray) \
V(JSCreateLiteralArray) \
......
......@@ -75,6 +75,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
case IrOpcode::kJSCreateLiteralArray:
case IrOpcode::kJSCreateLiteralObject:
case IrOpcode::kJSCreateLiteralRegExp:
case IrOpcode::kJSCreateObject:
// Property access operations
case IrOpcode::kJSLoadNamed:
......
......@@ -1241,6 +1241,10 @@ Type* Typer::Visitor::TypeJSCreateKeyValueArray(Node* node) {
return Type::OtherObject();
}
Type* Typer::Visitor::TypeJSCreateObject(Node* node) {
return Type::OtherObject();
}
Type* Typer::Visitor::TypeJSCreatePromise(Node* node) {
return Type::OtherObject();
}
......@@ -1269,7 +1273,6 @@ Type* Typer::Visitor::TypeJSCreateLiteralRegExp(Node* node) {
return Type::OtherObject();
}
Type* Typer::Visitor::TypeJSLoadProperty(Node* node) {
return Type::NonInternal();
}
......
......@@ -693,6 +693,10 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
// Type is OtherObject.
CheckTypeIs(node, Type::OtherObject());
break;
case IrOpcode::kJSCreateObject:
// Type is Object.
CheckTypeIs(node, Type::OtherObject());
break;
case IrOpcode::kJSCreatePromise:
// Type is OtherObject.
CheckTypeIs(node, Type::OtherObject());
......
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