[literals] Introduce CreateEmptyArrayLiteral Bytecode

Empty Array literals are amongst the most commonly used literal types on our
top25 page list. Using a custom bytecode we can drop the boilerplate for empty
Array literals alltogether. However, we still need a proper AllocationSite to
track ElementsKind transitions.

Bug: v8:6211, chromium:746935
Change-Id: I891eaa778e4e81e138e483a65f04ae00ae30bd28
Reviewed-on: https://chromium-review.googlesource.com/580932
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46875}
This commit is contained in:
Camillo Bruni 2017-07-24 14:12:34 +02:00 committed by Commit Bot
parent b3ff283754
commit 0392eb20ac
29 changed files with 589 additions and 45 deletions

View File

@ -1442,6 +1442,12 @@ class ArrayLiteral final : public AggregateLiteral {
ZoneList<Expression*>* values() const { return values_; } ZoneList<Expression*>* values() const { return values_; }
bool is_empty() const {
DCHECK(is_initialized());
return values()->is_empty() &&
(constant_elements().is_null() || constant_elements()->is_empty());
}
// Populate the depth field and flags, returns the depth. // Populate the depth field and flags, returns the depth.
int InitDepthAndFlags(); int InitDepthAndFlags();

View File

@ -192,14 +192,6 @@ Node* ConstructorBuiltinsAssembler::EmitFastNewClosure(Node* shared_info,
return result; return result;
} }
Node* ConstructorBuiltinsAssembler::LoadFeedbackVectorSlot(
Node* closure, Node* literal_index) {
Node* cell = LoadObjectField(closure, JSFunction::kFeedbackVectorOffset);
Node* feedback_vector = LoadObjectField(cell, Cell::kValueOffset);
return LoadFixedArrayElement(feedback_vector, literal_index, 0,
CodeStubAssembler::SMI_PARAMETERS);
}
Node* ConstructorBuiltinsAssembler::NotHasBoilerplate(Node* literal_site) { Node* ConstructorBuiltinsAssembler::NotHasBoilerplate(Node* literal_site) {
return TaggedIsSmi(literal_site); return TaggedIsSmi(literal_site);
} }
@ -560,6 +552,56 @@ TF_BUILTIN(FastCloneShallowArrayDontTrack, ConstructorBuiltinsAssembler) {
CreateFastCloneShallowArrayBuiltin(DONT_TRACK_ALLOCATION_SITE); CreateFastCloneShallowArrayBuiltin(DONT_TRACK_ALLOCATION_SITE);
} }
Node* ConstructorBuiltinsAssembler::EmitCreateEmptyArrayLiteral(
Node* closure, Node* literal_index, Node* context) {
// Array literals always have a valid AllocationSite to properly track
// elements transitions.
VARIABLE(allocation_site, MachineRepresentation::kTagged,
LoadFeedbackVectorSlot(closure, literal_index));
Label create_empty_array(this),
initialize_allocation_site(this, Label::kDeferred), done(this);
Branch(TaggedIsSmi(allocation_site.value()), &initialize_allocation_site,
&create_empty_array);
// TODO(cbruni): create the AllocationSite in CSA.
BIND(&initialize_allocation_site);
{
Node* feedback_vector = LoadFeedbackVector(closure);
allocation_site.Bind(
CreateAllocationSiteInFeedbackVector(feedback_vector, literal_index));
Goto(&create_empty_array);
}
BIND(&create_empty_array);
CSA_ASSERT(this, IsAllocationSite(allocation_site.value()));
Node* kind = SmiToWord32(
LoadObjectField(allocation_site.value(),
AllocationSite::kTransitionInfoOrBoilerplateOffset));
CSA_ASSERT(this, IsFastElementsKind(kind));
Node* native_context = LoadNativeContext(context);
Comment("LoadJSArrayElementsMap");
Node* array_map = LoadJSArrayElementsMap(kind, native_context);
Node* zero = SmiConstant(0);
Comment("Allocate JSArray");
Node* result =
AllocateJSArray(GetInitialFastElementsKind(), array_map, zero, zero,
allocation_site.value(), ParameterMode::SMI_PARAMETERS);
Goto(&done);
BIND(&done);
return result;
}
TF_BUILTIN(CreateEmptyArrayLiteral, ConstructorBuiltinsAssembler) {
Node* closure = Parameter(Descriptor::kClosure);
Node* literal_index = Parameter(Descriptor::kLiteralIndex);
Node* context = Parameter(Descriptor::kContext);
Node* result = EmitCreateEmptyArrayLiteral(closure, literal_index, context);
Return(result);
}
Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowObject( Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowObject(
Label* call_runtime, Node* closure, Node* literals_index) { Label* call_runtime, Node* closure, Node* literals_index) {
Node* allocation_site = LoadFeedbackVectorSlot(closure, literals_index); Node* allocation_site = LoadFeedbackVectorSlot(closure, literals_index);

View File

@ -26,6 +26,8 @@ class ConstructorBuiltinsAssembler : public CodeStubAssembler {
Node* context, Label* call_runtime, Node* context, Label* call_runtime,
AllocationSiteMode allocation_site_mode); AllocationSiteMode allocation_site_mode);
Node* EmitCreateEmptyArrayLiteral(Node* closure, Node* iteral_index,
Node* context);
void CreateFastCloneShallowArrayBuiltin( void CreateFastCloneShallowArrayBuiltin(
AllocationSiteMode allocation_site_mode); AllocationSiteMode allocation_site_mode);
@ -43,7 +45,6 @@ class ConstructorBuiltinsAssembler : public CodeStubAssembler {
Node* capacity, ElementsKind kind); Node* capacity, ElementsKind kind);
Node* CopyFixedArrayBase(Node* elements); Node* CopyFixedArrayBase(Node* elements);
Node* LoadFeedbackVectorSlot(Node* closure, Node* literal_index);
Node* NotHasBoilerplate(Node* literal_site); Node* NotHasBoilerplate(Node* literal_site);
Node* LoadAllocationSiteBoilerplate(Node* allocation_site); Node* LoadAllocationSiteBoilerplate(Node* allocation_site);
}; };

