[turbofan] Optimized lowering of DYNAMIC_LOCAL lookup slot loads.
authormstarzinger <mstarzinger@chromium.org>
Mon, 8 Jun 2015 08:59:00 +0000 (01:59 -0700)
committerCommit bot <commit-bot@chromium.org>
Mon, 8 Jun 2015 08:59:05 +0000 (08:59 +0000)
This adds handling of JSLoadDynamicContext nodes to JSTypedLowering to
perform extension checks and an inline fast path. The fast path is a
context slot load targeting a specific context.

R=bmeurer@chromium.org
BUG=v8:4131
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#28823}

src/compiler/ast-graph-builder.cc
src/compiler/js-typed-lowering.cc
src/compiler/js-typed-lowering.h
test/unittests/compiler/js-typed-lowering-unittest.cc
test/unittests/compiler/node-test-utils.cc

index d53d2c347a6a24f8d16c066af1f49c2557f6d8d3..5dc28518325ddadee2249ecdc856316682d0fb5f 100644 (file)
@@ -2968,7 +2968,7 @@ uint32_t AstGraphBuilder::ComputeBitsetForDynamicContext(Variable* variable) {
   EnumSet<int, uint32_t> check_depths;
   for (Scope* s = current_scope(); s != nullptr; s = s->outer_scope()) {
     if (s->num_heap_slots() <= 0) continue;
-    if (!s->calls_sloppy_eval()) continue;
+    if (!s->calls_sloppy_eval() && s != variable->scope()) continue;
     int depth = current_scope()->ContextChainLength(s);
     if (depth > DynamicContextAccess::kMaxCheckDepth) {
       return DynamicContextAccess::kFullCheckRequired;
@@ -3247,7 +3247,15 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
             name, check_bitset, depth, local->index());
         value = NewNode(op, current_context());
         PrepareFrameState(value, bailout_id, combine);
-        // TODO(mstarzinger): Hole checks are missing here when optimized.
+        VariableMode local_mode = local->mode();
+        if (local_mode == CONST_LEGACY) {
+          // Perform check for uninitialized legacy const variables.
+          Node* undefined = jsgraph()->UndefinedConstant();
+          value = BuildHoleCheckSilent(value, undefined, value);
+        } else if (local_mode == LET || local_mode == CONST) {
+          // Perform check for uninitialized let/const variables.
+          value = BuildHoleCheckThrow(value, local, value, bailout_id);
+        }
       } else if (mode == DYNAMIC) {
         uint32_t check_bitset = DynamicGlobalAccess::kFullCheckRequired;
         const Operator* op = javascript()->LoadDynamicGlobal(
index 2f0497f50393415217b011aa4c71a9a1a3b66f8a..1a09ba55b8996943ad1dab7790898277181513fd 100644 (file)
@@ -992,6 +992,63 @@ Reduction JSTypedLowering::ReduceJSLoadDynamicGlobal(Node* node) {
 }
 
 
+Reduction JSTypedLowering::ReduceJSLoadDynamicContext(Node* node) {
+  DCHECK_EQ(IrOpcode::kJSLoadDynamicContext, node->opcode());
+  DynamicContextAccess const& access = DynamicContextAccessOf(node->op());
+  ContextAccess const& context_access = access.context_access();
+  Node* const context = NodeProperties::GetContextInput(node);
+  Node* const state = NodeProperties::GetFrameStateInput(node, 0);
+  Node* const effect = NodeProperties::GetEffectInput(node);
+  Node* const control = NodeProperties::GetControlInput(node);
+  if (access.RequiresFullCheck()) return NoChange();
+
+  // Perform checks whether the fast mode applies, by looking for any extension
+  // object which might shadow the optimistic declaration.
+  uint32_t bitset = access.check_bitset();
+  Node* check_true = control;
+  Node* check_false = graph()->NewNode(common()->Merge(0));
+  for (int depth = 0; bitset != 0; bitset >>= 1, depth++) {
+    if ((bitset & 1) == 0) continue;
+    Node* load = graph()->NewNode(
+        javascript()->LoadContext(depth, Context::EXTENSION_INDEX, false),
+        context, context, effect);
+    Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Tagged()),
+                                   load, jsgraph()->ZeroConstant());
+    Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check,
+                                    check_true);
+    Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+    Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+    check_false->set_op(common()->Merge(check_false->InputCount() + 1));
+    check_false->AppendInput(graph()->zone(), if_false);
+    check_true = if_true;
+  }
+
+  // Fast case, because variable is not shadowed. Perform context slot load.
+  Node* fast =
+      graph()->NewNode(javascript()->LoadContext(context_access.depth(),
+                                                 context_access.index(), false),
+                       context, context, effect);
+
+  // Slow case, because variable potentially shadowed. Perform dynamic lookup.
+  uint32_t check_bitset = DynamicContextAccess::kFullCheckRequired;
+  Node* slow =
+      graph()->NewNode(javascript()->LoadDynamicContext(
+                           access.name(), check_bitset, context_access.depth(),
+                           context_access.index()),
+                       context, context, state, effect, check_false);
+
+  // Replace value, effect and control uses accordingly.
+  Node* new_control =
+      graph()->NewNode(common()->Merge(2), check_true, check_false);
+  Node* new_effect =
+      graph()->NewNode(common()->EffectPhi(2), fast, slow, new_control);
+  Node* new_value = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), fast,
+                                     slow, new_control);
+  ReplaceWithValue(node, new_value, new_effect, new_control);
+  return Changed(new_value);
+}
+
+
 Reduction JSTypedLowering::ReduceJSCreateClosure(Node* node) {
   DCHECK_EQ(IrOpcode::kJSCreateClosure, node->opcode());
   CreateClosureParameters const& p = CreateClosureParametersOf(node->op());
@@ -1501,6 +1558,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
       return ReduceJSStoreContext(node);
     case IrOpcode::kJSLoadDynamicGlobal:
       return ReduceJSLoadDynamicGlobal(node);
+    case IrOpcode::kJSLoadDynamicContext:
+      return ReduceJSLoadDynamicContext(node);
     case IrOpcode::kJSCreateClosure:
       return ReduceJSCreateClosure(node);
     case IrOpcode::kJSCreateLiteralArray:
index ae9cbdd7ea0140e19b14d89a3c4125ddbfc0fcba..260c900860cad606726bc33ab5b730d413300957 100644 (file)
@@ -47,6 +47,7 @@ class JSTypedLowering final : public AdvancedReducer {
   Reduction ReduceJSLoadContext(Node* node);
   Reduction ReduceJSStoreContext(Node* node);
   Reduction ReduceJSLoadDynamicGlobal(Node* node);
+  Reduction ReduceJSLoadDynamicContext(Node* node);
   Reduction ReduceJSEqual(Node* node, bool invert);
   Reduction ReduceJSStrictEqual(Node* node, bool invert);
   Reduction ReduceJSUnaryNot(Node* node);
index 9786ada24bf28aedaaf0e6fab7c03cb96d92d9d6..82c216283880ec536a54fc4a9c7e84ce6f3d8280 100644 (file)
@@ -931,6 +931,40 @@ TEST_F(JSTypedLoweringTest, JSLoadDynamicGlobal) {
   }
 }
 
+
+// -----------------------------------------------------------------------------
+// JSLoadDynamicContext
+
+
+TEST_F(JSTypedLoweringTest, JSLoadDynamicContext) {
+  Node* const context = Parameter(Type::Any());
+  Node* const frame_state = EmptyFrameState();
+  Node* const effect = graph()->start();
+  Node* const control = graph()->start();
+  Handle<String> name = factory()->object_string();
+  for (int i = 0; i < DynamicContextAccess::kMaxCheckDepth; ++i) {
+    uint32_t bitset = 1 << i;  // Only single check.
+    Reduction r = Reduce(
+        graph()->NewNode(javascript()->LoadDynamicContext(name, bitset, 23, 42),
+                         context, context, frame_state, effect, control));
+    ASSERT_TRUE(r.Changed());
+    EXPECT_THAT(
+        r.replacement(),
+        IsPhi(kMachAnyTagged,
+              IsLoadContext(ContextAccess(23, 42, false), context), _,
+              IsMerge(
+                  IsIfTrue(IsBranch(
+                      IsReferenceEqual(
+                          Type::Tagged(),
+                          IsLoadContext(
+                              ContextAccess(i, Context::EXTENSION_INDEX, false),
+                              context),
+                          IsNumberConstant(BitEq(0.0))),
+                      control)),
+                  _)));
+  }
+}
+
 #if V8_TURBOFAN_TARGET
 
 // -----------------------------------------------------------------------------
index d5d4e1a91450f350c877a148450f3701e32b8228..5d01185e29995b8dcc4bde726780b7a5b1ea739d 100644 (file)
@@ -1299,6 +1299,15 @@ class IsLoadContextMatcher final : public NodeMatcher {
         access_matcher_(access_matcher),
         context_matcher_(context_matcher) {}
 
+  void DescribeTo(std::ostream* os) const final {
+    NodeMatcher::DescribeTo(os);
+    *os << " whose access (";
+    access_matcher_.DescribeTo(os);
+    *os << ") and context (";
+    context_matcher_.DescribeTo(os);
+    *os << ")";
+  }
+
   bool MatchAndExplain(Node* node, MatchResultListener* listener) const final {
     return (NodeMatcher::MatchAndExplain(node, listener) &&
             PrintMatchAndExplain(OpParameter<ContextAccess>(node), "access",