Commit 055fe8c3 authored by sigurds@chromium.org's avatar sigurds@chromium.org

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=
R=mstarzinger@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22993 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 49e6abb9
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/compiler/common-operator.h" #include "src/compiler/common-operator.h"
#include "src/compiler/generic-node-inl.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-context-specialization.h"
#include "src/compiler/js-operator.h" #include "src/compiler/js-operator.h"
#include "src/compiler/node-aux-data-inl.h" #include "src/compiler/node-aux-data-inl.h"
...@@ -16,12 +17,16 @@ namespace compiler { ...@@ -16,12 +17,16 @@ namespace compiler {
// TODO(titzer): factor this out to a common routine with js-typed-lowering. // TODO(titzer): factor this out to a common routine with js-typed-lowering.
static void ReplaceEffectfulWithValue(Node* node, Node* value) { 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. // Requires distinguishing between value and effect edges.
UseIter iter = node->uses().begin(); UseIter iter = node->uses().begin();
while (iter != node->uses().end()) { while (iter != node->uses().end()) {
if (NodeProperties::IsEffectEdge(iter.edge())) { if (NodeProperties::IsEffectEdge(iter.edge())) {
DCHECK_NE(NULL, effect);
iter = iter.UpdateToAndIncrement(effect); iter = iter.UpdateToAndIncrement(effect);
} else { } else {
iter = iter.UpdateToAndIncrement(value); iter = iter.UpdateToAndIncrement(value);
...@@ -30,33 +35,59 @@ static void ReplaceEffectfulWithValue(Node* node, Node* value) { ...@@ -30,33 +35,59 @@ static void ReplaceEffectfulWithValue(Node* node, Node* value) {
} }
void JSContextSpecializer::SpecializeToContext() { class ContextSpecializationVisitor : public NullNodeVisitor {
ValueMatcher<Handle<Context> > match(context_); public:
explicit ContextSpecializationVisitor(JSContextSpecializer* spec)
// Iterate over all uses of the context and try to replace {LoadContext} : spec_(spec) {}
// nodes with their values from the constant context.
UseIter iter = match.node()->uses().begin(); GenericGraphVisit::Control Post(Node* node) {
while (iter != match.node()->uses().end()) { switch (node->opcode()) {
Node* use = *iter; case IrOpcode::kJSLoadContext: {
if (use->opcode() == IrOpcode::kJSLoadContext) { Reduction r = spec_->ReduceJSLoadContext(node);
Reduction r = ReduceJSLoadContext(use); if (r.Changed() && r.replacement() != node) {
if (r.Changed() && r.replacement() != use) { ReplaceEffectfulWithValue(node, r.replacement());
ReplaceEffectfulWithValue(use, 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) { Reduction JSContextSpecializer::ReduceJSLoadContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode()); DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
ContextAccess access = ValueMatcher<Handle<Context> > match(NodeProperties::GetValueInput(node, 0));
static_cast<Operator1<ContextAccess>*>(node->op())->parameter(); // 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. // Find the right parent context.
Context* context = *info_->context(); Context* context = *match.Value();
for (int i = access.depth(); i > 0; --i) { for (int i = access.depth(); i > 0; --i) {
context = context->previous(); context = context->previous();
} }
...@@ -81,13 +112,46 @@ Reduction JSContextSpecializer::ReduceJSLoadContext(Node* node) { ...@@ -81,13 +112,46 @@ Reduction JSContextSpecializer::ReduceJSLoadContext(Node* node) {
// before the function to which it belongs has initialized the slot. // 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 // 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. // 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. // Success. The context load can be replaced with the constant.
// TODO(titzer): record the specialization for sharing code across multiple // TODO(titzer): record the specialization for sharing code across multiple
// contexts that have the same value in the corresponding context slot. // contexts that have the same value in the corresponding context slot.
return Reducer::Replace(jsgraph_->Constant(value)); 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 } // namespace v8::internal::compiler
...@@ -15,7 +15,7 @@ namespace internal { ...@@ -15,7 +15,7 @@ namespace internal {
namespace compiler { namespace compiler {
// Specializes a given JSGraph to a given context, potentially constant folding // 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 { class JSContextSpecializer {
public: public:
JSContextSpecializer(CompilationInfo* info, JSGraph* jsgraph, Node* context) JSContextSpecializer(CompilationInfo* info, JSGraph* jsgraph, Node* context)
...@@ -23,6 +23,7 @@ class JSContextSpecializer { ...@@ -23,6 +23,7 @@ class JSContextSpecializer {
void SpecializeToContext(); void SpecializeToContext();
Reduction ReduceJSLoadContext(Node* node); Reduction ReduceJSLoadContext(Node* node);
Reduction ReduceJSStoreContext(Node* node);
private: private:
CompilationInfo* info_; CompilationInfo* info_;
......
...@@ -55,46 +55,45 @@ TEST(ReduceJSLoadContext) { ...@@ -55,46 +55,45 @@ TEST(ReduceJSLoadContext) {
// Make a context and initialize it a bit for this test. // Make a context and initialize it a bit for this test.
Handle<Context> native = t.factory()->NewNativeContext(); Handle<Context> native = t.factory()->NewNativeContext();
Handle<Context> ctx1 = t.factory()->NewNativeContext(); Handle<Context> subcontext1 = t.factory()->NewNativeContext();
Handle<Context> ctx2 = t.factory()->NewNativeContext(); Handle<Context> subcontext2 = t.factory()->NewNativeContext();
ctx2->set_previous(*ctx1); subcontext2->set_previous(*subcontext1);
ctx1->set_previous(*native); subcontext1->set_previous(*native);
Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!"); Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!");
const int slot = Context::GLOBAL_OBJECT_INDEX; const int slot = Context::GLOBAL_OBJECT_INDEX;
native->set(slot, *expected); native->set(slot, *expected);
Node* const_context = t.jsgraph()->Constant(native); 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); Node* param_context = t.NewNode(t.common()->Parameter(0), start);
JSContextSpecializer spec(t.info(), t.jsgraph(), const_context); JSContextSpecializer spec(t.info(), t.jsgraph(), const_context);
{ {
// Mutable slot, constant context, depth = 0 => do nothing. // Mutable slot, constant context, depth = 0 => do nothing.
t.info()->SetContext(native);
Node* load = t.NewNode(t.javascript()->LoadContext(0, 0, false), Node* load = t.NewNode(t.javascript()->LoadContext(0, 0, false),
const_context, start, start); const_context, const_context, start);
Reduction r = spec.ReduceJSLoadContext(load); Reduction r = spec.ReduceJSLoadContext(load);
CHECK(!r.Changed()); CHECK(!r.Changed());
} }
{ {
// Mutable slot, non-constant context, depth = 0 => do nothing. // Mutable slot, non-constant context, depth = 0 => do nothing.
t.info()->SetContext(native);
Node* load = t.NewNode(t.javascript()->LoadContext(0, 0, false), Node* load = t.NewNode(t.javascript()->LoadContext(0, 0, false),
param_context, start, start); param_context, param_context, start);
Reduction r = spec.ReduceJSLoadContext(load); Reduction r = spec.ReduceJSLoadContext(load);
CHECK(!r.Changed()); CHECK(!r.Changed());
} }
{ {
// Mutable slot, non-constant context, depth > 0 => fold-in parent context. // Mutable slot, constant context, depth > 0 => fold-in parent context.
t.info()->SetContext(ctx2);
Node* load = t.NewNode( Node* load = t.NewNode(
t.javascript()->LoadContext(2, Context::GLOBAL_EVAL_FUN_INDEX, false), 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); Reduction r = spec.ReduceJSLoadContext(load);
CHECK(r.Changed()); CHECK(r.Changed());
CHECK_EQ(IrOpcode::kHeapConstant, r.replacement()->InputAt(0)->opcode()); Node* new_context_input = NodeProperties::GetValueInput(r.replacement(), 0);
ValueMatcher<Handle<Context> > match(r.replacement()->InputAt(0)); CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode());
ValueMatcher<Handle<Context> > match(new_context_input);
CHECK_EQ(*native, *match.Value()); CHECK_EQ(*native, *match.Value());
ContextAccess access = static_cast<Operator1<ContextAccess>*>( ContextAccess access = static_cast<Operator1<ContextAccess>*>(
r.replacement()->op())->parameter(); r.replacement()->op())->parameter();
...@@ -104,10 +103,9 @@ TEST(ReduceJSLoadContext) { ...@@ -104,10 +103,9 @@ TEST(ReduceJSLoadContext) {
} }
{ {
// Immutable slot, constant context => specialize. // Immutable slot, constant context, depth = 0 => specialize.
t.info()->SetContext(native);
Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true), Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true),
const_context, start, start); const_context, const_context, start);
Reduction r = spec.ReduceJSLoadContext(load); Reduction r = spec.ReduceJSLoadContext(load);
CHECK(r.Changed()); CHECK(r.Changed());
CHECK(r.replacement() != load); CHECK(r.replacement() != load);
...@@ -117,22 +115,73 @@ TEST(ReduceJSLoadContext) { ...@@ -117,22 +115,73 @@ TEST(ReduceJSLoadContext) {
CHECK_EQ(*expected, *match.Value()); 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. // Mutable slot, constant context, depth = 0 => do nothing.
t.info()->SetContext(native); Node* load = t.NewNode(t.javascript()->StoreContext(0, 0), const_context,
Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true), const_context, start);
param_context, start, start); Reduction r = spec.ReduceJSStoreContext(load);
Reduction r = spec.ReduceJSLoadContext(load); CHECK(!r.Changed());
CHECK(r.Changed()); }
CHECK(r.replacement() != load);
ValueMatcher<Handle<Object> > match(r.replacement()); {
CHECK(match.HasValue()); // Mutable slot, non-constant context, depth = 0 => do nothing.
CHECK_EQ(*expected, *match.Value()); Node* load = t.NewNode(t.javascript()->StoreContext(0, 0), param_context,
param_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 // 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());
}
{
// 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 @@ TEST(SpecializeToContext) { ...@@ -162,17 +211,25 @@ TEST(SpecializeToContext) {
{ {
// Check that SpecializeToContext() replaces values and forwards effects // Check that SpecializeToContext() replaces values and forwards effects
// correctly, and folds values from constant and non-constant contexts // 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), 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* value_use = t.ChangeTaggedToInt32(load);
Node* other_load = t.NewNode(t.javascript()->LoadContext(0, slot, true), 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* effect_use = other_load;
Node* other_use = t.ChangeTaggedToInt32(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. // Double check the above graph is what we expect, or the test is broken.
CheckEffectInput(effect_in, load); CheckEffectInput(effect_in, load);
CheckEffectInput(load, effect_use); CheckEffectInput(load, effect_use);
......
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