View File

@ -103,6 +103,7 @@ namespace internal {
TFC(FastCloneRegExp, FastCloneRegExp, 1) \ TFC(FastCloneRegExp, FastCloneRegExp, 1) \
TFC(FastCloneShallowArrayTrack, FastCloneShallowArray, 1) \ TFC(FastCloneShallowArrayTrack, FastCloneShallowArray, 1) \
TFC(FastCloneShallowArrayDontTrack, FastCloneShallowArray, 1) \ TFC(FastCloneShallowArrayDontTrack, FastCloneShallowArray, 1) \
TFS(CreateEmptyArrayLiteral, kClosure, kLiteralIndex) \
TFC(FastCloneShallowObject, FastCloneShallowObject, 1) \ TFC(FastCloneShallowObject, FastCloneShallowObject, 1) \
/* ES6 section 9.5.14 [[Construct]] ( argumentsList, newTarget) */ \ /* ES6 section 9.5.14 [[Construct]] ( argumentsList, newTarget) */ \
TFC(ConstructProxy, ConstructTrampoline, 1) \ TFC(ConstructProxy, ConstructTrampoline, 1) \

View File

@ -1430,6 +1430,15 @@ Node* CodeStubAssembler::LoadNativeContext(Node* context) {
return LoadContextElement(context, Context::NATIVE_CONTEXT_INDEX); return LoadContextElement(context, Context::NATIVE_CONTEXT_INDEX);
} }
Node* CodeStubAssembler::LoadJSArrayElementsMap(Node* kind,
Node* native_context) {
CSA_ASSERT(this, IsFastElementsKind(kind));
CSA_ASSERT(this, IsNativeContext(native_context));
Node* offset = IntPtrAdd(IntPtrConstant(Context::FIRST_JS_ARRAY_MAP_SLOT),
ChangeInt32ToIntPtr(kind));
return LoadContextElement(native_context, offset);
}
Node* CodeStubAssembler::LoadJSArrayElementsMap(ElementsKind kind, Node* CodeStubAssembler::LoadJSArrayElementsMap(ElementsKind kind,
Node* native_context) { Node* native_context) {
CSA_ASSERT(this, IsNativeContext(native_context)); CSA_ASSERT(this, IsNativeContext(native_context));
@ -4944,6 +4953,16 @@ Node* CodeStubAssembler::DecodeWord(Node* word, uint32_t shift, uint32_t mask) {
return WordShr(WordAnd(word, IntPtrConstant(mask)), static_cast<int>(shift)); return WordShr(WordAnd(word, IntPtrConstant(mask)), static_cast<int>(shift));
} }
Node* CodeStubAssembler::UpdateWord(Node* word, Node* value, uint32_t shift,
uint32_t mask) {
Node* encoded_value = WordShl(value, static_cast<int>(shift));
Node* inverted_mask = IntPtrConstant(~static_cast<intptr_t>(mask));
// Ensure the {value} fits fully in the mask.
CSA_ASSERT(this, WordEqual(WordAnd(encoded_value, inverted_mask),
IntPtrConstant(0)));
return WordOr(WordAnd(word, inverted_mask), encoded_value);
}
void CodeStubAssembler::SetCounter(StatsCounter* counter, int value) { void CodeStubAssembler::SetCounter(StatsCounter* counter, int value) {
if (FLAG_native_code_counters && counter->Enabled()) { if (FLAG_native_code_counters && counter->Enabled()) {
Node* counter_address = ExternalConstant(ExternalReference(counter)); Node* counter_address = ExternalConstant(ExternalReference(counter));
@ -6334,11 +6353,30 @@ Node* CodeStubAssembler::ElementOffsetFromIndex(Node* index_node,
return IntPtrAdd(IntPtrConstant(base_size), shifted_index); return IntPtrAdd(IntPtrConstant(base_size), shifted_index);
} }
Node* CodeStubAssembler::LoadFeedbackVector(Node* closure) {
Node* cell = LoadObjectField(closure, JSFunction::kFeedbackVectorOffset);
return LoadObjectField(cell, Cell::kValueOffset);
}
Node* CodeStubAssembler::LoadFeedbackVectorForStub() { Node* CodeStubAssembler::LoadFeedbackVectorForStub() {
Node* function = Node* function =
LoadFromParentFrame(JavaScriptFrameConstants::kFunctionOffset); LoadFromParentFrame(JavaScriptFrameConstants::kFunctionOffset);
Node* cell = LoadObjectField(function, JSFunction::kFeedbackVectorOffset); return LoadFeedbackVector(function);
return LoadObjectField(cell, Cell::kValueOffset); }
Node* CodeStubAssembler::LoadFeedbackVectorSlot(Node* closure,
Node* smi_index) {
Node* feedback_vector = LoadFeedbackVector(closure);
return LoadFixedArrayElement(feedback_vector, smi_index, 0,
CodeStubAssembler::SMI_PARAMETERS);
}
void CodeStubAssembler::StoreFeedbackVectorSlot(Node* closure, Node* smi_index,
Node* value) {
Node* feedback_vector = LoadFeedbackVector(closure);
StoreFixedArrayElement(feedback_vector, smi_index, value,
UPDATE_WRITE_BARRIER, 0,
CodeStubAssembler::SMI_PARAMETERS);
} }
void CodeStubAssembler::UpdateFeedback(Node* feedback, Node* feedback_vector, void CodeStubAssembler::UpdateFeedback(Node* feedback, Node* feedback_vector,
@ -6942,11 +6980,12 @@ Node* CodeStubAssembler::CreateAllocationSiteInFeedbackVector(
Node* feedback_vector, Node* slot) { Node* feedback_vector, Node* slot) {
Node* size = IntPtrConstant(AllocationSite::kSize); Node* size = IntPtrConstant(AllocationSite::kSize);
Node* site = Allocate(size, CodeStubAssembler::kPretenured); Node* site = Allocate(size, CodeStubAssembler::kPretenured);
StoreMapNoWriteBarrier(site, Heap::kAllocationSiteMapRootIndex);
StoreMap(site, AllocationSiteMapConstant()); // Should match AllocationSite::Initialize.
Node* kind = SmiConstant(GetInitialFastElementsKind()); Node* field = UpdateWord<AllocationSite::ElementsKindBits>(
IntPtrConstant(0), IntPtrConstant(GetInitialFastElementsKind()));
StoreObjectFieldNoWriteBarrier( StoreObjectFieldNoWriteBarrier(
site, AllocationSite::kTransitionInfoOrBoilerplateOffset, kind); site, AllocationSite::kTransitionInfoOrBoilerplateOffset, SmiTag(field));
// Unlike literals, constructed arrays don't have nested sites // Unlike literals, constructed arrays don't have nested sites
Node* zero = SmiConstant(0); Node* zero = SmiConstant(0);

View File

@ -488,6 +488,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* LoadNativeContext(Node* context); Node* LoadNativeContext(Node* context);
Node* LoadJSArrayElementsMap(ElementsKind kind, Node* native_context); Node* LoadJSArrayElementsMap(ElementsKind kind, Node* native_context);
Node* LoadJSArrayElementsMap(Node* kind, Node* native_context);
// Load the "prototype" property of a JSFunction. // Load the "prototype" property of a JSFunction.
Node* LoadJSFunctionPrototype(Node* function, Label* if_bailout); Node* LoadJSFunctionPrototype(Node* function, Label* if_bailout);
@ -949,31 +950,31 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
ToIntegerTruncationMode mode = kNoTruncation); ToIntegerTruncationMode mode = kNoTruncation);
// Returns a node that contains a decoded (unsigned!) value of a bit // Returns a node that contains a decoded (unsigned!) value of a bit
// field |T| in |word32|. Returns result as an uint32 node. // field |BitField| in |word32|. Returns result as an uint32 node.
template <typename T> template <typename BitField>
Node* DecodeWord32(Node* word32) { Node* DecodeWord32(Node* word32) {
return DecodeWord32(word32, T::kShift, T::kMask); return DecodeWord32(word32, BitField::kShift, BitField::kMask);
} }
// Returns a node that contains a decoded (unsigned!) value of a bit // Returns a node that contains a decoded (unsigned!) value of a bit
// field |T| in |word|. Returns result as a word-size node. // field |BitField| in |word|. Returns result as a word-size node.
template <typename T> template <typename BitField>
Node* DecodeWord(Node* word) { Node* DecodeWord(Node* word) {
return DecodeWord(word, T::kShift, T::kMask); return DecodeWord(word, BitField::kShift, BitField::kMask);
} }
// Returns a node that contains a decoded (unsigned!) value of a bit // Returns a node that contains a decoded (unsigned!) value of a bit
// field |T| in |word32|. Returns result as a word-size node. // field |BitField| in |word32|. Returns result as a word-size node.
template <typename T> template <typename BitField>
Node* DecodeWordFromWord32(Node* word32) { Node* DecodeWordFromWord32(Node* word32) {
return DecodeWord<T>(ChangeUint32ToWord(word32)); return DecodeWord<BitField>(ChangeUint32ToWord(word32));
} }
// Returns a node that contains a decoded (unsigned!) value of a bit // Returns a node that contains a decoded (unsigned!) value of a bit
// field |T| in |word|. Returns result as an uint32 node. // field |BitField| in |word|. Returns result as an uint32 node.
template <typename T> template <typename BitField>
Node* DecodeWord32FromWord(Node* word) { Node* DecodeWord32FromWord(Node* word) {
return TruncateWordToWord32(DecodeWord<T>(word)); return TruncateWordToWord32(DecodeWord<BitField>(word));
} }
// Decodes an unsigned (!) value from |word32| to an uint32 node. // Decodes an unsigned (!) value from |word32| to an uint32 node.
@ -982,6 +983,16 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Decodes an unsigned (!) value from |word| to a word-size node. // Decodes an unsigned (!) value from |word| to a word-size node.
Node* DecodeWord(Node* word, uint32_t shift, uint32_t mask); Node* DecodeWord(Node* word, uint32_t shift, uint32_t mask);
// Returns a node that contains the updated values of a |BitField|.
template <typename BitField>
Node* UpdateWord(Node* word, Node* value) {
return UpdateWord(word, value, BitField::kShift, BitField::kMask);
}
// Returns a node that contains the updated {value} inside {word} starting
// at {shift} and fitting in {mask}.
Node* UpdateWord(Node* word, Node* value, uint32_t shift, uint32_t mask);
// Returns true if any of the |T|'s bits in given |word32| are set. // Returns true if any of the |T|'s bits in given |word32| are set.
template <typename T> template <typename T>
Node* IsSetWord32(Node* word32) { Node* IsSetWord32(Node* word32) {
@ -1302,6 +1313,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Load type feedback vector from the stub caller's frame. // Load type feedback vector from the stub caller's frame.
Node* LoadFeedbackVectorForStub(); Node* LoadFeedbackVectorForStub();
Node* LoadFeedbackVector(Node* closure);
Node* LoadFeedbackVectorSlot(Node* closure, Node* smi_index);
void StoreFeedbackVectorSlot(Node* closure, Node* smi_index, Node* value);
// Update the type feedback vector. // Update the type feedback vector.
void UpdateFeedback(Node* feedback, Node* feedback_vector, Node* slot_id, void UpdateFeedback(Node* feedback, Node* feedback_vector, Node* slot_id,
Node* function); Node* function);

View File

@ -1295,6 +1295,13 @@ void BytecodeGraphBuilder::VisitCreateArrayLiteral() {
environment()->BindAccumulator(literal, Environment::kAttachFrameState); environment()->BindAccumulator(literal, Environment::kAttachFrameState);
} }
void BytecodeGraphBuilder::VisitCreateEmptyArrayLiteral() {
int literal_index = bytecode_iterator().GetIndexOperand(0);
Node* literal = NewNode(javascript()->CreateEmptyLiteralArray(literal_index),
GetFunctionClosure());
environment()->BindAccumulator(literal);
}
void BytecodeGraphBuilder::VisitCreateObjectLiteral() { void BytecodeGraphBuilder::VisitCreateObjectLiteral() {
Handle<BoilerplateDescription> constant_properties = Handle<BoilerplateDescription> constant_properties =
Handle<BoilerplateDescription>::cast( Handle<BoilerplateDescription>::cast(

View File

@ -223,6 +223,8 @@ Reduction JSCreateLowering::Reduce(Node* node) {
return ReduceJSCreateLiteralArrayOrObject(node); return ReduceJSCreateLiteralArrayOrObject(node);
case IrOpcode::kJSCreateLiteralRegExp: case IrOpcode::kJSCreateLiteralRegExp:
return ReduceJSCreateLiteralRegExp(node); return ReduceJSCreateLiteralRegExp(node);
case IrOpcode::kJSCreateEmptyLiteralArray:
return ReduceJSCreateEmptyLiteralArray(node);
case IrOpcode::kJSCreateFunctionContext: case IrOpcode::kJSCreateFunctionContext:
return ReduceJSCreateFunctionContext(node); return ReduceJSCreateFunctionContext(node);
case IrOpcode::kJSCreateWithContext: case IrOpcode::kJSCreateWithContext:
@ -623,7 +625,8 @@ Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) {
Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length, Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length,
int capacity, int capacity,
Handle<AllocationSite> site) { Handle<AllocationSite> site) {
DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode()); DCHECK(node->opcode() == IrOpcode::kJSCreateArray ||
node->opcode() == IrOpcode::kJSCreateEmptyLiteralArray);
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
@ -918,6 +921,23 @@ Reduction JSCreateLowering::ReduceJSCreateLiteralArrayOrObject(Node* node) {
return NoChange(); return NoChange();
} }
Reduction JSCreateLowering::ReduceJSCreateEmptyLiteralArray(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kJSCreateEmptyLiteralArray);
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);
DCHECK(!site->PointsToLiteral());
Node* length = jsgraph()->ZeroConstant();
return ReduceNewArray(node, length, 0, site);
}
}
return NoChange();
}
Reduction JSCreateLowering::ReduceJSCreateLiteralRegExp(Node* node) { Reduction JSCreateLowering::ReduceJSCreateLiteralRegExp(Node* node) {
DCHECK(node->opcode() == IrOpcode::kJSCreateLiteralRegExp); DCHECK(node->opcode() == IrOpcode::kJSCreateLiteralRegExp);
CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op()); CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op());

View File

@ -54,6 +54,7 @@ class V8_EXPORT_PRIVATE JSCreateLowering final
Reduction ReduceJSCreateArray(Node* node); Reduction ReduceJSCreateArray(Node* node);
Reduction ReduceJSCreateIterResultObject(Node* node); Reduction ReduceJSCreateIterResultObject(Node* node);
Reduction ReduceJSCreateKeyValueArray(Node* node); Reduction ReduceJSCreateKeyValueArray(Node* node);
Reduction ReduceJSCreateEmptyLiteralArray(Node* node);
Reduction ReduceJSCreateLiteralArrayOrObject(Node* node); Reduction ReduceJSCreateLiteralArrayOrObject(Node* node);
Reduction ReduceJSCreateLiteralRegExp(Node* node); Reduction ReduceJSCreateLiteralRegExp(Node* node);
Reduction ReduceJSCreateFunctionContext(Node* node); Reduction ReduceJSCreateFunctionContext(Node* node);

View File

@ -476,6 +476,14 @@ void JSGenericLowering::LowerJSCreateLiteralArray(Node* node) {
} }
} }
void JSGenericLowering::LowerJSCreateEmptyLiteralArray(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::kCreateEmptyArrayLiteral);
ReplaceWithStubCall(node, callable, flags);
}
void JSGenericLowering::LowerJSCreateLiteralObject(Node* node) { void JSGenericLowering::LowerJSCreateLiteralObject(Node* node) {
CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op()); CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op());

View File

@ -1020,6 +1020,15 @@ const Operator* JSOperatorBuilder::CreateLiteralArray(
parameters); // parameter parameters); // parameter
} }
const Operator* JSOperatorBuilder::CreateEmptyLiteralArray(int literal_index) {
return new (zone()) Operator1<int>( // --
IrOpcode::kJSCreateEmptyLiteralArray, // opcode
Operator::kNoProperties, // properties
"JSCreateEmptyLiteralArray", // name
1, 1, 1, 1, 1, 2, // counts
literal_index); // parameter
}
const Operator* JSOperatorBuilder::CreateLiteralObject( const Operator* JSOperatorBuilder::CreateLiteralObject(
Handle<BoilerplateDescription> constant_properties, int literal_flags, Handle<BoilerplateDescription> constant_properties, int literal_flags,
int literal_index, int number_of_properties) { int literal_index, int number_of_properties) {

View File

@ -636,6 +636,8 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* CreateLiteralArray(Handle<ConstantElementsPair> constant, const Operator* CreateLiteralArray(Handle<ConstantElementsPair> constant,
int literal_flags, int literal_index, int literal_flags, int literal_index,
int number_of_elements); int number_of_elements);
const Operator* CreateEmptyLiteralArray(int literal_index);
const Operator* CreateLiteralObject(Handle<BoilerplateDescription> constant, const Operator* CreateLiteralObject(Handle<BoilerplateDescription> constant,
int literal_flags, int literal_index, int literal_flags, int literal_index,
int number_of_properties); int number_of_properties);

View File

@ -135,6 +135,7 @@
V(JSCreateIterResultObject) \ V(JSCreateIterResultObject) \
V(JSCreateKeyValueArray) \ V(JSCreateKeyValueArray) \
V(JSCreateLiteralArray) \ V(JSCreateLiteralArray) \
V(JSCreateEmptyLiteralArray) \
V(JSCreateLiteralObject) \ V(JSCreateLiteralObject) \
V(JSCreateLiteralRegExp) \ V(JSCreateLiteralRegExp) \
V(JSLoadProperty) \ V(JSLoadProperty) \

View File

@ -1124,6 +1124,9 @@ Type* Typer::Visitor::TypeJSCreateLiteralArray(Node* node) {
return Type::Array(); return Type::Array();
} }
Type* Typer::Visitor::TypeJSCreateEmptyLiteralArray(Node* node) {
return Type::Array();
}
Type* Typer::Visitor::TypeJSCreateLiteralObject(Node* node) { Type* Typer::Visitor::TypeJSCreateLiteralObject(Node* node) {
return Type::OtherObject(); return Type::OtherObject();

View File

@ -611,6 +611,10 @@ void Verifier::Visitor::Check(Node* node) {
// Type is Array. // Type is Array.
CheckTypeIs(node, Type::Array()); CheckTypeIs(node, Type::Array());
break; break;
case IrOpcode::kJSCreateEmptyLiteralArray:
// Type is Array.
CheckTypeIs(node, Type::Array());
break;
case IrOpcode::kJSCreateLiteralObject: case IrOpcode::kJSCreateLiteralObject:
case IrOpcode::kJSCreateLiteralRegExp: case IrOpcode::kJSCreateLiteralRegExp:
// Type is OtherObject. // Type is OtherObject.

View File

@ -954,6 +954,12 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral(
return *this; return *this;
} }
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateEmptyArrayLiteral(
int literal_index) {
OutputCreateEmptyArrayLiteral(literal_index);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral( BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral(
size_t constant_elements_entry, int literal_index, int flags) { size_t constant_elements_entry, int literal_index, int flags) {
OutputCreateArrayLiteral(constant_elements_entry, literal_index, flags); OutputCreateArrayLiteral(constant_elements_entry, literal_index, flags);

View File

@ -220,6 +220,7 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
int literal_index, int flags); int literal_index, int flags);
BytecodeArrayBuilder& CreateArrayLiteral(size_t constant_elements_entry, BytecodeArrayBuilder& CreateArrayLiteral(size_t constant_elements_entry,
int literal_index, int flags); int literal_index, int flags);
BytecodeArrayBuilder& CreateEmptyArrayLiteral(int literal_index);
BytecodeArrayBuilder& CreateObjectLiteral(size_t constant_properties_entry, BytecodeArrayBuilder& CreateObjectLiteral(size_t constant_properties_entry,
int literal_index, int flags, int literal_index, int flags,
Register output); Register output);

View File

@ -2049,12 +2049,18 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
void BytecodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { void BytecodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
// Deep-copy the literal boilerplate. // Deep-copy the literal boilerplate.
int literal_index = feedback_index(expr->literal_slot());
if (expr->is_empty()) {
// Empty array literal fast-path.
DCHECK(expr->IsFastCloningSupported());
builder()->CreateEmptyArrayLiteral(literal_index);
return;
}
uint8_t flags = CreateArrayLiteralFlags::Encode( uint8_t flags = CreateArrayLiteralFlags::Encode(
expr->IsFastCloningSupported(), expr->ComputeFlags()); expr->IsFastCloningSupported(), expr->ComputeFlags());
size_t entry = builder()->AllocateDeferredConstantPoolEntry(); size_t entry = builder()->AllocateDeferredConstantPoolEntry();
builder()->CreateArrayLiteral(entry, feedback_index(expr->literal_slot()), builder()->CreateArrayLiteral(entry, literal_index, flags);
flags);
array_literals_.push_back(std::make_pair(expr, entry)); array_literals_.push_back(std::make_pair(expr, entry));
Register index, literal; Register index, literal;

View File

@ -227,6 +227,7 @@ namespace interpreter {
OperandType::kIdx, OperandType::kFlag8) \ OperandType::kIdx, OperandType::kFlag8) \
V(CreateArrayLiteral, AccumulatorUse::kWrite, OperandType::kIdx, \ V(CreateArrayLiteral, AccumulatorUse::kWrite, OperandType::kIdx, \
OperandType::kIdx, OperandType::kFlag8) \ OperandType::kIdx, OperandType::kFlag8) \
V(CreateEmptyArrayLiteral, AccumulatorUse::kWrite, OperandType::kIdx) \
V(CreateObjectLiteral, AccumulatorUse::kNone, OperandType::kIdx, \ V(CreateObjectLiteral, AccumulatorUse::kNone, OperandType::kIdx, \
OperandType::kIdx, OperandType::kFlag8, OperandType::kRegOut) \ OperandType::kIdx, OperandType::kFlag8, OperandType::kRegOut) \
\ \

View File

@ -2654,6 +2654,20 @@ IGNITION_HANDLER(CreateArrayLiteral, InterpreterAssembler) {
} }
} }
// CreateEmptyArrayLiteral <literal_idx>
//
// Creates an empty JSArray literal for literal index <literal_idx>.
IGNITION_HANDLER(CreateEmptyArrayLiteral, InterpreterAssembler) {
Node* literal_index = BytecodeOperandIdxSmi(0);
Node* closure = LoadRegister(Register::function_closure());
Node* context = GetContext();
ConstructorBuiltinsAssembler constructor_assembler(state());
Node* result = constructor_assembler.EmitCreateEmptyArrayLiteral(
closure, literal_index, context);
SetAccumulator(result);
Dispatch();
}
// CreateObjectLiteral <element_idx> <literal_idx> <flags> // CreateObjectLiteral <element_idx> <literal_idx> <flags>
// //
// Creates an object literal for literal index <literal_idx> with // Creates an object literal for literal index <literal_idx> with

View File

@ -4435,6 +4435,9 @@ ACCESSORS(ContextExtension, extension, Object, kExtensionOffset)
SMI_ACCESSORS(ConstantElementsPair, elements_kind, kElementsKindOffset) SMI_ACCESSORS(ConstantElementsPair, elements_kind, kElementsKindOffset)
ACCESSORS(ConstantElementsPair, constant_values, FixedArrayBase, ACCESSORS(ConstantElementsPair, constant_values, FixedArrayBase,
kConstantValuesOffset) kConstantValuesOffset)
bool ConstantElementsPair::is_empty() const {
return constant_values()->length() == 0;
}
ACCESSORS(JSModuleNamespace, module, Module, kModuleOffset) ACCESSORS(JSModuleNamespace, module, Module, kModuleOffset)

View File

@ -45,6 +45,8 @@ class ConstantElementsPair : public Tuple2 {
DECL_INT_ACCESSORS(elements_kind) DECL_INT_ACCESSORS(elements_kind)
DECL_ACCESSORS(constant_values, FixedArrayBase) DECL_ACCESSORS(constant_values, FixedArrayBase)
inline bool is_empty() const;
DECL_CAST(ConstantElementsPair) DECL_CAST(ConstantElementsPair)
static const int kElementsKindOffset = kValue1Offset; static const int kElementsKindOffset = kValue1Offset;

View File

@ -16,8 +16,8 @@ namespace internal {
namespace { namespace {
bool IsUninitializedLiteralSite(Handle<Object> literal_site) { bool IsUninitializedLiteralSite(Object* literal_site) {
return *literal_site == Smi::kZero; return literal_site == Smi::kZero;
} }
bool HasBoilerplate(Isolate* isolate, Handle<Object> literal_site) { bool HasBoilerplate(Isolate* isolate, Handle<Object> literal_site) {
@ -478,7 +478,7 @@ MaybeHandle<JSObject> CreateLiteral(Isolate* isolate,
// TODO(cbruni): Even in the case where we need an initial allocation site // TODO(cbruni): Even in the case where we need an initial allocation site
// we could still create the boilerplate lazily to save memory. // we could still create the boilerplate lazily to save memory.
if (!needs_initial_allocation_site && if (!needs_initial_allocation_site &&
IsUninitializedLiteralSite(literal_site)) { IsUninitializedLiteralSite(*literal_site)) {
PreInitializeLiteralSite(vector, literals_slot); PreInitializeLiteralSite(vector, literals_slot);
boilerplate = boilerplate =
Boilerplate::Create(isolate, description, flags, NOT_TENURED); Boilerplate::Create(isolate, description, flags, NOT_TENURED);
@ -559,7 +559,7 @@ RUNTIME_FUNCTION(Runtime_CreateRegExpLiteral) {
if (!HasBoilerplate(isolate, literal_site)) { if (!HasBoilerplate(isolate, literal_site)) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION( ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, boilerplate, JSRegExp::New(pattern, JSRegExp::Flags(flags))); isolate, boilerplate, JSRegExp::New(pattern, JSRegExp::Flags(flags)));
if (IsUninitializedLiteralSite(literal_site)) { if (IsUninitializedLiteralSite(*literal_site)) {
PreInitializeLiteralSite(vector, literal_slot); PreInitializeLiteralSite(vector, literal_slot);
return *boilerplate; return *boilerplate;
} }

View File

@ -4011,6 +4011,95 @@ TEST(EnsureAllocationSiteDependentCodesProcessed) {
WeakCell::cast(site->dependent_code()->object_at(0))->cleared()); WeakCell::cast(site->dependent_code()->object_at(0))->cleared());
} }
TEST(AllocationSiteCreation) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
Heap* heap = isolate->heap();
HandleScope scope(isolate);
int prev_count = 0;
int count = 0;
// Array literals.
prev_count = AllocationSitesCount(heap);
CompileRun("(function f1() { return []; })()");
count = AllocationSitesCount(heap);
CHECK_EQ(1, count - prev_count);
prev_count = count;
CompileRun("(function f2() { return [1, 2]; })()");
count = AllocationSitesCount(heap);
CHECK_EQ(1, count - prev_count);
prev_count = count;
CompileRun("(function f3() { return [[1], [2]]; })()");
count = AllocationSitesCount(heap);
CHECK_EQ(3, count - prev_count);
prev_count = count;
CompileRun(
"(function f4() { "
"return [0, [1, 1.1, 1.2, "
"], 1.5, [2.1, 2.2], 3];"
"})()");
count = AllocationSitesCount(heap);
CHECK_EQ(3, count - prev_count);
// Object literals have lazy AllocationSites
prev_count = AllocationSitesCount(heap);
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);
prev_count = AllocationSitesCount(heap);
CompileRun("function f6() { return {a:1}; }; f6(); ");
count = AllocationSitesCount(heap);
CHECK_EQ(0, count - prev_count);
prev_count = AllocationSitesCount(heap);
CompileRun("f6(); ");
count = AllocationSitesCount(heap);
CHECK_EQ(1, count - prev_count);
prev_count = AllocationSitesCount(heap);
CompileRun("function f7() { return {a:1, b:2}; }; f7(); ");
count = AllocationSitesCount(heap);
CHECK_EQ(0, count - prev_count);
prev_count = AllocationSitesCount(heap);
CompileRun("f7(); ");
count = AllocationSitesCount(heap);
CHECK_EQ(1, count - prev_count);
prev_count = AllocationSitesCount(heap);
CompileRun(
"function f8() {"
"return {a:{}, b:{ a:2, c:{ d:{f:{}}} } }; "
"}; f8(); ");
count = AllocationSitesCount(heap);
CHECK_EQ(0, count - prev_count);
prev_count = AllocationSitesCount(heap);
CompileRun("f8(); ");
count = AllocationSitesCount(heap);
CHECK_EQ(6, count - prev_count);
// We currently eagerly create allocation sites if there are sub-arrays.
prev_count = AllocationSitesCount(heap);
CompileRun(
"function f9() {"
"return {a:[1, 2, 3], b:{ a:2, c:{ d:{f:[]} } }}; "
"}; f9(); ");
count = AllocationSitesCount(heap);
CHECK_EQ(6, count - prev_count);
prev_count = AllocationSitesCount(heap);
CompileRun("f9(); ");
count = AllocationSitesCount(heap);
// No new AllocationSites created on the second invocation.
CHECK_EQ(0, count - prev_count);
}
TEST(CellsInOptimizedCodeAreWeak) { TEST(CellsInOptimizedCodeAreWeak) {
if (FLAG_always_opt || !FLAG_opt) return; if (FLAG_always_opt || !FLAG_opt) return;

View File

@ -2526,6 +2526,29 @@ TEST(DirectMemoryTest16BitWord32) {
CHECK_EQ(1, ft.CallChecked<Smi>()->value()); CHECK_EQ(1, ft.CallChecked<Smi>()->value());
} }
TEST(LoadJSArrayElementsMap) {
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 1;
CodeAssemblerTester asm_tester(isolate, kNumParams);
{
CodeStubAssembler m(asm_tester.state());
Node* context = m.Parameter(kNumParams + 2);
Node* native_context = m.LoadNativeContext(context);
Node* kind = m.SmiToWord32(m.Parameter(0));
m.Return(m.LoadJSArrayElementsMap(kind, native_context));
}
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
for (int kind = 0; kind <= HOLEY_DOUBLE_ELEMENTS; kind++) {
Handle<Map> csa_result =
ft.CallChecked<Map>(handle(Smi::FromInt(kind), isolate));
ElementsKind elements_kind = static_cast<ElementsKind>(kind);
Handle<Map> result(
isolate->native_context()->GetInitialJSArrayMap(elements_kind));
CHECK_EQ(*csa_result, *result);
}
}
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8

View File

@ -29,9 +29,9 @@
// Flags: --opt --no-always-opt --no-stress-fullcodegen // Flags: --opt --no-always-opt --no-stress-fullcodegen
var elements_kind = { var elements_kind = {
fast_smi_only : 'fast smi only elements', packed_smi : 'packed smi elements',
fast : 'fast elements', packed : 'packed elements',
fast_double : 'fast double elements', packed_double : 'packed double elements',
dictionary : 'dictionary elements', dictionary : 'dictionary elements',
external_byte : 'external byte elements', external_byte : 'external byte elements',
external_unsigned_byte : 'external unsigned byte elements', external_unsigned_byte : 'external unsigned byte elements',
@ -45,9 +45,9 @@ var elements_kind = {
} }
function getKind(obj) { function getKind(obj) {
if (%HasSmiElements(obj)) return elements_kind.fast_smi_only; if (%HasSmiElements(obj)) return elements_kind.packed_smi;
if (%HasObjectElements(obj)) return elements_kind.fast; if (%HasObjectElements(obj)) return elements_kind.packed;
if (%HasDoubleElements(obj)) return elements_kind.fast_double; if (%HasDoubleElements(obj)) return elements_kind.packed_double;
if (%HasDictionaryElements(obj)) return elements_kind.dictionary; if (%HasDictionaryElements(obj)) return elements_kind.dictionary;
} }
@ -93,7 +93,7 @@ assertOptimized(get_literal);
// Test: make sure allocation site information is updated through a // Test: make sure allocation site information is updated through a
// transition from SMI->DOUBLE->FAST // transition from SMI->DOUBLE->PACKED
(function() { (function() {
function bar(a, b, c) { function bar(a, b, c) {
return [a, b, c]; return [a, b, c];
@ -103,5 +103,132 @@ assertOptimized(get_literal);
a[0] = 3.5; a[0] = 3.5;
a[1] = 'hi'; a[1] = 'hi';
b = bar(1, 2, 3); b = bar(1, 2, 3);
assertKind(elements_kind.fast, b); assertKind(elements_kind.packed, b);
})();
(function changeOptimizedEmptyArrayKind() {
function f() {
return new Array();
}
var a = f();
assertKind('packed smi elements', a);
a = f();
assertKind('packed smi elements', a);
a = f();
a.push(0.5);
assertKind('packed double elements', a);
%OptimizeFunctionOnNextCall(f);
a = f();
assertKind('packed double elements', a);
})();
(function changeOptimizedArrayLiteralKind() {
function f() {
return [1, 2];
}
var a = f();
assertKind('packed smi elements', a);
a = f();
a.push(0.5);
assertKind('packed double elements', a);
a = f();
assertKind('packed double elements', a);
assertFalse(isHoley(a));
a = f();
a.push(undefined);
assertKind('packed elements', a);
assertFalse(isHoley(a));
a = f();
assertKind('packed elements', a);
assertFalse(isHoley(a));
%OptimizeFunctionOnNextCall(f);
a = f();
assertKind('packed elements', a);
assertFalse(isHoley(a));
a = f();
assertKind('packed elements', a);
assertFalse(isHoley(a));
})();
(function changeOptimizedEmptyArrayLiteralKind() {
function f() {
return [];
}
var a = f();
assertKind('packed smi elements', a);
assertFalse(isHoley(a));
a = f();
a.push(0.5);
assertKind('packed double elements', a);
assertFalse(isHoley(a));
a = f();
assertKind('packed double elements', a);
assertFalse(isHoley(a));
%OptimizeFunctionOnNextCall(f);
a = f();
assertKind('packed double elements', a);
assertFalse(isHoley(a));
a = f();
assertKind('packed double elements', a);
assertFalse(isHoley(a));
})();
(function changeEmptyArrayLiteralKind2() {
function f() {
var literal = [];
%HeapObjectVerify(literal);
return literal;
}
var a = f();
assertKind('packed smi elements', a);
assertFalse(isHoley(a));
a = f();
a.push(0.5);
assertKind('packed double elements', a);
assertFalse(isHoley(a));
a = f();
assertKind('packed double elements', a);
assertFalse(isHoley(a));
a = f();
a.push(undefined);
assertKind('packed elements', a);
assertFalse(isHoley(a));
a = f();
assertKind('packed elements', a);
assertFalse(isHoley(a));
a = f();
a[10] = 1;
assertKind('packed elements', a);
assertTrue(isHoley(a));
a = f();
assertKind('packed elements', a);
assertTrue(isHoley(a));
a = f();
a[10000] = 1;
assertKind('dictionary elements', a);
assertFalse(isHoley(a));
a = f();
assertKind('packed elements', a);
assertTrue(isHoley(a));
})(); })();

View File

@ -0,0 +1,112 @@
// Copyright 2017 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.
// This example crashes if the AllocationSites have not been properly added to
// the allocation_sites_list.
// Flags: --expose-gc
function __getProperties() {
return [];
let properties = [];
for (let name of Object.getOwnPropertyNames()) {;
}
return properties;
}
function __getRandomProperty() {
let properties = __getProperties();
if (!properties.length)
return undefined;
return properties[seed % properties.length];
}
var kWasmH0 = 0;
var kWasmH1 = 0x61;
var kWasmH2 = 0x73;
var kWasmH3 = 0x6d;
var kWasmV0 = 0x1;
var kWasmV1 = 0;
var kWasmV2 = 0;
var kWasmV3 = 0;
class Binary extends Array {
emit_header() {
this.push(kWasmH0, kWasmH1, kWasmH2, kWasmH3,
kWasmV0, kWasmV1, kWasmV2, kWasmV3);
}
}
class WasmModuleBuilder {
constructor() {
this.exports = [];
}
addImportedMemory() {}
setFunctionTableLength() {}
toArray() {
let binary = new Binary;
let wasm = this;
binary.emit_header();
"emitting imports @ " + binary.length;
section => {};
var mem_export = (wasm.memory !== undefined && wasm.memory.exp);
var exports_count = wasm.exports.length + (mem_export ? 1 : 0);
return binary;
}
toBuffer() {
let bytes = this.toArray();
let buffer = new ArrayBuffer(bytes.length);
let view = new Uint8Array(buffer);
for (let i = 0; i < bytes.length; i++) {
let val = bytes[i];
view[i] = val | 0;
}
return buffer;
}
instantiate(ffi) {
let module = new WebAssembly.Module(this.toBuffer());
let instance = new WebAssembly.Instance(module);
}
}
var v_40 = 0;
var v_43 = NaN;
try {
v_23 = new WasmModuleBuilder();
} catch (e) {
print("Caught: " + e);
}
try {
v_31 = [0xff];
v_29 = [v_31];
} catch (e) {
print("Caught: " + e);
}
try {
v_25 = ["main"];
gc();
} catch (e) {
print("Caught: " + e);
}
for (var v_28 of [[2]]) {
try {
gc();
} catch (e) {
print("Caught: " + e);
}
}
try {
module = v_23.instantiate();
} catch (e) {
print("Caught: " + e);
}
try {
v_41 = [];
} catch (e) {;
}
for (var v_43 = 0; v_43 < 100000; v_43++) try {
v_41[v_43] = [];
} catch (e) {
"Caught: " + e;
}

View File

@ -351,6 +351,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
builder builder
.CreateRegExpLiteral(ast_factory.GetOneByteString("wide_literal"), 0, 0) .CreateRegExpLiteral(ast_factory.GetOneByteString("wide_literal"), 0, 0)
.CreateArrayLiteral(0, 0, 0) .CreateArrayLiteral(0, 0, 0)
.CreateEmptyArrayLiteral(0)
.CreateObjectLiteral(0, 0, 0, reg); .CreateObjectLiteral(0, 0, 0, reg);
// Emit load and store operations for module variables. // Emit load and store operations for module variables.