Commit 34de39bf authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Add support to inline new Array(n) calls.

Make calls like

  new Array(n)
  new A(n)

(where A is a subclass of Array) inlinable into TurboFan. We do this by
speculatively checking that n is an unsigned integer that is not greater
than JSArray::kInitialMaxFastElementArray, and then lowering the backing
store allocation to a builtin call. The speculative optimization is
either protected by the AllocationSite for the Array constructor
invocation (if we have one), or by a newly introduced global protector
cell that is used for Array constructor invocations that don't have an
AllocationSite, i.e. the ones from Array#map, Array#filter, or from
subclasses of Array.

Next step will be to implement the backing store allocations inline in
TurboFan, but that requires Loop support in the GraphAssembler, so it's
done as a separate CL. This should further boost the performance.

This boosts the ARES6 ML benchmark by up to 8% on the steady state,
and also improves monomorphic Array#map calls by around 20-25% on the
initial setup.

Bug: v8:6399
Tbr: ulan@chromium.org
Change-Id: I7c8bdecf7c814ce52db6ee3051c3206a4f7d4bb6
Reviewed-on: https://chromium-review.googlesource.com/704639
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarMichael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48348}
parent b7db31a2
...@@ -167,6 +167,12 @@ namespace internal { ...@@ -167,6 +167,12 @@ namespace internal {
TFC(GrowFastDoubleElements, GrowArrayElements, 1) \ TFC(GrowFastDoubleElements, GrowArrayElements, 1) \
TFC(GrowFastSmiOrObjectElements, GrowArrayElements, 1) \ TFC(GrowFastSmiOrObjectElements, GrowArrayElements, 1) \
TFC(NewArgumentsElements, NewArgumentsElements, 1) \ TFC(NewArgumentsElements, NewArgumentsElements, 1) \
/* TODO(bmeurer): Remove these four builtins when the operations are */ \
/* inlined into TurboFan, which requires Loop support in GraphAssembler. */ \
TFS(NewFastDoubleElements_NotTenured, kLength) \
TFS(NewFastDoubleElements_Tenured, kLength) \
TFS(NewFastSmiOrObjectElements_NotTenured, kLength) \
TFS(NewFastSmiOrObjectElements_Tenured, kLength) \
\ \
/* Debugger */ \ /* Debugger */ \
ASM(FrameDropperTrampoline) \ ASM(FrameDropperTrampoline) \
......
...@@ -94,6 +94,88 @@ TF_BUILTIN(GrowFastSmiOrObjectElements, CodeStubAssembler) { ...@@ -94,6 +94,88 @@ TF_BUILTIN(GrowFastSmiOrObjectElements, CodeStubAssembler) {
TailCallRuntime(Runtime::kGrowArrayElements, context, object, key); TailCallRuntime(Runtime::kGrowArrayElements, context, object, key);
} }
// TODO(bmeurer): Temporary builtin, will disappear soon.
TF_BUILTIN(NewFastDoubleElements_NotTenured, CodeStubAssembler) {
Node* length = SmiUntag(Parameter(Descriptor::kLength));
Node* const zero = IntPtrConstant(0);
ElementsKind const elements_kind = PACKED_DOUBLE_ELEMENTS;
Label if_empty(this, Label::kDeferred), if_nonempty(this);
Branch(WordEqual(length, zero), &if_empty, &if_nonempty);
BIND(&if_empty);
Return(EmptyFixedArrayConstant());
BIND(&if_nonempty);
Node* const elements = AllocateFixedArray(elements_kind, length);
FillFixedArrayWithValue(elements_kind, elements, zero, length,
Heap::kTheHoleValueRootIndex);
Return(elements);
}
// TODO(bmeurer): Temporary builtin, will disappear soon.
TF_BUILTIN(NewFastDoubleElements_Tenured, CodeStubAssembler) {
Node* length = SmiUntag(Parameter(Descriptor::kLength));
Node* const zero = IntPtrConstant(0);
ElementsKind const elements_kind = PACKED_DOUBLE_ELEMENTS;
Label if_empty(this, Label::kDeferred), if_nonempty(this);
Branch(WordEqual(length, zero), &if_empty, &if_nonempty);
BIND(&if_empty);
Return(EmptyFixedArrayConstant());
BIND(&if_nonempty);
Node* const elements =
AllocateFixedArray(elements_kind, length, INTPTR_PARAMETERS, kPretenured);
FillFixedArrayWithValue(elements_kind, elements, zero, length,
Heap::kTheHoleValueRootIndex);
Return(elements);
}
// TODO(bmeurer): Temporary builtin, will disappear soon.
TF_BUILTIN(NewFastSmiOrObjectElements_NotTenured, CodeStubAssembler) {
Node* length = SmiUntag(Parameter(Descriptor::kLength));
Node* const zero = IntPtrConstant(0);
ElementsKind const elements_kind = PACKED_ELEMENTS;
Label if_empty(this, Label::kDeferred), if_nonempty(this);
Branch(WordEqual(length, zero), &if_empty, &if_nonempty);
BIND(&if_empty);
Return(EmptyFixedArrayConstant());
BIND(&if_nonempty);
Node* const elements = AllocateFixedArray(elements_kind, length);
FillFixedArrayWithValue(elements_kind, elements, zero, length,
Heap::kTheHoleValueRootIndex);
Return(elements);
}
// TODO(bmeurer): Temporary builtin, will disappear soon.
TF_BUILTIN(NewFastSmiOrObjectElements_Tenured, CodeStubAssembler) {
Node* length = SmiUntag(Parameter(Descriptor::kLength));
Node* const zero = IntPtrConstant(0);
ElementsKind const elements_kind = PACKED_ELEMENTS;
Label if_empty(this, Label::kDeferred), if_nonempty(this);
Branch(WordEqual(length, zero), &if_empty, &if_nonempty);
BIND(&if_empty);
Return(EmptyFixedArrayConstant());
BIND(&if_nonempty);
Node* const elements =
AllocateFixedArray(elements_kind, length, INTPTR_PARAMETERS, kPretenured);
FillFixedArrayWithValue(elements_kind, elements, zero, length,
Heap::kTheHoleValueRootIndex);
Return(elements);
}
TF_BUILTIN(NewArgumentsElements, CodeStubAssembler) { TF_BUILTIN(NewArgumentsElements, CodeStubAssembler) {
Node* frame = Parameter(Descriptor::kFrame); Node* frame = Parameter(Descriptor::kFrame);
Node* length = SmiToWord(Parameter(Descriptor::kLength)); Node* length = SmiToWord(Parameter(Descriptor::kLength));
......
...@@ -754,6 +754,12 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -754,6 +754,12 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kArgumentsLength: case IrOpcode::kArgumentsLength:
result = LowerArgumentsLength(node); result = LowerArgumentsLength(node);
break; break;
case IrOpcode::kNewFastDoubleElements:
result = LowerNewFastDoubleElements(node);
break;
case IrOpcode::kNewFastSmiOrObjectElements:
result = LowerNewFastSmiOrObjectElements(node);
break;
case IrOpcode::kNewArgumentsElements: case IrOpcode::kNewArgumentsElements:
result = LowerNewArgumentsElements(node); result = LowerNewArgumentsElements(node);
break; break;
...@@ -2215,6 +2221,38 @@ Node* EffectControlLinearizer::LowerArgumentsFrame(Node* node) { ...@@ -2215,6 +2221,38 @@ Node* EffectControlLinearizer::LowerArgumentsFrame(Node* node) {
return done.PhiAt(0); return done.PhiAt(0);
} }
Node* EffectControlLinearizer::LowerNewFastDoubleElements(Node* node) {
PretenureFlag const pretenure = PretenureFlagOf(node->op());
Node* length = node->InputAt(0);
Callable const callable = Builtins::CallableFor(
isolate(), pretenure == NOT_TENURED
? Builtins::kNewFastDoubleElements_NotTenured
: Builtins::kNewFastDoubleElements_Tenured);
Operator::Properties const properties = node->op()->properties();
CallDescriptor::Flags const flags = CallDescriptor::kNoFlags;
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties);
return __ Call(desc, __ HeapConstant(callable.code()), length,
__ NoContextConstant());
}
Node* EffectControlLinearizer::LowerNewFastSmiOrObjectElements(Node* node) {
PretenureFlag const pretenure = PretenureFlagOf(node->op());
Node* length = node->InputAt(0);
Callable const callable = Builtins::CallableFor(
isolate(), pretenure == NOT_TENURED
? Builtins::kNewFastSmiOrObjectElements_NotTenured
: Builtins::kNewFastSmiOrObjectElements_Tenured);
Operator::Properties const properties = node->op()->properties();
CallDescriptor::Flags const flags = CallDescriptor::kNoFlags;
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties);
return __ Call(desc, __ HeapConstant(callable.code()), length,
__ NoContextConstant());
}
Node* EffectControlLinearizer::LowerNewArgumentsElements(Node* node) { Node* EffectControlLinearizer::LowerNewArgumentsElements(Node* node) {
Node* frame = NodeProperties::GetValueInput(node, 0); Node* frame = NodeProperties::GetValueInput(node, 0);
Node* length = NodeProperties::GetValueInput(node, 1); Node* length = NodeProperties::GetValueInput(node, 1);
......
...@@ -98,6 +98,8 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { ...@@ -98,6 +98,8 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerObjectIsUndetectable(Node* node); Node* LowerObjectIsUndetectable(Node* node);
Node* LowerArgumentsFrame(Node* node); Node* LowerArgumentsFrame(Node* node);
Node* LowerArgumentsLength(Node* node); Node* LowerArgumentsLength(Node* node);
Node* LowerNewFastDoubleElements(Node* node);
Node* LowerNewFastSmiOrObjectElements(Node* node);
Node* LowerNewArgumentsElements(Node* node); Node* LowerNewArgumentsElements(Node* node);
Node* LowerArrayBufferWasNeutered(Node* node); Node* LowerArrayBufferWasNeutered(Node* node);
Node* LowerStringCharAt(Node* node); Node* LowerStringCharAt(Node* node);
......
...@@ -579,6 +579,57 @@ Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) { ...@@ -579,6 +579,57 @@ Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) {
return NoChange(); return NoChange();
} }
// Constructs an array with a variable {length} when no upper bound
// is known for the capacity.
Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length,
Handle<Map> initial_map,
PretenureFlag pretenure) {
DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode());
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Constructing an Array via new Array(N) where N is an unsigned
// integer, always creates a holey backing store.
if (!IsHoleyElementsKind(initial_map->elements_kind())) {
initial_map = Map::AsElementsKind(
initial_map, GetHoleyElementsKind(initial_map->elements_kind()));
}
// Check that the {limit} is an unsigned integer in the valid range.
// This has to be kept in sync with src/runtime/runtime-array.cc,
// where this limit is protected.
length = effect = graph()->NewNode(
simplified()->CheckBounds(), length,
jsgraph()->Constant(JSArray::kInitialMaxFastElementArray), effect,
control);
// Construct elements and properties for the resulting JSArray.
Node* elements = effect = graph()->NewNode(
IsDoubleElementsKind(initial_map->elements_kind())
? simplified()->NewFastDoubleElements(pretenure)
: simplified()->NewFastSmiOrObjectElements(pretenure),
length, effect, control);
Node* properties = jsgraph()->EmptyFixedArrayConstant();
// Perform the allocation of the actual JSArray object.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(initial_map->instance_size(), pretenure);
a.Store(AccessBuilder::ForMap(), initial_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForJSArrayLength(initial_map->elements_kind()),
length);
for (int i = 0; i < initial_map->GetInObjectProperties(); ++i) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
jsgraph()->UndefinedConstant());
}
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
// Constructs an array with a variable {length} when an actual
// upper bound is known for the {capacity}.
Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length, Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length,
int capacity, int capacity,
Handle<Map> initial_map, Handle<Map> initial_map,
...@@ -771,16 +822,23 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) { ...@@ -771,16 +822,23 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) {
// deoptimized whenever the {initial_map} changes. // deoptimized whenever the {initial_map} changes.
dependencies()->AssumeInitialMapCantChange(initial_map); dependencies()->AssumeInitialMapCantChange(initial_map);
// Tells whether we are protected by either the {site} or a
// protector cell to do certain speculative optimizations.
bool can_inline_call = false;
// Check if we have a feedback {site} on the {node}. // Check if we have a feedback {site} on the {node}.
if (!site.is_null()) { if (!site.is_null()) {
ElementsKind elements_kind = site->GetElementsKind(); ElementsKind elements_kind = site->GetElementsKind();
if (initial_map->elements_kind() != elements_kind) { if (initial_map->elements_kind() != elements_kind) {
initial_map = Map::AsElementsKind(initial_map, elements_kind); initial_map = Map::AsElementsKind(initial_map, elements_kind);
} }
can_inline_call = site->CanInlineCall();
pretenure = site->GetPretenureMode(); pretenure = site->GetPretenureMode();
dependencies()->AssumeTransitionStable(site); dependencies()->AssumeTransitionStable(site);
dependencies()->AssumeTenuringDecision(site); dependencies()->AssumeTenuringDecision(site);
} else {
can_inline_call = isolate()->IsArrayConstructorIntact();
} }
if (arity == 0) { if (arity == 0) {
...@@ -799,7 +857,8 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) { ...@@ -799,7 +857,8 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) {
? HOLEY_ELEMENTS ? HOLEY_ELEMENTS
: PACKED_ELEMENTS); : PACKED_ELEMENTS);
initial_map = Map::AsElementsKind(initial_map, elements_kind); initial_map = Map::AsElementsKind(initial_map, elements_kind);
return ReduceNewArray(node, {length}, initial_map, pretenure); return ReduceNewArray(node, std::vector<Node*>{length}, initial_map,
pretenure);
} }
if (length_type->Is(Type::SignedSmall()) && length_type->Min() >= 0 && if (length_type->Is(Type::SignedSmall()) && length_type->Min() >= 0 &&
length_type->Max() <= kElementLoopUnrollLimit && length_type->Max() <= kElementLoopUnrollLimit &&
...@@ -807,6 +866,9 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) { ...@@ -807,6 +866,9 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) {
int capacity = static_cast<int>(length_type->Max()); int capacity = static_cast<int>(length_type->Max());
return ReduceNewArray(node, length, capacity, initial_map, pretenure); return ReduceNewArray(node, length, capacity, initial_map, pretenure);
} }
if (length_type->Maybe(Type::UnsignedSmall()) && can_inline_call) {
return ReduceNewArray(node, length, initial_map, pretenure);
}
} else if (arity <= JSArray::kInitialMaxFastElementArray) { } else if (arity <= JSArray::kInitialMaxFastElementArray) {
// Gather the values to store into the newly created array. // Gather the values to store into the newly created array.
bool values_all_smis = true, values_all_numbers = true, bool values_all_smis = true, values_all_numbers = true,
...@@ -842,7 +904,7 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) { ...@@ -842,7 +904,7 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) {
elements_kind, IsHoleyElementsKind(elements_kind) elements_kind, IsHoleyElementsKind(elements_kind)
? HOLEY_ELEMENTS ? HOLEY_ELEMENTS
: PACKED_ELEMENTS); : PACKED_ELEMENTS);
} else if (site.is_null() || !site->CanInlineCall()) { } else if (!can_inline_call) {
// We have some crazy combination of types for the {values} where // We have some crazy combination of types for the {values} where
// there's no clear decision on the elements kind statically. And // there's no clear decision on the elements kind statically. And
// we don't have a protection against deoptimization loops for the // we don't have a protection against deoptimization loops for the
......
...@@ -62,6 +62,8 @@ class V8_EXPORT_PRIVATE JSCreateLowering final ...@@ -62,6 +62,8 @@ class V8_EXPORT_PRIVATE JSCreateLowering final
Reduction ReduceJSCreateCatchContext(Node* node); Reduction ReduceJSCreateCatchContext(Node* node);
Reduction ReduceJSCreateBlockContext(Node* node); Reduction ReduceJSCreateBlockContext(Node* node);
Reduction ReduceJSCreateGeneratorObject(Node* node); Reduction ReduceJSCreateGeneratorObject(Node* node);
Reduction ReduceNewArray(Node* node, Node* length, Handle<Map> initial_map,
PretenureFlag pretenure);
Reduction ReduceNewArray(Node* node, Node* length, int capacity, Reduction ReduceNewArray(Node* node, Node* length, int capacity,
Handle<Map> initial_map, PretenureFlag pretenure); Handle<Map> initial_map, PretenureFlag pretenure);
Reduction ReduceNewArray(Node* node, std::vector<Node*> values, Reduction ReduceNewArray(Node* node, std::vector<Node*> values,
......
...@@ -366,6 +366,8 @@ ...@@ -366,6 +366,8 @@
V(ObjectIsUndetectable) \ V(ObjectIsUndetectable) \
V(ArgumentsFrame) \ V(ArgumentsFrame) \
V(ArgumentsLength) \ V(ArgumentsLength) \
V(NewFastDoubleElements) \
V(NewFastSmiOrObjectElements) \
V(NewArgumentsElements) \ V(NewArgumentsElements) \
V(ArrayBufferWasNeutered) \ V(ArrayBufferWasNeutered) \
V(EnsureWritableFastElements) \ V(EnsureWritableFastElements) \
......
...@@ -2777,6 +2777,12 @@ class RepresentationSelector { ...@@ -2777,6 +2777,12 @@ class RepresentationSelector {
MachineRepresentation::kTaggedSigned); MachineRepresentation::kTaggedSigned);
return; return;
} }
case IrOpcode::kNewFastDoubleElements:
case IrOpcode::kNewFastSmiOrObjectElements: {
VisitUnop(node, UseInfo::TaggedSigned(),
MachineRepresentation::kTaggedPointer);
return;
}
case IrOpcode::kNewArgumentsElements: { case IrOpcode::kNewArgumentsElements: {
VisitBinop(node, UseInfo::PointerInt(), UseInfo::TaggedSigned(), VisitBinop(node, UseInfo::PointerInt(), UseInfo::TaggedSigned(),
MachineRepresentation::kTaggedPointer); MachineRepresentation::kTaggedPointer);
......
...@@ -397,6 +397,10 @@ bool operator!=(AllocateParameters const& lhs, AllocateParameters const& rhs) { ...@@ -397,6 +397,10 @@ bool operator!=(AllocateParameters const& lhs, AllocateParameters const& rhs) {
} }
PretenureFlag PretenureFlagOf(const Operator* op) { PretenureFlag PretenureFlagOf(const Operator* op) {
if (op->opcode() == IrOpcode::kNewFastDoubleElements ||
op->opcode() == IrOpcode::kNewFastSmiOrObjectElements) {
return OpParameter<PretenureFlag>(op);
}
DCHECK_EQ(IrOpcode::kAllocate, op->opcode()); DCHECK_EQ(IrOpcode::kAllocate, op->opcode());
return OpParameter<AllocateParameters>(op).pretenure(); return OpParameter<AllocateParameters>(op).pretenure();
} }
...@@ -973,6 +977,26 @@ bool IsRestLengthOf(const Operator* op) { ...@@ -973,6 +977,26 @@ bool IsRestLengthOf(const Operator* op) {
return OpParameter<ArgumentsLengthParameters>(op).is_rest_length; return OpParameter<ArgumentsLengthParameters>(op).is_rest_length;
} }
const Operator* SimplifiedOperatorBuilder::NewFastDoubleElements(
PretenureFlag pretenure) {
return new (zone()) Operator1<PretenureFlag>( // --
IrOpcode::kNewFastDoubleElements, // opcode
Operator::kEliminatable, // flags
"NewFastDoubleElements", // name
1, 1, 1, 1, 1, 0, // counts
pretenure); // parameter
}
const Operator* SimplifiedOperatorBuilder::NewFastSmiOrObjectElements(
PretenureFlag pretenure) {
return new (zone()) Operator1<PretenureFlag>( // --
IrOpcode::kNewFastSmiOrObjectElements, // opcode
Operator::kEliminatable, // flags
"NewFastSmiOrObjectElements", // name
1, 1, 1, 1, 1, 0, // counts
pretenure); // parameter
}
const Operator* SimplifiedOperatorBuilder::NewArgumentsElements( const Operator* SimplifiedOperatorBuilder::NewArgumentsElements(
int mapped_count) { int mapped_count) {
return new (zone()) Operator1<int>( // -- return new (zone()) Operator1<int>( // --
......
...@@ -452,6 +452,9 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -452,6 +452,9 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* ArgumentsLength(int formal_parameter_count, const Operator* ArgumentsLength(int formal_parameter_count,
bool is_rest_length); bool is_rest_length);
const Operator* NewFastDoubleElements(PretenureFlag);
const Operator* NewFastSmiOrObjectElements(PretenureFlag);
// new-arguments-elements arguments-frame, arguments-length // new-arguments-elements arguments-frame, arguments-length
const Operator* NewArgumentsElements(int mapped_count); const Operator* NewArgumentsElements(int mapped_count);
......
...@@ -2034,6 +2034,14 @@ Type* Typer::Visitor::TypeArgumentsFrame(Node* node) { ...@@ -2034,6 +2034,14 @@ Type* Typer::Visitor::TypeArgumentsFrame(Node* node) {
return Type::ExternalPointer(); return Type::ExternalPointer();
} }
Type* Typer::Visitor::TypeNewFastDoubleElements(Node* node) {
return Type::OtherInternal();
}
Type* Typer::Visitor::TypeNewFastSmiOrObjectElements(Node* node) {
return Type::OtherInternal();
}
Type* Typer::Visitor::TypeNewArgumentsElements(Node* node) { Type* Typer::Visitor::TypeNewArgumentsElements(Node* node) {
return Type::OtherInternal(); return Type::OtherInternal();
} }
......
...@@ -1035,6 +1035,12 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -1035,6 +1035,12 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kArgumentsFrame: case IrOpcode::kArgumentsFrame:
CheckTypeIs(node, Type::ExternalPointer()); CheckTypeIs(node, Type::ExternalPointer());
break; break;
case IrOpcode::kNewFastDoubleElements:
case IrOpcode::kNewFastSmiOrObjectElements:
CheckValueInputIs(node, 0,
Type::Range(0.0, FixedArray::kMaxLength, zone));
CheckTypeIs(node, Type::OtherInternal());
break;
case IrOpcode::kNewArgumentsElements: case IrOpcode::kNewArgumentsElements:
CheckValueInputIs(node, 0, Type::ExternalPointer()); CheckValueInputIs(node, 0, Type::ExternalPointer());
CheckValueInputIs(node, 1, Type::Range(-Code::kMaxArguments, CheckValueInputIs(node, 1, Type::Range(-Code::kMaxArguments,
......
...@@ -186,6 +186,7 @@ using v8::MemoryPressureLevel; ...@@ -186,6 +186,7 @@ using v8::MemoryPressureLevel;
V(WeakCell, empty_weak_cell, EmptyWeakCell) \ V(WeakCell, empty_weak_cell, EmptyWeakCell) \
V(InterceptorInfo, noop_interceptor_info, NoOpInterceptorInfo) \ V(InterceptorInfo, noop_interceptor_info, NoOpInterceptorInfo) \
/* Protectors */ \ /* Protectors */ \
V(Cell, array_constructor_protector, ArrayConstructorProtector) \
V(PropertyCell, array_protector, ArrayProtector) \ V(PropertyCell, array_protector, ArrayProtector) \
V(Cell, is_concat_spreadable_protector, IsConcatSpreadableProtector) \ V(Cell, is_concat_spreadable_protector, IsConcatSpreadableProtector) \
V(PropertyCell, species_protector, SpeciesProtector) \ V(PropertyCell, species_protector, SpeciesProtector) \
......
...@@ -574,6 +574,10 @@ void Heap::CreateInitialObjects() { ...@@ -574,6 +574,10 @@ void Heap::CreateInitialObjects() {
script->set_type(Script::TYPE_NATIVE); script->set_type(Script::TYPE_NATIVE);
set_empty_script(*script); set_empty_script(*script);
Handle<Cell> array_constructor_cell = factory->NewCell(
handle(Smi::FromInt(Isolate::kProtectorValid), isolate()));
set_array_constructor_protector(*array_constructor_cell);
Handle<PropertyCell> cell = factory->NewPropertyCell(factory->empty_string()); Handle<PropertyCell> cell = factory->NewPropertyCell(factory->empty_string());
cell->set_value(Smi::FromInt(Isolate::kProtectorValid)); cell->set_value(Smi::FromInt(Isolate::kProtectorValid));
set_array_protector(*cell); set_array_protector(*cell);
......
...@@ -126,6 +126,11 @@ Isolate::ExceptionScope::~ExceptionScope() { ...@@ -126,6 +126,11 @@ Isolate::ExceptionScope::~ExceptionScope() {
NATIVE_CONTEXT_FIELDS(NATIVE_CONTEXT_FIELD_ACCESSOR) NATIVE_CONTEXT_FIELDS(NATIVE_CONTEXT_FIELD_ACCESSOR)
#undef NATIVE_CONTEXT_FIELD_ACCESSOR #undef NATIVE_CONTEXT_FIELD_ACCESSOR
bool Isolate::IsArrayConstructorIntact() {
Cell* array_constructor_cell = heap()->array_constructor_protector();
return array_constructor_cell->value() == Smi::FromInt(kProtectorValid);
}
bool Isolate::IsArraySpeciesLookupChainIntact() { bool Isolate::IsArraySpeciesLookupChainIntact() {
// Note: It would be nice to have debug checks to make sure that the // Note: It would be nice to have debug checks to make sure that the
// species protector is accurate, but this would be hard to do for most of // species protector is accurate, but this would be hard to do for most of
......
...@@ -3164,6 +3164,14 @@ void Isolate::InvalidateIsConcatSpreadableProtector() { ...@@ -3164,6 +3164,14 @@ void Isolate::InvalidateIsConcatSpreadableProtector() {
DCHECK(!IsIsConcatSpreadableLookupChainIntact()); DCHECK(!IsIsConcatSpreadableLookupChainIntact());
} }
void Isolate::InvalidateArrayConstructorProtector() {
DCHECK(factory()->array_constructor_protector()->value()->IsSmi());
DCHECK(IsArrayConstructorIntact());
factory()->array_constructor_protector()->set_value(
Smi::FromInt(kProtectorInvalid));
DCHECK(!IsArrayConstructorIntact());
}
void Isolate::InvalidateArraySpeciesProtector() { void Isolate::InvalidateArraySpeciesProtector() {
DCHECK(factory()->species_protector()->value()->IsSmi()); DCHECK(factory()->species_protector()->value()->IsSmi());
DCHECK(IsArraySpeciesLookupChainIntact()); DCHECK(IsArraySpeciesLookupChainIntact());
......
...@@ -1069,6 +1069,7 @@ class Isolate { ...@@ -1069,6 +1069,7 @@ class Isolate {
static const int kProtectorValid = 1; static const int kProtectorValid = 1;
static const int kProtectorInvalid = 0; static const int kProtectorInvalid = 0;
inline bool IsArrayConstructorIntact();
bool IsFastArrayConstructorPrototypeChainIntact(); bool IsFastArrayConstructorPrototypeChainIntact();
inline bool IsArraySpeciesLookupChainIntact(); inline bool IsArraySpeciesLookupChainIntact();
bool IsIsConcatSpreadableLookupChainIntact(); bool IsIsConcatSpreadableLookupChainIntact();
...@@ -1096,6 +1097,7 @@ class Isolate { ...@@ -1096,6 +1097,7 @@ class Isolate {
void UpdateArrayProtectorOnNormalizeElements(Handle<JSObject> object) { void UpdateArrayProtectorOnNormalizeElements(Handle<JSObject> object) {
UpdateArrayProtectorOnSetElement(object); UpdateArrayProtectorOnSetElement(object);
} }
void InvalidateArrayConstructorProtector();
void InvalidateArraySpeciesProtector(); void InvalidateArraySpeciesProtector();
void InvalidateIsConcatSpreadableProtector(); void InvalidateIsConcatSpreadableProtector();
void InvalidateStringLengthOverflowProtector(); void InvalidateStringLengthOverflowProtector();
......
...@@ -460,13 +460,25 @@ RUNTIME_FUNCTION(Runtime_NewArray) { ...@@ -460,13 +460,25 @@ RUNTIME_FUNCTION(Runtime_NewArray) {
ElementsKind old_kind = array->GetElementsKind(); ElementsKind old_kind = array->GetElementsKind();
RETURN_FAILURE_ON_EXCEPTION(isolate, RETURN_FAILURE_ON_EXCEPTION(isolate,
ArrayConstructInitializeElements(array, &argv)); ArrayConstructInitializeElements(array, &argv));
if (!site.is_null() && if (!site.is_null()) {
(old_kind != array->GetElementsKind() || !can_use_type_feedback || if ((old_kind != array->GetElementsKind() || !can_use_type_feedback ||
!can_inline_array_constructor)) { !can_inline_array_constructor)) {
// The arguments passed in caused a transition. This kind of complexity // The arguments passed in caused a transition. This kind of complexity
// can't be dealt with in the inlined hydrogen array constructor case. // can't be dealt with in the inlined hydrogen array constructor case.
// We must mark the allocationsite as un-inlinable. // We must mark the allocationsite as un-inlinable.
site->SetDoNotInlineCall(); site->SetDoNotInlineCall();
}
} else {
if (old_kind != array->GetElementsKind() || !can_inline_array_constructor) {
// We don't have an AllocationSite for this Array constructor invocation,
// i.e. it might a call from Array#map or from an Array subclass, so we
// just flip the bit on the global protector cell instead.
// TODO(bmeurer): Find a better way to mark this. Global protectors
// tend to back-fire over time...
if (isolate->IsArrayConstructorIntact()) {
isolate->InvalidateArrayConstructorProtector();
}
}
} }
return *array; return *array;
......
...@@ -106,14 +106,9 @@ function assertKind(expected, obj, name_opt) { ...@@ -106,14 +106,9 @@ function assertKind(expected, obj, name_opt) {
a = bar(10); a = bar(10);
assertKind(elements_kind.fast, a); assertKind(elements_kind.fast, a);
assertOptimized(bar); assertOptimized(bar);
bar(100000); bar(10000);
assertOptimized(bar); assertOptimized(bar);
// If the argument isn't a smi, things should still work.
a = bar("oops");
assertOptimized(bar);
assertKind(elements_kind.fast, a);
function barn(one, two, three) { function barn(one, two, three) {
return new Array(one, two, three); return new Array(one, two, three);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment