diff options
Diffstat (limited to 'deps/v8/src/compiler/js-builtin-reducer.cc')
-rw-r--r-- | deps/v8/src/compiler/js-builtin-reducer.cc | 742 |
1 files changed, 80 insertions, 662 deletions
diff --git a/deps/v8/src/compiler/js-builtin-reducer.cc b/deps/v8/src/compiler/js-builtin-reducer.cc index df6fdba3f0..7ff2bf6d5e 100644 --- a/deps/v8/src/compiler/js-builtin-reducer.cc +++ b/deps/v8/src/compiler/js-builtin-reducer.cc @@ -109,49 +109,22 @@ JSBuiltinReducer::JSBuiltinReducer(Editor* editor, JSGraph* jsgraph, namespace { -MaybeHandle<Map> GetMapWitness(Node* node) { +Maybe<InstanceType> GetInstanceTypeWitness(Node* node) { ZoneHandleSet<Map> maps; Node* receiver = NodeProperties::GetValueInput(node, 1); Node* effect = NodeProperties::GetEffectInput(node); NodeProperties::InferReceiverMapsResult result = NodeProperties::InferReceiverMaps(receiver, effect, &maps); - if (result == NodeProperties::kReliableReceiverMaps && maps.size() == 1) { - return maps[0]; - } - return MaybeHandle<Map>(); -} -// TODO(turbofan): This was copied from Crankshaft, might be too restrictive. -bool IsReadOnlyLengthDescriptor(Handle<Map> jsarray_map) { - DCHECK(!jsarray_map->is_dictionary_map()); - Isolate* isolate = jsarray_map->GetIsolate(); - Handle<Name> length_string = isolate->factory()->length_string(); - DescriptorArray* descriptors = jsarray_map->instance_descriptors(); - int number = - descriptors->SearchWithCache(isolate, *length_string, *jsarray_map); - DCHECK_NE(DescriptorArray::kNotFound, number); - return descriptors->GetDetails(number).IsReadOnly(); -} + if (result == NodeProperties::kNoReceiverMaps || maps.size() == 0) { + return Nothing<InstanceType>(); + } -// TODO(turbofan): This was copied from Crankshaft, might be too restrictive. -bool CanInlineArrayResizeOperation(Handle<Map> receiver_map) { - Isolate* const isolate = receiver_map->GetIsolate(); - if (!receiver_map->prototype()->IsJSArray()) return false; - Handle<JSArray> receiver_prototype(JSArray::cast(receiver_map->prototype()), - isolate); - // Ensure that all prototypes of the {receiver} are stable. - for (PrototypeIterator it(isolate, receiver_prototype, kStartAtReceiver); - !it.IsAtEnd(); it.Advance()) { - Handle<JSReceiver> current = PrototypeIterator::GetCurrent<JSReceiver>(it); - if (!current->map()->is_stable()) return false; + InstanceType first_type = maps[0]->instance_type(); + for (const Handle<Map>& map : maps) { + if (map->instance_type() != first_type) return Nothing<InstanceType>(); } - return receiver_map->instance_type() == JS_ARRAY_TYPE && - IsFastElementsKind(receiver_map->elements_kind()) && - !receiver_map->is_dictionary_map() && receiver_map->is_extensible() && - (!receiver_map->is_prototype_map() || receiver_map->is_stable()) && - isolate->IsNoElementsProtectorIntact() && - isolate->IsAnyInitialArrayPrototype(receiver_prototype) && - !IsReadOnlyLengthDescriptor(receiver_map); + return Just(first_type); } bool CanInlineJSArrayIteration(Handle<Map> receiver_map) { @@ -189,7 +162,7 @@ bool CanInlineJSArrayIteration(Handle<Map> receiver_map) { Reduction JSBuiltinReducer::ReduceArrayIterator(Node* node, IterationKind kind) { Handle<Map> receiver_map; - if (GetMapWitness(node).ToHandle(&receiver_map)) { + if (NodeProperties::GetMapWitness(node).ToHandle(&receiver_map)) { return ReduceArrayIterator(receiver_map, node, kind, ArrayIteratorKind::kArray); } @@ -199,7 +172,7 @@ Reduction JSBuiltinReducer::ReduceArrayIterator(Node* node, Reduction JSBuiltinReducer::ReduceTypedArrayIterator(Node* node, IterationKind kind) { Handle<Map> receiver_map; - if (GetMapWitness(node).ToHandle(&receiver_map) && + if (NodeProperties::GetMapWitness(node).ToHandle(&receiver_map) && receiver_map->instance_type() == JS_TYPED_ARRAY_TYPE) { return ReduceArrayIterator(receiver_map, node, kind, ArrayIteratorKind::kTypedArray); @@ -313,8 +286,9 @@ Reduction JSBuiltinReducer::ReduceArrayIterator(Handle<Map> receiver_map, return Replace(value); } -Reduction JSBuiltinReducer::ReduceFastArrayIteratorNext( - Handle<Map> iterator_map, Node* node, IterationKind kind) { +Reduction JSBuiltinReducer::ReduceFastArrayIteratorNext(InstanceType type, + Node* node, + IterationKind kind) { Node* iterator = NodeProperties::GetValueInput(node, 1); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); @@ -327,8 +301,8 @@ Reduction JSBuiltinReducer::ReduceFastArrayIteratorNext( return NoChange(); } - ElementsKind elements_kind = JSArrayIterator::ElementsKindForInstanceType( - iterator_map->instance_type()); + ElementsKind elements_kind = + JSArrayIterator::ElementsKindForInstanceType(type); if (IsHoleyElementsKind(elements_kind)) { if (!isolate()->IsNoElementsProtectorIntact()) { @@ -484,15 +458,16 @@ Reduction JSBuiltinReducer::ReduceFastArrayIteratorNext( return Replace(value); } -Reduction JSBuiltinReducer::ReduceTypedArrayIteratorNext( - Handle<Map> iterator_map, Node* node, IterationKind kind) { +Reduction JSBuiltinReducer::ReduceTypedArrayIteratorNext(InstanceType type, + Node* node, + IterationKind kind) { Node* iterator = NodeProperties::GetValueInput(node, 1); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); Node* context = NodeProperties::GetContextInput(node); - ElementsKind elements_kind = JSArrayIterator::ElementsKindForInstanceType( - iterator_map->instance_type()); + ElementsKind elements_kind = + JSArrayIterator::ElementsKindForInstanceType(type); Node* array = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForJSArrayIteratorObject()), @@ -725,65 +700,58 @@ Reduction JSBuiltinReducer::ReduceTypedArrayToStringTag(Node* node) { } Reduction JSBuiltinReducer::ReduceArrayIteratorNext(Node* node) { - Handle<Map> receiver_map; - if (GetMapWitness(node).ToHandle(&receiver_map)) { - switch (receiver_map->instance_type()) { - case JS_TYPED_ARRAY_KEY_ITERATOR_TYPE: - return ReduceTypedArrayIteratorNext(receiver_map, node, - IterationKind::kKeys); - - case JS_FAST_ARRAY_KEY_ITERATOR_TYPE: - return ReduceFastArrayIteratorNext(receiver_map, node, - IterationKind::kKeys); - - case JS_INT8_ARRAY_KEY_VALUE_ITERATOR_TYPE: - case JS_UINT8_ARRAY_KEY_VALUE_ITERATOR_TYPE: - case JS_INT16_ARRAY_KEY_VALUE_ITERATOR_TYPE: - case JS_UINT16_ARRAY_KEY_VALUE_ITERATOR_TYPE: - case JS_INT32_ARRAY_KEY_VALUE_ITERATOR_TYPE: - case JS_UINT32_ARRAY_KEY_VALUE_ITERATOR_TYPE: - case JS_FLOAT32_ARRAY_KEY_VALUE_ITERATOR_TYPE: - case JS_FLOAT64_ARRAY_KEY_VALUE_ITERATOR_TYPE: - case JS_UINT8_CLAMPED_ARRAY_KEY_VALUE_ITERATOR_TYPE: - return ReduceTypedArrayIteratorNext(receiver_map, node, - IterationKind::kEntries); - - case JS_FAST_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE: - case JS_FAST_HOLEY_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE: - case JS_FAST_ARRAY_KEY_VALUE_ITERATOR_TYPE: - case JS_FAST_HOLEY_ARRAY_KEY_VALUE_ITERATOR_TYPE: - case JS_FAST_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE: - case JS_FAST_HOLEY_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE: - return ReduceFastArrayIteratorNext(receiver_map, node, - IterationKind::kEntries); - - case JS_INT8_ARRAY_VALUE_ITERATOR_TYPE: - case JS_UINT8_ARRAY_VALUE_ITERATOR_TYPE: - case JS_INT16_ARRAY_VALUE_ITERATOR_TYPE: - case JS_UINT16_ARRAY_VALUE_ITERATOR_TYPE: - case JS_INT32_ARRAY_VALUE_ITERATOR_TYPE: - case JS_UINT32_ARRAY_VALUE_ITERATOR_TYPE: - case JS_FLOAT32_ARRAY_VALUE_ITERATOR_TYPE: - case JS_FLOAT64_ARRAY_VALUE_ITERATOR_TYPE: - case JS_UINT8_CLAMPED_ARRAY_VALUE_ITERATOR_TYPE: - return ReduceTypedArrayIteratorNext(receiver_map, node, - IterationKind::kValues); - - case JS_FAST_SMI_ARRAY_VALUE_ITERATOR_TYPE: - case JS_FAST_HOLEY_SMI_ARRAY_VALUE_ITERATOR_TYPE: - case JS_FAST_ARRAY_VALUE_ITERATOR_TYPE: - case JS_FAST_HOLEY_ARRAY_VALUE_ITERATOR_TYPE: - case JS_FAST_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE: - case JS_FAST_HOLEY_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE: - return ReduceFastArrayIteratorNext(receiver_map, node, - IterationKind::kValues); - - default: - // Slow array iterators are not reduced - return NoChange(); - } + Maybe<InstanceType> maybe_type = GetInstanceTypeWitness(node); + if (!maybe_type.IsJust()) return NoChange(); + InstanceType type = maybe_type.FromJust(); + switch (type) { + case JS_TYPED_ARRAY_KEY_ITERATOR_TYPE: + return ReduceTypedArrayIteratorNext(type, node, IterationKind::kKeys); + + case JS_FAST_ARRAY_KEY_ITERATOR_TYPE: + return ReduceFastArrayIteratorNext(type, node, IterationKind::kKeys); + + case JS_INT8_ARRAY_KEY_VALUE_ITERATOR_TYPE: + case JS_UINT8_ARRAY_KEY_VALUE_ITERATOR_TYPE: + case JS_INT16_ARRAY_KEY_VALUE_ITERATOR_TYPE: + case JS_UINT16_ARRAY_KEY_VALUE_ITERATOR_TYPE: + case JS_INT32_ARRAY_KEY_VALUE_ITERATOR_TYPE: + case JS_UINT32_ARRAY_KEY_VALUE_ITERATOR_TYPE: + case JS_FLOAT32_ARRAY_KEY_VALUE_ITERATOR_TYPE: + case JS_FLOAT64_ARRAY_KEY_VALUE_ITERATOR_TYPE: + case JS_UINT8_CLAMPED_ARRAY_KEY_VALUE_ITERATOR_TYPE: + return ReduceTypedArrayIteratorNext(type, node, IterationKind::kEntries); + + case JS_FAST_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE: + case JS_FAST_HOLEY_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE: + case JS_FAST_ARRAY_KEY_VALUE_ITERATOR_TYPE: + case JS_FAST_HOLEY_ARRAY_KEY_VALUE_ITERATOR_TYPE: + case JS_FAST_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE: + case JS_FAST_HOLEY_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE: + return ReduceFastArrayIteratorNext(type, node, IterationKind::kEntries); + + case JS_INT8_ARRAY_VALUE_ITERATOR_TYPE: + case JS_UINT8_ARRAY_VALUE_ITERATOR_TYPE: + case JS_INT16_ARRAY_VALUE_ITERATOR_TYPE: + case JS_UINT16_ARRAY_VALUE_ITERATOR_TYPE: + case JS_INT32_ARRAY_VALUE_ITERATOR_TYPE: + case JS_UINT32_ARRAY_VALUE_ITERATOR_TYPE: + case JS_FLOAT32_ARRAY_VALUE_ITERATOR_TYPE: + case JS_FLOAT64_ARRAY_VALUE_ITERATOR_TYPE: + case JS_UINT8_CLAMPED_ARRAY_VALUE_ITERATOR_TYPE: + return ReduceTypedArrayIteratorNext(type, node, IterationKind::kValues); + + case JS_FAST_SMI_ARRAY_VALUE_ITERATOR_TYPE: + case JS_FAST_HOLEY_SMI_ARRAY_VALUE_ITERATOR_TYPE: + case JS_FAST_ARRAY_VALUE_ITERATOR_TYPE: + case JS_FAST_HOLEY_ARRAY_VALUE_ITERATOR_TYPE: + case JS_FAST_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE: + case JS_FAST_HOLEY_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE: + return ReduceFastArrayIteratorNext(type, node, IterationKind::kValues); + + default: + // Slow array iterators are not reduced + return NoChange(); } - return NoChange(); } // ES6 section 22.1.2.2 Array.isArray ( arg ) @@ -896,398 +864,6 @@ Reduction JSBuiltinReducer::ReduceArrayIsArray(Node* node) { return Replace(value); } -// ES6 section 22.1.3.17 Array.prototype.pop ( ) -Reduction JSBuiltinReducer::ReduceArrayPop(Node* node) { - Handle<Map> receiver_map; - Node* receiver = NodeProperties::GetValueInput(node, 1); - Node* effect = NodeProperties::GetEffectInput(node); - Node* control = NodeProperties::GetControlInput(node); - // TODO(turbofan): Extend this to also handle fast holey double elements - // once we got the hole NaN mess sorted out in TurboFan/V8. - if (GetMapWitness(node).ToHandle(&receiver_map) && - CanInlineArrayResizeOperation(receiver_map) && - receiver_map->elements_kind() != HOLEY_DOUBLE_ELEMENTS) { - // Install code dependencies on the {receiver} prototype maps and the - // global array protector cell. - dependencies()->AssumePropertyCell(factory()->no_elements_protector()); - dependencies()->AssumePrototypeMapsStable(receiver_map); - - // Load the "length" property of the {receiver}. - Node* length = effect = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())), - receiver, effect, control); - - // Check if the {receiver} has any elements. - Node* check = graph()->NewNode(simplified()->NumberEqual(), length, - jsgraph()->ZeroConstant()); - Node* branch = - graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); - - Node* if_true = graph()->NewNode(common()->IfTrue(), branch); - Node* etrue = effect; - Node* vtrue = jsgraph()->UndefinedConstant(); - - Node* if_false = graph()->NewNode(common()->IfFalse(), branch); - Node* efalse = effect; - Node* vfalse; - { - // TODO(tebbi): We should trim the backing store if the capacity is too - // big, as implemented in elements.cc:ElementsAccessorBase::SetLengthImpl. - - // Load the elements backing store from the {receiver}. - Node* elements = efalse = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForJSObjectElements()), - receiver, efalse, if_false); - - // Ensure that we aren't popping from a copy-on-write backing store. - if (IsSmiOrObjectElementsKind(receiver_map->elements_kind())) { - elements = efalse = - graph()->NewNode(simplified()->EnsureWritableFastElements(), - receiver, elements, efalse, if_false); - } - - // Compute the new {length}. - length = graph()->NewNode(simplified()->NumberSubtract(), length, - jsgraph()->OneConstant()); - - // Store the new {length} to the {receiver}. - efalse = graph()->NewNode( - simplified()->StoreField( - AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())), - receiver, length, efalse, if_false); - - // Load the last entry from the {elements}. - vfalse = efalse = graph()->NewNode( - simplified()->LoadElement(AccessBuilder::ForFixedArrayElement( - receiver_map->elements_kind())), - elements, length, efalse, if_false); - - // Store a hole to the element we just removed from the {receiver}. - efalse = graph()->NewNode( - simplified()->StoreElement(AccessBuilder::ForFixedArrayElement( - GetHoleyElementsKind(receiver_map->elements_kind()))), - elements, length, jsgraph()->TheHoleConstant(), efalse, if_false); - } - - control = graph()->NewNode(common()->Merge(2), if_true, if_false); - effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); - Node* value = - graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), - vtrue, vfalse, control); - - // Convert the hole to undefined. Do this last, so that we can optimize - // conversion operator via some smart strength reduction in many cases. - if (IsHoleyElementsKind(receiver_map->elements_kind())) { - value = - graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value); - } - - ReplaceWithValue(node, value, effect, control); - return Replace(value); - } - return NoChange(); -} - -// ES6 section 22.1.3.18 Array.prototype.push ( ) -Reduction JSBuiltinReducer::ReduceArrayPush(Node* node) { - DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); - int const num_values = node->op()->ValueInputCount() - 2; - Node* receiver = NodeProperties::GetValueInput(node, 1); - Node* effect = NodeProperties::GetEffectInput(node); - Node* control = NodeProperties::GetControlInput(node); - ZoneHandleSet<Map> receiver_maps; - NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps); - if (receiver_maps.size() != 1) return NoChange(); - DCHECK_NE(NodeProperties::kNoReceiverMaps, result); - - // TODO(turbofan): Relax this to deal with multiple {receiver} maps. - Handle<Map> receiver_map = receiver_maps[0]; - if (CanInlineArrayResizeOperation(receiver_map)) { - // Collect the value inputs to push. - std::vector<Node*> values(num_values); - for (int i = 0; i < num_values; ++i) { - values[i] = NodeProperties::GetValueInput(node, 2 + i); - } - - // Install code dependencies on the {receiver} prototype maps and the - // global array protector cell. - dependencies()->AssumePropertyCell(factory()->no_elements_protector()); - dependencies()->AssumePrototypeMapsStable(receiver_map); - - // If the {receiver_maps} information is not reliable, we need - // to check that the {receiver} still has one of these maps. - if (result == NodeProperties::kUnreliableReceiverMaps) { - if (receiver_map->is_stable()) { - dependencies()->AssumeMapStable(receiver_map); - } else { - // TODO(turbofan): This is a potential - yet unlikely - deoptimization - // loop, since we might not learn from this deoptimization in baseline - // code. We need a way to learn from deoptimizations in optimized to - // address these problems. - effect = graph()->NewNode( - simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), - receiver, effect, control); - } - } - - // TODO(turbofan): Perform type checks on the {values}. We are not - // guaranteed to learn from these checks in case they fail, as the witness - // (i.e. the map check from the LoadIC for a.push) might not be executed in - // baseline code (after we stored the value in the builtin and thereby - // changed the elements kind of a) before be decide to optimize this - // function again. We currently don't have a proper way to deal with this; - // the proper solution here is to learn on deopt, i.e. disable - // Array.prototype.push inlining for this function. - for (auto& value : values) { - if (IsSmiElementsKind(receiver_map->elements_kind())) { - value = effect = - graph()->NewNode(simplified()->CheckSmi(), value, effect, control); - } else if (IsDoubleElementsKind(receiver_map->elements_kind())) { - value = effect = graph()->NewNode(simplified()->CheckNumber(), value, - effect, control); - // Make sure we do not store signaling NaNs into double arrays. - value = graph()->NewNode(simplified()->NumberSilenceNaN(), value); - } - } - - // Load the "length" property of the {receiver}. - Node* length = effect = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())), - receiver, effect, control); - Node* value = length; - - // Check if we have any {values} to push. - if (num_values > 0) { - // Compute the resulting "length" of the {receiver}. - Node* new_length = value = graph()->NewNode( - simplified()->NumberAdd(), length, jsgraph()->Constant(num_values)); - - // Load the elements backing store of the {receiver}. - Node* elements = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForJSObjectElements()), - receiver, effect, control); - Node* elements_length = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), - elements, effect, control); - - // TODO(turbofan): Check if we need to grow the {elements} backing store. - // This will deopt if we cannot grow the array further, and we currently - // don't necessarily learn from it. See the comment on the value type - // check above. - GrowFastElementsMode mode = - IsDoubleElementsKind(receiver_map->elements_kind()) - ? GrowFastElementsMode::kDoubleElements - : GrowFastElementsMode::kSmiOrObjectElements; - elements = effect = graph()->NewNode( - simplified()->MaybeGrowFastElements(mode), receiver, elements, - graph()->NewNode(simplified()->NumberAdd(), length, - jsgraph()->Constant(num_values - 1)), - elements_length, effect, control); - - // Update the JSArray::length field. Since this is observable, - // there must be no other check after this. - effect = graph()->NewNode( - simplified()->StoreField( - AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())), - receiver, new_length, effect, control); - - // Append the {values} to the {elements}. - for (int i = 0; i < num_values; ++i) { - Node* value = values[i]; - Node* index = graph()->NewNode(simplified()->NumberAdd(), length, - jsgraph()->Constant(i)); - effect = graph()->NewNode( - simplified()->StoreElement(AccessBuilder::ForFixedArrayElement( - receiver_map->elements_kind())), - elements, index, value, effect, control); - } - } - - ReplaceWithValue(node, value, effect, control); - return Replace(value); - } - return NoChange(); -} - -// ES6 section 22.1.3.22 Array.prototype.shift ( ) -Reduction JSBuiltinReducer::ReduceArrayShift(Node* node) { - Node* target = NodeProperties::GetValueInput(node, 0); - Node* receiver = NodeProperties::GetValueInput(node, 1); - Node* context = NodeProperties::GetContextInput(node); - Node* frame_state = NodeProperties::GetFrameStateInput(node); - Node* effect = NodeProperties::GetEffectInput(node); - Node* control = NodeProperties::GetControlInput(node); - - // TODO(turbofan): Extend this to also handle fast holey double elements - // once we got the hole NaN mess sorted out in TurboFan/V8. - Handle<Map> receiver_map; - if (GetMapWitness(node).ToHandle(&receiver_map) && - CanInlineArrayResizeOperation(receiver_map) && - receiver_map->elements_kind() != HOLEY_DOUBLE_ELEMENTS) { - // Install code dependencies on the {receiver} prototype maps and the - // global array protector cell. - dependencies()->AssumePropertyCell(factory()->no_elements_protector()); - dependencies()->AssumePrototypeMapsStable(receiver_map); - - // Load length of the {receiver}. - Node* length = effect = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())), - receiver, effect, control); - - // Return undefined if {receiver} has no elements. - Node* check0 = graph()->NewNode(simplified()->NumberEqual(), length, - jsgraph()->ZeroConstant()); - Node* branch0 = - graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control); - - Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); - Node* etrue0 = effect; - Node* vtrue0 = jsgraph()->UndefinedConstant(); - - Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); - Node* efalse0 = effect; - Node* vfalse0; - { - // Check if we should take the fast-path. - Node* check1 = - graph()->NewNode(simplified()->NumberLessThanOrEqual(), length, - jsgraph()->Constant(JSArray::kMaxCopyElements)); - Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue), - check1, if_false0); - - Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); - Node* etrue1 = efalse0; - Node* vtrue1; - { - Node* elements = etrue1 = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForJSObjectElements()), - receiver, etrue1, if_true1); - - // Load the first element here, which we return below. - vtrue1 = etrue1 = graph()->NewNode( - simplified()->LoadElement(AccessBuilder::ForFixedArrayElement( - receiver_map->elements_kind())), - elements, jsgraph()->ZeroConstant(), etrue1, if_true1); - - // Ensure that we aren't shifting a copy-on-write backing store. - if (IsSmiOrObjectElementsKind(receiver_map->elements_kind())) { - elements = etrue1 = - graph()->NewNode(simplified()->EnsureWritableFastElements(), - receiver, elements, etrue1, if_true1); - } - - // Shift the remaining {elements} by one towards the start. - Node* loop = graph()->NewNode(common()->Loop(2), if_true1, if_true1); - Node* eloop = - graph()->NewNode(common()->EffectPhi(2), etrue1, etrue1, loop); - Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); - NodeProperties::MergeControlToEnd(graph(), common(), terminate); - Node* index = graph()->NewNode( - common()->Phi(MachineRepresentation::kTagged, 2), - jsgraph()->OneConstant(), - jsgraph()->Constant(JSArray::kMaxCopyElements - 1), loop); - - { - Node* check2 = - graph()->NewNode(simplified()->NumberLessThan(), index, length); - Node* branch2 = graph()->NewNode(common()->Branch(), check2, loop); - - if_true1 = graph()->NewNode(common()->IfFalse(), branch2); - etrue1 = eloop; - - Node* control = graph()->NewNode(common()->IfTrue(), branch2); - Node* effect = etrue1; - - ElementAccess const access = AccessBuilder::ForFixedArrayElement( - receiver_map->elements_kind()); - Node* value = effect = - graph()->NewNode(simplified()->LoadElement(access), elements, - index, effect, control); - effect = graph()->NewNode( - simplified()->StoreElement(access), elements, - graph()->NewNode(simplified()->NumberSubtract(), index, - jsgraph()->OneConstant()), - value, effect, control); - - loop->ReplaceInput(1, control); - eloop->ReplaceInput(1, effect); - index->ReplaceInput(1, - graph()->NewNode(simplified()->NumberAdd(), index, - jsgraph()->OneConstant())); - } - - // Compute the new {length}. - length = graph()->NewNode(simplified()->NumberSubtract(), length, - jsgraph()->OneConstant()); - - // Store the new {length} to the {receiver}. - etrue1 = graph()->NewNode( - simplified()->StoreField( - AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())), - receiver, length, etrue1, if_true1); - - // Store a hole to the element we just removed from the {receiver}. - etrue1 = graph()->NewNode( - simplified()->StoreElement(AccessBuilder::ForFixedArrayElement( - GetHoleyElementsKind(receiver_map->elements_kind()))), - elements, length, jsgraph()->TheHoleConstant(), etrue1, if_true1); - } - - Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); - Node* efalse1 = efalse0; - Node* vfalse1; - { - // Call the generic C++ implementation. - const int builtin_index = Builtins::kArrayShift; - CallDescriptor const* const desc = Linkage::GetCEntryStubCallDescriptor( - graph()->zone(), 1, BuiltinArguments::kNumExtraArgsWithReceiver, - Builtins::name(builtin_index), node->op()->properties(), - CallDescriptor::kNeedsFrameState); - Node* stub_code = jsgraph()->CEntryStubConstant(1, kDontSaveFPRegs, - kArgvOnStack, true); - Address builtin_entry = Builtins::CppEntryOf(builtin_index); - Node* entry = jsgraph()->ExternalConstant( - ExternalReference(builtin_entry, isolate())); - Node* argc = - jsgraph()->Constant(BuiltinArguments::kNumExtraArgsWithReceiver); - if_false1 = efalse1 = vfalse1 = - graph()->NewNode(common()->Call(desc), stub_code, receiver, - jsgraph()->PaddingConstant(), argc, target, - jsgraph()->UndefinedConstant(), entry, argc, - context, frame_state, efalse1, if_false1); - } - - if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); - efalse0 = - graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0); - vfalse0 = - graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), - vtrue1, vfalse1, 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); - - // Convert the hole to undefined. Do this last, so that we can optimize - // conversion operator via some smart strength reduction in many cases. - if (IsHoleyElementsKind(receiver_map->elements_kind())) { - value = - graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value); - } - - ReplaceWithValue(node, value, effect, control); - return Replace(value); - } - return NoChange(); -} - namespace { bool HasInstanceTypeWitness(Node* receiver, Node* effect, @@ -1451,6 +1027,7 @@ Reduction JSBuiltinReducer::ReduceCollectionIteratorNext( index = effect = graph()->NewNode( common()->Call(desc), jsgraph()->HeapConstant(callable.code()), table, index, jsgraph()->NoContextConstant(), effect); + NodeProperties::SetType(index, type_cache_.kFixedArrayLengthType); // Update the {index} and {table} on the {receiver}. effect = graph()->NewNode( @@ -1562,8 +1139,9 @@ Reduction JSBuiltinReducer::ReduceCollectionIteratorNext( // Abort loop with resulting value. Node* control = graph()->NewNode(common()->IfFalse(), branch1); Node* effect = etrue0; - Node* value = graph()->NewNode( - common()->TypeGuard(Type::NonInternal()), entry_key, control); + Node* value = effect = + graph()->NewNode(common()->TypeGuard(Type::NonInternal()), + entry_key, effect, control); Node* done = jsgraph()->FalseConstant(); // Advance the index on the {receiver}. @@ -2369,122 +1947,6 @@ Node* GetStringWitness(Node* node) { } // namespace -// ES6 section 21.1.3.1 String.prototype.charAt ( pos ) -Reduction JSBuiltinReducer::ReduceStringCharAt(Node* node) { - // We need at least target, receiver and index parameters. - if (node->op()->ValueInputCount() >= 3) { - Node* index = NodeProperties::GetValueInput(node, 2); - Type* index_type = NodeProperties::GetType(index); - Node* effect = NodeProperties::GetEffectInput(node); - Node* control = NodeProperties::GetControlInput(node); - - if (index_type->Is(Type::Integral32OrMinusZeroOrNaN())) { - if (Node* receiver = GetStringWitness(node)) { - if (!index_type->Is(Type::Unsigned32())) { - // Map -0 and NaN to 0 (as per ToInteger), and the values in - // the [-2^31,-1] range to the [2^31,2^32-1] range, which will - // be considered out-of-bounds as well, because of the maximal - // String length limit in V8. - STATIC_ASSERT(String::kMaxLength <= kMaxInt); - index = graph()->NewNode(simplified()->NumberToUint32(), index); - } - - // Determine the {receiver} length. - Node* receiver_length = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForStringLength()), receiver, - effect, control); - - // Check if {index} is less than {receiver} length. - Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, - receiver_length); - Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), - check, control); - - // Return the character from the {receiver} as single character string. - Node* if_true = graph()->NewNode(common()->IfTrue(), branch); - - Node* masked_index = graph()->NewNode( - simplified()->MaskIndexWithBound(), index, receiver_length); - - Node* vtrue = graph()->NewNode(simplified()->StringCharAt(), receiver, - masked_index, if_true); - - // Return the empty string otherwise. - Node* if_false = graph()->NewNode(common()->IfFalse(), branch); - Node* vfalse = jsgraph()->EmptyStringConstant(); - - control = graph()->NewNode(common()->Merge(2), if_true, if_false); - Node* value = - graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), - vtrue, vfalse, control); - - ReplaceWithValue(node, value, effect, control); - return Replace(value); - } - } - } - - return NoChange(); -} - -// ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos ) -Reduction JSBuiltinReducer::ReduceStringCharCodeAt(Node* node) { - // We need at least target, receiver and index parameters. - if (node->op()->ValueInputCount() >= 3) { - Node* index = NodeProperties::GetValueInput(node, 2); - Type* index_type = NodeProperties::GetType(index); - Node* effect = NodeProperties::GetEffectInput(node); - Node* control = NodeProperties::GetControlInput(node); - - if (index_type->Is(Type::Integral32OrMinusZeroOrNaN())) { - if (Node* receiver = GetStringWitness(node)) { - if (!index_type->Is(Type::Unsigned32())) { - // Map -0 and NaN to 0 (as per ToInteger), and the values in - // the [-2^31,-1] range to the [2^31,2^32-1] range, which will - // be considered out-of-bounds as well, because of the maximal - // String length limit in V8. - STATIC_ASSERT(String::kMaxLength <= kMaxInt); - index = graph()->NewNode(simplified()->NumberToUint32(), index); - } - - // Determine the {receiver} length. - Node* receiver_length = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForStringLength()), receiver, - effect, control); - - // Check if {index} is less than {receiver} length. - Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, - receiver_length); - Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), - check, control); - - // Load the character from the {receiver}. - Node* if_true = graph()->NewNode(common()->IfTrue(), branch); - - Node* masked_index = graph()->NewNode( - simplified()->MaskIndexWithBound(), index, receiver_length); - - Node* vtrue = graph()->NewNode(simplified()->StringCharCodeAt(), - receiver, masked_index, if_true); - - // Return NaN otherwise. - Node* if_false = graph()->NewNode(common()->IfFalse(), branch); - Node* vfalse = jsgraph()->NaNConstant(); - - control = graph()->NewNode(common()->Merge(2), if_true, if_false); - Node* value = - graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), - vtrue, vfalse, control); - - ReplaceWithValue(node, value, effect, control); - return Replace(value); - } - } - } - - return NoChange(); -} - // ES6 String.prototype.concat(...args) // #sec-string.prototype.concat Reduction JSBuiltinReducer::ReduceStringConcat(Node* node) { @@ -2516,34 +1978,6 @@ Reduction JSBuiltinReducer::ReduceStringConcat(Node* node) { return NoChange(); } -// ES6 String.prototype.indexOf(searchString [, position]) -// #sec-string.prototype.indexof -Reduction JSBuiltinReducer::ReduceStringIndexOf(Node* node) { - // We need at least target, receiver and search_string parameters. - if (node->op()->ValueInputCount() >= 3) { - Node* search_string = NodeProperties::GetValueInput(node, 2); - Type* search_string_type = NodeProperties::GetType(search_string); - Node* position = (node->op()->ValueInputCount() >= 4) - ? NodeProperties::GetValueInput(node, 3) - : jsgraph()->ZeroConstant(); - Type* position_type = NodeProperties::GetType(position); - - if (search_string_type->Is(Type::String()) && - position_type->Is(Type::SignedSmall())) { - if (Node* receiver = GetStringWitness(node)) { - RelaxEffectsAndControls(node); - node->ReplaceInput(0, receiver); - node->ReplaceInput(1, search_string); - node->ReplaceInput(2, position); - node->TrimInputCount(3); - NodeProperties::ChangeOp(node, simplified()->StringIndexOf()); - return Changed(node); - } - } - } - return NoChange(); -} - Reduction JSBuiltinReducer::ReduceStringIterator(Node* node) { if (Node* receiver = GetStringWitness(node)) { Node* effect = NodeProperties::GetEffectInput(node); @@ -2584,9 +2018,7 @@ Reduction JSBuiltinReducer::ReduceStringIteratorNext(Node* node) { Node* index = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForJSStringIteratorIndex()), receiver, effect, control); - Node* length = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForStringLength()), string, - effect, control); + Node* length = graph()->NewNode(simplified()->StringLength(), string); // branch0: if (index < length) Node* check0 = @@ -2677,9 +2109,8 @@ Reduction JSBuiltinReducer::ReduceStringIteratorNext(Node* node) { simplified()->StringFromCodePoint(UnicodeEncoding::UTF16), vtrue0); // Update iterator.[[NextIndex]] - Node* char_length = etrue0 = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForStringLength()), vtrue0, - etrue0, if_true0); + Node* char_length = + graph()->NewNode(simplified()->StringLength(), vtrue0); index = graph()->NewNode(simplified()->NumberAdd(), index, char_length); etrue0 = graph()->NewNode( simplified()->StoreField(AccessBuilder::ForJSStringIteratorIndex()), @@ -2728,9 +2159,8 @@ Reduction JSBuiltinReducer::ReduceStringSlice(Node* node) { if (start_type->Is(type_cache_.kSingletonMinusOne) && end_type->Is(Type::Undefined())) { - Node* receiver_length = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForStringLength()), receiver, - effect, control); + Node* receiver_length = + graph()->NewNode(simplified()->StringLength(), receiver); Node* check = graph()->NewNode(simplified()->NumberEqual(), receiver_length, @@ -2855,12 +2285,6 @@ Reduction JSBuiltinReducer::Reduce(Node* node) { return ReduceArrayIteratorNext(node); case kArrayIsArray: return ReduceArrayIsArray(node); - case kArrayPop: - return ReduceArrayPop(node); - case kArrayPush: - return ReduceArrayPush(node); - case kArrayShift: - return ReduceArrayShift(node); case kDateNow: return ReduceDateNow(node); case kDateGetTime: @@ -3024,14 +2448,8 @@ Reduction JSBuiltinReducer::Reduce(Node* node) { case kStringFromCharCode: reduction = ReduceStringFromCharCode(node); break; - case kStringCharAt: - return ReduceStringCharAt(node); - case kStringCharCodeAt: - return ReduceStringCharCodeAt(node); case kStringConcat: return ReduceStringConcat(node); - case kStringIndexOf: - return ReduceStringIndexOf(node); case kStringIterator: return ReduceStringIterator(node); case kStringIteratorNext: |