From 02892ad2219de0b3eb989ae08e8fd17df48ca0f8 Mon Sep 17 00:00:00 2001 From: Georg Neis Date: Fri, 21 Jun 2019 13:30:18 +0200 Subject: [PATCH] [turbofan] Remove most remaining heap accesses from property loads A few are still left and made explicit with Allow* scopes. Bug: v8:7790 Change-Id: I85e78949730d046d3449e0cee70997e60a043825 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1622108 Commit-Queue: Georg Neis Reviewed-by: Jaroslav Sevcik Reviewed-by: Maya Lekova Cr-Commit-Position: refs/heads/master@{#62310} --- src/compiler/access-info.cc | 5 +- src/compiler/access-info.h | 2 +- src/compiler/compilation-dependencies.cc | 5 + src/compiler/js-heap-broker.cc | 66 ++++- src/compiler/js-heap-broker.h | 29 +- .../js-native-context-specialization.cc | 268 ++++++++---------- .../js-native-context-specialization.h | 31 +- src/compiler/property-access-builder.cc | 13 +- .../serializer-for-background-compilation.cc | 11 +- .../serializer-for-background-compilation.h | 3 +- 10 files changed, 241 insertions(+), 192 deletions(-) diff --git a/src/compiler/access-info.cc b/src/compiler/access-info.cc index 172a6b5e2c..66eeea07e6 100644 --- a/src/compiler/access-info.cc +++ b/src/compiler/access-info.cc @@ -258,9 +258,9 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that, } } -Handle PropertyAccessInfo::export_cell() const { +CellRef PropertyAccessInfo::export_cell(JSHeapBroker* broker) const { DCHECK_EQ(kModuleExport, kind_); - return Handle::cast(constant_); + return ObjectRef(broker, constant_).AsCell(); } AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker, @@ -793,6 +793,7 @@ PropertyAccessInfo AccessInfoFactory::LookupTransition( unrecorded_dependencies.push_back( dependencies()->TransitionDependencyOffTheRecord( MapRef(broker(), transition_map))); + transition_map_ref.SerializeBackPointer(); // For BuildPropertyStore. // Transitioning stores *may* store to const fields. The resulting // DataConstant access infos can be distinguished from later, i.e. redundant, // stores to the same constant field by the presence of a transition map. diff --git a/src/compiler/access-info.h b/src/compiler/access-info.h index 3499069fc4..35689b3968 100644 --- a/src/compiler/access-info.h +++ b/src/compiler/access-info.h @@ -127,7 +127,7 @@ class PropertyAccessInfo final { ZoneVector> const& receiver_maps() const { return receiver_maps_; } - Handle export_cell() const; + CellRef export_cell(JSHeapBroker* broker) const; private: explicit PropertyAccessInfo(Zone* zone); diff --git a/src/compiler/compilation-dependencies.cc b/src/compiler/compilation-dependencies.cc index f0bb797b68..07c422e1a5 100644 --- a/src/compiler/compilation-dependencies.cc +++ b/src/compiler/compilation-dependencies.cc @@ -565,6 +565,11 @@ namespace { // This function expects to never see a JSProxy. void DependOnStablePrototypeChain(CompilationDependencies* deps, MapRef map, base::Optional last_prototype) { + // TODO(neis): Remove heap access (SerializePrototype call). + AllowCodeDependencyChange dependency_change_; + AllowHandleAllocation handle_allocation_; + AllowHandleDereference handle_dereference_; + AllowHeapAllocation heap_allocation_; while (true) { map.SerializePrototype(); HeapObjectRef proto = map.prototype(); diff --git a/src/compiler/js-heap-broker.cc b/src/compiler/js-heap-broker.cc index 83f79dda4a..4e4f874588 100644 --- a/src/compiler/js-heap-broker.cc +++ b/src/compiler/js-heap-broker.cc @@ -3492,10 +3492,67 @@ base::Optional GlobalAccessFeedback::GetConstantHint() const { return {}; } -ElementAccessFeedback::ElementAccessFeedback(Zone* zone) +KeyedAccessMode KeyedAccessMode::FromNexus(FeedbackNexus const& nexus) { + if (IsKeyedLoadICKind(nexus.kind())) { + return KeyedAccessMode(AccessMode::kLoad, nexus.GetKeyedAccessLoadMode()); + } + if (IsKeyedHasICKind(nexus.kind())) { + return KeyedAccessMode(AccessMode::kHas, nexus.GetKeyedAccessLoadMode()); + } + if (IsKeyedStoreICKind(nexus.kind())) { + return KeyedAccessMode(AccessMode::kStore, nexus.GetKeyedAccessStoreMode()); + } + if (IsStoreInArrayLiteralICKind(nexus.kind())) { + return KeyedAccessMode(AccessMode::kStoreInLiteral, + nexus.GetKeyedAccessStoreMode()); + } + UNREACHABLE(); +} + +AccessMode KeyedAccessMode::access_mode() const { return access_mode_; } + +bool KeyedAccessMode::IsLoad() const { + return access_mode_ == AccessMode::kLoad || access_mode_ == AccessMode::kHas; +} +bool KeyedAccessMode::IsStore() const { + return access_mode_ == AccessMode::kStore || + access_mode_ == AccessMode::kStoreInLiteral; +} + +KeyedAccessLoadMode KeyedAccessMode::load_mode() const { + CHECK(IsLoad()); + return load_store_mode_.load_mode; +} + +KeyedAccessStoreMode KeyedAccessMode::store_mode() const { + CHECK(IsStore()); + return load_store_mode_.store_mode; +} + +KeyedAccessMode::LoadStoreMode::LoadStoreMode(KeyedAccessLoadMode load_mode) + : load_mode(load_mode) {} +KeyedAccessMode::LoadStoreMode::LoadStoreMode(KeyedAccessStoreMode store_mode) + : store_mode(store_mode) {} + +KeyedAccessMode::KeyedAccessMode(AccessMode access_mode, + KeyedAccessLoadMode load_mode) + : access_mode_(access_mode), load_store_mode_(load_mode) { + CHECK(!IsStore()); + CHECK(IsLoad()); +} +KeyedAccessMode::KeyedAccessMode(AccessMode access_mode, + KeyedAccessStoreMode store_mode) + : access_mode_(access_mode), load_store_mode_(store_mode) { + CHECK(!IsLoad()); + CHECK(IsStore()); +} + +ElementAccessFeedback::ElementAccessFeedback(Zone* zone, + KeyedAccessMode const& keyed_mode) : ProcessedFeedback(kElementAccess), receiver_maps(zone), - transitions(zone) {} + transitions(zone), + keyed_mode(keyed_mode) {} ElementAccessFeedback::MapIterator::MapIterator( ElementAccessFeedback const& processed, JSHeapBroker* broker) @@ -3568,7 +3625,7 @@ GlobalAccessFeedback const* JSHeapBroker::GetGlobalAccessFeedback( } ElementAccessFeedback const* JSHeapBroker::ProcessFeedbackMapsForElementAccess( - MapHandles const& maps) { + MapHandles const& maps, KeyedAccessMode const& keyed_mode) { DCHECK(!maps.empty()); // Collect possible transition targets. @@ -3582,7 +3639,8 @@ ElementAccessFeedback const* JSHeapBroker::ProcessFeedbackMapsForElementAccess( } } - ElementAccessFeedback* result = new (zone()) ElementAccessFeedback(zone()); + ElementAccessFeedback* result = + new (zone()) ElementAccessFeedback(zone(), keyed_mode); // Separate the actual receiver maps and the possible transition sources. for (Handle map : maps) { diff --git a/src/compiler/js-heap-broker.h b/src/compiler/js-heap-broker.h index daf004aab1..7e3da7b436 100644 --- a/src/compiler/js-heap-broker.h +++ b/src/compiler/js-heap-broker.h @@ -777,15 +777,40 @@ class GlobalAccessFeedback : public ProcessedFeedback { int const index_and_immutable_; }; +class KeyedAccessMode { + public: + static KeyedAccessMode FromNexus(FeedbackNexus const& nexus); + + AccessMode access_mode() const; + bool IsLoad() const; + bool IsStore() const; + KeyedAccessLoadMode load_mode() const; + KeyedAccessStoreMode store_mode() const; + + private: + AccessMode const access_mode_; + union LoadStoreMode { + LoadStoreMode(KeyedAccessLoadMode load_mode); + LoadStoreMode(KeyedAccessStoreMode store_mode); + KeyedAccessLoadMode load_mode; + KeyedAccessStoreMode store_mode; + } const load_store_mode_; + + KeyedAccessMode(AccessMode access_mode, KeyedAccessLoadMode load_mode); + KeyedAccessMode(AccessMode access_mode, KeyedAccessStoreMode store_mode); +}; + class ElementAccessFeedback : public ProcessedFeedback { public: - explicit ElementAccessFeedback(Zone* zone); + ElementAccessFeedback(Zone* zone, KeyedAccessMode const& keyed_mode); // No transition sources appear in {receiver_maps}. // All transition targets appear in {receiver_maps}. ZoneVector> receiver_maps; ZoneVector, Handle>> transitions; + KeyedAccessMode const keyed_mode; + class MapIterator { public: bool done() const; @@ -901,7 +926,7 @@ class V8_EXPORT_PRIVATE JSHeapBroker { // TODO(neis): Move these into serializer when we're always in the background. ElementAccessFeedback const* ProcessFeedbackMapsForElementAccess( - MapHandles const& maps); + MapHandles const& maps, KeyedAccessMode const& keyed_mode); GlobalAccessFeedback const* ProcessFeedbackForGlobalAccess( FeedbackSource const& source); diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc index 88b1c1a133..a763cc8133 100644 --- a/src/compiler/js-native-context-specialization.cc +++ b/src/compiler/js-native-context-specialization.cc @@ -969,9 +969,8 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( } Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) { - DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode()); DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining); - + DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode()); LoadGlobalParameters const& p = LoadGlobalParametersOf(node->op()); if (!p.feedback().IsValid()) return NoChange(); FeedbackSource source(p.feedback()); @@ -1001,9 +1000,8 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) { } Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) { - DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode()); DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining); - + DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode()); Node* value = NodeProperties::GetValueInput(node, 0); StoreGlobalParameters const& p = StoreGlobalParametersOf(node->op()); @@ -1292,7 +1290,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( } Reduction JSNativeContextSpecialization::ReduceNamedAccessFromNexus( - Node* node, Node* value, FeedbackNexus const& nexus, NameRef const& name, + Node* node, Node* value, FeedbackSource const& source, NameRef const& name, AccessMode access_mode) { DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || node->opcode() == IrOpcode::kJSStoreNamed || @@ -1306,11 +1304,11 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccessFromNexus( return ReduceGlobalAccess(node, nullptr, value, name, access_mode); } - return ReducePropertyAccessUsingProcessedFeedback(node, nullptr, name, value, - nexus, access_mode); + return ReducePropertyAccess(node, nullptr, name, value, source, access_mode); } Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { + DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining); DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); NamedAccess const& p = NamedAccessOf(node->op()); Node* const receiver = NodeProperties::GetValueInput(node, 0); @@ -1349,40 +1347,32 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { } } - // Extract receiver maps from the load IC using the FeedbackNexus. if (!p.feedback().IsValid()) return NoChange(); - FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); - - // Try to lower the named access based on the {receiver_maps}. - return ReduceNamedAccessFromNexus(node, jsgraph()->Dead(), nexus, name, + return ReduceNamedAccessFromNexus(node, jsgraph()->Dead(), + FeedbackSource(p.feedback()), name, AccessMode::kLoad); } Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { + DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining); DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode()); NamedAccess const& p = NamedAccessOf(node->op()); Node* const value = NodeProperties::GetValueInput(node, 1); - // Extract receiver maps from the store IC using the FeedbackNexus. if (!p.feedback().IsValid()) return NoChange(); - FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); - - // Try to lower the named access based on the {receiver_maps}. - return ReduceNamedAccessFromNexus( - node, value, nexus, NameRef(broker(), p.name()), AccessMode::kStore); + return ReduceNamedAccessFromNexus(node, value, FeedbackSource(p.feedback()), + NameRef(broker(), p.name()), + AccessMode::kStore); } Reduction JSNativeContextSpecialization::ReduceJSStoreNamedOwn(Node* node) { + DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining); DCHECK_EQ(IrOpcode::kJSStoreNamedOwn, node->opcode()); StoreNamedOwnParameters const& p = StoreNamedOwnParametersOf(node->op()); Node* const value = NodeProperties::GetValueInput(node, 1); - // Extract receiver maps from the IC using the FeedbackNexus. if (!p.feedback().IsValid()) return NoChange(); - FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); - - // Try to lower the creation of a named property based on the {receiver_maps}. - return ReduceNamedAccessFromNexus(node, value, nexus, + return ReduceNamedAccessFromNexus(node, value, FeedbackSource(p.feedback()), NameRef(broker(), p.name()), AccessMode::kStoreInLiteral); } @@ -1431,24 +1421,31 @@ base::Optional GetTypedArrayConstant(JSHeapBroker* broker, Reduction JSNativeContextSpecialization::ReduceElementAccess( Node* node, Node* index, Node* value, - ElementAccessFeedback const& processed, AccessMode access_mode, - KeyedAccessLoadMode load_mode, KeyedAccessStoreMode store_mode) { + ElementAccessFeedback const& processed) { DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining); - DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || node->opcode() == IrOpcode::kJSStoreProperty || node->opcode() == IrOpcode::kJSStoreInArrayLiteral || node->opcode() == IrOpcode::kJSHasProperty); + Node* receiver = NodeProperties::GetValueInput(node, 0); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); Node* frame_state = NodeProperties::FindFrameStateBefore(node, jsgraph()->Dead()); + AccessMode access_mode = processed.keyed_mode.access_mode(); + if ((access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) && + receiver->opcode() == IrOpcode::kHeapConstant) { + Reduction reduction = ReduceKeyedLoadFromHeapConstant( + node, index, access_mode, processed.keyed_mode.load_mode()); + if (reduction.Changed()) return reduction; + } + if (HasOnlyStringMaps(broker(), processed.receiver_maps)) { DCHECK(processed.transitions.empty()); return ReduceElementAccessOnString(node, index, value, access_mode, - load_mode); + processed.keyed_mode.load_mode()); } // Compute element access infos for the receiver maps. @@ -1479,7 +1476,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( // then we need to check that all prototypes have stable maps with // fast elements (and we need to guard against changes to that below). if ((IsHoleyOrDictionaryElementsKind(receiver_map.elements_kind()) || - IsGrowStoreMode(store_mode)) && + IsGrowStoreMode(processed.keyed_mode.store_mode())) && !receiver_map.HasOnlyStablePrototypesWithFastElements( &prototype_maps)) { return NoChange(); @@ -1552,7 +1549,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( // Access the actual element. ValueEffectControl continuation = BuildElementAccess(receiver, index, value, effect, control, access_info, - access_mode, load_mode, store_mode); + processed.keyed_mode); value = continuation.value(); effect = continuation.effect(); control = continuation.control(); @@ -1617,9 +1614,9 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( } // Access the actual element. - ValueEffectControl continuation = BuildElementAccess( - this_receiver, this_index, this_value, this_effect, this_control, - access_info, access_mode, load_mode, store_mode); + ValueEffectControl continuation = + BuildElementAccess(this_receiver, this_index, this_value, this_effect, + this_control, access_info, processed.keyed_mode); values.push_back(continuation.value()); effects.push_back(continuation.effect()); controls.push_back(continuation.control()); @@ -1653,7 +1650,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( } Reduction JSNativeContextSpecialization::ReduceKeyedLoadFromHeapConstant( - Node* node, Node* key, FeedbackNexus const& nexus, AccessMode access_mode, + Node* node, Node* key, AccessMode access_mode, KeyedAccessLoadMode load_mode) { DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || node->opcode() == IrOpcode::kJSHasProperty); @@ -1709,54 +1706,24 @@ Reduction JSNativeContextSpecialization::ReduceKeyedLoadFromHeapConstant( // accesses using the known length, which doesn't change. if (receiver_ref.IsString()) { DCHECK_NE(access_mode, AccessMode::kHas); - // 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 {key} is less than {receiver} length. - Node* length = jsgraph()->Constant(receiver_ref.AsString().length()); + // Ensure that {key} is less than {receiver} length. + Node* length = jsgraph()->Constant(receiver_ref.AsString().length()); - // Load the single character string from {receiver} or yield - // undefined if the {key} is out of bounds (depending on the - // {load_mode}). - Node* value = BuildIndexedStringLoad(receiver, key, length, &effect, - &control, load_mode); - ReplaceWithValue(node, value, effect, control); - return Replace(value); - } + // Load the single character string from {receiver} or yield + // undefined if the {key} is out of bounds (depending on the + // {load_mode}). + Node* value = BuildIndexedStringLoad(receiver, key, length, &effect, + &control, load_mode); + ReplaceWithValue(node, value, effect, control); + return Replace(value); } return NoChange(); } -Reduction JSNativeContextSpecialization::ReduceKeyedAccess( - Node* node, Node* key, Node* value, FeedbackNexus const& nexus, - AccessMode access_mode, KeyedAccessLoadMode load_mode, - KeyedAccessStoreMode store_mode) { - DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || - node->opcode() == IrOpcode::kJSStoreProperty || - node->opcode() == IrOpcode::kJSStoreInArrayLiteral || - node->opcode() == IrOpcode::kJSHasProperty); - - Node* receiver = NodeProperties::GetValueInput(node, 0); - - if ((access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) && - receiver->opcode() == IrOpcode::kHeapConstant) { - Reduction reduction = ReduceKeyedLoadFromHeapConstant( - node, key, nexus, access_mode, load_mode); - if (reduction.Changed()) return reduction; - } - - return ReducePropertyAccessUsingProcessedFeedback(node, key, base::nullopt, - value, nexus, access_mode, - load_mode, store_mode); -} - -Reduction -JSNativeContextSpecialization::ReducePropertyAccessUsingProcessedFeedback( +Reduction JSNativeContextSpecialization::ReducePropertyAccess( Node* node, Node* key, base::Optional static_name, Node* value, - FeedbackNexus const& nexus, AccessMode access_mode, - KeyedAccessLoadMode load_mode, KeyedAccessStoreMode store_mode) { + FeedbackSource const& source, AccessMode access_mode) { DCHECK_EQ(key == nullptr, static_name.has_value()); DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || node->opcode() == IrOpcode::kJSStoreProperty || @@ -1771,11 +1738,12 @@ JSNativeContextSpecialization::ReducePropertyAccessUsingProcessedFeedback( ProcessedFeedback const* processed = nullptr; if (FLAG_concurrent_inlining) { - processed = broker()->GetFeedback(FeedbackSource(nexus)); + processed = broker()->GetFeedback(source); // TODO(neis): Infer maps from the graph and consolidate with feedback/hints // and filter impossible candidates based on inferred root map. } else { // TODO(neis): Try to unify this with the similar code in the serializer. + FeedbackNexus nexus(source.vector, source.slot); if (nexus.ic_state() == UNINITIALIZED) { processed = new (zone()) InsufficientFeedback(); } else { @@ -1795,8 +1763,8 @@ JSNativeContextSpecialization::ReducePropertyAccessUsingProcessedFeedback( processed = new (zone()) NamedAccessFeedback(*name, access_infos); } else if (nexus.GetKeyType() == ELEMENT && MEGAMORPHIC != nexus.ic_state()) { - processed = - broker()->ProcessFeedbackMapsForElementAccess(receiver_maps); + processed = broker()->ProcessFeedbackMapsForElementAccess( + receiver_maps, KeyedAccessMode::FromNexus(nexus)); } } } @@ -1812,9 +1780,10 @@ JSNativeContextSpecialization::ReducePropertyAccessUsingProcessedFeedback( return ReduceNamedAccess(node, value, *processed->AsNamedAccess(), access_mode, key); case ProcessedFeedback::kElementAccess: + CHECK_EQ(processed->AsElementAccess()->keyed_mode.access_mode(), + access_mode); return ReduceElementAccess(node, key, value, - *processed->AsElementAccess(), access_mode, - load_mode, store_mode); + *processed->AsElementAccess()); case ProcessedFeedback::kGlobalAccess: UNREACHABLE(); } @@ -1840,21 +1809,15 @@ Reduction JSNativeContextSpecialization::ReduceSoftDeoptimize( } Reduction JSNativeContextSpecialization::ReduceJSHasProperty(Node* node) { + DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining); DCHECK_EQ(IrOpcode::kJSHasProperty, node->opcode()); PropertyAccess const& p = PropertyAccessOf(node->op()); Node* key = NodeProperties::GetValueInput(node, 1); Node* value = jsgraph()->Dead(); - // Extract receiver maps from the has property IC using the FeedbackNexus. if (!p.feedback().IsValid()) return NoChange(); - FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); - - // Extract the keyed access load mode from the keyed load IC. - KeyedAccessLoadMode load_mode = nexus.GetKeyedAccessLoadMode(); - - // Try to lower the keyed access based on the {nexus}. - return ReduceKeyedAccess(node, key, value, nexus, AccessMode::kHas, load_mode, - STANDARD_STORE); + return ReducePropertyAccess(node, key, base::nullopt, value, + FeedbackSource(p.feedback()), AccessMode::kHas); } Reduction JSNativeContextSpecialization::ReduceJSLoadPropertyWithEnumeratedKey( @@ -1964,6 +1927,7 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadPropertyWithEnumeratedKey( } Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) { + DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining); DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode()); PropertyAccess const& p = PropertyAccessOf(node->op()); Node* name = NodeProperties::GetValueInput(node, 1); @@ -1973,60 +1937,51 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) { if (reduction.Changed()) return reduction; } - // Extract receiver maps from the keyed load IC using the FeedbackNexus. if (!p.feedback().IsValid()) return NoChange(); - FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); - - // Extract the keyed access load mode from the keyed load IC. - KeyedAccessLoadMode load_mode = nexus.GetKeyedAccessLoadMode(); - - // Try to lower the keyed access based on the {nexus}. Node* value = jsgraph()->Dead(); - return ReduceKeyedAccess(node, name, value, nexus, AccessMode::kLoad, - load_mode, STANDARD_STORE); + return ReducePropertyAccess(node, name, base::nullopt, value, + FeedbackSource(p.feedback()), AccessMode::kLoad); } Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) { + DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining); DCHECK_EQ(IrOpcode::kJSStoreProperty, node->opcode()); PropertyAccess const& p = PropertyAccessOf(node->op()); Node* const key = NodeProperties::GetValueInput(node, 1); Node* const value = NodeProperties::GetValueInput(node, 2); - // Extract receiver maps from the keyed store IC using the FeedbackNexus. if (!p.feedback().IsValid()) return NoChange(); - FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); - - // 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, key, value, nexus, AccessMode::kStore, - STANDARD_LOAD, store_mode); + return ReducePropertyAccess(node, key, base::nullopt, value, + FeedbackSource(p.feedback()), AccessMode::kStore); } Node* JSNativeContextSpecialization::InlinePropertyGetterCall( Node* receiver, Node* context, Node* frame_state, Node** effect, Node** control, ZoneVector* if_exceptions, PropertyAccessInfo const& access_info) { - Node* target = jsgraph()->Constant(access_info.constant()); + ObjectRef constant(broker(), access_info.constant()); + Node* target = jsgraph()->Constant(constant); FrameStateInfo const& frame_info = FrameStateInfoOf(frame_state->op()); // Introduce the call to the getter function. Node* value; - ObjectRef constant(broker(), access_info.constant()); if (constant.IsJSFunction()) { value = *effect = *control = graph()->NewNode( jsgraph()->javascript()->Call(2, CallFrequency(), VectorSlotPair(), ConvertReceiverMode::kNotNullOrUndefined), target, receiver, context, frame_state, *effect, *control); } else { - // TODO(mslekova): Move this to the serialization of property loads. - FunctionTemplateInfoRef function_template_info = - constant.AsFunctionTemplateInfo(); - function_template_info.Serialize(); - Node* holder = - access_info.holder().is_null() - ? receiver - : jsgraph()->Constant(access_info.holder().ToHandleChecked()); + auto function_template_info = constant.AsFunctionTemplateInfo(); + { // TODO(mslekova): Move this to the serialization of property loads. + AllowCodeDependencyChange dependency_change_; + AllowHandleAllocation handle_allocation_; + AllowHandleDereference handle_dereference_; + AllowHeapAllocation heap_allocation_; + function_template_info.Serialize(); + } + Node* holder = access_info.holder().is_null() + ? receiver + : jsgraph()->Constant(ObjectRef( + broker(), access_info.holder().ToHandleChecked())); SharedFunctionInfoRef shared_info( broker(), frame_info.shared_info().ToHandleChecked()); @@ -2049,10 +2004,10 @@ void JSNativeContextSpecialization::InlinePropertySetterCall( Node* receiver, Node* value, Node* context, Node* frame_state, Node** effect, Node** control, ZoneVector* if_exceptions, PropertyAccessInfo const& access_info) { - Node* target = jsgraph()->Constant(access_info.constant()); + ObjectRef constant(broker(), access_info.constant()); + Node* target = jsgraph()->Constant(constant); FrameStateInfo const& frame_info = FrameStateInfoOf(frame_state->op()); // Introduce the call to the setter function. - ObjectRef constant(broker(), access_info.constant()); if (constant.IsJSFunction()) { *effect = *control = graph()->NewNode( jsgraph()->javascript()->Call(3, CallFrequency(), VectorSlotPair(), @@ -2061,11 +2016,17 @@ void JSNativeContextSpecialization::InlinePropertySetterCall( } else { // TODO(mslekova): Move this to the serialization of property stores. auto function_template_info = constant.AsFunctionTemplateInfo(); - function_template_info.Serialize(); - Node* holder = - access_info.holder().is_null() - ? receiver - : jsgraph()->Constant(access_info.holder().ToHandleChecked()); + { // TODO(mslekova): Remove this Serialize call. + AllowCodeDependencyChange dependency_change_; + AllowHandleAllocation handle_allocation_; + AllowHandleDereference handle_dereference_; + AllowHeapAllocation heap_allocation_; + function_template_info.Serialize(); + } + Node* holder = access_info.holder().is_null() + ? receiver + : jsgraph()->Constant(ObjectRef( + broker(), access_info.holder().ToHandleChecked())); SharedFunctionInfoRef shared_info( broker(), frame_info.shared_info().ToHandleChecked()); InlineApiCall(receiver, holder, frame_state, value, effect, control, @@ -2149,7 +2110,7 @@ JSNativeContextSpecialization::BuildPropertyLoad( 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()); + Node* cell = jsgraph()->Constant(access_info.export_cell(broker())); value = effect = graph()->NewNode(simplified()->LoadField(AccessBuilder::ForCellValue()), cell, effect, control); @@ -2380,7 +2341,6 @@ JSNativeContextSpecialization::BuildPropertyStore( // Check if we need to grow the properties backing store // with this transitioning store. MapRef transition_map_ref(broker(), transition_map); - transition_map_ref.SerializeBackPointer(); MapRef original_map = transition_map_ref.GetBackPointer().AsMap(); if (original_map.UnusedPropertyFields() == 0) { DCHECK(!field_index.is_inobject()); @@ -2402,7 +2362,7 @@ JSNativeContextSpecialization::BuildPropertyStore( common()->BeginRegion(RegionObservability::kObservable), effect); effect = graph()->NewNode( simplified()->StoreField(AccessBuilder::ForMap()), receiver, - jsgraph()->Constant(transition_map), effect, control); + jsgraph()->Constant(transition_map_ref), effect, control); effect = graph()->NewNode(simplified()->StoreField(field_access), storage, value, effect, control); effect = graph()->NewNode(common()->FinishRegion(), @@ -2493,21 +2453,16 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreDataPropertyInLiteral( Reduction JSNativeContextSpecialization::ReduceJSStoreInArrayLiteral( Node* node) { + DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining); DCHECK_EQ(IrOpcode::kJSStoreInArrayLiteral, node->opcode()); FeedbackParameter const& p = FeedbackParameterOf(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 FeedbackNexus. if (!p.feedback().IsValid()) return NoChange(); - FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); - - // Extract the keyed access store mode from the keyed store IC. - KeyedAccessStoreMode store_mode = nexus.GetKeyedAccessStoreMode(); - - return ReduceKeyedAccess(node, index, value, nexus, - AccessMode::kStoreInLiteral, STANDARD_LOAD, - store_mode); + return ReducePropertyAccess(node, index, base::nullopt, value, + FeedbackSource(p.feedback()), + AccessMode::kStoreInLiteral); } Reduction JSNativeContextSpecialization::ReduceJSToObject(Node* node) { @@ -2544,8 +2499,7 @@ ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind) { JSNativeContextSpecialization::ValueEffectControl JSNativeContextSpecialization::BuildElementAccess( Node* receiver, Node* index, Node* value, Node* effect, Node* control, - ElementAccessInfo const& access_info, AccessMode access_mode, - KeyedAccessLoadMode load_mode, KeyedAccessStoreMode store_mode) { + ElementAccessInfo const& access_info, KeyedAccessMode const& keyed_mode) { // TODO(bmeurer): We currently specialize based on elements kind. We should // also be able to properly support strings and other JSObjects here. ElementsKind elements_kind = access_info.elements_kind(); @@ -2627,8 +2581,10 @@ JSNativeContextSpecialization::BuildElementAccess( buffer_or_receiver = buffer; } - if (load_mode == LOAD_IGNORE_OUT_OF_BOUNDS || - store_mode == STORE_IGNORE_OUT_OF_BOUNDS) { + if ((keyed_mode.IsLoad() && + keyed_mode.load_mode() == LOAD_IGNORE_OUT_OF_BOUNDS) || + (keyed_mode.IsStore() && + keyed_mode.store_mode() == STORE_IGNORE_OUT_OF_BOUNDS)) { // Only check that the {index} is in SignedSmall range. We do the actual // bounds check below and just skip the property access if it's out of // bounds for the {receiver}. @@ -2649,10 +2605,10 @@ JSNativeContextSpecialization::BuildElementAccess( // Access the actual element. ExternalArrayType external_array_type = GetArrayTypeFromElementsKind(elements_kind); - switch (access_mode) { + switch (keyed_mode.access_mode()) { case AccessMode::kLoad: { // Check if we can return undefined for out-of-bounds loads. - if (load_mode == LOAD_IGNORE_OUT_OF_BOUNDS) { + if (keyed_mode.load_mode() == LOAD_IGNORE_OUT_OF_BOUNDS) { Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, length); Node* branch = graph()->NewNode( @@ -2714,7 +2670,7 @@ JSNativeContextSpecialization::BuildElementAccess( } // Check if we can skip the out-of-bounds store. - if (store_mode == STORE_IGNORE_OUT_OF_BOUNDS) { + if (keyed_mode.store_mode() == STORE_IGNORE_OUT_OF_BOUNDS) { Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, length); Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), @@ -2764,9 +2720,9 @@ JSNativeContextSpecialization::BuildElementAccess( // Don't try to store to a copy-on-write backing store (unless supported by // the store mode). - if (access_mode == AccessMode::kStore && + if (keyed_mode.access_mode() == AccessMode::kStore && IsSmiOrObjectElementsKind(elements_kind) && - !IsCOWHandlingStoreMode(store_mode)) { + !IsCOWHandlingStoreMode(keyed_mode.store_mode())) { effect = graph()->NewNode( simplified()->CheckMaps( CheckMapsFlag::kNone, @@ -2789,11 +2745,10 @@ JSNativeContextSpecialization::BuildElementAccess( elements, effect, control); // Check if we might need to grow the {elements} backing store. - if (IsGrowStoreMode(store_mode)) { + if (keyed_mode.IsStore() && IsGrowStoreMode(keyed_mode.store_mode())) { // For growing stores we validate the {index} below. - DCHECK(access_mode == AccessMode::kStore || - access_mode == AccessMode::kStoreInLiteral); - } else if (load_mode == LOAD_IGNORE_OUT_OF_BOUNDS && + } else if (keyed_mode.IsLoad() && + keyed_mode.load_mode() == LOAD_IGNORE_OUT_OF_BOUNDS && CanTreatHoleAsUndefined(receiver_maps)) { // Check that the {index} is a valid array index, we do the actual // bounds check below and just skip the store below if it's out of @@ -2824,7 +2779,7 @@ JSNativeContextSpecialization::BuildElementAccess( kFullWriteBarrier, LoadSensitivity::kCritical}; // Access the actual element. - if (access_mode == AccessMode::kLoad) { + if (keyed_mode.access_mode() == AccessMode::kLoad) { // Compute the real element access type, which includes the hole in case // of holey backing stores. if (IsHoleyElementsKind(elements_kind)) { @@ -2837,7 +2792,7 @@ JSNativeContextSpecialization::BuildElementAccess( } // Check if we can return undefined for out-of-bounds loads. - if (load_mode == LOAD_IGNORE_OUT_OF_BOUNDS && + if (keyed_mode.load_mode() == LOAD_IGNORE_OUT_OF_BOUNDS && CanTreatHoleAsUndefined(receiver_maps)) { Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, length); @@ -2921,7 +2876,7 @@ JSNativeContextSpecialization::BuildElementAccess( effect, control); } } - } else if (access_mode == AccessMode::kHas) { + } else if (keyed_mode.access_mode() == AccessMode::kHas) { // For packed arrays with NoElementsProctector valid, a bound check // is equivalent to HasProperty. value = effect = graph()->NewNode(simplified()->SpeculativeNumberLessThan( @@ -2994,8 +2949,9 @@ JSNativeContextSpecialization::BuildElementAccess( vtrue, vfalse, control); } } else { - DCHECK(access_mode == AccessMode::kStore || - access_mode == AccessMode::kStoreInLiteral); + DCHECK(keyed_mode.access_mode() == AccessMode::kStore || + keyed_mode.access_mode() == AccessMode::kStoreInLiteral); + if (IsSmiElementsKind(elements_kind)) { value = effect = graph()->NewNode( simplified()->CheckSmi(VectorSlotPair()), value, effect, control); @@ -3009,11 +2965,11 @@ JSNativeContextSpecialization::BuildElementAccess( // Ensure that copy-on-write backing store is writable. if (IsSmiOrObjectElementsKind(elements_kind) && - store_mode == STORE_HANDLE_COW) { + keyed_mode.store_mode() == STORE_HANDLE_COW) { elements = effect = graph()->NewNode(simplified()->EnsureWritableFastElements(), receiver, elements, effect, control); - } else if (IsGrowStoreMode(store_mode)) { + } else if (IsGrowStoreMode(keyed_mode.store_mode())) { // Determine the length of the {elements} backing store. Node* elements_length = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), @@ -3051,7 +3007,7 @@ JSNativeContextSpecialization::BuildElementAccess( // If we didn't grow {elements}, it might still be COW, in which case we // copy it now. if (IsSmiOrObjectElementsKind(elements_kind) && - store_mode == STORE_AND_GROW_HANDLE_COW) { + keyed_mode.store_mode() == STORE_AND_GROW_HANDLE_COW) { elements = effect = graph()->NewNode(simplified()->EnsureWritableFastElements(), receiver, elements, effect, control); diff --git a/src/compiler/js-native-context-specialization.h b/src/compiler/js-native-context-specialization.h index 7de2639966..4e25284b81 100644 --- a/src/compiler/js-native-context-specialization.h +++ b/src/compiler/js-native-context-specialization.h @@ -93,24 +93,15 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final Reduction ReduceJSToObject(Node* node); Reduction ReduceElementAccess(Node* node, Node* index, Node* value, - ElementAccessFeedback const& processed, - AccessMode access_mode, - KeyedAccessLoadMode load_mode, - KeyedAccessStoreMode store_mode); + ElementAccessFeedback const& processed); // In the case of non-keyed (named) accesses, pass the name as {static_name} // and use {nullptr} for {key} (load/store modes are irrelevant). - Reduction ReducePropertyAccessUsingProcessedFeedback( - Node* node, Node* key, base::Optional static_name, Node* value, - FeedbackNexus const& nexus, AccessMode access_mode, - KeyedAccessLoadMode load_mode = STANDARD_LOAD, - KeyedAccessStoreMode store_mode = STANDARD_STORE); - Reduction ReduceKeyedAccess(Node* node, Node* key, Node* value, - FeedbackNexus const& nexus, - AccessMode access_mode, - KeyedAccessLoadMode load_mode, - KeyedAccessStoreMode store_mode); + Reduction ReducePropertyAccess(Node* node, Node* key, + base::Optional static_name, + Node* value, FeedbackSource const& source, + AccessMode access_mode); Reduction ReduceNamedAccessFromNexus(Node* node, Node* value, - FeedbackNexus const& nexus, + FeedbackSource const& source, NameRef const& name, AccessMode access_mode); Reduction ReduceNamedAccess(Node* node, Node* value, @@ -123,7 +114,6 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final NameRef const& name, AccessMode access_mode, Node* key, PropertyCellRef const& property_cell); Reduction ReduceKeyedLoadFromHeapConstant(Node* node, Node* key, - FeedbackNexus const& nexus, AccessMode access_mode, KeyedAccessLoadMode load_mode); Reduction ReduceElementAccessOnString(Node* node, Node* index, Node* value, @@ -197,10 +187,11 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final FunctionTemplateInfoRef const& function_template_info); // Construct the appropriate subgraph for element access. - ValueEffectControl BuildElementAccess( - Node* receiver, Node* index, Node* value, Node* effect, Node* control, - ElementAccessInfo const& access_info, AccessMode access_mode, - KeyedAccessLoadMode load_mode, KeyedAccessStoreMode store_mode); + ValueEffectControl BuildElementAccess(Node* receiver, Node* index, + Node* value, Node* effect, + Node* control, + ElementAccessInfo const& access_info, + KeyedAccessMode const& keyed_mode); // Construct appropriate subgraph to load from a String. Node* BuildIndexedStringLoad(Node* receiver, Node* index, Node* length, diff --git a/src/compiler/property-access-builder.cc b/src/compiler/property-access-builder.cc index dafd481797..99a06ef874 100644 --- a/src/compiler/property-access-builder.cc +++ b/src/compiler/property-access-builder.cc @@ -127,7 +127,7 @@ Node* PropertyAccessBuilder::ResolveHolder( PropertyAccessInfo const& access_info, Node* receiver) { Handle holder; if (access_info.holder().ToHandle(&holder)) { - return jsgraph()->Constant(holder); + return jsgraph()->Constant(ObjectRef(broker(), holder)); } return receiver; } @@ -151,7 +151,16 @@ MachineRepresentation PropertyAccessBuilder::ConvertRepresentation( Node* PropertyAccessBuilder::TryBuildLoadConstantDataField( NameRef const& name, PropertyAccessInfo const& access_info, Node* receiver) { + // TODO(neis): Eliminate FastPropertyAt call below by doing the lookup during + // acccess info computation. Requires extra care in the case where the + // receiver is the holder. + AllowCodeDependencyChange dependency_change_; + AllowHandleAllocation handle_allocation_; + AllowHandleDereference handle_dereference_; + AllowHeapAllocation heap_allocation_; + if (!access_info.IsDataConstant()) return nullptr; + // First, determine if we have a constant holder to load from. Handle holder; // If {access_info} has a holder, just use it. @@ -165,7 +174,7 @@ Node* PropertyAccessBuilder::TryBuildLoadConstantDataField( MapRef receiver_map = m.Ref(broker()).map(); if (std::find_if(access_info.receiver_maps().begin(), access_info.receiver_maps().end(), [&](Handle map) { - return map.address() == receiver_map.object().address(); + return map.equals(receiver_map.object()); }) == access_info.receiver_maps().end()) { // The map of the receiver is not in the feedback, let us bail out. return nullptr; diff --git a/src/compiler/serializer-for-background-compilation.cc b/src/compiler/serializer-for-background-compilation.cc index 5a39792f3b..7bf5cc0a7e 100644 --- a/src/compiler/serializer-for-background-compilation.cc +++ b/src/compiler/serializer-for-background-compilation.cc @@ -1075,9 +1075,10 @@ MapHandles GetRelevantReceiverMaps(Isolate* isolate, MapContainer const& maps) { ElementAccessFeedback const* SerializerForBackgroundCompilation::ProcessFeedbackMapsForElementAccess( - const MapHandles& maps, AccessMode mode) { + const MapHandles& maps, AccessMode mode, + KeyedAccessMode const& keyed_mode) { ElementAccessFeedback const* result = - broker()->ProcessFeedbackMapsForElementAccess(maps); + broker()->ProcessFeedbackMapsForElementAccess(maps, keyed_mode); for (ElementAccessFeedback::MapIterator it = result->all_maps(broker()); !it.done(); it.advance()) { switch (mode) { @@ -1145,8 +1146,10 @@ void SerializerForBackgroundCompilation::ProcessFeedbackForPropertyAccess( static_name.has_value() ? static_name : broker()->GetNameFeedback(nexus); if (name.has_value()) { processed = ProcessFeedbackMapsForNamedAccess(maps, mode, *name); - } else if (nexus.GetKeyType() == ELEMENT && nexus.ic_state() != MEGAMORPHIC) { - processed = ProcessFeedbackMapsForElementAccess(maps, mode); + } else if (nexus.GetKeyType() == ELEMENT) { + DCHECK_NE(nexus.ic_state(), MEGAMORPHIC); + processed = ProcessFeedbackMapsForElementAccess( + maps, mode, KeyedAccessMode::FromNexus(nexus)); } broker()->SetFeedback(source, processed); } diff --git a/src/compiler/serializer-for-background-compilation.h b/src/compiler/serializer-for-background-compilation.h index fb1665d74d..049f5f3c29 100644 --- a/src/compiler/serializer-for-background-compilation.h +++ b/src/compiler/serializer-for-background-compilation.h @@ -341,7 +341,8 @@ class SerializerForBackgroundCompilation { NamedAccessFeedback const* ProcessFeedbackMapsForNamedAccess( const MapHandles& maps, AccessMode mode, NameRef const& name); ElementAccessFeedback const* ProcessFeedbackMapsForElementAccess( - const MapHandles& maps, AccessMode mode); + const MapHandles& maps, AccessMode mode, + KeyedAccessMode const& keyed_mode); void ProcessFeedbackForPropertyAccess(FeedbackSlot slot, AccessMode mode, base::Optional static_name); void ProcessMapForNamedPropertyAccess(MapRef const& map, NameRef const& name);