diff options
author | Michaël Zasso <targos@protonmail.com> | 2017-12-05 16:41:55 +0100 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2017-12-06 12:52:07 +0100 |
commit | 1854ba04e9a68f062beb299dd6e1479279b26363 (patch) | |
tree | d5b2df9b8c1deb6388f7a728fca8e1c98c779abe /deps/v8/src/compiler/js-native-context-specialization.cc | |
parent | b52c23b75f96e1c9d2c7b3a7e5619170d0a0d8e1 (diff) | |
download | node-new-1854ba04e9a68f062beb299dd6e1479279b26363.tar.gz |
deps: update V8 to 6.3.292.46
PR-URL: https://github.com/nodejs/node/pull/16271
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Myles Borins <myles.borins@gmail.com>
Diffstat (limited to 'deps/v8/src/compiler/js-native-context-specialization.cc')
-rw-r--r-- | deps/v8/src/compiler/js-native-context-specialization.cc | 383 |
1 files changed, 285 insertions, 98 deletions
diff --git a/deps/v8/src/compiler/js-native-context-specialization.cc b/deps/v8/src/compiler/js-native-context-specialization.cc index dbe3fc9608..06f059e24e 100644 --- a/deps/v8/src/compiler/js-native-context-specialization.cc +++ b/deps/v8/src/compiler/js-native-context-specialization.cc @@ -5,6 +5,7 @@ #include "src/compiler/js-native-context-specialization.h" #include "src/accessors.h" +#include "src/api.h" #include "src/code-factory.h" #include "src/compilation-dependencies.h" #include "src/compiler/access-builder.h" @@ -652,7 +653,7 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) { Reduction JSNativeContextSpecialization::ReduceNamedAccess( Node* node, Node* value, MapHandles const& receiver_maps, Handle<Name> name, - AccessMode access_mode, LanguageMode language_mode, Node* index) { + AccessMode access_mode, Node* index) { DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || node->opcode() == IrOpcode::kJSStoreNamed || node->opcode() == IrOpcode::kJSLoadProperty || @@ -731,7 +732,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( // Generate the actual property access. ValueEffectControl continuation = BuildPropertyAccess( receiver, value, context, frame_state, effect, control, name, - if_exceptions, access_info, access_mode, language_mode); + if_exceptions, access_info, access_mode); value = continuation.value(); effect = continuation.effect(); control = continuation.control(); @@ -836,10 +837,9 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( } // Generate the actual property access. - ValueEffectControl continuation = - BuildPropertyAccess(this_receiver, this_value, context, frame_state, - this_effect, this_control, name, if_exceptions, - access_info, access_mode, language_mode); + ValueEffectControl continuation = BuildPropertyAccess( + this_receiver, this_value, context, frame_state, this_effect, + this_control, name, if_exceptions, access_info, access_mode); values.push_back(continuation.value()); effects.push_back(continuation.effect()); controls.push_back(continuation.control()); @@ -891,7 +891,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( Reduction JSNativeContextSpecialization::ReduceNamedAccessFromNexus( Node* node, Node* value, FeedbackNexus const& nexus, Handle<Name> name, - AccessMode access_mode, LanguageMode language_mode) { + AccessMode access_mode) { DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || node->opcode() == IrOpcode::kJSStoreNamed || node->opcode() == IrOpcode::kJSStoreNamedOwn); @@ -929,8 +929,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccessFromNexus( } // Try to lower the named access based on the {receiver_maps}. - return ReduceNamedAccess(node, value, receiver_maps, name, access_mode, - language_mode); + return ReduceNamedAccess(node, value, receiver_maps, name, access_mode); } Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { @@ -968,13 +967,13 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { } } - // Extract receiver maps from the LOAD_IC using the LoadICNexus. + // Extract receiver maps from the load IC using the LoadICNexus. if (!p.feedback().IsValid()) return NoChange(); LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); // Try to lower the named access based on the {receiver_maps}. return ReduceNamedAccessFromNexus(node, value, nexus, p.name(), - AccessMode::kLoad, p.language_mode()); + AccessMode::kLoad); } @@ -983,13 +982,13 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { NamedAccess const& p = NamedAccessOf(node->op()); Node* const value = NodeProperties::GetValueInput(node, 1); - // Extract receiver maps from the STORE_IC using the StoreICNexus. + // Extract receiver maps from the store IC using the StoreICNexus. if (!p.feedback().IsValid()) return NoChange(); StoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); // Try to lower the named access based on the {receiver_maps}. return ReduceNamedAccessFromNexus(node, value, nexus, p.name(), - AccessMode::kStore, p.language_mode()); + AccessMode::kStore); } Reduction JSNativeContextSpecialization::ReduceJSStoreNamedOwn(Node* node) { @@ -1003,13 +1002,12 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreNamedOwn(Node* node) { // Try to lower the creation of a named property based on the {receiver_maps}. return ReduceNamedAccessFromNexus(node, value, nexus, p.name(), - AccessMode::kStoreInLiteral, STRICT); + AccessMode::kStoreInLiteral); } Reduction JSNativeContextSpecialization::ReduceElementAccess( Node* node, Node* index, Node* value, MapHandles const& receiver_maps, - AccessMode access_mode, LanguageMode language_mode, - KeyedAccessStoreMode store_mode) { + AccessMode access_mode, KeyedAccessStoreMode store_mode) { DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || node->opcode() == IrOpcode::kJSStoreProperty); Node* receiver = NodeProperties::GetValueInput(node, 0); @@ -1250,47 +1248,89 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( template <typename KeyedICNexus> Reduction JSNativeContextSpecialization::ReduceKeyedAccess( Node* node, Node* index, Node* value, KeyedICNexus const& nexus, - AccessMode access_mode, LanguageMode language_mode, - KeyedAccessStoreMode store_mode) { + AccessMode access_mode, KeyedAccessStoreMode store_mode) { DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || node->opcode() == IrOpcode::kJSStoreProperty); Node* receiver = NodeProperties::GetValueInput(node, 0); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); - // Optimize access for constant {receiver}. - HeapObjectMatcher mreceiver(receiver); - if (mreceiver.HasValue() && mreceiver.Value()->IsString()) { - Handle<String> string = Handle<String>::cast(mreceiver.Value()); - - // Strings are immutable in JavaScript. - if (access_mode == AccessMode::kStore) return NoChange(); - - // Properly deal with constant {index}. - NumberMatcher mindex(index); - if (mindex.IsInteger() && mindex.IsInRange(0.0, string->length() - 1)) { - // Constant-fold the {index} access to {string}. - Node* value = jsgraph()->HeapConstant( - factory()->LookupSingleCharacterStringFromCode( - string->Get(static_cast<int>(mindex.Value())))); - ReplaceWithValue(node, value, effect, control); - return Replace(value); - } + // Optimize the case where we load from a constant {receiver}. + if (access_mode == AccessMode::kLoad) { + HeapObjectMatcher mreceiver(receiver); + if (mreceiver.HasValue() && !mreceiver.Value()->IsTheHole(isolate()) && + !mreceiver.Value()->IsNullOrUndefined(isolate())) { + // Check whether we're accessing a known element on the {receiver} + // that is non-configurable, non-writable (i.e. the {receiver} was + // frozen using Object.freeze). + NumberMatcher mindex(index); + if (mindex.IsInteger() && mindex.IsInRange(0.0, kMaxUInt32 - 1.0)) { + LookupIterator it(isolate(), mreceiver.Value(), + static_cast<uint32_t>(mindex.Value()), + LookupIterator::OWN); + if (it.state() == LookupIterator::DATA) { + if (it.IsReadOnly() && !it.IsConfigurable()) { + // We can safely constant-fold the {index} access to {receiver}, + // since the element is non-configurable, non-writable and thus + // cannot change anymore. + value = jsgraph()->Constant(it.GetDataValue()); + ReplaceWithValue(node, value, effect, control); + return Replace(value); + } - // We can only assume that the {index} is a valid array index if the IC - // is in element access mode and not MEGAMORPHIC, otherwise there's no - // guard for the bounds check below. - if (nexus.ic_state() != MEGAMORPHIC && nexus.GetKeyType() == ELEMENT) { - // Ensure that {index} is less than {receiver} length. - Node* length = jsgraph()->Constant(string->length()); - index = effect = graph()->NewNode(simplified()->CheckBounds(), index, - length, effect, control); + // Check if the {receiver} is a known constant with a copy-on-write + // backing store, and whether {index} is within the appropriate + // bounds. In that case we can constant-fold the access and only + // check that the {elements} didn't change. This is sufficient as + // the backing store of a copy-on-write JSArray is defensively copied + // whenever the length or the elements (might) change. + // + // What's interesting here is that we don't need to map check the + // {receiver}, since JSArray's will always have their elements in + // the backing store. + if (mreceiver.Value()->IsJSArray()) { + Handle<JSArray> array = Handle<JSArray>::cast(mreceiver.Value()); + if (array->elements()->IsCowArray()) { + Node* elements = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForJSObjectElements()), + receiver, effect, control); + Handle<FixedArray> array_elements( + FixedArray::cast(array->elements()), isolate()); + Node* check = + graph()->NewNode(simplified()->ReferenceEqual(), elements, + jsgraph()->HeapConstant(array_elements)); + effect = graph()->NewNode(simplified()->CheckIf(), check, effect, + control); + value = jsgraph()->Constant(it.GetDataValue()); + ReplaceWithValue(node, value, effect, control); + return Replace(value); + } + } + } + } - // Return the character from the {receiver} as single character string. - value = graph()->NewNode(simplified()->StringCharAt(), receiver, index, - control); - ReplaceWithValue(node, value, effect, control); - return Replace(value); + // For constant Strings we can eagerly strength-reduce the keyed + // accesses using the known length, which doesn't change. + if (mreceiver.Value()->IsString()) { + Handle<String> string = Handle<String>::cast(mreceiver.Value()); + + // We can only assume that the {index} is a valid array index if the IC + // is in element access mode and not MEGAMORPHIC, otherwise there's no + // guard for the bounds check below. + if (nexus.ic_state() != MEGAMORPHIC && nexus.GetKeyType() == ELEMENT) { + // Ensure that {index} is less than {receiver} length. + Node* length = jsgraph()->Constant(string->length()); + index = effect = graph()->NewNode(simplified()->CheckBounds(), index, + length, effect, control); + + // Return the character from the {receiver} as single character + // string. + value = graph()->NewNode(simplified()->StringCharAt(), receiver, + index, control); + ReplaceWithValue(node, value, effect, control); + return Replace(value); + } + } } } @@ -1332,8 +1372,7 @@ Reduction JSNativeContextSpecialization::ReduceKeyedAccess( index = jsgraph()->Constant(static_cast<double>(array_index)); } else { name = factory()->InternalizeName(name); - return ReduceNamedAccess(node, value, receiver_maps, name, access_mode, - language_mode); + return ReduceNamedAccess(node, value, receiver_maps, name, access_mode); } } } @@ -1341,8 +1380,7 @@ Reduction JSNativeContextSpecialization::ReduceKeyedAccess( // Check if we have feedback for a named access. if (Name* name = nexus.FindFirstName()) { return ReduceNamedAccess(node, value, receiver_maps, - handle(name, isolate()), access_mode, - language_mode, index); + handle(name, isolate()), access_mode, index); } else if (nexus.GetKeyType() != ELEMENT) { // The KeyedLoad/StoreIC has seen non-element accesses, so we cannot assume // that the {index} is a valid array index, thus we just let the IC continue @@ -1358,7 +1396,7 @@ Reduction JSNativeContextSpecialization::ReduceKeyedAccess( // Try to lower the element access based on the {receiver_maps}. return ReduceElementAccess(node, index, value, receiver_maps, access_mode, - language_mode, store_mode); + store_mode); } Reduction JSNativeContextSpecialization::ReduceSoftDeoptimize( @@ -1377,39 +1415,137 @@ Reduction JSNativeContextSpecialization::ReduceSoftDeoptimize( return Changed(node); } - Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) { DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode()); PropertyAccess const& p = PropertyAccessOf(node->op()); - Node* const index = NodeProperties::GetValueInput(node, 1); - Node* const value = jsgraph()->Dead(); + Node* receiver = NodeProperties::GetValueInput(node, 0); + Node* name = NodeProperties::GetValueInput(node, 1); + Node* value = jsgraph()->Dead(); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + + // We can optimize a property load if it's being used inside a for..in, + // so for code like this: + // + // for (name in receiver) { + // value = receiver[name]; + // ... + // } + // + // If the for..in is in fast-mode, we know that the {receiver} has {name} + // as own property, otherwise the enumeration wouldn't include it. The graph + // constructed by the BytecodeGraphBuilder in this case looks like this: + + // receiver + // ^ ^ + // | | + // | +-+ + // | | + // | JSToObject + // | ^ + // | | + // | | + // | JSForInNext + // | ^ + // | | + // +----+ | + // | | + // | | + // JSLoadProperty + + // If the for..in has only seen maps with enum cache consisting of keys + // and indices so far, we can turn the {JSLoadProperty} into a map check + // on the {receiver} and then just load the field value dynamically via + // the {LoadFieldByIndex} operator. The map check is only necessary when + // TurboFan cannot prove that there is no observable side effect between + // the {JSForInNext} and the {JSLoadProperty} node. + // + // Also note that it's safe to look through the {JSToObject}, since the + // [[Get]] operation does an implicit ToObject anyway, and these operations + // are not observable. + if (name->opcode() == IrOpcode::kJSForInNext) { + ForInMode const mode = ForInModeOf(name->op()); + if (mode == ForInMode::kUseEnumCacheKeysAndIndices) { + Node* object = NodeProperties::GetValueInput(name, 0); + Node* enumerator = NodeProperties::GetValueInput(name, 2); + Node* index = NodeProperties::GetValueInput(name, 3); + if (object->opcode() == IrOpcode::kJSToObject) { + object = NodeProperties::GetValueInput(object, 0); + } + if (object == receiver) { + // No need to repeat the map check if we can prove that there's no + // observable side effect between {effect} and {name]. + if (!NodeProperties::NoObservableSideEffectBetween(effect, name)) { + // Check that the {receiver} map is still valid. + Node* receiver_map = effect = + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), + receiver, effect, control); + Node* check = graph()->NewNode(simplified()->ReferenceEqual(), + receiver_map, enumerator); + effect = + graph()->NewNode(simplified()->CheckIf(), check, effect, control); + } + + // Load the enum cache indices from the {cache_type}. + Node* descriptor_array = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForMapDescriptors()), + enumerator, effect, control); + Node* enum_cache = effect = + graph()->NewNode(simplified()->LoadField( + AccessBuilder::ForDescriptorArrayEnumCache()), + descriptor_array, effect, control); + Node* enum_indices = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForEnumCacheIndices()), + enum_cache, effect, control); + + // Ensure that the {enum_indices} are valid. + Node* check = graph()->NewNode( + simplified()->BooleanNot(), + graph()->NewNode(simplified()->ReferenceEqual(), enum_indices, + jsgraph()->EmptyFixedArrayConstant())); + effect = + graph()->NewNode(simplified()->CheckIf(), check, effect, control); - // Extract receiver maps from the KEYED_LOAD_IC using the KeyedLoadICNexus. + // Determine the index from the {enum_indices}. + index = effect = graph()->NewNode( + simplified()->LoadElement( + AccessBuilder::ForFixedArrayElement(PACKED_SMI_ELEMENTS)), + enum_indices, index, effect, control); + + // Load the actual field value. + Node* value = effect = graph()->NewNode( + simplified()->LoadFieldByIndex(), receiver, index, effect, control); + ReplaceWithValue(node, value, effect, control); + return Replace(value); + } + } + } + + // Extract receiver maps from the keyed load IC using the KeyedLoadICNexus. if (!p.feedback().IsValid()) return NoChange(); KeyedLoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); // Try to lower the keyed access based on the {nexus}. - return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kLoad, - p.language_mode(), STANDARD_STORE); + return ReduceKeyedAccess(node, name, value, nexus, AccessMode::kLoad, + STANDARD_STORE); } - Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) { DCHECK_EQ(IrOpcode::kJSStoreProperty, node->opcode()); PropertyAccess const& p = PropertyAccessOf(node->op()); Node* const index = NodeProperties::GetValueInput(node, 1); Node* const value = NodeProperties::GetValueInput(node, 2); - // Extract receiver maps from the KEYED_STORE_IC using the KeyedStoreICNexus. + // Extract receiver maps from the keyed store IC using the KeyedStoreICNexus. if (!p.feedback().IsValid()) return NoChange(); KeyedStoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); - // Extract the keyed access store mode from the KEYED_STORE_IC. + // Extract the keyed access store mode from the keyed store IC. KeyedAccessStoreMode store_mode = nexus.GetKeyedAccessStoreMode(); // Try to lower the keyed access based on the {nexus}. return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kStore, - p.language_mode(), store_mode); + store_mode); } Node* JSNativeContextSpecialization::InlinePropertyGetterCall( @@ -1570,7 +1706,7 @@ JSNativeContextSpecialization::ValueEffectControl JSNativeContextSpecialization::BuildPropertyLoad( Node* receiver, Node* context, Node* frame_state, Node* effect, Node* control, Handle<Name> name, ZoneVector<Node*>* if_exceptions, - PropertyAccessInfo const& access_info, LanguageMode language_mode) { + PropertyAccessInfo const& access_info) { // Determine actual holder and perform prototype chain checks. Handle<JSObject> holder; PropertyAccessBuilder access_builder(jsgraph(), dependencies()); @@ -1589,6 +1725,11 @@ JSNativeContextSpecialization::BuildPropertyLoad( } else if (access_info.IsAccessorConstant()) { value = InlinePropertyGetterCall(receiver, context, frame_state, &effect, &control, if_exceptions, access_info); + } else if (access_info.IsModuleExport()) { + Node* cell = jsgraph()->Constant(access_info.export_cell()); + value = effect = + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForCellValue()), + cell, effect, control); } else { DCHECK(access_info.IsDataField() || access_info.IsDataConstantField()); value = access_builder.BuildLoadDataField(name, access_info, receiver, @@ -1602,17 +1743,16 @@ JSNativeContextSpecialization::ValueEffectControl JSNativeContextSpecialization::BuildPropertyAccess( Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect, Node* control, Handle<Name> name, ZoneVector<Node*>* if_exceptions, - PropertyAccessInfo const& access_info, AccessMode access_mode, - LanguageMode language_mode) { + PropertyAccessInfo const& access_info, AccessMode access_mode) { switch (access_mode) { case AccessMode::kLoad: return BuildPropertyLoad(receiver, context, frame_state, effect, control, - name, if_exceptions, access_info, language_mode); + name, if_exceptions, access_info); case AccessMode::kStore: case AccessMode::kStoreInLiteral: return BuildPropertyStore(receiver, value, context, frame_state, effect, control, name, if_exceptions, access_info, - access_mode, language_mode); + access_mode); } UNREACHABLE(); return ValueEffectControl(); @@ -1622,8 +1762,7 @@ JSNativeContextSpecialization::ValueEffectControl JSNativeContextSpecialization::BuildPropertyStore( Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect, Node* control, Handle<Name> name, ZoneVector<Node*>* if_exceptions, - PropertyAccessInfo const& access_info, AccessMode access_mode, - LanguageMode language_mode) { + PropertyAccessInfo const& access_info, AccessMode access_mode) { // Determine actual holder and perform prototype chain checks. Handle<JSObject> holder; PropertyAccessBuilder access_builder(jsgraph(), dependencies()); @@ -1889,7 +2028,7 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreDataPropertyInLiteral( // Generate the actual property access. ValueEffectControl continuation = BuildPropertyAccess( receiver, value, context, frame_state_lazy, effect, control, cached_name, - nullptr, access_info, AccessMode::kStoreInLiteral, LanguageMode::SLOPPY); + nullptr, access_info, AccessMode::kStoreInLiteral); value = continuation.value(); effect = continuation.effect(); control = continuation.control(); @@ -2021,9 +2160,12 @@ JSNativeContextSpecialization::BuildElementAccess( UNREACHABLE(); break; case AccessMode::kStore: { - // Ensure that the {value} is actually a Number. - value = effect = graph()->NewNode(simplified()->CheckNumber(), value, - effect, control); + // Ensure that the {value} is actually a Number or an Oddball, + // and truncate it to a Number appropriately. + value = effect = + graph()->NewNode(simplified()->SpeculativeToNumber( + NumberOperationHint::kNumberOrOddball), + value, effect, control); // Introduce the appropriate truncation for {value}. Currently we // only need to do this for ClamedUint8Array {receiver}s, as the @@ -2100,13 +2242,8 @@ JSNativeContextSpecialization::BuildElementAccess( // Check if we might need to grow the {elements} backing store. if (IsGrowStoreMode(store_mode)) { + // For growing stores we validate the {index} below. DCHECK_EQ(AccessMode::kStore, access_mode); - - // Check that the {index} is a valid array index; the actual checking - // happens below right before the element store. - index = effect = graph()->NewNode(simplified()->CheckBounds(), index, - jsgraph()->Constant(Smi::kMaxValue), - effect, control); } else { // Check that the {index} is in the valid range for the {receiver}. index = effect = graph()->NewNode(simplified()->CheckBounds(), index, @@ -2187,23 +2324,69 @@ JSNativeContextSpecialization::BuildElementAccess( graph()->NewNode(simplified()->EnsureWritableFastElements(), receiver, elements, effect, control); } else if (IsGrowStoreMode(store_mode)) { - // Grow {elements} backing store if necessary. Also updates the - // "length" property for JSArray {receiver}s, hence there must - // not be any other check after this operation, as the write - // to the "length" property is observable. - GrowFastElementsFlags flags = GrowFastElementsFlag::kNone; + // Determine the length of the {elements} backing store. + Node* elements_length = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), + elements, effect, control); + + // Validate the {index} depending on holeyness: + // + // For HOLEY_*_ELEMENTS the {index} must not exceed the {elements} + // backing store capacity plus the maximum allowed gap, as otherwise + // the (potential) backing store growth would normalize and thus + // the elements kind of the {receiver} would change to slow mode. + // + // For PACKED_*_ELEMENTS the {index} must be within the range + // [0,length+1[ to be valid. In case {index} equals {length}, + // the {receiver} will be extended, but kept packed. + Node* limit = + IsHoleyElementsKind(elements_kind) + ? graph()->NewNode(simplified()->NumberAdd(), elements_length, + jsgraph()->Constant(JSObject::kMaxGap)) + : graph()->NewNode(simplified()->NumberAdd(), length, + jsgraph()->OneConstant()); + index = effect = graph()->NewNode(simplified()->CheckBounds(), index, + limit, effect, control); + + // Grow {elements} backing store if necessary. + GrowFastElementsMode mode = + IsDoubleElementsKind(elements_kind) + ? GrowFastElementsMode::kDoubleElements + : GrowFastElementsMode::kSmiOrObjectElements; + elements = effect = graph()->NewNode( + simplified()->MaybeGrowFastElements(mode), receiver, elements, + index, elements_length, effect, control); + + // Also update the "length" property if {receiver} is a JSArray. if (receiver_is_jsarray) { - flags |= GrowFastElementsFlag::kArrayObject; - } - if (IsHoleyOrDictionaryElementsKind(elements_kind)) { - flags |= GrowFastElementsFlag::kHoleyElements; - } - if (IsDoubleElementsKind(elements_kind)) { - flags |= GrowFastElementsFlag::kDoubleElements; + Node* check = + graph()->NewNode(simplified()->NumberLessThan(), index, length); + Node* branch = graph()->NewNode(common()->Branch(), check, control); + + Node* if_true = graph()->NewNode(common()->IfTrue(), branch); + Node* etrue = effect; + { + // We don't need to do anything, the {index} is within + // the valid bounds for the JSArray {receiver}. + } + + Node* if_false = graph()->NewNode(common()->IfFalse(), branch); + Node* efalse = effect; + { + // Update the JSArray::length field. Since this is observable, + // there must be no other check after this. + Node* new_length = graph()->NewNode( + simplified()->NumberAdd(), index, jsgraph()->OneConstant()); + efalse = graph()->NewNode( + simplified()->StoreField( + AccessBuilder::ForJSArrayLength(elements_kind)), + receiver, new_length, efalse, if_false); + } + + control = graph()->NewNode(common()->Merge(2), if_true, if_false); + effect = + graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); } - elements = effect = graph()->NewNode( - simplified()->MaybeGrowFastElements(flags), receiver, elements, - index, length, effect, control); } // Perform the actual element access. @@ -2255,14 +2438,18 @@ Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore( jsgraph()->SmiConstant(PropertyArray::kNoHashSentinel)); hash = graph()->NewNode(common()->TypeGuard(Type::SignedSmall()), hash, control); + hash = + graph()->NewNode(simplified()->NumberShiftLeft(), hash, + jsgraph()->Constant(PropertyArray::HashField::kShift)); } else { hash = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForPropertyArrayLengthAndHash()), properties, effect, control); effect = graph()->NewNode( common()->BeginRegion(RegionObservability::kNotObservable), effect); - hash = graph()->NewNode(simplified()->NumberBitwiseAnd(), hash, - jsgraph()->Constant(JSReceiver::kHashMask)); + hash = + graph()->NewNode(simplified()->NumberBitwiseAnd(), hash, + jsgraph()->Constant(PropertyArray::HashField::kMask)); } Node* new_length_and_hash = graph()->NewNode( |