Revision: 22993
Author: [email protected]
Date: Fri Aug 8 11:05:31 2014 UTC
Log: Update and extend context specialization.
* Add store-chain folding to context specialization.
* Replace all uses of context with constant and then use
a visitor to find the places to optimize.
BUG=
[email protected]
Review URL: https://codereview.chromium.org/440053002
http://code.google.com/p/v8/source/detail?r=22993
Modified:
/branches/bleeding_edge/src/compiler/js-context-specialization.cc
/branches/bleeding_edge/src/compiler/js-context-specialization.h
/branches/bleeding_edge/test/cctest/compiler/test-js-context-specialization.cc
=======================================
--- /branches/bleeding_edge/src/compiler/js-context-specialization.cc Mon
Aug 4 11:34:54 2014 UTC
+++ /branches/bleeding_edge/src/compiler/js-context-specialization.cc Fri
Aug 8 11:05:31 2014 UTC
@@ -4,6 +4,7 @@
#include "src/compiler/common-operator.h"
#include "src/compiler/generic-node-inl.h"
+#include "src/compiler/graph-inl.h"
#include "src/compiler/js-context-specialization.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/node-aux-data-inl.h"
@@ -16,12 +17,16 @@
// TODO(titzer): factor this out to a common routine with
js-typed-lowering.
static void ReplaceEffectfulWithValue(Node* node, Node* value) {
- Node* effect = NodeProperties::GetEffectInput(node);
+ Node* effect = NULL;
+ if (OperatorProperties::HasEffectInput(node->op())) {
+ effect = NodeProperties::GetEffectInput(node);
+ }
// Requires distinguishing between value and effect edges.
UseIter iter = node->uses().begin();
while (iter != node->uses().end()) {
if (NodeProperties::IsEffectEdge(iter.edge())) {
+ DCHECK_NE(NULL, effect);
iter = iter.UpdateToAndIncrement(effect);
} else {
iter = iter.UpdateToAndIncrement(value);
@@ -30,33 +35,59 @@
}
-void JSContextSpecializer::SpecializeToContext() {
- ValueMatcher<Handle<Context> > match(context_);
+class ContextSpecializationVisitor : public NullNodeVisitor {
+ public:
+ explicit ContextSpecializationVisitor(JSContextSpecializer* spec)
+ : spec_(spec) {}
- // Iterate over all uses of the context and try to replace {LoadContext}
- // nodes with their values from the constant context.
- UseIter iter = match.node()->uses().begin();
- while (iter != match.node()->uses().end()) {
- Node* use = *iter;
- if (use->opcode() == IrOpcode::kJSLoadContext) {
- Reduction r = ReduceJSLoadContext(use);
- if (r.Changed() && r.replacement() != use) {
- ReplaceEffectfulWithValue(use, r.replacement());
+ GenericGraphVisit::Control Post(Node* node) {
+ switch (node->opcode()) {
+ case IrOpcode::kJSLoadContext: {
+ Reduction r = spec_->ReduceJSLoadContext(node);
+ if (r.Changed() && r.replacement() != node) {
+ ReplaceEffectfulWithValue(node, r.replacement());
+ }
+ break;
}
+ case IrOpcode::kJSStoreContext: {
+ Reduction r = spec_->ReduceJSStoreContext(node);
+ if (r.Changed() && r.replacement() != node) {
+ ReplaceEffectfulWithValue(node, r.replacement());
+ }
+ break;
+ }
+ default:
+ break;
}
- ++iter;
+ return GenericGraphVisit::CONTINUE;
}
+
+ private:
+ JSContextSpecializer* spec_;
+};
+
+
+void JSContextSpecializer::SpecializeToContext() {
+ ReplaceEffectfulWithValue(context_,
jsgraph_->Constant(info_->context()));
+
+ ContextSpecializationVisitor visitor(this);
+ jsgraph_->graph()->VisitNodeInputsFromEnd(&visitor);
}
Reduction JSContextSpecializer::ReduceJSLoadContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
- ContextAccess access =
- static_cast<Operator1<ContextAccess>*>(node->op())->parameter();
+ ValueMatcher<Handle<Context> > match(NodeProperties::GetValueInput(node,
0));
+ // If the context is not constant, no reduction can occur.
+ if (!match.HasValue()) {
+ return Reducer::NoChange();
+ }
+
+ ContextAccess access = OpParameter<ContextAccess>(node);
// Find the right parent context.
- Context* context = *info_->context();
+ Context* context = *match.Value();
for (int i = access.depth(); i > 0; --i) {
context = context->previous();
}
@@ -81,13 +112,46 @@
// before the function to which it belongs has initialized the slot.
// We must be conservative and check if the value in the slot is
currently the
// hole or undefined. If it is neither of these, then it must be
initialized.
- if (value->IsUndefined() || value->IsTheHole()) return
Reducer::NoChange();
+ if (value->IsUndefined() || value->IsTheHole()) {
+ return Reducer::NoChange();
+ }
// Success. The context load can be replaced with the constant.
// TODO(titzer): record the specialization for sharing code across
multiple
// contexts that have the same value in the corresponding context slot.
return Reducer::Replace(jsgraph_->Constant(value));
}
+
+
+Reduction JSContextSpecializer::ReduceJSStoreContext(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
+
+ ValueMatcher<Handle<Context> > match(NodeProperties::GetValueInput(node,
0));
+ // If the context is not constant, no reduction can occur.
+ if (!match.HasValue()) {
+ return Reducer::NoChange();
+ }
+
+ ContextAccess access = OpParameter<ContextAccess>(node);
+
+ // The access does not have to look up a parent, nothing to fold.
+ if (access.depth() == 0) {
+ return Reducer::NoChange();
+ }
+
+ // Find the right parent context.
+ Context* context = *match.Value();
+ for (int i = access.depth(); i > 0; --i) {
+ context = context->previous();
+ }
+
+ Operator* op = jsgraph_->javascript()->StoreContext(0, access.index());
+ node->set_op(op);
+ Handle<Object> new_context_handle = Handle<Object>(context,
info_->isolate());
+ node->ReplaceInput(0, jsgraph_->Constant(new_context_handle));
+
+ return Reducer::Changed(node);
+}
}
}
} // namespace v8::internal::compiler
=======================================
--- /branches/bleeding_edge/src/compiler/js-context-specialization.h Wed
Jul 30 13:54:45 2014 UTC
+++ /branches/bleeding_edge/src/compiler/js-context-specialization.h Fri
Aug 8 11:05:31 2014 UTC
@@ -15,7 +15,7 @@
namespace compiler {
// Specializes a given JSGraph to a given context, potentially constant
folding
-// some {LoadContext} nodes.
+// some {LoadContext} nodes or strength reducing some {StoreContext} nodes.
class JSContextSpecializer {
public:
JSContextSpecializer(CompilationInfo* info, JSGraph* jsgraph, Node*
context)
@@ -23,6 +23,7 @@
void SpecializeToContext();
Reduction ReduceJSLoadContext(Node* node);
+ Reduction ReduceJSStoreContext(Node* node);
private:
CompilationInfo* info_;
=======================================
---
/branches/bleeding_edge/test/cctest/compiler/test-js-context-specialization.cc
Tue Aug 5 08:47:39 2014 UTC
+++
/branches/bleeding_edge/test/cctest/compiler/test-js-context-specialization.cc
Fri Aug 8 11:05:31 2014 UTC
@@ -55,46 +55,45 @@
// Make a context and initialize it a bit for this test.
Handle<Context> native = t.factory()->NewNativeContext();
- Handle<Context> ctx1 = t.factory()->NewNativeContext();
- Handle<Context> ctx2 = t.factory()->NewNativeContext();
- ctx2->set_previous(*ctx1);
- ctx1->set_previous(*native);
+ Handle<Context> subcontext1 = t.factory()->NewNativeContext();
+ Handle<Context> subcontext2 = t.factory()->NewNativeContext();
+ subcontext2->set_previous(*subcontext1);
+ subcontext1->set_previous(*native);
Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!");
const int slot = Context::GLOBAL_OBJECT_INDEX;
native->set(slot, *expected);
Node* const_context = t.jsgraph()->Constant(native);
+ Node* deep_const_context = t.jsgraph()->Constant(subcontext2);
Node* param_context = t.NewNode(t.common()->Parameter(0), start);
JSContextSpecializer spec(t.info(), t.jsgraph(), const_context);
{
// Mutable slot, constant context, depth = 0 => do nothing.
- t.info()->SetContext(native);
Node* load = t.NewNode(t.javascript()->LoadContext(0, 0, false),
- const_context, start, start);
+ const_context, const_context, start);
Reduction r = spec.ReduceJSLoadContext(load);
CHECK(!r.Changed());
}
{
// Mutable slot, non-constant context, depth = 0 => do nothing.
- t.info()->SetContext(native);
Node* load = t.NewNode(t.javascript()->LoadContext(0, 0, false),
- param_context, start, start);
+ param_context, param_context, start);
Reduction r = spec.ReduceJSLoadContext(load);
CHECK(!r.Changed());
}
{
- // Mutable slot, non-constant context, depth > 0 => fold-in parent
context.
- t.info()->SetContext(ctx2);
+ // Mutable slot, constant context, depth > 0 => fold-in parent context.
Node* load = t.NewNode(
t.javascript()->LoadContext(2, Context::GLOBAL_EVAL_FUN_INDEX,
false),
- param_context, start, start);
+ deep_const_context, deep_const_context, start);
Reduction r = spec.ReduceJSLoadContext(load);
CHECK(r.Changed());
- CHECK_EQ(IrOpcode::kHeapConstant,
r.replacement()->InputAt(0)->opcode());
- ValueMatcher<Handle<Context> > match(r.replacement()->InputAt(0));
+ Node* new_context_input =
NodeProperties::GetValueInput(r.replacement(), 0);
+ CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode());
+ ValueMatcher<Handle<Context> > match(new_context_input);
CHECK_EQ(*native, *match.Value());
ContextAccess access = static_cast<Operator1<ContextAccess>*>(
r.replacement()->op())->parameter();
@@ -104,10 +103,9 @@
}
{
- // Immutable slot, constant context => specialize.
- t.info()->SetContext(native);
+ // Immutable slot, constant context, depth = 0 => specialize.
Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true),
- const_context, start, start);
+ const_context, const_context, start);
Reduction r = spec.ReduceJSLoadContext(load);
CHECK(r.Changed());
CHECK(r.replacement() != load);
@@ -116,23 +114,74 @@
CHECK(match.HasValue());
CHECK_EQ(*expected, *match.Value());
}
+
+ // TODO(titzer): test with other kinds of contexts, e.g. a function
context.
+ // TODO(sigurds): test that loads below create context are not optimized
+}
+
+
+TEST(ReduceJSStoreContext) {
+ ContextSpecializationTester t;
+
+ Node* start = t.NewNode(t.common()->Start(0));
+ t.graph()->SetStart(start);
+
+ // Make a context and initialize it a bit for this test.
+ Handle<Context> native = t.factory()->NewNativeContext();
+ Handle<Context> subcontext1 = t.factory()->NewNativeContext();
+ Handle<Context> subcontext2 = t.factory()->NewNativeContext();
+ subcontext2->set_previous(*subcontext1);
+ subcontext1->set_previous(*native);
+ Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!");
+ const int slot = Context::GLOBAL_OBJECT_INDEX;
+ native->set(slot, *expected);
+
+ Node* const_context = t.jsgraph()->Constant(native);
+ Node* deep_const_context = t.jsgraph()->Constant(subcontext2);
+ Node* param_context = t.NewNode(t.common()->Parameter(0), start);
+ JSContextSpecializer spec(t.info(), t.jsgraph(), const_context);
{
- // Immutable slot, non-constant context => specialize.
- t.info()->SetContext(native);
- Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true),
- param_context, start, start);
- Reduction r = spec.ReduceJSLoadContext(load);
- CHECK(r.Changed());
- CHECK(r.replacement() != load);
+ // Mutable slot, constant context, depth = 0 => do nothing.
+ Node* load = t.NewNode(t.javascript()->StoreContext(0, 0),
const_context,
+ const_context, start);
+ Reduction r = spec.ReduceJSStoreContext(load);
+ CHECK(!r.Changed());
+ }
+
+ {
+ // Mutable slot, non-constant context, depth = 0 => do nothing.
+ Node* load = t.NewNode(t.javascript()->StoreContext(0, 0),
param_context,
+ param_context, start);
+ Reduction r = spec.ReduceJSStoreContext(load);
+ CHECK(!r.Changed());
+ }
- ValueMatcher<Handle<Object> > match(r.replacement());
- CHECK(match.HasValue());
- CHECK_EQ(*expected, *match.Value());
+ {
+ // Immutable slot, constant context, depth = 0 => do nothing.
+ Node* load = t.NewNode(t.javascript()->StoreContext(0, slot),
const_context,
+ const_context, start);
+ Reduction r = spec.ReduceJSStoreContext(load);
+ CHECK(!r.Changed());
}
- // TODO(titzer): test with other kinds of contexts, e.g. a function
context.
- // TODO(sigurds): test that loads below create context are not optimized
+ {
+ // Mutable slot, constant context, depth > 0 => fold-in parent context.
+ Node* load = t.NewNode(
+ t.javascript()->StoreContext(2, Context::GLOBAL_EVAL_FUN_INDEX),
+ deep_const_context, deep_const_context, start);
+ Reduction r = spec.ReduceJSStoreContext(load);
+ CHECK(r.Changed());
+ Node* new_context_input =
NodeProperties::GetValueInput(r.replacement(), 0);
+ CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode());
+ ValueMatcher<Handle<Context> > match(new_context_input);
+ CHECK_EQ(*native, *match.Value());
+ ContextAccess access = static_cast<Operator1<ContextAccess>*>(
+ r.replacement()->op())->parameter();
+ CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, access.index());
+ CHECK_EQ(0, access.depth());
+ CHECK_EQ(false, access.immutable());
+ }
}
@@ -162,17 +211,25 @@
{
// Check that SpecializeToContext() replaces values and forwards
effects
// correctly, and folds values from constant and non-constant contexts
- Node* effect_in = t.NewNode(t.common()->Start(0));
+ Node* effect_in = start;
Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true),
- const_context, const_context, effect_in, start);
+ const_context, const_context, effect_in);
Node* value_use = t.ChangeTaggedToInt32(load);
Node* other_load = t.NewNode(t.javascript()->LoadContext(0, slot,
true),
- param_context, param_context, load,
start);
+ param_context, param_context, load);
Node* effect_use = other_load;
Node* other_use = t.ChangeTaggedToInt32(other_load);
+ Node* add = t.NewNode(t.javascript()->Add(), value_use, other_use,
+ param_context, other_load, start);
+
+ Node* ret = t.NewNode(t.common()->Return(), add, effect_use, start);
+ Node* end = t.NewNode(t.common()->End(), ret);
+ USE(end);
+ t.graph()->SetEnd(end);
+
// Double check the above graph is what we expect, or the test is
broken.
CheckEffectInput(effect_in, load);
CheckEffectInput(load, effect_use);
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.