| Index: src/compiler/js-builtin-reducer.cc
|
| diff --git a/src/compiler/js-builtin-reducer.cc b/src/compiler/js-builtin-reducer.cc
|
| index 8bc03fae6239b850a901aab90cc41bf09ee30983..50d40c0c043adfcee48649fab63b62dcf8fec552 100644
|
| --- a/src/compiler/js-builtin-reducer.cc
|
| +++ b/src/compiler/js-builtin-reducer.cc
|
| @@ -175,8 +175,551 @@ bool CanInlineArrayResizeOperation(Handle<Map> receiver_map) {
|
| !IsReadOnlyLengthDescriptor(receiver_map);
|
| }
|
|
|
| +bool CanInlineJSArrayIteration(Handle<Map> receiver_map) {
|
| + Isolate* const isolate = receiver_map->GetIsolate();
|
| + // Ensure that the [[Prototype]] is actually an exotic Array
|
| + if (!receiver_map->prototype()->IsJSArray()) return false;
|
| +
|
| + // Don't inline JSArrays with slow elements of any kind
|
| + if (!IsFastElementsKind(receiver_map->elements_kind())) return false;
|
| +
|
| + // If the receiver map has packed elements, no need to check the prototype.
|
| + // This requires a MapCheck where this is used.
|
| + if (!IsFastHoleyElementsKind(receiver_map->elements_kind())) return true;
|
| +
|
| + Handle<JSArray> receiver_prototype(JSArray::cast(receiver_map->prototype()),
|
| + isolate);
|
| + // Ensure 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;
|
| + }
|
| +
|
| + // For holey Arrays, ensure that the array_protector cell is valid (must be
|
| + // a CompilationDependency), and the JSArray prototype has not been altered.
|
| + return receiver_map->instance_type() == JS_ARRAY_TYPE &&
|
| + (!receiver_map->is_dictionary_map() || receiver_map->is_stable()) &&
|
| + isolate->IsFastArrayConstructorPrototypeChainIntact() &&
|
| + isolate->IsAnyInitialArrayPrototype(receiver_prototype);
|
| +}
|
| +
|
| } // namespace
|
|
|
| +Reduction JSBuiltinReducer::ReduceArrayIterator(Node* node,
|
| + IterationKind kind) {
|
| + Handle<Map> receiver_map;
|
| + if (GetMapWitness(node).ToHandle(&receiver_map)) {
|
| + return ReduceArrayIterator(receiver_map, node, kind,
|
| + ArrayIteratorKind::kArray);
|
| + }
|
| + return NoChange();
|
| +}
|
| +
|
| +Reduction JSBuiltinReducer::ReduceTypedArrayIterator(Node* node,
|
| + IterationKind kind) {
|
| + Handle<Map> receiver_map;
|
| + if (GetMapWitness(node).ToHandle(&receiver_map) &&
|
| + receiver_map->instance_type() == JS_TYPED_ARRAY_TYPE) {
|
| + return ReduceArrayIterator(receiver_map, node, kind,
|
| + ArrayIteratorKind::kTypedArray);
|
| + }
|
| + return NoChange();
|
| +}
|
| +
|
| +Reduction JSBuiltinReducer::ReduceArrayIterator(Handle<Map> receiver_map,
|
| + Node* node, IterationKind kind,
|
| + ArrayIteratorKind iter_kind) {
|
| + Node* receiver = NodeProperties::GetValueInput(node, 1);
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + Node* control = NodeProperties::GetControlInput(node);
|
| +
|
| + if (iter_kind == ArrayIteratorKind::kTypedArray) {
|
| + // For JSTypedArray iterator methods, deopt if the buffer is neutered. This
|
| + // is potentially a deopt loop, but should be extremely unlikely.
|
| + DCHECK_EQ(JS_TYPED_ARRAY_TYPE, receiver_map->instance_type());
|
| + Node* buffer = effect = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
|
| + receiver, effect, control);
|
| +
|
| + Node* check = effect = graph()->NewNode(
|
| + simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
|
| + check = graph()->NewNode(simplified()->BooleanNot(), check);
|
| + effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
|
| + }
|
| +
|
| + int map_index = -1;
|
| + Node* object_map = jsgraph()->UndefinedConstant();
|
| + switch (receiver_map->instance_type()) {
|
| + case JS_ARRAY_TYPE:
|
| + if (kind == IterationKind::kKeys) {
|
| + map_index = Context::FAST_ARRAY_KEY_ITERATOR_MAP_INDEX;
|
| + } else {
|
| + map_index = kind == IterationKind::kValues
|
| + ? Context::FAST_SMI_ARRAY_VALUE_ITERATOR_MAP_INDEX
|
| + : Context::FAST_SMI_ARRAY_KEY_VALUE_ITERATOR_MAP_INDEX;
|
| +
|
| + if (CanInlineJSArrayIteration(receiver_map)) {
|
| + // Use `generic` elements for holey arrays if there may be elements
|
| + // on the prototype chain.
|
| + map_index += static_cast<int>(receiver_map->elements_kind());
|
| + object_map = jsgraph()->Constant(receiver_map);
|
| + if (IsFastHoleyElementsKind(receiver_map->elements_kind())) {
|
| + Handle<JSObject> initial_array_prototype(
|
| + native_context()->initial_array_prototype(), isolate());
|
| + dependencies()->AssumePrototypeMapsStable(receiver_map,
|
| + initial_array_prototype);
|
| + }
|
| + } else {
|
| + map_index += (Context::GENERIC_ARRAY_VALUE_ITERATOR_MAP_INDEX -
|
| + Context::FAST_SMI_ARRAY_VALUE_ITERATOR_MAP_INDEX);
|
| + }
|
| + }
|
| + break;
|
| + case JS_TYPED_ARRAY_TYPE:
|
| + if (kind == IterationKind::kKeys) {
|
| + map_index = Context::TYPED_ARRAY_KEY_ITERATOR_MAP_INDEX;
|
| + } else {
|
| + DCHECK_GE(receiver_map->elements_kind(), UINT8_ELEMENTS);
|
| + DCHECK_LE(receiver_map->elements_kind(), UINT8_CLAMPED_ELEMENTS);
|
| + map_index = (kind == IterationKind::kValues
|
| + ? Context::UINT8_ARRAY_VALUE_ITERATOR_MAP_INDEX
|
| + : Context::UINT8_ARRAY_KEY_VALUE_ITERATOR_MAP_INDEX) +
|
| + (receiver_map->elements_kind() - UINT8_ELEMENTS);
|
| + }
|
| + break;
|
| + default:
|
| + if (kind == IterationKind::kKeys) {
|
| + map_index = Context::GENERIC_ARRAY_KEY_ITERATOR_MAP_INDEX;
|
| + } else if (kind == IterationKind::kValues) {
|
| + map_index = Context::GENERIC_ARRAY_VALUE_ITERATOR_MAP_INDEX;
|
| + } else {
|
| + map_index = Context::GENERIC_ARRAY_KEY_VALUE_ITERATOR_MAP_INDEX;
|
| + }
|
| + break;
|
| + }
|
| +
|
| + DCHECK_GE(map_index, Context::TYPED_ARRAY_KEY_ITERATOR_MAP_INDEX);
|
| + DCHECK_LE(map_index, Context::GENERIC_ARRAY_VALUE_ITERATOR_MAP_INDEX);
|
| +
|
| + Handle<Map> map(Map::cast(native_context()->get(map_index)), isolate());
|
| +
|
| + // allocate new iterator
|
| + effect = graph()->NewNode(
|
| + common()->BeginRegion(RegionObservability::kNotObservable), effect);
|
| + Node* value = effect = graph()->NewNode(
|
| + simplified()->Allocate(NOT_TENURED),
|
| + jsgraph()->Constant(JSArrayIterator::kSize), effect, control);
|
| + effect = graph()->NewNode(simplified()->StoreField(AccessBuilder::ForMap()),
|
| + value, jsgraph()->Constant(map), effect, control);
|
| + effect = graph()->NewNode(
|
| + simplified()->StoreField(AccessBuilder::ForJSObjectProperties()), value,
|
| + jsgraph()->EmptyFixedArrayConstant(), effect, control);
|
| + effect = graph()->NewNode(
|
| + simplified()->StoreField(AccessBuilder::ForJSObjectElements()), value,
|
| + jsgraph()->EmptyFixedArrayConstant(), effect, control);
|
| +
|
| + // attach the iterator to this object
|
| + effect = graph()->NewNode(
|
| + simplified()->StoreField(AccessBuilder::ForJSArrayIteratorObject()),
|
| + value, receiver, effect, control);
|
| + effect = graph()->NewNode(
|
| + simplified()->StoreField(AccessBuilder::ForJSArrayIteratorIndex()), value,
|
| + jsgraph()->ZeroConstant(), effect, control);
|
| + effect = graph()->NewNode(
|
| + simplified()->StoreField(AccessBuilder::ForJSArrayIteratorObjectMap()),
|
| + value, object_map, effect, control);
|
| +
|
| + value = effect = graph()->NewNode(common()->FinishRegion(), value, effect);
|
| +
|
| + // replace it
|
| + ReplaceWithValue(node, value, effect, control);
|
| + return Replace(value);
|
| +}
|
| +
|
| +Reduction JSBuiltinReducer::ReduceFastArrayIteratorNext(
|
| + Handle<Map> iterator_map, 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);
|
| +
|
| + if (kind != IterationKind::kKeys && !isolate()->CanInlineArrayIterator()) {
|
| + // Avoid deopt loops for non-key iteration if the array_iterator_protector
|
| + // cell has been invalidated.
|
| + return NoChange();
|
| + }
|
| +
|
| + ElementsKind elements_kind = JSArrayIterator::ElementsKindForInstanceType(
|
| + iterator_map->instance_type());
|
| +
|
| + if (IsFastHoleyElementsKind(elements_kind)) {
|
| + if (!isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
|
| + return NoChange();
|
| + } else {
|
| + Handle<JSObject> initial_array_prototype(
|
| + native_context()->initial_array_prototype(), isolate());
|
| + dependencies()->AssumePropertyCell(factory()->array_protector());
|
| + }
|
| + }
|
| +
|
| + Node* array = effect = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSArrayIteratorObject()),
|
| + iterator, effect, control);
|
| + Node* check0 = graph()->NewNode(simplified()->ReferenceEqual(), array,
|
| + jsgraph()->UndefinedConstant());
|
| + Node* branch0 =
|
| + graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control);
|
| +
|
| + Node* vdone_false0;
|
| + Node* vfalse0;
|
| + Node* efalse0 = effect;
|
| + Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
|
| + {
|
| + // iterator.[[IteratedObject]] !== undefined, continue iterating.
|
| + Node* index = efalse0 = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSArrayIteratorIndex(
|
| + JS_ARRAY_TYPE, elements_kind)),
|
| + iterator, efalse0, if_false0);
|
| +
|
| + Node* length = efalse0 = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSArrayLength(elements_kind)),
|
| + array, efalse0, if_false0);
|
| + Node* check1 =
|
| + graph()->NewNode(simplified()->NumberLessThan(), index, length);
|
| + Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
|
| + check1, if_false0);
|
| +
|
| + Node* vdone_true1;
|
| + Node* vtrue1;
|
| + Node* etrue1 = efalse0;
|
| + Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
| + {
|
| + // iterator.[[NextIndex]] < array.length, continue iterating
|
| + vdone_true1 = jsgraph()->FalseConstant();
|
| + if (kind == IterationKind::kKeys) {
|
| + vtrue1 = index;
|
| + } else {
|
| + // For value/entry iteration, first step is a mapcheck to ensure
|
| + // inlining is still valid.
|
| + Node* orig_map = etrue1 =
|
| + graph()->NewNode(simplified()->LoadField(
|
| + AccessBuilder::ForJSArrayIteratorObjectMap()),
|
| + iterator, etrue1, if_true1);
|
| + etrue1 = graph()->NewNode(simplified()->CheckMaps(1), array, orig_map,
|
| + etrue1, if_true1);
|
| + }
|
| +
|
| + if (kind != IterationKind::kKeys) {
|
| + Node* elements = etrue1 = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
|
| + array, etrue1, if_true1);
|
| + Node* value = etrue1 = graph()->NewNode(
|
| + simplified()->LoadElement(
|
| + AccessBuilder::ForFixedArrayElement(elements_kind)),
|
| + elements, index, etrue1, if_true1);
|
| +
|
| + // Convert hole to undefined if needed.
|
| + if (elements_kind == FAST_HOLEY_ELEMENTS ||
|
| + elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
|
| + value = graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(),
|
| + value);
|
| + } else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) {
|
| + // TODO(bmeurer): avoid deopt if not all uses of value are truncated.
|
| + CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole;
|
| + value = etrue1 = graph()->NewNode(
|
| + simplified()->CheckFloat64Hole(mode), value, etrue1, if_true1);
|
| + }
|
| +
|
| + if (kind == IterationKind::kEntries) {
|
| + // Allocate elements for key/value pair
|
| + vtrue1 = etrue1 = graph()->NewNode(
|
| + javascript()->CreateKeyValueArray(), index, value, etrue1);
|
| + } else {
|
| + DCHECK_EQ(kind, IterationKind::kValues);
|
| + vtrue1 = value;
|
| + }
|
| + }
|
| +
|
| + Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index,
|
| + jsgraph()->OneConstant());
|
| + next_index = graph()->NewNode(simplified()->NumberToUint32(), next_index);
|
| +
|
| + etrue1 = graph()->NewNode(
|
| + simplified()->StoreField(AccessBuilder::ForJSArrayIteratorIndex(
|
| + JS_ARRAY_TYPE, elements_kind)),
|
| + iterator, next_index, etrue1, if_true1);
|
| + }
|
| +
|
| + Node* vdone_false1;
|
| + Node* vfalse1;
|
| + Node* efalse1 = efalse0;
|
| + Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
|
| + {
|
| + // iterator.[[NextIndex]] >= array.length, stop iterating.
|
| + vdone_false1 = jsgraph()->TrueConstant();
|
| + vfalse1 = jsgraph()->UndefinedConstant();
|
| + efalse1 = graph()->NewNode(
|
| + simplified()->StoreField(AccessBuilder::ForJSArrayIteratorObject()),
|
| + iterator, vfalse1, 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);
|
| + vdone_false0 =
|
| + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
|
| + vdone_true1, vdone_false1, if_false0);
|
| + }
|
| +
|
| + Node* vdone_true0;
|
| + Node* vtrue0;
|
| + Node* etrue0 = effect;
|
| + Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
| + {
|
| + // iterator.[[IteratedObject]] === undefined, the iterator is done.
|
| + vdone_true0 = jsgraph()->TrueConstant();
|
| + vtrue0 = jsgraph()->UndefinedConstant();
|
| + }
|
| +
|
| + control = graph()->NewNode(common()->Merge(2), if_false0, if_true0);
|
| + effect = graph()->NewNode(common()->EffectPhi(2), efalse0, etrue0, control);
|
| + Node* value =
|
| + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
|
| + vfalse0, vtrue0, control);
|
| + Node* done =
|
| + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
|
| + vdone_false0, vdone_true0, control);
|
| +
|
| + // Create IteratorResult object.
|
| + value = effect = graph()->NewNode(javascript()->CreateIterResultObject(),
|
| + value, done, context, effect);
|
| + ReplaceWithValue(node, value, effect, control);
|
| + return Replace(value);
|
| +}
|
| +
|
| +Reduction JSBuiltinReducer::ReduceTypedArrayIteratorNext(
|
| + Handle<Map> iterator_map, 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());
|
| +
|
| + Node* array = effect = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSArrayIteratorObject()),
|
| + iterator, effect, control);
|
| + Node* check0 = graph()->NewNode(simplified()->ReferenceEqual(), array,
|
| + jsgraph()->UndefinedConstant());
|
| + Node* branch0 =
|
| + graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control);
|
| +
|
| + Node* vdone_false0;
|
| + Node* vfalse0;
|
| + Node* efalse0 = effect;
|
| + Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
|
| + {
|
| + // iterator.[[IteratedObject]] !== undefined, continue iterating.
|
| + Node* index = efalse0 = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSArrayIteratorIndex(
|
| + JS_TYPED_ARRAY_TYPE, elements_kind)),
|
| + iterator, efalse0, if_false0);
|
| +
|
| + // typedarray.[[ViewedArrayBuffer]]
|
| + Node* buffer = efalse0 = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
|
| + array, efalse0, if_false0);
|
| +
|
| + Node* check1 = efalse0 = graph()->NewNode(
|
| + simplified()->ArrayBufferWasNeutered(), buffer, efalse0, if_false0);
|
| + check1 = graph()->NewNode(simplified()->BooleanNot(), check1);
|
| + efalse0 =
|
| + graph()->NewNode(simplified()->CheckIf(), check1, efalse0, if_false0);
|
| +
|
| + Node* length = efalse0 = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()), array,
|
| + efalse0, if_false0);
|
| +
|
| + Node* check2 =
|
| + graph()->NewNode(simplified()->NumberLessThan(), index, length);
|
| + Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
|
| + check2, if_false0);
|
| +
|
| + Node* vdone_true2;
|
| + Node* vtrue2;
|
| + Node* etrue2 = efalse0;
|
| + Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
|
| + {
|
| + // iterator.[[NextIndex]] < array.length, continue iterating
|
| + vdone_true2 = jsgraph()->FalseConstant();
|
| + if (kind == IterationKind::kKeys) {
|
| + vtrue2 = index;
|
| + }
|
| +
|
| + Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index,
|
| + jsgraph()->OneConstant());
|
| + next_index = graph()->NewNode(simplified()->NumberToUint32(), next_index);
|
| +
|
| + etrue2 = graph()->NewNode(
|
| + simplified()->StoreField(AccessBuilder::ForJSArrayIteratorIndex(
|
| + JS_TYPED_ARRAY_TYPE, elements_kind)),
|
| + iterator, next_index, etrue2, if_true2);
|
| +
|
| + if (kind != IterationKind::kKeys) {
|
| + Node* elements = etrue2 = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
|
| + array, etrue2, if_true2);
|
| + Node* base_ptr = etrue2 = graph()->NewNode(
|
| + simplified()->LoadField(
|
| + AccessBuilder::ForFixedTypedArrayBaseBasePointer()),
|
| + elements, etrue2, if_true2);
|
| + Node* external_ptr = etrue2 = graph()->NewNode(
|
| + simplified()->LoadField(
|
| + AccessBuilder::ForFixedTypedArrayBaseExternalPointer()),
|
| + elements, etrue2, if_true2);
|
| +
|
| + ExternalArrayType array_type = kExternalInt8Array;
|
| + switch (elements_kind) {
|
| +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
|
| + case TYPE##_ELEMENTS: \
|
| + array_type = kExternal##Type##Array; \
|
| + break;
|
| + TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
| + default:
|
| + UNREACHABLE();
|
| +#undef TYPED_ARRAY_CASE
|
| + }
|
| +
|
| + Node* value = etrue2 =
|
| + graph()->NewNode(simplified()->LoadTypedElement(array_type), buffer,
|
| + base_ptr, external_ptr, index, etrue2, if_true2);
|
| +
|
| + if (kind == IterationKind::kEntries) {
|
| + // Allocate elements for key/value pair
|
| + vtrue2 = etrue2 = graph()->NewNode(
|
| + javascript()->CreateKeyValueArray(), index, value, etrue2);
|
| + } else {
|
| + DCHECK(kind == IterationKind::kValues);
|
| + vtrue2 = value;
|
| + }
|
| + }
|
| + }
|
| +
|
| + Node* vdone_false2;
|
| + Node* vfalse2;
|
| + Node* efalse2 = efalse0;
|
| + Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
|
| + {
|
| + // iterator.[[NextIndex]] >= array.length, stop iterating.
|
| + vdone_false2 = jsgraph()->TrueConstant();
|
| + vfalse2 = jsgraph()->UndefinedConstant();
|
| + efalse2 = graph()->NewNode(
|
| + simplified()->StoreField(AccessBuilder::ForJSArrayIteratorObject()),
|
| + iterator, vfalse2, efalse2, if_false2);
|
| + }
|
| +
|
| + if_false0 = graph()->NewNode(common()->Merge(2), if_true2, if_false2);
|
| + efalse0 =
|
| + graph()->NewNode(common()->EffectPhi(2), etrue2, efalse2, if_false0);
|
| + vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
|
| + vtrue2, vfalse2, if_false0);
|
| + vdone_false0 =
|
| + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
|
| + vdone_true2, vdone_false2, if_false0);
|
| + }
|
| +
|
| + Node* vdone_true0;
|
| + Node* vtrue0;
|
| + Node* etrue0 = effect;
|
| + Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
| + {
|
| + // iterator.[[IteratedObject]] === undefined, the iterator is done.
|
| + vdone_true0 = jsgraph()->TrueConstant();
|
| + vtrue0 = jsgraph()->UndefinedConstant();
|
| + }
|
| +
|
| + control = graph()->NewNode(common()->Merge(2), if_false0, if_true0);
|
| + effect = graph()->NewNode(common()->EffectPhi(2), efalse0, etrue0, control);
|
| + Node* value =
|
| + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
|
| + vfalse0, vtrue0, control);
|
| + Node* done =
|
| + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
|
| + vdone_false0, vdone_true0, control);
|
| +
|
| + // Create IteratorResult object.
|
| + value = effect = graph()->NewNode(javascript()->CreateIterResultObject(),
|
| + value, done, context, effect);
|
| + ReplaceWithValue(node, value, effect, control);
|
| + return Replace(value);
|
| +}
|
| +
|
| +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();
|
| + }
|
| + }
|
| + return NoChange();
|
| +}
|
| +
|
| // ES6 section 22.1.3.17 Array.prototype.pop ( )
|
| Reduction JSBuiltinReducer::ReduceArrayPop(Node* node) {
|
| Handle<Map> receiver_map;
|
| @@ -1260,6 +1803,14 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
|
| // Dispatch according to the BuiltinFunctionId if present.
|
| if (!r.HasBuiltinFunctionId()) return NoChange();
|
| switch (r.GetBuiltinFunctionId()) {
|
| + case kArrayEntries:
|
| + return ReduceArrayIterator(node, IterationKind::kEntries);
|
| + case kArrayKeys:
|
| + return ReduceArrayIterator(node, IterationKind::kKeys);
|
| + case kArrayValues:
|
| + return ReduceArrayIterator(node, IterationKind::kValues);
|
| + case kArrayIteratorNext:
|
| + return ReduceArrayIteratorNext(node);
|
| case kArrayPop:
|
| return ReduceArrayPop(node);
|
| case kArrayPush:
|
| @@ -1416,6 +1967,12 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
|
| case kTypedArrayLength:
|
| return ReduceArrayBufferViewAccessor(
|
| node, JS_TYPED_ARRAY_TYPE, AccessBuilder::ForJSTypedArrayLength());
|
| + case kTypedArrayEntries:
|
| + return ReduceTypedArrayIterator(node, IterationKind::kEntries);
|
| + case kTypedArrayKeys:
|
| + return ReduceTypedArrayIterator(node, IterationKind::kKeys);
|
| + case kTypedArrayValues:
|
| + return ReduceTypedArrayIterator(node, IterationKind::kValues);
|
| default:
|
| break;
|
| }
|
|
|