diff options
Diffstat (limited to 'deps/v8/src/compiler/js-builtin-reducer.cc')
-rw-r--r-- | deps/v8/src/compiler/js-builtin-reducer.cc | 1357 |
1 files changed, 0 insertions, 1357 deletions
diff --git a/deps/v8/src/compiler/js-builtin-reducer.cc b/deps/v8/src/compiler/js-builtin-reducer.cc index a6d98586ad..c3483092d2 100644 --- a/deps/v8/src/compiler/js-builtin-reducer.cc +++ b/deps/v8/src/compiler/js-builtin-reducer.cc @@ -115,653 +115,6 @@ JSBuiltinReducer::JSBuiltinReducer(Editor* editor, JSGraph* jsgraph, native_context_(native_context), type_cache_(TypeCache::Get()) {} -namespace { - -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::kNoReceiverMaps || maps.size() == 0) { - return Nothing<InstanceType>(); - } - - InstanceType first_type = maps[0]->instance_type(); - for (const Handle<Map>& map : maps) { - if (map->instance_type() != first_type) return Nothing<InstanceType>(); - } - return Just(first_type); -} - -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 (!IsHoleyElementsKind(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 no_elements_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->IsNoElementsProtectorIntact() && - isolate->IsAnyInitialArrayPrototype(receiver_prototype); -} - -} // namespace - -Reduction JSBuiltinReducer::ReduceArrayIterator(Node* node, - IterationKind kind) { - Handle<Map> receiver_map; - if (NodeProperties::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 (NodeProperties::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) { - // See if we can skip the neutering check. - if (isolate()->IsArrayBufferNeuteringIntact()) { - // Add a code dependency so we are deoptimized in case an ArrayBuffer - // gets neutered. - dependencies()->AssumePropertyCell( - factory()->array_buffer_neutering_protector()); - } else { - // 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); - - // Deoptimize if the {buffer} has been neutered. - Node* check = effect = graph()->NewNode( - simplified()->ArrayBufferWasNeutered(), buffer, effect, control); - check = graph()->NewNode(simplified()->BooleanNot(), check); - effect = graph()->NewNode( - simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasNeutered), - 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 (IsHoleyElementsKind(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(), BIGINT64_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 and attach the iterator to this object. - AllocationBuilder a(jsgraph(), effect, control); - a.Allocate(JSArrayIterator::kSize, NOT_TENURED, Type::OtherObject()); - a.Store(AccessBuilder::ForMap(), map); - a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), - jsgraph()->EmptyFixedArrayConstant()); - a.Store(AccessBuilder::ForJSObjectElements(), - jsgraph()->EmptyFixedArrayConstant()); - a.Store(AccessBuilder::ForJSArrayIteratorObject(), receiver); - a.Store(AccessBuilder::ForJSArrayIteratorIndex(), jsgraph()->ZeroConstant()); - a.Store(AccessBuilder::ForJSArrayIteratorObjectMap(), object_map); - Node* value = effect = a.Finish(); - - // Replace it. - ReplaceWithValue(node, value, effect, control); - return Replace(value); -} - -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); - Node* context = NodeProperties::GetContextInput(node); - - if (kind != IterationKind::kKeys && - !isolate()->IsFastArrayIterationIntact()) { - // Avoid deopt loops for non-key iteration if the - // fast_array_iteration_protector cell has been invalidated. - return NoChange(); - } - - ElementsKind elements_kind = - JSArrayIterator::ElementsKindForInstanceType(type); - - if (IsHoleyElementsKind(elements_kind)) { - if (!isolate()->IsNoElementsProtectorIntact()) { - return NoChange(); - } else { - Handle<JSObject> initial_array_prototype( - native_context()->initial_array_prototype(), isolate()); - dependencies()->AssumePropertyCell(factory()->no_elements_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* array_map = etrue1 = - graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), - array, etrue1, if_true1); - Node* orig_map = etrue1 = - graph()->NewNode(simplified()->LoadField( - AccessBuilder::ForJSArrayIteratorObjectMap()), - iterator, etrue1, if_true1); - Node* check_map = graph()->NewNode(simplified()->ReferenceEqual(), - array_map, orig_map); - etrue1 = - graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongMap), - check_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 == HOLEY_ELEMENTS || - elements_kind == HOLEY_SMI_ELEMENTS) { - value = graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), - value); - } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) { - // TODO(6587): 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, context, 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(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(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); - - // See if we can skip the neutering check. - if (isolate()->IsArrayBufferNeuteringIntact()) { - // Add a code dependency so we are deoptimized in case an ArrayBuffer - // gets neutered. - dependencies()->AssumePropertyCell( - factory()->array_buffer_neutering_protector()); - } else { - // Deoptimize if the array buffer was neutered. - Node* check1 = efalse0 = graph()->NewNode( - simplified()->ArrayBufferWasNeutered(), buffer, efalse0, if_false0); - check1 = graph()->NewNode(simplified()->BooleanNot(), check1); - efalse0 = graph()->NewNode( - simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasNeutered), - 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, context, etrue2); - } else { - DCHECK_EQ(IterationKind::kValues, kind); - 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); -} - -// ES #sec-get-%typedarray%.prototype-@@tostringtag -Reduction JSBuiltinReducer::ReduceTypedArrayToStringTag(Node* node) { - Node* receiver = NodeProperties::GetValueInput(node, 1); - Node* effect = NodeProperties::GetEffectInput(node); - Node* control = NodeProperties::GetControlInput(node); - - NodeVector values(graph()->zone()); - NodeVector effects(graph()->zone()); - NodeVector controls(graph()->zone()); - - Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); - control = - graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); - - values.push_back(jsgraph()->UndefinedConstant()); - effects.push_back(effect); - controls.push_back(graph()->NewNode(common()->IfTrue(), control)); - - control = graph()->NewNode(common()->IfFalse(), control); - Node* receiver_map = effect = - graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), - receiver, effect, control); - Node* receiver_bit_field2 = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForMapBitField2()), receiver_map, - effect, control); - Node* receiver_elements_kind = graph()->NewNode( - simplified()->NumberShiftRightLogical(), - graph()->NewNode(simplified()->NumberBitwiseAnd(), receiver_bit_field2, - jsgraph()->Constant(Map::ElementsKindBits::kMask)), - jsgraph()->Constant(Map::ElementsKindBits::kShift)); - - // Offset the elements kind by FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND, - // so that the branch cascade below is turned into a simple table - // switch by the ControlFlowOptimizer later. - receiver_elements_kind = graph()->NewNode( - simplified()->NumberSubtract(), receiver_elements_kind, - jsgraph()->Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)); - -#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ - do { \ - Node* check = graph()->NewNode( \ - simplified()->NumberEqual(), receiver_elements_kind, \ - jsgraph()->Constant(TYPE##_ELEMENTS - \ - FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)); \ - control = graph()->NewNode(common()->Branch(), check, control); \ - values.push_back(jsgraph()->HeapConstant( \ - factory()->InternalizeUtf8String(#Type "Array"))); \ - effects.push_back(effect); \ - controls.push_back(graph()->NewNode(common()->IfTrue(), control)); \ - control = graph()->NewNode(common()->IfFalse(), control); \ - } while (false); - TYPED_ARRAYS(TYPED_ARRAY_CASE) -#undef TYPED_ARRAY_CASE - - values.push_back(jsgraph()->UndefinedConstant()); - effects.push_back(effect); - controls.push_back(control); - - int const count = static_cast<int>(controls.size()); - control = graph()->NewNode(common()->Merge(count), count, &controls.front()); - effects.push_back(control); - effect = - graph()->NewNode(common()->EffectPhi(count), count + 1, &effects.front()); - values.push_back(control); - Node* value = - graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count), - count + 1, &values.front()); - ReplaceWithValue(node, value, effect, control); - return Replace(value); -} - -Reduction JSBuiltinReducer::ReduceArrayIteratorNext(Node* node) { - 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(); - } -} - // ES6 section 22.1.2.2 Array.isArray ( arg ) Reduction JSBuiltinReducer::ReduceArrayIsArray(Node* node) { // We certainly know that undefined is not an array. @@ -872,343 +225,6 @@ Reduction JSBuiltinReducer::ReduceArrayIsArray(Node* node) { return Replace(value); } -Reduction JSBuiltinReducer::ReduceCollectionIterator( - Node* node, InstanceType collection_instance_type, - int collection_iterator_map_index) { - DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); - Node* receiver = NodeProperties::GetValueInput(node, 1); - Node* effect = NodeProperties::GetEffectInput(node); - Node* control = NodeProperties::GetControlInput(node); - if (NodeProperties::HasInstanceTypeWitness(receiver, effect, - collection_instance_type)) { - // Figure out the proper collection iterator map. - Handle<Map> collection_iterator_map( - Map::cast(native_context()->get(collection_iterator_map_index)), - isolate()); - - // Load the OrderedHashTable from the {receiver}. - Node* table = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), - receiver, effect, control); - - // Create the JSCollectionIterator result. - AllocationBuilder a(jsgraph(), effect, control); - a.Allocate(JSCollectionIterator::kSize, NOT_TENURED, Type::OtherObject()); - a.Store(AccessBuilder::ForMap(), collection_iterator_map); - a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), - jsgraph()->EmptyFixedArrayConstant()); - a.Store(AccessBuilder::ForJSObjectElements(), - jsgraph()->EmptyFixedArrayConstant()); - a.Store(AccessBuilder::ForJSCollectionIteratorTable(), table); - a.Store(AccessBuilder::ForJSCollectionIteratorIndex(), - jsgraph()->ZeroConstant()); - Node* value = effect = a.Finish(); - ReplaceWithValue(node, value, effect, control); - return Replace(value); - } - return NoChange(); -} - -Reduction JSBuiltinReducer::ReduceCollectionSize( - Node* node, InstanceType collection_instance_type) { - DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); - Node* receiver = NodeProperties::GetValueInput(node, 1); - Node* effect = NodeProperties::GetEffectInput(node); - Node* control = NodeProperties::GetControlInput(node); - if (NodeProperties::HasInstanceTypeWitness(receiver, effect, - collection_instance_type)) { - Node* table = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), - receiver, effect, control); - Node* value = effect = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForOrderedHashTableBaseNumberOfElements()), - table, effect, control); - ReplaceWithValue(node, value, effect, control); - return Replace(value); - } - return NoChange(); -} - -Reduction JSBuiltinReducer::ReduceCollectionIteratorNext( - Node* node, int entry_size, Handle<HeapObject> empty_collection, - InstanceType collection_iterator_instance_type_first, - InstanceType collection_iterator_instance_type_last) { - DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); - Node* receiver = NodeProperties::GetValueInput(node, 1); - Node* context = NodeProperties::GetContextInput(node); - Node* effect = NodeProperties::GetEffectInput(node); - Node* control = NodeProperties::GetControlInput(node); - - // A word of warning to begin with: This whole method might look a bit - // strange at times, but that's mostly because it was carefully handcrafted - // to allow for full escape analysis and scalar replacement of both the - // collection iterator object and the iterator results, including the - // key-value arrays in case of Set/Map entry iteration. - // - // TODO(turbofan): Currently the escape analysis (and the store-load - // forwarding) is unable to eliminate the allocations for the key-value - // arrays in case of Set/Map entry iteration, and we should investigate - // how to update the escape analysis / arrange the graph in a way that - // this becomes possible. - - // Infer the {receiver} instance type. - InstanceType receiver_instance_type; - ZoneHandleSet<Map> receiver_maps; - NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps); - if (result == NodeProperties::kNoReceiverMaps) return NoChange(); - DCHECK_NE(0, receiver_maps.size()); - receiver_instance_type = receiver_maps[0]->instance_type(); - for (size_t i = 1; i < receiver_maps.size(); ++i) { - if (receiver_maps[i]->instance_type() != receiver_instance_type) { - return NoChange(); - } - } - if (receiver_instance_type < collection_iterator_instance_type_first || - receiver_instance_type > collection_iterator_instance_type_last) { - return NoChange(); - } - - // Transition the JSCollectionIterator {receiver} if necessary - // (i.e. there were certain mutations while we're iterating). - { - Node* done_loop; - Node* done_eloop; - Node* loop = control = - graph()->NewNode(common()->Loop(2), control, control); - Node* eloop = effect = - graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); - Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); - NodeProperties::MergeControlToEnd(graph(), common(), terminate); - - // Check if reached the final table of the {receiver}. - Node* table = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()), - receiver, effect, control); - Node* next_table = effect = - graph()->NewNode(simplified()->LoadField( - AccessBuilder::ForOrderedHashTableBaseNextTable()), - table, effect, control); - Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), next_table); - control = - graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); - - // Abort the {loop} when we reach the final table. - done_loop = graph()->NewNode(common()->IfTrue(), control); - done_eloop = effect; - - // Migrate to the {next_table} otherwise. - control = graph()->NewNode(common()->IfFalse(), control); - - // Self-heal the {receiver}s index. - Node* index = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()), - receiver, effect, control); - Callable const callable = - Builtins::CallableFor(isolate(), Builtins::kOrderedHashTableHealIndex); - auto call_descriptor = Linkage::GetStubCallDescriptor( - isolate(), graph()->zone(), callable.descriptor(), 0, - CallDescriptor::kNoFlags, Operator::kEliminatable); - index = effect = - graph()->NewNode(common()->Call(call_descriptor), - 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( - simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorIndex()), - receiver, index, effect, control); - effect = graph()->NewNode( - simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorTable()), - receiver, next_table, effect, control); - - // Tie the knot. - loop->ReplaceInput(1, control); - eloop->ReplaceInput(1, effect); - - control = done_loop; - effect = done_eloop; - } - - // Get current index and table from the JSCollectionIterator {receiver}. - Node* index = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()), - receiver, effect, control); - Node* table = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()), - receiver, effect, control); - - // Create the {JSIteratorResult} first to ensure that we always have - // a dominating Allocate node for the allocation folding phase. - Node* iterator_result = effect = graph()->NewNode( - javascript()->CreateIterResultObject(), jsgraph()->UndefinedConstant(), - jsgraph()->TrueConstant(), context, effect); - - // Look for the next non-holey key, starting from {index} in the {table}. - Node* controls[2]; - Node* effects[3]; - { - // Compute the currently used capacity. - Node* number_of_buckets = effect = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForOrderedHashTableBaseNumberOfBuckets()), - table, effect, control); - Node* number_of_elements = effect = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForOrderedHashTableBaseNumberOfElements()), - table, effect, control); - Node* number_of_deleted_elements = effect = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForOrderedHashTableBaseNumberOfDeletedElements()), - table, effect, control); - Node* used_capacity = - graph()->NewNode(simplified()->NumberAdd(), number_of_elements, - number_of_deleted_elements); - - // Skip holes and update the {index}. - Node* loop = graph()->NewNode(common()->Loop(2), control, control); - Node* eloop = - graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); - Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); - NodeProperties::MergeControlToEnd(graph(), common(), terminate); - Node* iloop = graph()->NewNode( - common()->Phi(MachineRepresentation::kTagged, 2), index, index, loop); - NodeProperties::SetType(iloop, type_cache_.kFixedArrayLengthType); - { - Node* check0 = graph()->NewNode(simplified()->NumberLessThan(), iloop, - used_capacity); - Node* branch0 = - graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, loop); - - Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); - Node* efalse0 = eloop; - { - // Mark the {receiver} as exhausted. - efalse0 = graph()->NewNode( - simplified()->StoreField( - AccessBuilder::ForJSCollectionIteratorTable()), - receiver, jsgraph()->HeapConstant(empty_collection), efalse0, - if_false0); - - controls[0] = if_false0; - effects[0] = efalse0; - } - - Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); - Node* etrue0 = eloop; - { - // Load the key of the entry. - Node* entry_start_position = graph()->NewNode( - simplified()->NumberAdd(), - graph()->NewNode( - simplified()->NumberAdd(), - graph()->NewNode(simplified()->NumberMultiply(), iloop, - jsgraph()->Constant(entry_size)), - number_of_buckets), - jsgraph()->Constant(OrderedHashTableBase::kHashTableStartIndex)); - Node* entry_key = etrue0 = graph()->NewNode( - simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()), - table, entry_start_position, etrue0, if_true0); - - // Advance the index. - Node* index = graph()->NewNode(simplified()->NumberAdd(), iloop, - jsgraph()->OneConstant()); - - Node* check1 = - graph()->NewNode(simplified()->ReferenceEqual(), entry_key, - jsgraph()->TheHoleConstant()); - Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), - check1, if_true0); - - { - // Abort loop with resulting value. - Node* control = graph()->NewNode(common()->IfFalse(), branch1); - Node* effect = etrue0; - Node* value = effect = - graph()->NewNode(common()->TypeGuard(Type::NonInternal()), - entry_key, effect, control); - Node* done = jsgraph()->FalseConstant(); - - // Advance the index on the {receiver}. - effect = graph()->NewNode( - simplified()->StoreField( - AccessBuilder::ForJSCollectionIteratorIndex()), - receiver, index, effect, control); - - // The actual {value} depends on the {receiver} iteration type. - switch (receiver_instance_type) { - case JS_MAP_KEY_ITERATOR_TYPE: - case JS_SET_VALUE_ITERATOR_TYPE: - break; - - case JS_SET_KEY_VALUE_ITERATOR_TYPE: - value = effect = - graph()->NewNode(javascript()->CreateKeyValueArray(), value, - value, context, effect); - break; - - case JS_MAP_VALUE_ITERATOR_TYPE: - value = effect = graph()->NewNode( - simplified()->LoadElement( - AccessBuilder::ForFixedArrayElement()), - table, - graph()->NewNode( - simplified()->NumberAdd(), entry_start_position, - jsgraph()->Constant(OrderedHashMap::kValueOffset)), - effect, control); - break; - - case JS_MAP_KEY_VALUE_ITERATOR_TYPE: - value = effect = graph()->NewNode( - simplified()->LoadElement( - AccessBuilder::ForFixedArrayElement()), - table, - graph()->NewNode( - simplified()->NumberAdd(), entry_start_position, - jsgraph()->Constant(OrderedHashMap::kValueOffset)), - effect, control); - value = effect = - graph()->NewNode(javascript()->CreateKeyValueArray(), - entry_key, value, context, effect); - break; - - default: - UNREACHABLE(); - break; - } - - // Store final {value} and {done} into the {iterator_result}. - effect = - graph()->NewNode(simplified()->StoreField( - AccessBuilder::ForJSIteratorResultValue()), - iterator_result, value, effect, control); - effect = - graph()->NewNode(simplified()->StoreField( - AccessBuilder::ForJSIteratorResultDone()), - iterator_result, done, effect, control); - - controls[1] = control; - effects[1] = effect; - } - - // Continue with next loop index. - loop->ReplaceInput(1, graph()->NewNode(common()->IfTrue(), branch1)); - eloop->ReplaceInput(1, etrue0); - iloop->ReplaceInput(1, index); - } - } - - control = effects[2] = graph()->NewNode(common()->Merge(2), 2, controls); - effect = graph()->NewNode(common()->EffectPhi(2), 3, effects); - } - - // Yield the final {iterator_result}. - ReplaceWithValue(node, iterator_result, effect, control); - return Replace(iterator_result); -} - // ES6 section 20.3.3.1 Date.now ( ) Reduction JSBuiltinReducer::ReduceDateNow(Node* node) { NodeProperties::RemoveValueInputs(node); @@ -1260,131 +276,6 @@ Reduction JSBuiltinReducer::ReduceGlobalIsNaN(Node* node) { return NoChange(); } -Reduction JSBuiltinReducer::ReduceMapGet(Node* node) { - // We only optimize if we have target, receiver and key parameters. - if (node->op()->ValueInputCount() != 3) return NoChange(); - Node* receiver = NodeProperties::GetValueInput(node, 1); - Node* effect = NodeProperties::GetEffectInput(node); - Node* control = NodeProperties::GetControlInput(node); - Node* key = NodeProperties::GetValueInput(node, 2); - - if (!NodeProperties::HasInstanceTypeWitness(receiver, effect, JS_MAP_TYPE)) - return NoChange(); - - Node* table = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), receiver, - effect, control); - - Node* entry = effect = graph()->NewNode( - simplified()->FindOrderedHashMapEntry(), table, key, effect, control); - - Node* check = graph()->NewNode(simplified()->NumberEqual(), entry, - jsgraph()->MinusOneConstant()); - - Node* branch = graph()->NewNode(common()->Branch(), check, control); - - // Key not found. - Node* if_true = graph()->NewNode(common()->IfTrue(), branch); - Node* etrue = effect; - Node* vtrue = jsgraph()->UndefinedConstant(); - - // Key found. - Node* if_false = graph()->NewNode(common()->IfFalse(), branch); - Node* efalse = effect; - Node* vfalse = efalse = graph()->NewNode( - simplified()->LoadElement(AccessBuilder::ForOrderedHashMapEntryValue()), - table, entry, efalse, if_false); - - control = graph()->NewNode(common()->Merge(2), if_true, if_false); - Node* value = graph()->NewNode( - common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control); - effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); - - ReplaceWithValue(node, value, effect, control); - return Replace(value); -} - -Reduction JSBuiltinReducer::ReduceMapHas(Node* node) { - // We only optimize if we have target, receiver and key parameters. - if (node->op()->ValueInputCount() != 3) return NoChange(); - Node* receiver = NodeProperties::GetValueInput(node, 1); - Node* effect = NodeProperties::GetEffectInput(node); - Node* control = NodeProperties::GetControlInput(node); - Node* key = NodeProperties::GetValueInput(node, 2); - - if (!NodeProperties::HasInstanceTypeWitness(receiver, effect, JS_MAP_TYPE)) - return NoChange(); - - Node* table = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), receiver, - effect, control); - - Node* index = effect = graph()->NewNode( - simplified()->FindOrderedHashMapEntry(), table, key, effect, control); - - Node* value = graph()->NewNode(simplified()->NumberEqual(), index, - jsgraph()->MinusOneConstant()); - value = graph()->NewNode(simplified()->BooleanNot(), value); - - ReplaceWithValue(node, value, effect, control); - return Replace(value); -} - -// ES6 section 20.1.2.2 Number.isFinite ( number ) -Reduction JSBuiltinReducer::ReduceNumberIsFinite(Node* node) { - JSCallReduction r(node); - if (r.InputsMatchOne(Type::Number())) { - // Number.isFinite(a:number) -> NumberEqual(a', a') - // where a' = NumberSubtract(a, a) - Node* input = r.GetJSCallInput(0); - Node* diff = graph()->NewNode(simplified()->NumberSubtract(), input, input); - Node* value = graph()->NewNode(simplified()->NumberEqual(), diff, diff); - return Replace(value); - } - return NoChange(); -} - -// ES6 section 20.1.2.3 Number.isInteger ( number ) -Reduction JSBuiltinReducer::ReduceNumberIsInteger(Node* node) { - JSCallReduction r(node); - if (r.InputsMatchOne(Type::Number())) { - // Number.isInteger(x:number) -> NumberEqual(NumberSubtract(x, x'), #0) - // where x' = NumberTrunc(x) - Node* input = r.GetJSCallInput(0); - Node* trunc = graph()->NewNode(simplified()->NumberTrunc(), input); - Node* diff = graph()->NewNode(simplified()->NumberSubtract(), input, trunc); - Node* value = graph()->NewNode(simplified()->NumberEqual(), diff, - jsgraph()->ZeroConstant()); - return Replace(value); - } - return NoChange(); -} - -// ES6 section 20.1.2.4 Number.isNaN ( number ) -Reduction JSBuiltinReducer::ReduceNumberIsNaN(Node* node) { - JSCallReduction r(node); - if (r.InputsMatchZero()) { - // Number.isNaN() -> #false - Node* value = jsgraph()->FalseConstant(); - return Replace(value); - } - // Number.isNaN(a:number) -> ObjectIsNaN(a) - Node* input = r.GetJSCallInput(0); - Node* value = graph()->NewNode(simplified()->ObjectIsNaN(), input); - return Replace(value); -} - -// ES6 section 20.1.2.5 Number.isSafeInteger ( number ) -Reduction JSBuiltinReducer::ReduceNumberIsSafeInteger(Node* node) { - JSCallReduction r(node); - if (r.InputsMatchOne(type_cache_.kSafeInteger)) { - // Number.isInteger(x:safe-integer) -> #true - Node* value = jsgraph()->TrueConstant(); - return Replace(value); - } - return NoChange(); -} - // ES6 section 20.1.2.13 Number.parseInt ( string, radix ) Reduction JSBuiltinReducer::ReduceNumberParseInt(Node* node) { JSCallReduction r(node); @@ -1483,168 +374,6 @@ Reduction JSBuiltinReducer::ReduceObjectCreate(Node* node) { return Replace(value); } -namespace { - -Node* GetStringWitness(Node* node) { - Node* receiver = NodeProperties::GetValueInput(node, 1); - Type* receiver_type = NodeProperties::GetType(receiver); - Node* effect = NodeProperties::GetEffectInput(node); - if (receiver_type->Is(Type::String())) return receiver; - // Check if the {node} is dominated by a CheckString renaming for - // it's {receiver}, and if so use that renaming as {receiver} for - // the lowering below. - for (Node* dominator = effect;;) { - if ((dominator->opcode() == IrOpcode::kCheckString || - dominator->opcode() == IrOpcode::kCheckInternalizedString || - dominator->opcode() == IrOpcode::kCheckSeqString) && - NodeProperties::IsSame(dominator->InputAt(0), receiver)) { - return dominator; - } - if (dominator->op()->EffectInputCount() != 1) { - // Didn't find any appropriate CheckString node. - return nullptr; - } - dominator = NodeProperties::GetEffectInput(dominator); - } -} - -} // namespace - -// ES6 String.prototype.concat(...args) -// #sec-string.prototype.concat -Reduction JSBuiltinReducer::ReduceStringConcat(Node* node) { - if (Node* receiver = GetStringWitness(node)) { - JSCallReduction r(node); - if (r.InputsMatchOne(Type::PlainPrimitive())) { - // String.prototype.concat(lhs:string, rhs:plain-primitive) - // -> Call[StringAddStub](lhs, rhs) - StringAddFlags flags = r.InputsMatchOne(Type::String()) - ? STRING_ADD_CHECK_NONE - : STRING_ADD_CONVERT_RIGHT; - // TODO(turbofan): Massage the FrameState of the {node} here once we - // have an artificial builtin frame type, so that it looks like the - // exception from StringAdd overflow came from String.prototype.concat - // builtin instead of the calling function. - Callable const callable = - CodeFactory::StringAdd(isolate(), flags, NOT_TENURED); - auto call_descriptor = Linkage::GetStubCallDescriptor( - isolate(), graph()->zone(), callable.descriptor(), 0, - CallDescriptor::kNeedsFrameState, - Operator::kNoDeopt | Operator::kNoWrite); - node->ReplaceInput(0, jsgraph()->HeapConstant(callable.code())); - node->ReplaceInput(1, receiver); - NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); - return Changed(node); - } - } - - return NoChange(); -} - -// ES section #sec-string.prototype.slice -Reduction JSBuiltinReducer::ReduceStringSlice(Node* node) { - if (Node* receiver = GetStringWitness(node)) { - Node* start = node->op()->ValueInputCount() >= 3 - ? NodeProperties::GetValueInput(node, 2) - : jsgraph()->UndefinedConstant(); - Type* start_type = NodeProperties::GetType(start); - Node* end = node->op()->ValueInputCount() >= 4 - ? NodeProperties::GetValueInput(node, 3) - : jsgraph()->UndefinedConstant(); - Type* end_type = NodeProperties::GetType(end); - Node* effect = NodeProperties::GetEffectInput(node); - Node* control = NodeProperties::GetControlInput(node); - - if (start_type->Is(type_cache_.kSingletonMinusOne) && - end_type->Is(Type::Undefined())) { - Node* receiver_length = - graph()->NewNode(simplified()->StringLength(), receiver); - - Node* check = - graph()->NewNode(simplified()->NumberEqual(), receiver_length, - jsgraph()->ZeroConstant()); - Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), - check, control); - - Node* if_true = graph()->NewNode(common()->IfTrue(), branch); - Node* vtrue = jsgraph()->EmptyStringConstant(); - - Node* if_false = graph()->NewNode(common()->IfFalse(), branch); - Node* vfalse; - Node* efalse; - { - // We need to convince TurboFan that {receiver_length}-1 is a valid - // Unsigned32 value, so we just apply NumberToUint32 to the result - // of the subtraction, which is a no-op and merely acts as a marker. - Node* index = - graph()->NewNode(simplified()->NumberSubtract(), receiver_length, - jsgraph()->OneConstant()); - index = graph()->NewNode(simplified()->NumberToUint32(), index); - vfalse = efalse = graph()->NewNode(simplified()->StringCharAt(), - receiver, index, effect, if_false); - } - - control = graph()->NewNode(common()->Merge(2), if_true, if_false); - Node* value = - graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), - vtrue, vfalse, control); - effect = - graph()->NewNode(common()->EffectPhi(2), effect, efalse, control); - ReplaceWithValue(node, value, effect, control); - return Replace(value); - } - } - return NoChange(); -} - -Reduction JSBuiltinReducer::ReduceArrayBufferIsView(Node* node) { - Node* value = node->op()->ValueInputCount() >= 3 - ? NodeProperties::GetValueInput(node, 2) - : jsgraph()->UndefinedConstant(); - RelaxEffectsAndControls(node); - node->ReplaceInput(0, value); - node->TrimInputCount(1); - NodeProperties::ChangeOp(node, simplified()->ObjectIsArrayBufferView()); - return Changed(node); -} - -Reduction JSBuiltinReducer::ReduceArrayBufferViewAccessor( - Node* node, InstanceType instance_type, FieldAccess const& access) { - Node* receiver = NodeProperties::GetValueInput(node, 1); - Node* effect = NodeProperties::GetEffectInput(node); - Node* control = NodeProperties::GetControlInput(node); - if (NodeProperties::HasInstanceTypeWitness(receiver, effect, instance_type)) { - // Load the {receiver}s field. - Node* value = effect = graph()->NewNode(simplified()->LoadField(access), - receiver, effect, control); - - // See if we can skip the neutering check. - if (isolate()->IsArrayBufferNeuteringIntact()) { - // Add a code dependency so we are deoptimized in case an ArrayBuffer - // gets neutered. - dependencies()->AssumePropertyCell( - factory()->array_buffer_neutering_protector()); - } else { - // Check if the {receiver}s buffer was neutered. - Node* receiver_buffer = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), - receiver, effect, control); - Node* check = effect = - graph()->NewNode(simplified()->ArrayBufferWasNeutered(), - receiver_buffer, effect, control); - - // Default to zero if the {receiver}s buffer was neutered. - value = graph()->NewNode( - common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse), - check, jsgraph()->ZeroConstant(), value); - } - - ReplaceWithValue(node, value, effect, control); - return Replace(value); - } - return NoChange(); -} - Reduction JSBuiltinReducer::Reduce(Node* node) { Reduction reduction = NoChange(); JSCallReduction r(node); @@ -1653,14 +382,6 @@ Reduction JSBuiltinReducer::Reduce(Node* node) { if (!r.HasBuiltinFunctionId()) return NoChange(); if (!r.BuiltinCanBeInlined()) 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 kArrayIsArray: return ReduceArrayIsArray(node); case kDateNow: @@ -1673,90 +394,12 @@ Reduction JSBuiltinReducer::Reduce(Node* node) { case kGlobalIsNaN: reduction = ReduceGlobalIsNaN(node); break; - case kMapEntries: - return ReduceCollectionIterator( - node, JS_MAP_TYPE, Context::MAP_KEY_VALUE_ITERATOR_MAP_INDEX); - case kMapGet: - reduction = ReduceMapGet(node); - break; - case kMapHas: - reduction = ReduceMapHas(node); - break; - case kMapKeys: - return ReduceCollectionIterator(node, JS_MAP_TYPE, - Context::MAP_KEY_ITERATOR_MAP_INDEX); - case kMapSize: - return ReduceCollectionSize(node, JS_MAP_TYPE); - case kMapValues: - return ReduceCollectionIterator(node, JS_MAP_TYPE, - Context::MAP_VALUE_ITERATOR_MAP_INDEX); - case kMapIteratorNext: - return ReduceCollectionIteratorNext( - node, OrderedHashMap::kEntrySize, factory()->empty_ordered_hash_map(), - FIRST_MAP_ITERATOR_TYPE, LAST_MAP_ITERATOR_TYPE); - case kNumberIsFinite: - reduction = ReduceNumberIsFinite(node); - break; - case kNumberIsInteger: - reduction = ReduceNumberIsInteger(node); - break; - case kNumberIsNaN: - reduction = ReduceNumberIsNaN(node); - break; - case kNumberIsSafeInteger: - reduction = ReduceNumberIsSafeInteger(node); - break; case kNumberParseInt: reduction = ReduceNumberParseInt(node); break; case kObjectCreate: reduction = ReduceObjectCreate(node); break; - case kSetEntries: - return ReduceCollectionIterator( - node, JS_SET_TYPE, Context::SET_KEY_VALUE_ITERATOR_MAP_INDEX); - case kSetSize: - return ReduceCollectionSize(node, JS_SET_TYPE); - case kSetValues: - return ReduceCollectionIterator(node, JS_SET_TYPE, - Context::SET_VALUE_ITERATOR_MAP_INDEX); - case kSetIteratorNext: - return ReduceCollectionIteratorNext( - node, OrderedHashSet::kEntrySize, factory()->empty_ordered_hash_set(), - FIRST_SET_ITERATOR_TYPE, LAST_SET_ITERATOR_TYPE); - case kStringConcat: - return ReduceStringConcat(node); - case kStringSlice: - return ReduceStringSlice(node); - case kArrayBufferIsView: - return ReduceArrayBufferIsView(node); - case kDataViewByteLength: - return ReduceArrayBufferViewAccessor( - node, JS_DATA_VIEW_TYPE, - AccessBuilder::ForJSArrayBufferViewByteLength()); - case kDataViewByteOffset: - return ReduceArrayBufferViewAccessor( - node, JS_DATA_VIEW_TYPE, - AccessBuilder::ForJSArrayBufferViewByteOffset()); - case kTypedArrayByteLength: - return ReduceArrayBufferViewAccessor( - node, JS_TYPED_ARRAY_TYPE, - AccessBuilder::ForJSArrayBufferViewByteLength()); - case kTypedArrayByteOffset: - return ReduceArrayBufferViewAccessor( - node, JS_TYPED_ARRAY_TYPE, - AccessBuilder::ForJSArrayBufferViewByteOffset()); - 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); - case kTypedArrayToStringTag: - return ReduceTypedArrayToStringTag(node); default: break; } |