[turbofan] Add support for keyed access to named properties.
The compiler can generate a named access for o[x] if x is a compile time constant that can be turned into a name using ToName (limited to primitive x values, because other ToName invocations might be observable), or the KeyedLoadIC/KeyedStoreIC have gather constant name feedback for x (i.e. the access always goes to the same symbol). R=jarin@chromium.org BUG=v8:4470 LOG=n Review URL: https://codereview.chromium.org/1414013004 Cr-Commit-Position: refs/heads/master@{#31703}
This commit is contained in:
parent
f8a43459d4
commit
7fcad712ed
@ -57,6 +57,10 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) {
|
||||
return ReduceJSLoadNamed(node);
|
||||
case IrOpcode::kJSStoreNamed:
|
||||
return ReduceJSStoreNamed(node);
|
||||
case IrOpcode::kJSLoadProperty:
|
||||
return ReduceJSLoadProperty(node);
|
||||
case IrOpcode::kJSStoreProperty:
|
||||
return ReduceJSStoreProperty(node);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -304,9 +308,11 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) {
|
||||
Reduction JSNativeContextSpecialization::ReduceNamedAccess(
|
||||
Node* node, Node* value, MapHandleList const& receiver_maps,
|
||||
Handle<Name> name, PropertyAccessMode access_mode,
|
||||
LanguageMode language_mode) {
|
||||
LanguageMode language_mode, Node* index) {
|
||||
DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
|
||||
node->opcode() == IrOpcode::kJSStoreNamed);
|
||||
node->opcode() == IrOpcode::kJSStoreNamed ||
|
||||
node->opcode() == IrOpcode::kJSLoadProperty ||
|
||||
node->opcode() == IrOpcode::kJSStoreProperty);
|
||||
Node* receiver = NodeProperties::GetValueInput(node, 0);
|
||||
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
|
||||
Node* effect = NodeProperties::GetEffectInput(node);
|
||||
@ -336,6 +342,16 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
|
||||
Node* const exit_effect = effect;
|
||||
ZoneVector<Node*> exit_controls(zone());
|
||||
|
||||
// Ensure that {index} matches the specified {name} (if {index} is given).
|
||||
if (index != nullptr) {
|
||||
Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Name()),
|
||||
index, jsgraph()->HeapConstant(name));
|
||||
Node* branch =
|
||||
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
|
||||
exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch));
|
||||
control = graph()->NewNode(common()->IfTrue(), branch);
|
||||
}
|
||||
|
||||
// Ensure that {receiver} is a heap object.
|
||||
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
|
||||
Node* branch =
|
||||
@ -653,6 +669,80 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) {
|
||||
}
|
||||
|
||||
|
||||
Reduction JSNativeContextSpecialization::ReduceKeyedAccess(
|
||||
Node* node, Node* index, Node* value, FeedbackNexus const& nexus,
|
||||
PropertyAccessMode access_mode, LanguageMode language_mode) {
|
||||
DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
|
||||
node->opcode() == IrOpcode::kJSStoreProperty);
|
||||
|
||||
// Extract receiver maps from the {nexus}.
|
||||
MapHandleList receiver_maps;
|
||||
if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange();
|
||||
DCHECK_LT(0, receiver_maps.length());
|
||||
|
||||
// Optimize access for constant {index}.
|
||||
HeapObjectMatcher mindex(index);
|
||||
if (mindex.HasValue() && mindex.Value()->IsPrimitive()) {
|
||||
// Keyed access requires a ToPropertyKey on the {index} first before
|
||||
// looking up the property on the object (see ES6 section 12.3.2.1).
|
||||
// We can only do this for non-observable ToPropertyKey invocations,
|
||||
// so we limit the constant indices to primitives at this point.
|
||||
Handle<Name> name;
|
||||
if (Object::ToName(isolate(), mindex.Value()).ToHandle(&name)) {
|
||||
uint32_t array_index;
|
||||
if (name->AsArrayIndex(&array_index)) {
|
||||
// TODO(bmeurer): Optimize element access with constant {index}.
|
||||
} else {
|
||||
name = factory()->InternalizeName(name);
|
||||
return ReduceNamedAccess(node, value, receiver_maps, name, access_mode,
|
||||
language_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
|
||||
// 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, PropertyAccessMode::kLoad,
|
||||
p.language_mode());
|
||||
}
|
||||
|
||||
|
||||
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.
|
||||
if (!p.feedback().IsValid()) return NoChange();
|
||||
KeyedStoreICNexus nexus(p.feedback().vector(), p.feedback().slot());
|
||||
|
||||
// Try to lower the keyed access based on the {nexus}.
|
||||
return ReduceKeyedAccess(node, index, value, nexus,
|
||||
PropertyAccessMode::kStore, p.language_mode());
|
||||
}
|
||||
|
||||
|
||||
Reduction JSNativeContextSpecialization::Replace(Node* node,
|
||||
Handle<Object> value) {
|
||||
return Replace(node, jsgraph()->Constant(value));
|
||||
|
@ -16,6 +16,7 @@ namespace internal {
|
||||
// Forward declarations.
|
||||
class CompilationDependencies;
|
||||
class Factory;
|
||||
class FeedbackNexus;
|
||||
class TypeCache;
|
||||
|
||||
|
||||
@ -54,6 +55,8 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
|
||||
Reduction ReduceJSStoreGlobal(Node* node);
|
||||
Reduction ReduceJSLoadNamed(Node* node);
|
||||
Reduction ReduceJSStoreNamed(Node* node);
|
||||
Reduction ReduceJSLoadProperty(Node* node);
|
||||
Reduction ReduceJSStoreProperty(Node* node);
|
||||
|
||||
Reduction Replace(Node* node, Node* value, Node* effect = nullptr,
|
||||
Node* control = nullptr) {
|
||||
@ -62,10 +65,15 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
|
||||
}
|
||||
Reduction Replace(Node* node, Handle<Object> value);
|
||||
|
||||
Reduction ReduceKeyedAccess(Node* node, Node* index, Node* value,
|
||||
FeedbackNexus const& nexus,
|
||||
PropertyAccessMode access_mode,
|
||||
LanguageMode language_mode);
|
||||
Reduction ReduceNamedAccess(Node* node, Node* value,
|
||||
MapHandleList const& receiver_maps,
|
||||
Handle<Name> name, PropertyAccessMode access_mode,
|
||||
LanguageMode language_mode);
|
||||
LanguageMode language_mode,
|
||||
Node* index = nullptr);
|
||||
|
||||
struct ScriptContextTableLookupResult;
|
||||
bool LookupInScriptContextTable(Handle<Name> name,
|
||||
|
@ -244,6 +244,12 @@ Handle<String> Factory::InternalizeStringWithKey(StringTableKey* key) {
|
||||
}
|
||||
|
||||
|
||||
Handle<Name> Factory::InternalizeName(Handle<Name> name) {
|
||||
if (name->IsUniqueName()) return name;
|
||||
return InternalizeString(Handle<String>::cast(name));
|
||||
}
|
||||
|
||||
|
||||
MaybeHandle<String> Factory::NewStringFromOneByte(Vector<const uint8_t> string,
|
||||
PretenureFlag pretenure) {
|
||||
int length = string.length();
|
||||
|
@ -80,6 +80,8 @@ class Factory final {
|
||||
template<class StringTableKey>
|
||||
Handle<String> InternalizeStringWithKey(StringTableKey* key);
|
||||
|
||||
Handle<Name> InternalizeName(Handle<Name> name);
|
||||
|
||||
|
||||
// String creation functions. Most of the string creation functions take
|
||||
// a Heap::PretenureFlag argument to optionally request that they be
|
||||
|
Loading…
Reference in New Issue
Block a user