1// Copyright 2014 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/compiler/js-context-specialization.h"
6
7#include "src/compiler/common-operator.h"
8#include "src/compiler/js-graph.h"
9#include "src/compiler/js-operator.h"
10#include "src/compiler/node-matchers.h"
11#include "src/compiler/node-properties.h"
12#include "src/contexts.h"
13#include "src/objects-inl.h"
14
15namespace v8 {
16namespace internal {
17namespace compiler {
18
19Reduction JSContextSpecialization::Reduce(Node* node) {
20  switch (node->opcode()) {
21    case IrOpcode::kJSLoadContext:
22      return ReduceJSLoadContext(node);
23    case IrOpcode::kJSStoreContext:
24      return ReduceJSStoreContext(node);
25    default:
26      break;
27  }
28  return NoChange();
29}
30
31Reduction JSContextSpecialization::SimplifyJSLoadContext(Node* node,
32                                                         Node* new_context,
33                                                         size_t new_depth) {
34  DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
35  const ContextAccess& access = ContextAccessOf(node->op());
36  DCHECK_LE(new_depth, access.depth());
37
38  if (new_depth == access.depth() &&
39      new_context == NodeProperties::GetContextInput(node)) {
40    return NoChange();
41  }
42
43  const Operator* op = jsgraph_->javascript()->LoadContext(
44      new_depth, access.index(), access.immutable());
45  NodeProperties::ReplaceContextInput(node, new_context);
46  NodeProperties::ChangeOp(node, op);
47  return Changed(node);
48}
49
50Reduction JSContextSpecialization::SimplifyJSStoreContext(Node* node,
51                                                          Node* new_context,
52                                                          size_t new_depth) {
53  DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
54  const ContextAccess& access = ContextAccessOf(node->op());
55  DCHECK_LE(new_depth, access.depth());
56
57  if (new_depth == access.depth() &&
58      new_context == NodeProperties::GetContextInput(node)) {
59    return NoChange();
60  }
61
62  const Operator* op =
63      jsgraph_->javascript()->StoreContext(new_depth, access.index());
64  NodeProperties::ReplaceContextInput(node, new_context);
65  NodeProperties::ChangeOp(node, op);
66  return Changed(node);
67}
68
69Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
70  DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
71
72  const ContextAccess& access = ContextAccessOf(node->op());
73  size_t depth = access.depth();
74
75  // First walk up the context chain in the graph as far as possible.
76  Node* outer = NodeProperties::GetOuterContext(node, &depth);
77
78  Handle<Context> concrete;
79  if (!NodeProperties::GetSpecializationContext(outer, context())
80           .ToHandle(&concrete)) {
81    // We do not have a concrete context object, so we can only partially reduce
82    // the load by folding-in the outer context node.
83    return SimplifyJSLoadContext(node, outer, depth);
84  }
85
86  // Now walk up the concrete context chain for the remaining depth.
87  for (; depth > 0; --depth) {
88    concrete = handle(concrete->previous(), isolate());
89  }
90
91  if (!access.immutable()) {
92    // We found the requested context object but since the context slot is
93    // mutable we can only partially reduce the load.
94    return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
95  }
96
97  // Even though the context slot is immutable, the context might have escaped
98  // before the function to which it belongs has initialized the slot.
99  // We must be conservative and check if the value in the slot is currently
100  // the hole or undefined. Only if it is neither of these, can we be sure that
101  // it won't change anymore.
102  Handle<Object> value(concrete->get(static_cast<int>(access.index())),
103                       isolate());
104  if (value->IsUndefined(isolate()) || value->IsTheHole(isolate())) {
105    return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
106  }
107
108  // Success. The context load can be replaced with the constant.
109  // TODO(titzer): record the specialization for sharing code across multiple
110  // contexts that have the same value in the corresponding context slot.
111  Node* constant = jsgraph_->Constant(value);
112  ReplaceWithValue(node, constant);
113  return Replace(constant);
114}
115
116
117Reduction JSContextSpecialization::ReduceJSStoreContext(Node* node) {
118  DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
119
120  const ContextAccess& access = ContextAccessOf(node->op());
121  size_t depth = access.depth();
122
123  // First walk up the context chain in the graph until we reduce the depth to 0
124  // or hit a node that does not have a CreateXYZContext operator.
125  Node* outer = NodeProperties::GetOuterContext(node, &depth);
126
127  Handle<Context> concrete;
128  if (!NodeProperties::GetSpecializationContext(outer, context())
129           .ToHandle(&concrete)) {
130    // We do not have a concrete context object, so we can only partially reduce
131    // the load by folding-in the outer context node.
132    return SimplifyJSStoreContext(node, outer, depth);
133  }
134
135  // Now walk up the concrete context chain for the remaining depth.
136  for (; depth > 0; --depth) {
137    concrete = handle(concrete->previous(), isolate());
138  }
139
140  return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
141}
142
143
144Isolate* JSContextSpecialization::isolate() const {
145  return jsgraph()->isolate();
146}
147
148
149JSOperatorBuilder* JSContextSpecialization::javascript() const {
150  return jsgraph()->javascript();
151}
152
153}  // namespace compiler
154}  // namespace internal
155}  // namespace v8
156