[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 <neis@chromium.org>
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62310}
This commit is contained in:
Georg Neis 2019-06-21 13:30:18 +02:00 committed by Commit Bot
parent dd65ef6a9a
commit 02892ad221
10 changed files with 241 additions and 192 deletions

View File

@ -258,9 +258,9 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
}
}
Handle<Cell> PropertyAccessInfo::export_cell() const {
CellRef PropertyAccessInfo::export_cell(JSHeapBroker* broker) const {
DCHECK_EQ(kModuleExport, kind_);
return Handle<Cell>::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.

View File

@ -127,7 +127,7 @@ class PropertyAccessInfo final {
ZoneVector<Handle<Map>> const& receiver_maps() const {
return receiver_maps_;
}
Handle<Cell> export_cell() const;
CellRef export_cell(JSHeapBroker* broker) const;
private:
explicit PropertyAccessInfo(Zone* zone);

View File

@ -565,6 +565,11 @@ namespace {
// This function expects to never see a JSProxy.
void DependOnStablePrototypeChain(CompilationDependencies* deps, MapRef map,
base::Optional<JSObjectRef> 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();

View File

@ -3492,10 +3492,67 @@ base::Optional<ObjectRef> 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> map : maps) {

View File

@ -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<Handle<Map>> receiver_maps;
ZoneVector<std::pair<Handle<Map>, Handle<Map>>> 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);

View File

@ -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<JSTypedArrayRef> 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<NameRef> 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<Node*>* 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<Node*>* 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);

View File

@ -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<NameRef> 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<NameRef> 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,

View File

@ -127,7 +127,7 @@ Node* PropertyAccessBuilder::ResolveHolder(
PropertyAccessInfo const& access_info, Node* receiver) {
Handle<JSObject> 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<JSObject> 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> 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;

View File

@ -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);
}

View File

@ -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<NameRef> static_name);
void ProcessMapForNamedPropertyAccess(MapRef const& map, NameRef const& name);