[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:
parent
b3ff283754
commit
0392eb20ac
@ -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();
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
@ -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) \
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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(
|
||||||
|
@ -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());
|
||||||
|
@ -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);
|
||||||
|
@ -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());
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
@ -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) \
|
||||||
|
@ -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();
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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) \
|
||||||
\
|
\
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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));
|
||||||
})();
|
})();
|
||||||
|
@ -147,7 +147,7 @@ array = deopt_array_literal_all_smis(4);
|
|||||||
assertEquals(0, array[0]);
|
assertEquals(0, array[0]);
|
||||||
assertEquals(1, array[1]);
|
assertEquals(1, array[1]);
|
||||||
assertEquals(4, array[2]);
|
assertEquals(4, array[2]);
|
||||||
%OptimizeFunctionOnNextCall(deopt_array_literal_all_smis);
|
%OptimizeFunctionOnNextCall(deopt_array_literal_all_smis);
|
||||||
array = deopt_array_literal_all_smis(5);
|
array = deopt_array_literal_all_smis(5);
|
||||||
array = deopt_array_literal_all_smis(6);
|
array = deopt_array_literal_all_smis(6);
|
||||||
assertOptimized(deopt_array_literal_all_smis);
|
assertOptimized(deopt_array_literal_all_smis);
|
||||||
@ -172,7 +172,7 @@ array = deopt_array_literal_all_doubles(0.5);
|
|||||||
assertEquals(0.5, array[0]);
|
assertEquals(0.5, array[0]);
|
||||||
assertEquals(1, array[1]);
|
assertEquals(1, array[1]);
|
||||||
assertEquals(0.5, array[2]);
|
assertEquals(0.5, array[2]);
|
||||||
%OptimizeFunctionOnNextCall(deopt_array_literal_all_doubles);
|
%OptimizeFunctionOnNextCall(deopt_array_literal_all_doubles);
|
||||||
array = deopt_array_literal_all_doubles(5);
|
array = deopt_array_literal_all_doubles(5);
|
||||||
array = deopt_array_literal_all_doubles(6);
|
array = deopt_array_literal_all_doubles(6);
|
||||||
assertOptimized(deopt_array_literal_all_doubles);
|
assertOptimized(deopt_array_literal_all_doubles);
|
||||||
|
112
test/mjsunit/regress/regress-crbug-746835.js
Normal file
112
test/mjsunit/regress/regress-crbug-746835.js
Normal 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;
|
||||||
|
}
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user