Commit e6822a83 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Add initial support for growing stores.

Introduce a dedicated MaybeGrowFastElements simplified operator, which
tries to grow a fast elements backing store for a given element that
should be added to an array/object. Use that to lower a growing keyed
store to a sequence of

 1) check index is a valid array index,
 2) check stored value,
 3) maybe grow elements backing store (and deoptimize if it would
    normalize), and
 4) store the actual element.

The actual growing is done by two dedicated GrowFastDoubleElements
and GrowFastSmiOrObjectElements builtins, which are very similar to
the GrowArrayElementsStub that is used by Crankshaft.

Drive-by-fix: Turn CopyFixedArray into CopyFastSmiOrObjectElements
builtin, similar to the new growing builtins, so we don't need to
inline the store+write barrier for the elements into all optimized
code objects anymore.

Also fix a bug in the OperationTyper for NumberSilenceNaN, which was
triggered by this change.

BUG=v8:5272

Review-Url: https://codereview.chromium.org/2227493002
Cr-Commit-Position: refs/heads/master@{#38418}
parent 552601bb
...@@ -51,15 +51,19 @@ void Builtins::Generate_StackCheck(MacroAssembler* masm) { ...@@ -51,15 +51,19 @@ void Builtins::Generate_StackCheck(MacroAssembler* masm) {
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// FixedArray helpers. // TurboFan support builtins.
void Builtins::Generate_CopyFixedArray(CodeStubAssembler* assembler) { void Builtins::Generate_CopyFastSmiOrObjectElements(
CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label; typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node; typedef compiler::Node Node;
typedef CodeStubAssembler::Variable Variable; typedef CodeStubAssembler::Variable Variable;
typedef CopyFixedArrayDescriptor Descriptor; typedef CopyFastSmiOrObjectElementsDescriptor Descriptor;
Node* source = assembler->Parameter(Descriptor::kSource); Node* object = assembler->Parameter(Descriptor::kObject);
// Load the {object}s elements.
Node* source = assembler->LoadObjectField(object, JSObject::kElementsOffset);
// Load the {source} length. // Load the {source} length.
Node* source_length_tagged = Node* source_length_tagged =
...@@ -122,8 +126,12 @@ void Builtins::Generate_CopyFixedArray(CodeStubAssembler* assembler) { ...@@ -122,8 +126,12 @@ void Builtins::Generate_CopyFixedArray(CodeStubAssembler* assembler) {
} }
assembler->Bind(&done_loop); assembler->Bind(&done_loop);
{
// Update the {object}s element to {target}.
assembler->StoreObjectField(object, JSObject::kElementsOffset, target);
assembler->Return(target); assembler->Return(target);
} }
}
assembler->Bind(&if_oldspace); assembler->Bind(&if_oldspace);
{ {
...@@ -173,8 +181,53 @@ void Builtins::Generate_CopyFixedArray(CodeStubAssembler* assembler) { ...@@ -173,8 +181,53 @@ void Builtins::Generate_CopyFixedArray(CodeStubAssembler* assembler) {
} }
assembler->Bind(&done_loop); assembler->Bind(&done_loop);
{
// Update the {object}s element to {target}.
assembler->StoreObjectField(object, JSObject::kElementsOffset, target);
assembler->Return(target); assembler->Return(target);
} }
}
}
void Builtins::Generate_GrowFastDoubleElements(CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef GrowArrayElementsDescriptor Descriptor;
Node* object = assembler->Parameter(Descriptor::kObject);
Node* key = assembler->Parameter(Descriptor::kKey);
Node* context = assembler->Parameter(Descriptor::kContext);
Label runtime(assembler, CodeStubAssembler::Label::kDeferred);
Node* elements = assembler->LoadElements(object);
elements = assembler->CheckAndGrowElementsCapacity(
context, elements, FAST_DOUBLE_ELEMENTS, key, &runtime);
assembler->StoreObjectField(object, JSObject::kElementsOffset, elements);
assembler->Return(elements);
assembler->Bind(&runtime);
assembler->TailCallRuntime(Runtime::kGrowArrayElements, context, object, key);
}
void Builtins::Generate_GrowFastSmiOrObjectElements(
CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef GrowArrayElementsDescriptor Descriptor;
Node* object = assembler->Parameter(Descriptor::kObject);
Node* key = assembler->Parameter(Descriptor::kKey);
Node* context = assembler->Parameter(Descriptor::kContext);
Label runtime(assembler, CodeStubAssembler::Label::kDeferred);
Node* elements = assembler->LoadElements(object);
elements = assembler->CheckAndGrowElementsCapacity(
context, elements, FAST_ELEMENTS, key, &runtime);
assembler->StoreObjectField(object, JSObject::kElementsOffset, elements);
assembler->Return(elements);
assembler->Bind(&runtime);
assembler->TailCallRuntime(Runtime::kGrowArrayElements, context, object, key);
} }
} // namespace internal } // namespace internal
......
...@@ -159,8 +159,12 @@ namespace internal { ...@@ -159,8 +159,12 @@ namespace internal {
ASM(AllocateInNewSpace) \ ASM(AllocateInNewSpace) \
ASM(AllocateInOldSpace) \ ASM(AllocateInOldSpace) \
\ \
/* FixedArray helpers */ \ /* TurboFan support builtins */ \
TFS(CopyFixedArray, BUILTIN, kNoExtraICState, CopyFixedArray) \ TFS(CopyFastSmiOrObjectElements, BUILTIN, kNoExtraICState, \
CopyFastSmiOrObjectElements) \
TFS(GrowFastDoubleElements, BUILTIN, kNoExtraICState, GrowArrayElements) \
TFS(GrowFastSmiOrObjectElements, BUILTIN, kNoExtraICState, \
GrowArrayElements) \
\ \
/* Debugger */ \ /* Debugger */ \
DBG(FrameDropper_LiveEdit) \ DBG(FrameDropper_LiveEdit) \
......
...@@ -510,9 +510,21 @@ Callable CodeFactory::FastNewStrictArguments(Isolate* isolate, ...@@ -510,9 +510,21 @@ Callable CodeFactory::FastNewStrictArguments(Isolate* isolate,
} }
// static // static
Callable CodeFactory::CopyFixedArray(Isolate* isolate) { Callable CodeFactory::CopyFastSmiOrObjectElements(Isolate* isolate) {
return Callable(isolate->builtins()->CopyFixedArray(), return Callable(isolate->builtins()->CopyFastSmiOrObjectElements(),
CopyFixedArrayDescriptor(isolate)); CopyFastSmiOrObjectElementsDescriptor(isolate));
}
// static
Callable CodeFactory::GrowFastDoubleElements(Isolate* isolate) {
return Callable(isolate->builtins()->GrowFastDoubleElements(),
GrowArrayElementsDescriptor(isolate));
}
// static
Callable CodeFactory::GrowFastSmiOrObjectElements(Isolate* isolate) {
return Callable(isolate->builtins()->GrowFastSmiOrObjectElements(),
GrowArrayElementsDescriptor(isolate));
} }
// static // static
......
...@@ -136,7 +136,9 @@ class CodeFactory final { ...@@ -136,7 +136,9 @@ class CodeFactory final {
static Callable FastNewStrictArguments(Isolate* isolate, static Callable FastNewStrictArguments(Isolate* isolate,
bool skip_stub_frame = false); bool skip_stub_frame = false);
static Callable CopyFixedArray(Isolate* isolate); static Callable CopyFastSmiOrObjectElements(Isolate* isolate);
static Callable GrowFastDoubleElements(Isolate* isolate);
static Callable GrowFastSmiOrObjectElements(Isolate* isolate);
static Callable AllocateHeapNumber(Isolate* isolate); static Callable AllocateHeapNumber(Isolate* isolate);
#define SIMD128_ALLOC(TYPE, Type, type, lane_count, lane_type) \ #define SIMD128_ALLOC(TYPE, Type, type, lane_count, lane_type) \
......
...@@ -726,6 +726,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -726,6 +726,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kEnsureWritableFastElements: case IrOpcode::kEnsureWritableFastElements:
state = LowerEnsureWritableFastElements(node, *effect, *control); state = LowerEnsureWritableFastElements(node, *effect, *control);
break; break;
case IrOpcode::kMaybeGrowFastElements:
state = LowerMaybeGrowFastElements(node, frame_state, *effect, *control);
break;
case IrOpcode::kTransitionElementsKind: case IrOpcode::kTransitionElementsKind:
state = LowerTransitionElementsKind(node, *effect, *control); state = LowerTransitionElementsKind(node, *effect, *control);
break; break;
...@@ -2632,19 +2635,14 @@ EffectControlLinearizer::LowerEnsureWritableFastElements(Node* node, ...@@ -2632,19 +2635,14 @@ EffectControlLinearizer::LowerEnsureWritableFastElements(Node* node,
{ {
// We need to create a copy of the {elements} for {object}. // We need to create a copy of the {elements} for {object}.
Operator::Properties properties = Operator::kEliminatable; Operator::Properties properties = Operator::kEliminatable;
Callable callable = CodeFactory::CopyFixedArray(isolate()); Callable callable = CodeFactory::CopyFastSmiOrObjectElements(isolate());
CallDescriptor::Flags flags = CallDescriptor::kNoFlags; CallDescriptor::Flags flags = CallDescriptor::kNoFlags;
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags, isolate(), graph()->zone(), callable.descriptor(), 0, flags,
properties); properties);
vfalse = efalse = graph()->NewNode( vfalse = efalse = graph()->NewNode(
common()->Call(desc), jsgraph()->HeapConstant(callable.code()), common()->Call(desc), jsgraph()->HeapConstant(callable.code()), object,
elements, jsgraph()->NoContextConstant(), efalse); jsgraph()->NoContextConstant(), efalse);
// Store the new {elements} into {object}.
efalse = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForJSObjectElements()), object,
vfalse, efalse, if_false);
} }
control = graph()->NewNode(common()->Merge(2), if_true, if_false); control = graph()->NewNode(common()->Merge(2), if_true, if_false);
...@@ -2655,6 +2653,115 @@ EffectControlLinearizer::LowerEnsureWritableFastElements(Node* node, ...@@ -2655,6 +2653,115 @@ EffectControlLinearizer::LowerEnsureWritableFastElements(Node* node,
return ValueEffectControl(value, effect, control); return ValueEffectControl(value, effect, control);
} }
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerMaybeGrowFastElements(Node* node,
Node* frame_state,
Node* effect,
Node* control) {
GrowFastElementsFlags flags = GrowFastElementsFlagsOf(node->op());
Node* object = node->InputAt(0);
Node* elements = node->InputAt(1);
Node* index = node->InputAt(2);
Node* length = node->InputAt(3);
Node* check0 = graph()->NewNode((flags & GrowFastElementsFlag::kHoleyElements)
? machine()->Uint32LessThanOrEqual()
: machine()->Word32Equal(),
length, index);
Node* branch0 = graph()->NewNode(common()->Branch(), check0, control);
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
Node* etrue0 = effect;
Node* vtrue0 = elements;
{
// Load the length of the {elements} backing store.
Node* elements_length = etrue0 = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), elements,
etrue0, if_true0);
elements_length = ChangeSmiToInt32(elements_length);
// Check if we need to grow the {elements} backing store.
Node* check1 =
graph()->NewNode(machine()->Uint32LessThan(), index, elements_length);
Node* branch1 =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check1, if_true0);
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
Node* etrue1 = etrue0;
Node* vtrue1 = vtrue0;
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
Node* efalse1 = etrue0;
Node* vfalse1 = vtrue0;
{
// We need to grow the {elements} for {object}.
Operator::Properties properties = Operator::kEliminatable;
Callable callable =
(flags & GrowFastElementsFlag::kDoubleElements)
? CodeFactory::GrowFastDoubleElements(isolate())
: CodeFactory::GrowFastSmiOrObjectElements(isolate());
CallDescriptor::Flags flags = CallDescriptor::kNoFlags;
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags,
properties);
vfalse1 = efalse1 = graph()->NewNode(
common()->Call(desc), jsgraph()->HeapConstant(callable.code()),
object, ChangeInt32ToSmi(index), jsgraph()->NoContextConstant(),
efalse1);
// Ensure that we were able to grow the {elements}.
// TODO(turbofan): We use kSmi as reason here similar to Crankshaft,
// but maybe we should just introduce a reason that makes sense.
efalse1 = if_false1 = graph()->NewNode(
common()->DeoptimizeIf(DeoptimizeReason::kSmi), ObjectIsSmi(vfalse1),
frame_state, efalse1, if_false1);
}
if_true0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
etrue0 =
graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_true0);
vtrue0 = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
vtrue1, vfalse1, if_true0);
// For JSArray {object}s we also need to update the "length".
if (flags & GrowFastElementsFlag::kArrayObject) {
// Compute the new {length}.
Node* object_length = ChangeInt32ToSmi(graph()->NewNode(
machine()->Int32Add(), index, jsgraph()->Int32Constant(1)));
// Update the "length" property of the {object}.
etrue0 =
graph()->NewNode(simplified()->StoreField(
AccessBuilder::ForJSArrayLength(FAST_ELEMENTS)),
object, object_length, etrue0, if_true0);
}
}
Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
Node* efalse0 = effect;
Node* vfalse0 = elements;
{
// In case of non-holey {elements}, we need to verify that the {index} is
// in-bounds, otherwise for holey {elements}, the check above already
// guards the index (and the operator forces {index} to be unsigned).
if (!(flags & GrowFastElementsFlag::kHoleyElements)) {
Node* check1 =
graph()->NewNode(machine()->Uint32LessThan(), index, length);
efalse0 = if_false0 = graph()->NewNode(
common()->DeoptimizeUnless(DeoptimizeReason::kOutOfBounds), check1,
frame_state, efalse0, if_false0);
}
}
control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
Node* value =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), vtrue0,
vfalse0, control);
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerTransitionElementsKind(Node* node, Node* effect, EffectControlLinearizer::LowerTransitionElementsKind(Node* node, Node* effect,
Node* control) { Node* control) {
......
...@@ -138,6 +138,8 @@ class EffectControlLinearizer { ...@@ -138,6 +138,8 @@ class EffectControlLinearizer {
Node* control); Node* control);
ValueEffectControl LowerEnsureWritableFastElements(Node* node, Node* effect, ValueEffectControl LowerEnsureWritableFastElements(Node* node, Node* effect,
Node* control); Node* control);
ValueEffectControl LowerMaybeGrowFastElements(Node* node, Node* frame_state,
Node* effect, Node* control);
ValueEffectControl LowerTransitionElementsKind(Node* node, Node* effect, ValueEffectControl LowerTransitionElementsKind(Node* node, Node* effect,
Node* control); Node* control);
ValueEffectControl LowerLoadTypedElement(Node* node, Node* effect, ValueEffectControl LowerLoadTypedElement(Node* node, Node* effect,
......
...@@ -57,6 +57,11 @@ Node* JSGraph::FixedArrayMapConstant() { ...@@ -57,6 +57,11 @@ Node* JSGraph::FixedArrayMapConstant() {
HeapConstant(factory()->fixed_array_map())); HeapConstant(factory()->fixed_array_map()));
} }
Node* JSGraph::FixedDoubleArrayMapConstant() {
return CACHED(kFixedDoubleArrayMapConstant,
HeapConstant(factory()->fixed_double_array_map()));
}
Node* JSGraph::HeapNumberMapConstant() { Node* JSGraph::HeapNumberMapConstant() {
return CACHED(kHeapNumberMapConstant, return CACHED(kHeapNumberMapConstant,
HeapConstant(factory()->heap_number_map())); HeapConstant(factory()->heap_number_map()));
......
...@@ -47,6 +47,7 @@ class JSGraph : public ZoneObject { ...@@ -47,6 +47,7 @@ class JSGraph : public ZoneObject {
Node* EmptyLiteralsArrayConstant(); Node* EmptyLiteralsArrayConstant();
Node* EmptyStringConstant(); Node* EmptyStringConstant();
Node* FixedArrayMapConstant(); Node* FixedArrayMapConstant();
Node* FixedDoubleArrayMapConstant();
Node* HeapNumberMapConstant(); Node* HeapNumberMapConstant();
Node* OptimizedOutConstant(); Node* OptimizedOutConstant();
Node* StaleRegisterConstant(); Node* StaleRegisterConstant();
...@@ -158,6 +159,7 @@ class JSGraph : public ZoneObject { ...@@ -158,6 +159,7 @@ class JSGraph : public ZoneObject {
kEmptyLiteralsArrayConstant, kEmptyLiteralsArrayConstant,
kEmptyStringConstant, kEmptyStringConstant,
kFixedArrayMapConstant, kFixedArrayMapConstant,
kFixedDoubleArrayMapConstant,
kHeapNumberMapConstant, kHeapNumberMapConstant,
kOptimizedOutConstant, kOptimizedOutConstant,
kStaleRegisterConstant, kStaleRegisterConstant,
......
...@@ -426,13 +426,6 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( ...@@ -426,13 +426,6 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
// Not much we can do if deoptimization support is disabled. // Not much we can do if deoptimization support is disabled.
if (!(flags() & kDeoptimizationEnabled)) return NoChange(); if (!(flags() & kDeoptimizationEnabled)) return NoChange();
// TODO(bmeurer): Add support for non-standard stores.
if (store_mode != STANDARD_STORE &&
store_mode != STORE_NO_TRANSITION_HANDLE_COW &&
store_mode != STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
return NoChange();
}
// Retrieve the native context from the given {node}. // Retrieve the native context from the given {node}.
Handle<Context> native_context; Handle<Context> native_context;
if (!GetNativeContext(node).ToHandle(&native_context)) return NoChange(); if (!GetNativeContext(node).ToHandle(&native_context)) return NoChange();
...@@ -1068,13 +1061,12 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -1068,13 +1061,12 @@ JSNativeContextSpecialization::BuildElementAccess(
} }
} }
} else { } else {
// TODO(turbofan): Add support for additional store modes. // Check if the {receiver} is a JSArray.
DCHECK(store_mode == STANDARD_STORE || bool receiver_is_jsarray = HasOnlyJSArrayMaps(receiver_maps);
store_mode == STORE_NO_TRANSITION_HANDLE_COW);
// Load the length of the {receiver}. // Load the length of the {receiver}.
Node* length = effect = Node* length = effect =
HasOnlyJSArrayMaps(receiver_maps) receiver_is_jsarray
? graph()->NewNode( ? graph()->NewNode(
simplified()->LoadField( simplified()->LoadField(
AccessBuilder::ForJSArrayLength(elements_kind)), AccessBuilder::ForJSArrayLength(elements_kind)),
...@@ -1083,9 +1075,20 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -1083,9 +1075,20 @@ JSNativeContextSpecialization::BuildElementAccess(
simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
elements, effect, control); elements, effect, control);
// Check if we might need to grow the {elements} backing store.
if (IsGrowStoreMode(store_mode)) {
DCHECK_EQ(AccessMode::kStore, access_mode);
// Check that the {index} is a valid array index; the actual checking
// happens below right before the element store.
index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
jsgraph()->Constant(Smi::kMaxValue),
effect, control);
} else {
// Check that the {index} is in the valid range for the {receiver}. // Check that the {index} is in the valid range for the {receiver}.
index = effect = graph()->NewNode(simplified()->CheckBounds(), index, index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
length, effect, control); length, effect, control);
}
// Compute the element access. // Compute the element access.
Type* element_type = Type::Any(); Type* element_type = Type::Any();
...@@ -1157,6 +1160,24 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -1157,6 +1160,24 @@ JSNativeContextSpecialization::BuildElementAccess(
elements = effect = elements = effect =
graph()->NewNode(simplified()->EnsureWritableFastElements(), graph()->NewNode(simplified()->EnsureWritableFastElements(),
receiver, elements, effect, control); receiver, elements, effect, control);
} else if (IsGrowStoreMode(store_mode)) {
// Grow {elements} backing store if necessary. Also updates the
// "length" property for JSArray {receiver}s, hence there must
// not be any other check after this operation, as the write
// to the "length" property is observable.
GrowFastElementsFlags flags = GrowFastElementsFlag::kNone;
if (receiver_is_jsarray) {
flags |= GrowFastElementsFlag::kArrayObject;
}
if (IsHoleyElementsKind(elements_kind)) {
flags |= GrowFastElementsFlag::kHoleyElements;
}
if (IsFastDoubleElementsKind(elements_kind)) {
flags |= GrowFastElementsFlag::kDoubleElements;
}
elements = effect = graph()->NewNode(
simplified()->MaybeGrowFastElements(flags), receiver, elements,
index, length, effect, control);
} }
// Perform the actual element access. // Perform the actual element access.
......
...@@ -56,6 +56,8 @@ Reduction LoadElimination::Reduce(Node* node) { ...@@ -56,6 +56,8 @@ Reduction LoadElimination::Reduce(Node* node) {
return ReduceCheckMaps(node); return ReduceCheckMaps(node);
case IrOpcode::kEnsureWritableFastElements: case IrOpcode::kEnsureWritableFastElements:
return ReduceEnsureWritableFastElements(node); return ReduceEnsureWritableFastElements(node);
case IrOpcode::kMaybeGrowFastElements:
return ReduceMaybeGrowFastElements(node);
case IrOpcode::kTransitionElementsKind: case IrOpcode::kTransitionElementsKind:
return ReduceTransitionElementsKind(node); return ReduceTransitionElementsKind(node);
case IrOpcode::kLoadField: case IrOpcode::kLoadField:
...@@ -352,6 +354,32 @@ Reduction LoadElimination::ReduceEnsureWritableFastElements(Node* node) { ...@@ -352,6 +354,32 @@ Reduction LoadElimination::ReduceEnsureWritableFastElements(Node* node) {
return UpdateState(node, state); return UpdateState(node, state);
} }
Reduction LoadElimination::ReduceMaybeGrowFastElements(Node* node) {
GrowFastElementsFlags flags = GrowFastElementsFlagsOf(node->op());
Node* const object = NodeProperties::GetValueInput(node, 0);
Node* const effect = NodeProperties::GetEffectInput(node);
AbstractState const* state = node_states_.Get(effect);
if (state == nullptr) return NoChange();
if (flags & GrowFastElementsFlag::kDoubleElements) {
// We know that the resulting elements have the fixed double array map.
Node* fixed_double_array_map = jsgraph()->FixedDoubleArrayMapConstant();
state = state->AddField(node, 0, fixed_double_array_map, zone());
} else {
// We know that the resulting elements have the fixed array map.
Node* fixed_array_map = jsgraph()->FixedArrayMapConstant();
state = state->AddField(node, 0, fixed_array_map, zone());
}
if (flags & GrowFastElementsFlag::kArrayObject) {
// Kill the previous Array::length on {object}.
state = state->KillField(object, 3, zone());
}
// Kill the previous elements on {object}.
state = state->KillField(object, 2, zone());
// Add the new elements on {object}.
state = state->AddField(object, 2, node, zone());
return UpdateState(node, state);
}
Reduction LoadElimination::ReduceTransitionElementsKind(Node* node) { Reduction LoadElimination::ReduceTransitionElementsKind(Node* node) {
Node* const object = NodeProperties::GetValueInput(node, 0); Node* const object = NodeProperties::GetValueInput(node, 0);
Node* const source_map = NodeProperties::GetValueInput(node, 1); Node* const source_map = NodeProperties::GetValueInput(node, 1);
...@@ -580,11 +608,19 @@ LoadElimination::AbstractState const* LoadElimination::ComputeLoopState( ...@@ -580,11 +608,19 @@ LoadElimination::AbstractState const* LoadElimination::ComputeLoopState(
switch (current->opcode()) { switch (current->opcode()) {
case IrOpcode::kEnsureWritableFastElements: { case IrOpcode::kEnsureWritableFastElements: {
Node* const object = NodeProperties::GetValueInput(current, 0); Node* const object = NodeProperties::GetValueInput(current, 0);
Node* const elements = NodeProperties::GetValueInput(current, 1);
state = state->KillField(elements, 0, zone());
state = state->KillField(object, 2, zone()); state = state->KillField(object, 2, zone());
break; break;
} }
case IrOpcode::kMaybeGrowFastElements: {
GrowFastElementsFlags flags =
GrowFastElementsFlagsOf(current->op());
Node* const object = NodeProperties::GetValueInput(current, 0);
state = state->KillField(object, 2, zone());
if (flags & GrowFastElementsFlag::kArrayObject) {
state = state->KillField(object, 3, zone());
}
break;
}
case IrOpcode::kTransitionElementsKind: { case IrOpcode::kTransitionElementsKind: {
Node* const object = NodeProperties::GetValueInput(current, 0); Node* const object = NodeProperties::GetValueInput(current, 0);
state = state->KillField(object, 0, zone()); state = state->KillField(object, 0, zone());
......
...@@ -152,6 +152,7 @@ class LoadElimination final : public AdvancedReducer { ...@@ -152,6 +152,7 @@ class LoadElimination final : public AdvancedReducer {
Reduction ReduceCheckMaps(Node* node); Reduction ReduceCheckMaps(Node* node);
Reduction ReduceEnsureWritableFastElements(Node* node); Reduction ReduceEnsureWritableFastElements(Node* node);
Reduction ReduceMaybeGrowFastElements(Node* node);
Reduction ReduceTransitionElementsKind(Node* node); Reduction ReduceTransitionElementsKind(Node* node);
Reduction ReduceLoadField(Node* node); Reduction ReduceLoadField(Node* node);
Reduction ReduceStoreField(Node* node); Reduction ReduceStoreField(Node* node);
......
...@@ -306,6 +306,7 @@ ...@@ -306,6 +306,7 @@
V(ObjectIsString) \ V(ObjectIsString) \
V(ObjectIsUndetectable) \ V(ObjectIsUndetectable) \
V(EnsureWritableFastElements) \ V(EnsureWritableFastElements) \
V(MaybeGrowFastElements) \
V(TransitionElementsKind) V(TransitionElementsKind)
#define SIMPLIFIED_OP_LIST(V) \ #define SIMPLIFIED_OP_LIST(V) \
......
...@@ -486,7 +486,10 @@ Type* OperationTyper::NumberToUint32(Type* type) { ...@@ -486,7 +486,10 @@ Type* OperationTyper::NumberToUint32(Type* type) {
Type* OperationTyper::NumberSilenceNaN(Type* type) { Type* OperationTyper::NumberSilenceNaN(Type* type) {
DCHECK(type->Is(Type::Number())); DCHECK(type->Is(Type::Number()));
// TODO(turbofan): We should have a dedicated type for the signaling NaN. // TODO(jarin): This is a terrible hack; we definitely need a dedicated type
// for the hole (tagged and/or double). Otherwise if the input is the hole
// NaN constant, we'd just eliminate this node in JSTypedLowering.
if (type->Maybe(Type::NaN())) return Type::Number();
return type; return type;
} }
......
...@@ -618,7 +618,9 @@ PipelineCompilationJob::Status PipelineCompilationJob::CreateGraphImpl() { ...@@ -618,7 +618,9 @@ PipelineCompilationJob::Status PipelineCompilationJob::CreateGraphImpl() {
// TODO(mstarzinger): Hack to ensure that certain call descriptors are // TODO(mstarzinger): Hack to ensure that certain call descriptors are
// initialized on the main thread, since it is needed off-thread by the // initialized on the main thread, since it is needed off-thread by the
// effect control linearizer. // effect control linearizer.
CodeFactory::CopyFixedArray(info()->isolate()); CodeFactory::CopyFastSmiOrObjectElements(info()->isolate());
CodeFactory::GrowFastDoubleElements(info()->isolate());
CodeFactory::GrowFastSmiOrObjectElements(info()->isolate());
CodeFactory::ToNumber(info()->isolate()); CodeFactory::ToNumber(info()->isolate());
linkage_ = new (&zone_) Linkage(Linkage::ComputeIncoming(&zone_, info())); linkage_ = new (&zone_) Linkage(Linkage::ComputeIncoming(&zone_, info()));
......
...@@ -2385,6 +2385,15 @@ class RepresentationSelector { ...@@ -2385,6 +2385,15 @@ class RepresentationSelector {
case IrOpcode::kEnsureWritableFastElements: case IrOpcode::kEnsureWritableFastElements:
return VisitBinop(node, UseInfo::AnyTagged(), return VisitBinop(node, UseInfo::AnyTagged(),
MachineRepresentation::kTagged); MachineRepresentation::kTagged);
case IrOpcode::kMaybeGrowFastElements: {
ProcessInput(node, 0, UseInfo::AnyTagged()); // object
ProcessInput(node, 1, UseInfo::AnyTagged()); // elements
ProcessInput(node, 2, UseInfo::TruncatingWord32()); // index
ProcessInput(node, 3, UseInfo::TruncatingWord32()); // length
ProcessRemainingInputs(node, 4);
SetOutput(node, MachineRepresentation::kTagged);
return;
}
//------------------------------------------------------------------ //------------------------------------------------------------------
// Machine-level operators. // Machine-level operators.
......
...@@ -249,6 +249,31 @@ CheckTaggedHoleMode CheckTaggedHoleModeOf(const Operator* op) { ...@@ -249,6 +249,31 @@ CheckTaggedHoleMode CheckTaggedHoleModeOf(const Operator* op) {
return OpParameter<CheckTaggedHoleMode>(op); return OpParameter<CheckTaggedHoleMode>(op);
} }
std::ostream& operator<<(std::ostream& os, GrowFastElementsFlags flags) {
bool empty = true;
if (flags & GrowFastElementsFlag::kArrayObject) {
os << "ArrayObject";
empty = false;
}
if (flags & GrowFastElementsFlag::kDoubleElements) {
if (!empty) os << "|";
os << "DoubleElements";
empty = false;
}
if (flags & GrowFastElementsFlag::kHoleyElements) {
if (!empty) os << "|";
os << "HoleyElements";
empty = false;
}
if (empty) os << "None";
return os;
}
GrowFastElementsFlags GrowFastElementsFlagsOf(const Operator* op) {
DCHECK_EQ(IrOpcode::kMaybeGrowFastElements, op->opcode());
return OpParameter<GrowFastElementsFlags>(op);
}
size_t hash_value(ElementsTransition transition) { size_t hash_value(ElementsTransition transition) {
return static_cast<uint8_t>(transition); return static_cast<uint8_t>(transition);
} }
...@@ -654,6 +679,16 @@ const Operator* SimplifiedOperatorBuilder::EnsureWritableFastElements() { ...@@ -654,6 +679,16 @@ const Operator* SimplifiedOperatorBuilder::EnsureWritableFastElements() {
return &cache_.kEnsureWritableFastElements; return &cache_.kEnsureWritableFastElements;
} }
const Operator* SimplifiedOperatorBuilder::MaybeGrowFastElements(
GrowFastElementsFlags flags) {
return new (zone()) Operator1<GrowFastElementsFlags>( // --
IrOpcode::kMaybeGrowFastElements, // opcode
Operator::kNoThrow, // flags
"MaybeGrowFastElements", // name
4, 1, 1, 1, 1, 0, // counts
flags); // parameter
}
const Operator* SimplifiedOperatorBuilder::TransitionElementsKind( const Operator* SimplifiedOperatorBuilder::TransitionElementsKind(
ElementsTransition transition) { ElementsTransition transition) {
return new (zone()) Operator1<ElementsTransition>( // -- return new (zone()) Operator1<ElementsTransition>( // --
......
...@@ -141,6 +141,22 @@ std::ostream& operator<<(std::ostream&, CheckForMinusZeroMode); ...@@ -141,6 +141,22 @@ std::ostream& operator<<(std::ostream&, CheckForMinusZeroMode);
CheckForMinusZeroMode CheckMinusZeroModeOf(const Operator*) WARN_UNUSED_RESULT; CheckForMinusZeroMode CheckMinusZeroModeOf(const Operator*) WARN_UNUSED_RESULT;
// A descriptor for growing elements backing stores.
enum class GrowFastElementsFlag : uint8_t {
kNone = 0u,
kArrayObject = 1u << 0, // Update JSArray::length field.
kHoleyElements = 1u << 1, // Backing store is holey.
kDoubleElements = 1u << 2, // Backing store contains doubles.
};
typedef base::Flags<GrowFastElementsFlag> GrowFastElementsFlags;
DEFINE_OPERATORS_FOR_FLAGS(GrowFastElementsFlags)
std::ostream& operator<<(std::ostream&, GrowFastElementsFlags);
GrowFastElementsFlags GrowFastElementsFlagsOf(const Operator*)
WARN_UNUSED_RESULT;
// A descriptor for elements kind transitions. // A descriptor for elements kind transitions.
enum class ElementsTransition : uint8_t { enum class ElementsTransition : uint8_t {
kFastTransition, // simple transition, just updating the map. kFastTransition, // simple transition, just updating the map.
...@@ -323,6 +339,9 @@ class SimplifiedOperatorBuilder final : public ZoneObject { ...@@ -323,6 +339,9 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
// ensure-writable-fast-elements object, elements // ensure-writable-fast-elements object, elements
const Operator* EnsureWritableFastElements(); const Operator* EnsureWritableFastElements();
// maybe-grow-fast-elements object, elements, index, length
const Operator* MaybeGrowFastElements(GrowFastElementsFlags flags);
// transition-elements-kind object, from-map, to-map // transition-elements-kind object, from-map, to-map
const Operator* TransitionElementsKind(ElementsTransition transition); const Operator* TransitionElementsKind(ElementsTransition transition);
......
...@@ -726,6 +726,10 @@ Type* Typer::Visitor::TypeEnsureWritableFastElements(Node* node) { ...@@ -726,6 +726,10 @@ Type* Typer::Visitor::TypeEnsureWritableFastElements(Node* node) {
return Operand(node, 1); return Operand(node, 1);
} }
Type* Typer::Visitor::TypeMaybeGrowFastElements(Node* node) {
return Operand(node, 1);
}
Type* Typer::Visitor::TypeTransitionElementsKind(Node* node) { Type* Typer::Visitor::TypeTransitionElementsKind(Node* node) {
UNREACHABLE(); UNREACHABLE();
return nullptr; return nullptr;
......
...@@ -856,6 +856,13 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -856,6 +856,13 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 1, Type::Internal()); CheckValueInputIs(node, 1, Type::Internal());
CheckUpperIs(node, Type::Internal()); CheckUpperIs(node, Type::Internal());
break; break;
case IrOpcode::kMaybeGrowFastElements:
CheckValueInputIs(node, 0, Type::Any());
CheckValueInputIs(node, 1, Type::Internal());
CheckValueInputIs(node, 2, Type::Unsigned31());
CheckValueInputIs(node, 3, Type::Unsigned31());
CheckUpperIs(node, Type::Internal());
break;
case IrOpcode::kTransitionElementsKind: case IrOpcode::kTransitionElementsKind:
CheckValueInputIs(node, 0, Type::Any()); CheckValueInputIs(node, 0, Type::Any());
CheckValueInputIs(node, 1, Type::Internal()); CheckValueInputIs(node, 1, Type::Internal());
......
...@@ -55,7 +55,7 @@ class PlatformInterfaceDescriptor; ...@@ -55,7 +55,7 @@ class PlatformInterfaceDescriptor;
V(ConstructStub) \ V(ConstructStub) \
V(ConstructTrampoline) \ V(ConstructTrampoline) \
V(RegExpConstructResult) \ V(RegExpConstructResult) \
V(CopyFixedArray) \ V(CopyFastSmiOrObjectElements) \
V(TransitionElementsKind) \ V(TransitionElementsKind) \
V(AllocateHeapNumber) \ V(AllocateHeapNumber) \
V(AllocateFloat32x4) \ V(AllocateFloat32x4) \
...@@ -649,11 +649,11 @@ class StoreGlobalViaContextDescriptor : public CallInterfaceDescriptor { ...@@ -649,11 +649,11 @@ class StoreGlobalViaContextDescriptor : public CallInterfaceDescriptor {
static const Register ValueRegister(); static const Register ValueRegister();
}; };
class CopyFixedArrayDescriptor : public CallInterfaceDescriptor { class CopyFastSmiOrObjectElementsDescriptor : public CallInterfaceDescriptor {
public: public:
DEFINE_PARAMETERS(kSource) DEFINE_PARAMETERS(kObject)
DECLARE_DEFAULT_DESCRIPTOR(CopyFixedArrayDescriptor, CallInterfaceDescriptor, DECLARE_DEFAULT_DESCRIPTOR(CopyFastSmiOrObjectElementsDescriptor,
kParameterCount) CallInterfaceDescriptor, kParameterCount)
}; };
class TransitionElementsKindDescriptor : public CallInterfaceDescriptor { class TransitionElementsKindDescriptor : public CallInterfaceDescriptor {
...@@ -876,6 +876,7 @@ class VarArgFunctionDescriptor : public CallInterfaceDescriptor { ...@@ -876,6 +876,7 @@ class VarArgFunctionDescriptor : public CallInterfaceDescriptor {
CallInterfaceDescriptor) CallInterfaceDescriptor)
}; };
// TODO(turbofan): We should probably rename this to GrowFastElementsDescriptor.
class GrowArrayElementsDescriptor : public CallInterfaceDescriptor { class GrowArrayElementsDescriptor : public CallInterfaceDescriptor {
public: public:
DEFINE_PARAMETERS(kObject, kKey) DEFINE_PARAMETERS(kObject, kKey)
......
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