summaryrefslogtreecommitdiff
path: root/deps/v8/src/compiler/js-native-context-specialization.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compiler/js-native-context-specialization.cc')
-rw-r--r--deps/v8/src/compiler/js-native-context-specialization.cc383
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(