From 7fcad712ed65ef2811944ef8902106f675956cd5 Mon Sep 17 00:00:00 2001 From: bmeurer Date: Mon, 2 Nov 2015 00:55:02 -0800 Subject: [PATCH] [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} --- .../js-native-context-specialization.cc | 94 ++++++++++++++++++- .../js-native-context-specialization.h | 10 +- src/factory.cc | 6 ++ src/factory.h | 2 + 4 files changed, 109 insertions(+), 3 deletions(-) diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc index 5502953488..fa3aaf006d 100644 --- a/src/compiler/js-native-context-specialization.cc +++ b/src/compiler/js-native-context-specialization.cc @@ -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, 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 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; + 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 value) { return Replace(node, jsgraph()->Constant(value)); diff --git a/src/compiler/js-native-context-specialization.h b/src/compiler/js-native-context-specialization.h index cd48be765d..e2ba62441d 100644 --- a/src/compiler/js-native-context-specialization.h +++ b/src/compiler/js-native-context-specialization.h @@ -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 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, PropertyAccessMode access_mode, - LanguageMode language_mode); + LanguageMode language_mode, + Node* index = nullptr); struct ScriptContextTableLookupResult; bool LookupInScriptContextTable(Handle name, diff --git a/src/factory.cc b/src/factory.cc index abd805e572..7f625d7260 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -244,6 +244,12 @@ Handle Factory::InternalizeStringWithKey(StringTableKey* key) { } +Handle Factory::InternalizeName(Handle name) { + if (name->IsUniqueName()) return name; + return InternalizeString(Handle::cast(name)); +} + + MaybeHandle Factory::NewStringFromOneByte(Vector string, PretenureFlag pretenure) { int length = string.length(); diff --git a/src/factory.h b/src/factory.h index 0828618606..28a0579500 100644 --- a/src/factory.h +++ b/src/factory.h @@ -80,6 +80,8 @@ class Factory final { template Handle InternalizeStringWithKey(StringTableKey* key); + Handle InternalizeName(Handle name); + // String creation functions. Most of the string creation functions take // a Heap::PretenureFlag argument to optionally request that they be