V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
js-context-specialization.cc
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/linkage.h"
11 #include "src/compiler/node-matchers.h"
12 #include "src/compiler/node-properties.h"
13 #include "src/contexts-inl.h"
14 
15 namespace v8 {
16 namespace internal {
17 namespace compiler {
18 
19 Reduction JSContextSpecialization::Reduce(Node* node) {
20  switch (node->opcode()) {
21  case IrOpcode::kParameter:
22  return ReduceParameter(node);
23  case IrOpcode::kJSLoadContext:
24  return ReduceJSLoadContext(node);
25  case IrOpcode::kJSStoreContext:
26  return ReduceJSStoreContext(node);
27  default:
28  break;
29  }
30  return NoChange();
31 }
32 
33 Reduction JSContextSpecialization::ReduceParameter(Node* node) {
34  DCHECK_EQ(IrOpcode::kParameter, node->opcode());
35  int const index = ParameterIndexOf(node->op());
36  if (index == Linkage::kJSCallClosureParamIndex) {
37  // Constant-fold the function parameter {node}.
38  Handle<JSFunction> function;
39  if (closure().ToHandle(&function)) {
40  Node* value = jsgraph()->HeapConstant(function);
41  return Replace(value);
42  }
43  }
44  return NoChange();
45 }
46 
47 Reduction JSContextSpecialization::SimplifyJSLoadContext(Node* node,
48  Node* new_context,
49  size_t new_depth) {
50  DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
51  const ContextAccess& access = ContextAccessOf(node->op());
52  DCHECK_LE(new_depth, access.depth());
53 
54  if (new_depth == access.depth() &&
55  new_context == NodeProperties::GetContextInput(node)) {
56  return NoChange();
57  }
58 
59  const Operator* op = jsgraph_->javascript()->LoadContext(
60  new_depth, access.index(), access.immutable());
61  NodeProperties::ReplaceContextInput(node, new_context);
62  NodeProperties::ChangeOp(node, op);
63  return Changed(node);
64 }
65 
66 Reduction JSContextSpecialization::SimplifyJSStoreContext(Node* node,
67  Node* new_context,
68  size_t new_depth) {
69  DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
70  const ContextAccess& access = ContextAccessOf(node->op());
71  DCHECK_LE(new_depth, access.depth());
72 
73  if (new_depth == access.depth() &&
74  new_context == NodeProperties::GetContextInput(node)) {
75  return NoChange();
76  }
77 
78  const Operator* op =
79  jsgraph_->javascript()->StoreContext(new_depth, access.index());
80  NodeProperties::ReplaceContextInput(node, new_context);
81  NodeProperties::ChangeOp(node, op);
82  return Changed(node);
83 }
84 
85 namespace {
86 
87 bool IsContextParameter(Node* node) {
88  DCHECK_EQ(IrOpcode::kParameter, node->opcode());
89  Node* const start = NodeProperties::GetValueInput(node, 0);
90  DCHECK_EQ(IrOpcode::kStart, start->opcode());
91  int const index = ParameterIndexOf(node->op());
92  // The context is always the last parameter to a JavaScript function, and
93  // {Parameter} indices start at -1, so value outputs of {Start} look like
94  // this: closure, receiver, param0, ..., paramN, context.
95  return index == start->op()->ValueOutputCount() - 2;
96 }
97 
98 // Given a context {node} and the {distance} from that context to the target
99 // context (which we want to read from or store to), try to return a
100 // specialization context. If successful, update {distance} to whatever
101 // distance remains from the specialization context.
102 base::Optional<ContextRef> GetSpecializationContext(
103  JSHeapBroker* broker, Node* node, size_t* distance,
104  Maybe<OuterContext> maybe_outer) {
105  switch (node->opcode()) {
106  case IrOpcode::kHeapConstant: {
107  HeapObjectRef object(broker, HeapConstantOf(node->op()));
108  if (object.IsContext()) return object.AsContext();
109  break;
110  }
111  case IrOpcode::kParameter: {
112  OuterContext outer;
113  if (maybe_outer.To(&outer) && IsContextParameter(node) &&
114  *distance >= outer.distance) {
115  *distance -= outer.distance;
116  return ContextRef(broker, outer.context);
117  }
118  break;
119  }
120  default:
121  break;
122  }
123  return base::Optional<ContextRef>();
124 }
125 
126 } // anonymous namespace
127 
128 Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
129  DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
130 
131  const ContextAccess& access = ContextAccessOf(node->op());
132  size_t depth = access.depth();
133 
134  // First walk up the context chain in the graph as far as possible.
135  Node* context = NodeProperties::GetOuterContext(node, &depth);
136 
137  base::Optional<ContextRef> maybe_concrete =
138  GetSpecializationContext(broker(), context, &depth, outer());
139  if (!maybe_concrete.has_value()) {
140  // We do not have a concrete context object, so we can only partially reduce
141  // the load by folding-in the outer context node.
142  return SimplifyJSLoadContext(node, context, depth);
143  }
144 
145  // Now walk up the concrete context chain for the remaining depth.
146  ContextRef concrete = maybe_concrete.value();
147  concrete.Serialize(); // TODO(neis): Remove later.
148  for (; depth > 0; --depth) {
149  concrete = concrete.previous();
150  }
151 
152  if (!access.immutable()) {
153  // We found the requested context object but since the context slot is
154  // mutable we can only partially reduce the load.
155  return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
156  }
157 
158  // This will hold the final value, if we can figure it out.
159  base::Optional<ObjectRef> maybe_value;
160 
161  maybe_value = concrete.get(static_cast<int>(access.index()));
162  if (maybe_value.has_value() && !maybe_value->IsSmi()) {
163  // Even though the context slot is immutable, the context might have escaped
164  // before the function to which it belongs has initialized the slot.
165  // We must be conservative and check if the value in the slot is currently
166  // the hole or undefined. Only if it is neither of these, can we be sure
167  // that it won't change anymore.
168  OddballType oddball_type = maybe_value->AsHeapObject().map().oddball_type();
169  if (oddball_type == OddballType::kUndefined ||
170  oddball_type == OddballType::kHole) {
171  maybe_value.reset();
172  }
173  }
174 
175  if (!maybe_value.has_value()) {
176  return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
177  }
178 
179  // Success. The context load can be replaced with the constant.
180  // TODO(titzer): record the specialization for sharing code across
181  // multiple contexts that have the same value in the corresponding context
182  // slot.
183  Node* constant = jsgraph_->Constant(*maybe_value);
184  ReplaceWithValue(node, constant);
185  return Replace(constant);
186 }
187 
188 
189 Reduction JSContextSpecialization::ReduceJSStoreContext(Node* node) {
190  DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
191 
192  const ContextAccess& access = ContextAccessOf(node->op());
193  size_t depth = access.depth();
194 
195  // First walk up the context chain in the graph until we reduce the depth to 0
196  // or hit a node that does not have a CreateXYZContext operator.
197  Node* context = NodeProperties::GetOuterContext(node, &depth);
198 
199  base::Optional<ContextRef> maybe_concrete =
200  GetSpecializationContext(broker(), context, &depth, outer());
201  if (!maybe_concrete.has_value()) {
202  // We do not have a concrete context object, so we can only partially reduce
203  // the load by folding-in the outer context node.
204  return SimplifyJSStoreContext(node, context, depth);
205  }
206 
207  // Now walk up the concrete context chain for the remaining depth.
208  ContextRef concrete = maybe_concrete.value();
209  concrete.Serialize(); // TODO(neis): Remove later.
210  for (; depth > 0; --depth) {
211  concrete = concrete.previous();
212  }
213 
214  return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
215 }
216 
217 
218 Isolate* JSContextSpecialization::isolate() const {
219  return jsgraph()->isolate();
220 }
221 
222 } // namespace compiler
223 } // namespace internal
224 } // namespace v8
Definition: libplatform.h:13