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.

Reply via email to