[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:
bmeurer 2015-11-02 00:55:02 -08:00 committed by Commit bot
parent f8a43459d4
commit 7fcad712ed
4 changed files with 109 additions and 3 deletions

View File

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

View File

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

View File

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

View File

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