Commit b19f118c authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Optimize string "length" property access based on types.

Optimize string "length" property access based on static type
information if possible, but also optimistically optimize the access
based on type feedback from the LoadIC.

R=jarin@chromium.org

Committed: https://crrev.com/17add22ff4b9c5ca638502e7708f0d9d99baca40
Cr-Commit-Position: refs/heads/master@{#29543}

Review URL: https://codereview.chromium.org/1216593003

Cr-Commit-Position: refs/heads/master@{#29588}
parent 3c9c7748
......@@ -687,6 +687,8 @@ source_set("v8_base") {
"src/compiler/js-operator.h",
"src/compiler/js-type-feedback.cc",
"src/compiler/js-type-feedback.h",
"src/compiler/js-type-feedback-lowering.cc",
"src/compiler/js-type-feedback-lowering.h",
"src/compiler/js-typed-lowering.cc",
"src/compiler/js-typed-lowering.h",
"src/compiler/jump-threading.cc",
......
......@@ -115,7 +115,9 @@ FieldAccess AccessBuilder::ForMapInstanceType() {
// static
FieldAccess AccessBuilder::ForStringLength(Zone* zone) {
return {kTaggedBase, String::kLengthOffset, Handle<Name>(),
Type::Range(0, String::kMaxLength, zone), kMachAnyTagged};
Type::Intersect(Type::Range(0, String::kMaxLength, zone),
Type::TaggedSigned(), zone),
kMachAnyTagged};
}
......
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/compiler/js-type-feedback-lowering.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-properties.h"
namespace v8 {
namespace internal {
namespace compiler {
JSTypeFeedbackLowering::JSTypeFeedbackLowering(Editor* editor, Flags flags,
JSGraph* jsgraph)
: AdvancedReducer(editor),
flags_(flags),
jsgraph_(jsgraph),
simplified_(graph()->zone()) {}
Reduction JSTypeFeedbackLowering::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kJSLoadNamed:
return ReduceJSLoadNamed(node);
default:
break;
}
return NoChange();
}
Reduction JSTypeFeedbackLowering::ReduceJSLoadNamed(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 0);
Type* receiver_type = NodeProperties::GetBounds(receiver).upper;
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// We need to make optimistic assumptions to continue.
if (!(flags() & kDeoptimizationEnabled)) return NoChange();
LoadNamedParameters const& p = LoadNamedParametersOf(node->op());
Handle<TypeFeedbackVector> vector;
if (!p.feedback().vector().ToHandle(&vector)) return NoChange();
if (p.name().handle().is_identical_to(factory()->length_string())) {
LoadICNexus nexus(vector, p.feedback().slot());
MapHandleList maps;
if (nexus.ExtractMaps(&maps) > 0) {
for (Handle<Map> map : maps) {
if (map->instance_type() >= FIRST_NONSTRING_TYPE) return NoChange();
}
// Optimistic optimization for "length" property of strings.
if (receiver_type->Maybe(Type::TaggedSigned())) {
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check, control);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state,
effect, if_true);
// TODO(bmeurer): This should be on the AdvancedReducer somehow.
NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
control = graph()->NewNode(common()->IfFalse(), branch);
}
Node* receiver_map = effect =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
receiver, effect, control);
Node* receiver_instance_type = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMapInstanceType()),
receiver_map, effect, control);
Node* check =
graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type,
jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE));
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state,
effect, if_false);
// TODO(bmeurer): This should be on the AdvancedReducer somehow.
NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
control = graph()->NewNode(common()->IfTrue(), branch);
Node* value = effect =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForStringLength(graph()->zone())),
receiver, effect, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
}
return NoChange();
}
Factory* JSTypeFeedbackLowering::factory() const {
return isolate()->factory();
}
CommonOperatorBuilder* JSTypeFeedbackLowering::common() const {
return jsgraph()->common();
}
Graph* JSTypeFeedbackLowering::graph() const { return jsgraph()->graph(); }
Isolate* JSTypeFeedbackLowering::isolate() const {
return jsgraph()->isolate();
}
MachineOperatorBuilder* JSTypeFeedbackLowering::machine() const {
return jsgraph()->machine();
}
} // namespace compiler
} // namespace internal
} // namespace v8
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_COMPILER_JS_TYPE_FEEDBACK_LOWERING_H_
#define V8_COMPILER_JS_TYPE_FEEDBACK_LOWERING_H_
#include "src/base/flags.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/simplified-operator.h"
namespace v8 {
namespace internal {
namespace compiler {
// Forward declarations.
class CommonOperatorBuilder;
class JSGraph;
class MachineOperatorBuilder;
// Lowers JS-level operators to simplified operators based on type feedback.
class JSTypeFeedbackLowering final : public AdvancedReducer {
public:
// Various configuration flags to control the operation of this lowering.
enum Flag {
kNoFlags = 0,
kDeoptimizationEnabled = 1 << 0,
};
typedef base::Flags<Flag> Flags;
JSTypeFeedbackLowering(Editor* editor, Flags flags, JSGraph* jsgraph);
~JSTypeFeedbackLowering() final {}
Reduction Reduce(Node* node) final;
private:
Reduction ReduceJSLoadNamed(Node* node);
Factory* factory() const;
Flags flags() const { return flags_; }
Graph* graph() const;
Isolate* isolate() const;
JSGraph* jsgraph() const { return jsgraph_; }
CommonOperatorBuilder* common() const;
MachineOperatorBuilder* machine() const;
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
Flags const flags_;
JSGraph* const jsgraph_;
SimplifiedOperatorBuilder simplified_;
DISALLOW_COPY_AND_ASSIGN(JSTypeFeedbackLowering);
};
DEFINE_OPERATORS_FOR_FLAGS(JSTypeFeedbackLowering::Flags)
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_JS_TYPE_FEEDBACK_LOWERING_H_
......@@ -797,6 +797,27 @@ Reduction JSTypedLowering::ReduceJSLoadGlobal(Node* node) {
}
Reduction JSTypedLowering::ReduceJSLoadNamed(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 0);
Type* receiver_type = NodeProperties::GetBounds(receiver).upper;
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Handle<Name> name = LoadNamedParametersOf(node->op()).name().handle();
// Optimize "length" property of strings.
if (name.is_identical_to(factory()->length_string()) &&
receiver_type->Is(Type::String())) {
Node* value = effect =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForStringLength(graph()->zone())),
receiver, effect, control);
ReplaceWithValue(node, value, effect);
return Replace(value);
}
return NoChange();
}
Reduction JSTypedLowering::ReduceJSLoadProperty(Node* node) {
Node* key = NodeProperties::GetValueInput(node, 1);
Node* base = NodeProperties::GetValueInput(node, 0);
......@@ -1621,6 +1642,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSToString(node);
case IrOpcode::kJSLoadGlobal:
return ReduceJSLoadGlobal(node);
case IrOpcode::kJSLoadNamed:
return ReduceJSLoadNamed(node);
case IrOpcode::kJSLoadProperty:
return ReduceJSLoadProperty(node);
case IrOpcode::kJSStoreProperty:
......
......@@ -42,6 +42,7 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceJSMultiply(Node* node);
Reduction ReduceJSComparison(Node* node);
Reduction ReduceJSLoadGlobal(Node* node);
Reduction ReduceJSLoadNamed(Node* node);
Reduction ReduceJSLoadProperty(Node* node);
Reduction ReduceJSStoreProperty(Node* node);
Reduction ReduceJSLoadContext(Node* node);
......
......@@ -31,6 +31,7 @@
#include "src/compiler/js-inlining.h"
#include "src/compiler/js-intrinsic-lowering.h"
#include "src/compiler/js-type-feedback.h"
#include "src/compiler/js-type-feedback-lowering.h"
#include "src/compiler/js-typed-lowering.h"
#include "src/compiler/jump-threading.h"
#include "src/compiler/load-elimination.h"
......@@ -577,6 +578,11 @@ struct TypedLoweringPhase {
LoadElimination load_elimination(&graph_reducer);
JSBuiltinReducer builtin_reducer(&graph_reducer, data->jsgraph());
JSTypedLowering typed_lowering(&graph_reducer, data->jsgraph(), temp_zone);
JSTypeFeedbackLowering type_feedback_lowering(
&graph_reducer, data->info()->is_deoptimization_enabled()
? JSTypeFeedbackLowering::kDeoptimizationEnabled
: JSTypeFeedbackLowering::kNoFlags,
data->jsgraph());
JSIntrinsicLowering intrinsic_lowering(
&graph_reducer, data->jsgraph(),
data->info()->is_deoptimization_enabled()
......@@ -588,6 +594,7 @@ struct TypedLoweringPhase {
AddReducer(data, &graph_reducer, &builtin_reducer);
AddReducer(data, &graph_reducer, &typed_lowering);
AddReducer(data, &graph_reducer, &intrinsic_lowering);
AddReducer(data, &graph_reducer, &type_feedback_lowering);
AddReducer(data, &graph_reducer, &load_elimination);
AddReducer(data, &graph_reducer, &common_reducer);
graph_reducer.ReduceGraph();
......
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
assertEquals(0, "".length);
assertEquals(1, "a".length);
assertEquals(2, ("a" + "b").length);
function id(x) { return x; }
function f1(x) {
return x.length;
}
assertEquals(0, f1(""));
assertEquals(1, f1("a"));
%OptimizeFunctionOnNextCall(f1);
assertEquals(2, f1("a" + "b"));
assertEquals(3, f1(id("a") + id("b" + id("c"))))
function f2(x, y, z) {
x = x ? "" + y : "" + z;
return x.length;
}
assertEquals(0, f2(true, "", "a"));
assertEquals(1, f2(false, "", "a"));
%OptimizeFunctionOnNextCall(f2);
assertEquals(0, f2(true, "", "a"));
assertEquals(1, f2(false, "", "a"));
assertEquals(3, f2(true, id("a") + id("b" + id("c")), ""));
......@@ -874,7 +874,11 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArrayWithSafeKey) {
}
TEST_F(JSTypedLoweringTest, JSLoadNamedGlobalConstants) {
// -----------------------------------------------------------------------------
// JSLoadGlobal
TEST_F(JSTypedLoweringTest, JSLoadGlobalConstants) {
Handle<String> names[] = {
Handle<String>(isolate()->heap()->undefined_string(), isolate()),
Handle<String>(isolate()->heap()->infinity_string(), isolate()),
......@@ -906,6 +910,31 @@ TEST_F(JSTypedLoweringTest, JSLoadNamedGlobalConstants) {
}
// -----------------------------------------------------------------------------
// JSLoadNamed
TEST_F(JSTypedLoweringTest, JSLoadNamedStringLength) {
VectorSlotPair feedback;
Unique<Name> name = Unique<Name>::CreateImmovable(factory()->length_string());
Node* const receiver = Parameter(Type::String(), 0);
Node* const vector = Parameter(Type::Internal(), 1);
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
Reduction const r = Reduce(
graph()->NewNode(javascript()->LoadNamed(name, feedback, language_mode),
receiver, vector, context, EmptyFrameState(),
EmptyFrameState(), effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsLoadField(AccessBuilder::ForStringLength(zone()), receiver,
effect, control));
}
}
// -----------------------------------------------------------------------------
// JSLoadDynamicGlobal
......
......@@ -519,6 +519,8 @@
'../../src/compiler/js-operator.h',
'../../src/compiler/js-type-feedback.cc',
'../../src/compiler/js-type-feedback.h',
'../../src/compiler/js-type-feedback-lowering.cc',
'../../src/compiler/js-type-feedback-lowering.h',
'../../src/compiler/js-typed-lowering.cc',
'../../src/compiler/js-typed-lowering.h',
'../../src/compiler/jump-threading.cc',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment