Commit 7daf8cf3 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[literals] Add CreateEmptyObjectLiteral bytecode

The quite common empty object literal doesn't need an AllocationSite
since it starts off with the general ElementsKind. By using a separate 
bytecode we can directly instantiate the empty object without jumping
to the runtime first.

Note: this experimentally disables pretenuring for empty object
      literals. Depending on the outcome of our benchmarks pretenuring
      will be enabled again or fully removed for empty object literals.

Bug: v8:6211
Change-Id: I2fee81cbefc70865fc436dbd3bc5fc8de04db91c
Reviewed-on: https://chromium-review.googlesource.com/577555
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47467}
parent 5c47d99e
......@@ -1339,6 +1339,16 @@ class ObjectLiteral final : public AggregateLiteral {
return HasNullPrototypeField::decode(bit_field_);
}
bool is_empty() const {
DCHECK(is_initialized());
return !has_elements() && properties_count() == 0 &&
properties()->length() == 0;
}
bool IsEmptyObjectLiteral() const {
return is_empty() && !has_null_prototype();
}
// Populate the depth field and flags, returns the depth.
int InitDepthAndFlags();
......
......@@ -777,5 +777,31 @@ TF_BUILTIN(FastCloneShallowObject, ConstructorBuiltinsAssembler) {
literals_index, boilerplate_description, flags);
}
// Used by the CreateEmptyObjectLiteral stub and bytecode.
Node* ConstructorBuiltinsAssembler::EmitCreateEmptyObjectLiteral(
Node* context) {
// TODO(cbruni): check whether we have to enable pretenuring support again for
// the empty object literal.
Node* native_context = LoadNativeContext(context);
Node* object_function =
LoadContextElement(native_context, Context::OBJECT_FUNCTION_INDEX);
Node* map = LoadObjectField(object_function,
JSFunction::kPrototypeOrInitialMapOffset);
CSA_ASSERT(this, IsMap(map));
Node* empty_fixed_array = EmptyFixedArrayConstant();
Node* result =
AllocateJSObjectFromMap(map, empty_fixed_array, empty_fixed_array);
HandleSlackTracking(context, result, map, JSObject::kHeaderSize);
return result;
}
TF_BUILTIN(CreateEmptyObjectLiteral, ConstructorBuiltinsAssembler) {
// TODO(cbruni): remove closure and literal_index paramters once it's clear
// whether we can live without pretenuring support for the empty object
// literal.
Node* context = Parameter(Descriptor::kContext);
Node* result = EmitCreateEmptyObjectLiteral(context);
Return(result);
}
} // namespace internal
} // namespace v8
......@@ -33,6 +33,7 @@ class ConstructorBuiltinsAssembler : public CodeStubAssembler {
Node* EmitFastCloneShallowObject(Label* call_runtime, Node* closure,
Node* literals_index);
Node* EmitCreateEmptyObjectLiteral(Node* context);
Node* EmitFastNewObject(Node* context, Node* target, Node* new_target);
......
......@@ -99,6 +99,7 @@ namespace internal {
TFC(FastCloneShallowArrayTrack, FastCloneShallowArray, 1) \
TFC(FastCloneShallowArrayDontTrack, FastCloneShallowArray, 1) \
TFS(CreateEmptyArrayLiteral, kClosure, kLiteralIndex) \
TFS(CreateEmptyObjectLiteral, kClosure, kLiteralIndex) \
TFC(FastCloneShallowObject, FastCloneShallowObject, 1) \
/* ES6 section 9.5.14 [[Construct]] ( argumentsList, newTarget) */ \
TFC(ConstructProxy, ConstructTrampoline, 1) \
......
......@@ -1539,6 +1539,13 @@ void BytecodeGraphBuilder::VisitCreateObjectLiteral() {
literal, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitCreateEmptyObjectLiteral() {
int literal_index = bytecode_iterator().GetIndexOperand(0);
Node* literal = NewNode(javascript()->CreateEmptyLiteralObject(literal_index),
GetFunctionClosure());
environment()->BindAccumulator(literal);
}
Node* const* BytecodeGraphBuilder::GetCallArgumentsFromRegister(
Node* callee, Node* receiver, interpreter::Register first_arg,
int arg_count) {
......
......@@ -225,6 +225,8 @@ Reduction JSCreateLowering::Reduce(Node* node) {
return ReduceJSCreateLiteralRegExp(node);
case IrOpcode::kJSCreateEmptyLiteralArray:
return ReduceJSCreateEmptyLiteralArray(node);
case IrOpcode::kJSCreateEmptyLiteralObject:
return ReduceJSCreateEmptyLiteralObject(node);
case IrOpcode::kJSCreateFunctionContext:
return ReduceJSCreateFunctionContext(node);
case IrOpcode::kJSCreateWithContext:
......@@ -892,6 +894,43 @@ Reduction JSCreateLowering::ReduceJSCreateKeyValueArray(Node* node) {
return Changed(node);
}
Reduction JSCreateLowering::ReduceNewObject(Node* node,
Handle<AllocationSite> site) {
DCHECK_EQ(IrOpcode::kJSCreateEmptyLiteralObject, node->opcode());
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Extract tenuring feedback from the {site} and add appropriate code
// dependencies on the {site} if deoptimization is enabled.
PretenureFlag pretenure = site->GetPretenureMode();
dependencies()->AssumeTenuringDecision(site);
// Retrieve the initial map for the object.
Handle<Map> map = factory()->ObjectLiteralMapFromCache(native_context(), 0);
DCHECK(!map->is_dictionary_map());
DCHECK(!map->IsInobjectSlackTrackingInProgress());
Node* js_object_map = jsgraph()->HeapConstant(map);
// Setup elements and properties.
Node* elements = jsgraph()->EmptyFixedArrayConstant();
Node* properties = jsgraph()->EmptyFixedArrayConstant();
// Perform the allocation of the actual JSArray object.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(map->instance_size(), pretenure);
a.Store(AccessBuilder::ForMap(), js_object_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
a.Store(AccessBuilder::ForJSObjectElements(), elements);
for (int i = 0; i < map->GetInObjectProperties(); i++) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(map, i),
jsgraph()->UndefinedConstant());
}
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateLiteralArrayOrObject(Node* node) {
DCHECK(node->opcode() == IrOpcode::kJSCreateLiteralArray ||
node->opcode() == IrOpcode::kJSCreateLiteralObject);
......@@ -958,6 +997,23 @@ Reduction JSCreateLowering::ReduceJSCreateLiteralRegExp(Node* node) {
return NoChange();
}
Reduction JSCreateLowering::ReduceJSCreateEmptyLiteralObject(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kJSCreateEmptyLiteralObject);
int literal_index = OpParameter<int>(node);
Handle<FeedbackVector> feedback_vector;
if (GetSpecializationFeedbackVector(node).ToHandle(&feedback_vector)) {
FeedbackSlot slot(FeedbackVector::ToSlot(literal_index));
Handle<Object> raw_site(feedback_vector->Get(slot), isolate());
if (raw_site->IsAllocationSite()) {
Handle<AllocationSite> site = Handle<AllocationSite>::cast(raw_site);
// The empty object literal doesn't need a boilerplate.
DCHECK(!site->PointsToLiteral());
return ReduceNewObject(node, site);
}
}
return NoChange();
}
Reduction JSCreateLowering::ReduceJSCreateFunctionContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateFunctionContext, node->opcode());
const CreateFunctionContextParameters& parameters =
......
......@@ -54,8 +54,9 @@ class V8_EXPORT_PRIVATE JSCreateLowering final
Reduction ReduceJSCreateArray(Node* node);
Reduction ReduceJSCreateIterResultObject(Node* node);
Reduction ReduceJSCreateKeyValueArray(Node* node);
Reduction ReduceJSCreateEmptyLiteralArray(Node* node);
Reduction ReduceJSCreateLiteralArrayOrObject(Node* node);
Reduction ReduceJSCreateEmptyLiteralObject(Node* node);
Reduction ReduceJSCreateEmptyLiteralArray(Node* node);
Reduction ReduceJSCreateLiteralRegExp(Node* node);
Reduction ReduceJSCreateFunctionContext(Node* node);
Reduction ReduceJSCreateWithContext(Node* node);
......@@ -66,6 +67,7 @@ class V8_EXPORT_PRIVATE JSCreateLowering final
Handle<AllocationSite> site);
Reduction ReduceNewArray(Node* node, std::vector<Node*> values,
Handle<AllocationSite> site);
Reduction ReduceNewObject(Node* node, Handle<AllocationSite> site);
Node* AllocateArguments(Node* effect, Node* control, Node* frame_state);
Node* AllocateRestArguments(Node* effect, Node* control, Node* frame_state,
......
......@@ -505,6 +505,14 @@ void JSGenericLowering::LowerJSCreateLiteralObject(Node* node) {
}
}
void JSGenericLowering::LowerJSCreateEmptyLiteralObject(Node* node) {
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
int literal_index = OpParameter<int>(node->op());
node->InsertInput(zone(), 1, jsgraph()->SmiConstant(literal_index));
Callable callable =
Builtins::CallableFor(isolate(), Builtins::kCreateEmptyObjectLiteral);
ReplaceWithStubCall(node, callable, flags);
}
void JSGenericLowering::LowerJSCreateLiteralRegExp(Node* node) {
CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op());
......
......@@ -1045,6 +1045,14 @@ const Operator* JSOperatorBuilder::CreateLiteralObject(
parameters); // parameter
}
const Operator* JSOperatorBuilder::CreateEmptyLiteralObject(int literal_index) {
return new (zone()) Operator1<int>( // --
IrOpcode::kJSCreateEmptyLiteralObject, // opcode
Operator::kNoProperties, // properties
"JSCreateEmptyLiteralObject", // name
1, 1, 1, 1, 1, 2, // counts
literal_index); // parameter
}
const Operator* JSOperatorBuilder::CreateLiteralRegExp(
Handle<String> constant_pattern, int literal_flags, int literal_index) {
......
......@@ -637,6 +637,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
int literal_flags, int literal_index,
int number_of_elements);
const Operator* CreateEmptyLiteralArray(int literal_index);
const Operator* CreateEmptyLiteralObject(int literal_index);
const Operator* CreateLiteralObject(Handle<BoilerplateDescription> constant,
int literal_flags, int literal_index,
......
......@@ -139,6 +139,7 @@
V(JSCreateLiteralArray) \
V(JSCreateEmptyLiteralArray) \
V(JSCreateLiteralObject) \
V(JSCreateEmptyLiteralObject) \
V(JSCreateLiteralRegExp) \
V(JSLoadProperty) \
V(JSLoadNamed) \
......
......@@ -1143,6 +1143,9 @@ Type* Typer::Visitor::TypeJSCreateLiteralObject(Node* node) {
return Type::OtherObject();
}
Type* Typer::Visitor::TypeJSCreateEmptyLiteralObject(Node* node) {
return Type::OtherObject();
}
Type* Typer::Visitor::TypeJSCreateLiteralRegExp(Node* node) {
return Type::OtherObject();
......
......@@ -618,6 +618,7 @@ void Verifier::Visitor::Check(Node* node) {
CheckTypeIs(node, Type::Array());
break;
case IrOpcode::kJSCreateLiteralObject:
case IrOpcode::kJSCreateEmptyLiteralObject:
case IrOpcode::kJSCreateLiteralRegExp:
// Type is OtherObject.
CheckTypeIs(node, Type::OtherObject());
......
......@@ -409,7 +409,9 @@ bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) {
case Bytecode::kCreateWithContext:
// Literals.
case Bytecode::kCreateArrayLiteral:
case Bytecode::kCreateEmptyArrayLiteral:
case Bytecode::kCreateObjectLiteral:
case Bytecode::kCreateEmptyObjectLiteral:
case Bytecode::kCreateRegExpLiteral:
// Allocations.
case Bytecode::kCreateClosure:
......
......@@ -977,6 +977,12 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateEmptyObjectLiteral(
int literal_index) {
OutputCreateEmptyObjectLiteral(literal_index);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::PushContext(Register context) {
OutputPushContext(context);
return *this;
......
......@@ -225,6 +225,7 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
BytecodeArrayBuilder& CreateObjectLiteral(size_t constant_properties_entry,
int literal_index, int flags,
Register output);
BytecodeArrayBuilder& CreateEmptyObjectLiteral(int literal_index);
// Push the context in accumulator as the new context, and store in register
// |context|.
......
......@@ -1892,6 +1892,16 @@ void BytecodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
}
void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
int literal_index = feedback_index(expr->literal_slot());
// Fast path for the empty object literal which doesn't need an
// AllocationSite.
if (expr->IsEmptyObjectLiteral()) {
DCHECK(expr->IsFastCloningSupported());
builder()->CreateEmptyObjectLiteral(literal_index);
return;
}
// Deep-copy the literal boilerplate.
uint8_t flags = CreateObjectLiteralFlags::Encode(
expr->ComputeFlags(), expr->IsFastCloningSupported());
......@@ -1909,8 +1919,7 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
// TODO(cbruni): Directly generate runtime call for literals we cannot
// optimize once the FastCloneShallowObject stub is in sync with the TF
// optimizations.
builder()->CreateObjectLiteral(entry, feedback_index(expr->literal_slot()),
flags, literal);
builder()->CreateObjectLiteral(entry, literal_index, flags, literal);
// Store computed values into the literal.
int property_index = 0;
......
......@@ -230,6 +230,7 @@ namespace interpreter {
V(CreateEmptyArrayLiteral, AccumulatorUse::kWrite, OperandType::kIdx) \
V(CreateObjectLiteral, AccumulatorUse::kNone, OperandType::kIdx, \
OperandType::kIdx, OperandType::kFlag8, OperandType::kRegOut) \
V(CreateEmptyObjectLiteral, AccumulatorUse::kWrite, OperandType::kIdx) \
\
/* Closure allocation */ \
V(CreateClosure, AccumulatorUse::kWrite, OperandType::kIdx, \
......
......@@ -2721,6 +2721,19 @@ IGNITION_HANDLER(CreateObjectLiteral, InterpreterAssembler) {
}
}
// CreateEmptyObjectLiteral <literal_idx>
//
// Creates an empty JSObject literal for literal index <literal_idx>.
IGNITION_HANDLER(CreateEmptyObjectLiteral, InterpreterAssembler) {
// TODO(cbruni): remove literal_index and closure parameter once we know
// whether empty object literals work without pretenuring support.
Node* context = GetContext();
ConstructorBuiltinsAssembler constructor_assembler(state());
Node* result = constructor_assembler.EmitCreateEmptyObjectLiteral(context);
SetAccumulator(result);
Dispatch();
}
// CreateClosure <index> <slot> <tenured>
//
// Creates a new closure for SharedFunctionInfo at position |index| in the
......
......@@ -3884,11 +3884,13 @@ TEST(AllocationSiteCreation) {
CompileRun("function f5() { return {}; }; f5(); ");
count = AllocationSitesCount(heap);
CHECK_EQ(0, count - prev_count);
// Allocation-sites + boilerplates are created on the second run only.
prev_count = AllocationSitesCount(heap);
CompileRun("f5(); ");
count = AllocationSitesCount(heap);
CHECK_EQ(1, count - prev_count);
// No AllocationSites are created for the empty object literal.
for (int i = 0; i < 5; i++) {
prev_count = AllocationSitesCount(heap);
CompileRun("f5(); ");
count = AllocationSitesCount(heap);
CHECK_EQ(0, count - prev_count);
}
prev_count = AllocationSitesCount(heap);
CompileRun("function f6() { return {a:1}; }; f6(); ");
......
......@@ -9,17 +9,15 @@ wrap: yes
snippet: "
return { };
"
frame size: 1
frame size: 0
parameter count: 1
bytecode array length: 9
bytecode array length: 4
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 34 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(41), R(0),
B(Ldar), R(0),
/* 34 S> */ B(CreateEmptyObjectLiteral), U8(0),
/* 45 S> */ B(Return),
]
constant pool: [
FIXED_ARRAY_TYPE,
]
handlers: [
]
......@@ -333,9 +331,9 @@ handlers: [
snippet: "
var a = 'test'; return { [a]: 1, __proto__: {} };
"
frame size: 5
frame size: 4
parameter count: 1
bytecode array length: 38
bytecode array length: 34
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 42 S> */ B(LdaConstant), U8(0),
......@@ -344,9 +342,9 @@ bytecodes: [
/* 60 E> */ B(ToName), R(2),
B(LdaSmi), I8(1),
B(StaDataPropertyInLiteral), R(1), R(2), U8(0), U8(2),
B(CreateObjectLiteral), U8(1), U8(0), U8(41), R(4),
B(CreateEmptyObjectLiteral), U8(0),
B(Star), R(3),
B(Mov), R(1), R(2),
B(Mov), R(4), R(3),
B(CallRuntime), U16(Runtime::kInternalSetPrototype), R(2), U8(2),
B(Ldar), R(2),
/* 83 S> */ B(Return),
......
......@@ -352,7 +352,8 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.CreateRegExpLiteral(ast_factory.GetOneByteString("wide_literal"), 0, 0)
.CreateArrayLiteral(0, 0, 0)
.CreateEmptyArrayLiteral(0)
.CreateObjectLiteral(0, 0, 0, reg);
.CreateObjectLiteral(0, 0, 0, reg)
.CreateEmptyObjectLiteral(0);
// Emit load and store operations for module variables.
builder.LoadModuleVariable(-1, 42)
......
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