• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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-heap-broker.h"
10 #include "src/compiler/js-operator.h"
11 #include "src/compiler/linkage.h"
12 #include "src/compiler/node-matchers.h"
13 #include "src/compiler/node-properties.h"
14 #include "src/objects/contexts-inl.h"
15 
16 namespace v8 {
17 namespace internal {
18 namespace compiler {
19 
Reduce(Node * node)20 Reduction JSContextSpecialization::Reduce(Node* node) {
21   switch (node->opcode()) {
22     case IrOpcode::kParameter:
23       return ReduceParameter(node);
24     case IrOpcode::kJSLoadContext:
25       return ReduceJSLoadContext(node);
26     case IrOpcode::kJSStoreContext:
27       return ReduceJSStoreContext(node);
28     case IrOpcode::kJSGetImportMeta:
29       return ReduceJSGetImportMeta(node);
30     default:
31       break;
32   }
33   return NoChange();
34 }
35 
ReduceParameter(Node * node)36 Reduction JSContextSpecialization::ReduceParameter(Node* node) {
37   DCHECK_EQ(IrOpcode::kParameter, node->opcode());
38   int const index = ParameterIndexOf(node->op());
39   if (index == Linkage::kJSCallClosureParamIndex) {
40     // Constant-fold the function parameter {node}.
41     Handle<JSFunction> function;
42     if (closure().ToHandle(&function)) {
43       Node* value = jsgraph()->Constant(JSFunctionRef(broker_, function));
44       return Replace(value);
45     }
46   }
47   return NoChange();
48 }
49 
SimplifyJSLoadContext(Node * node,Node * new_context,size_t new_depth)50 Reduction JSContextSpecialization::SimplifyJSLoadContext(Node* node,
51                                                          Node* new_context,
52                                                          size_t new_depth) {
53   DCHECK_EQ(IrOpcode::kJSLoadContext, 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 = jsgraph_->javascript()->LoadContext(
63       new_depth, access.index(), access.immutable());
64   NodeProperties::ReplaceContextInput(node, new_context);
65   NodeProperties::ChangeOp(node, op);
66   return Changed(node);
67 }
68 
SimplifyJSStoreContext(Node * node,Node * new_context,size_t new_depth)69 Reduction JSContextSpecialization::SimplifyJSStoreContext(Node* node,
70                                                           Node* new_context,
71                                                           size_t new_depth) {
72   DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
73   const ContextAccess& access = ContextAccessOf(node->op());
74   DCHECK_LE(new_depth, access.depth());
75 
76   if (new_depth == access.depth() &&
77       new_context == NodeProperties::GetContextInput(node)) {
78     return NoChange();
79   }
80 
81   const Operator* op =
82       jsgraph_->javascript()->StoreContext(new_depth, access.index());
83   NodeProperties::ReplaceContextInput(node, new_context);
84   NodeProperties::ChangeOp(node, op);
85   return Changed(node);
86 }
87 
88 namespace {
89 
IsContextParameter(Node * node)90 bool IsContextParameter(Node* node) {
91   DCHECK_EQ(IrOpcode::kParameter, node->opcode());
92   Node* const start = NodeProperties::GetValueInput(node, 0);
93   DCHECK_EQ(IrOpcode::kStart, start->opcode());
94   int const index = ParameterIndexOf(node->op());
95   // The context is always the last parameter to a JavaScript function, and
96   // {Parameter} indices start at -1, so value outputs of {Start} look like
97   // this: closure, receiver, param0, ..., paramN, context.
98   return index == start->op()->ValueOutputCount() - 2;
99 }
100 
101 // Given a context {node} and the {distance} from that context to the target
102 // context (which we want to read from or store to), try to return a
103 // specialization context.  If successful, update {distance} to whatever
104 // distance remains from the specialization context.
GetSpecializationContext(JSHeapBroker * broker,Node * node,size_t * distance,Maybe<OuterContext> maybe_outer)105 base::Optional<ContextRef> GetSpecializationContext(
106     JSHeapBroker* broker, Node* node, size_t* distance,
107     Maybe<OuterContext> maybe_outer) {
108   switch (node->opcode()) {
109     case IrOpcode::kHeapConstant: {
110       HeapObjectRef object(broker, HeapConstantOf(node->op()));
111       if (object.IsContext()) return object.AsContext();
112       break;
113     }
114     case IrOpcode::kParameter: {
115       OuterContext outer;
116       if (maybe_outer.To(&outer) && IsContextParameter(node) &&
117           *distance >= outer.distance) {
118         *distance -= outer.distance;
119         return ContextRef(broker, outer.context);
120       }
121       break;
122     }
123     default:
124       break;
125   }
126   return base::Optional<ContextRef>();
127 }
128 
129 }  // anonymous namespace
130 
ReduceJSLoadContext(Node * node)131 Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
132   DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
133 
134   const ContextAccess& access = ContextAccessOf(node->op());
135   size_t depth = access.depth();
136 
137   // First walk up the context chain in the graph as far as possible.
138   Node* context = NodeProperties::GetOuterContext(node, &depth);
139 
140   base::Optional<ContextRef> maybe_concrete =
141       GetSpecializationContext(broker(), context, &depth, outer());
142   if (!maybe_concrete.has_value()) {
143     // We do not have a concrete context object, so we can only partially reduce
144     // the load by folding-in the outer context node.
145     return SimplifyJSLoadContext(node, context, depth);
146   }
147 
148   // Now walk up the concrete context chain for the remaining depth.
149   ContextRef concrete = maybe_concrete.value();
150   concrete = concrete.previous(&depth);
151   if (depth > 0) {
152     TRACE_BROKER_MISSING(broker(), "previous value for context " << concrete);
153     return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
154   }
155 
156   if (!access.immutable()) {
157     // We found the requested context object but since the context slot is
158     // mutable we can only partially reduce the load.
159     return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
160   }
161 
162   // This will hold the final value, if we can figure it out.
163   base::Optional<ObjectRef> maybe_value;
164   maybe_value = concrete.get(static_cast<int>(access.index()));
165 
166   if (!maybe_value.has_value()) {
167     TRACE_BROKER_MISSING(broker(), "slot value " << access.index()
168                                                  << " for context "
169                                                  << concrete);
170     return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
171   }
172 
173   if (!maybe_value->IsSmi()) {
174     // Even though the context slot is immutable, the context might have escaped
175     // before the function to which it belongs has initialized the slot.
176     // We must be conservative and check if the value in the slot is currently
177     // the hole or undefined. Only if it is neither of these, can we be sure
178     // that it won't change anymore.
179     OddballType oddball_type = maybe_value->AsHeapObject().map().oddball_type();
180     if (oddball_type == OddballType::kUndefined ||
181         oddball_type == OddballType::kHole) {
182       return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
183     }
184   }
185 
186   // Success. The context load can be replaced with the constant.
187   Node* constant = jsgraph_->Constant(*maybe_value);
188   ReplaceWithValue(node, constant);
189   return Replace(constant);
190 }
191 
192 
ReduceJSStoreContext(Node * node)193 Reduction JSContextSpecialization::ReduceJSStoreContext(Node* node) {
194   DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
195 
196   const ContextAccess& access = ContextAccessOf(node->op());
197   size_t depth = access.depth();
198 
199   // First walk up the context chain in the graph until we reduce the depth to 0
200   // or hit a node that does not have a CreateXYZContext operator.
201   Node* context = NodeProperties::GetOuterContext(node, &depth);
202 
203   base::Optional<ContextRef> maybe_concrete =
204       GetSpecializationContext(broker(), context, &depth, outer());
205   if (!maybe_concrete.has_value()) {
206     // We do not have a concrete context object, so we can only partially reduce
207     // the load by folding-in the outer context node.
208     return SimplifyJSStoreContext(node, context, depth);
209   }
210 
211   // Now walk up the concrete context chain for the remaining depth.
212   ContextRef concrete = maybe_concrete.value();
213   concrete = concrete.previous(&depth);
214   if (depth > 0) {
215     TRACE_BROKER_MISSING(broker(), "previous value for context " << concrete);
216     return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
217   }
218 
219   return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
220 }
221 
GetModuleContext(JSHeapBroker * broker,Node * node,Maybe<OuterContext> maybe_context)222 base::Optional<ContextRef> GetModuleContext(JSHeapBroker* broker, Node* node,
223                                             Maybe<OuterContext> maybe_context) {
224   size_t depth = std::numeric_limits<size_t>::max();
225   Node* context = NodeProperties::GetOuterContext(node, &depth);
226 
227   auto find_context = [](ContextRef c) {
228     while (c.map().instance_type() != MODULE_CONTEXT_TYPE) {
229       size_t depth = 1;
230       c = c.previous(&depth);
231       CHECK_EQ(depth, 0);
232     }
233     return c;
234   };
235 
236   switch (context->opcode()) {
237     case IrOpcode::kHeapConstant: {
238       HeapObjectRef object(broker, HeapConstantOf(context->op()));
239       if (object.IsContext()) {
240         return find_context(object.AsContext());
241       }
242       break;
243     }
244     case IrOpcode::kParameter: {
245       OuterContext outer;
246       if (maybe_context.To(&outer) && IsContextParameter(context)) {
247         return find_context(ContextRef(broker, outer.context));
248       }
249       break;
250     }
251     default:
252       break;
253   }
254 
255   return base::Optional<ContextRef>();
256 }
257 
ReduceJSGetImportMeta(Node * node)258 Reduction JSContextSpecialization::ReduceJSGetImportMeta(Node* node) {
259   base::Optional<ContextRef> maybe_context =
260       GetModuleContext(broker(), node, outer());
261   if (!maybe_context.has_value()) return NoChange();
262 
263   ContextRef context = maybe_context.value();
264   SourceTextModuleRef module =
265       context.get(Context::EXTENSION_INDEX).value().AsSourceTextModule();
266   ObjectRef import_meta = module.import_meta();
267   if (import_meta.IsJSObject()) {
268     Node* import_meta_const = jsgraph()->Constant(import_meta);
269     ReplaceWithValue(node, import_meta_const);
270     return Changed(import_meta_const);
271   } else {
272     DCHECK(import_meta.IsTheHole());
273     // The import.meta object has not yet been created. Let JSGenericLowering
274     // replace the operator with a runtime call.
275     return NoChange();
276   }
277 }
278 
isolate() const279 Isolate* JSContextSpecialization::isolate() const {
280   return jsgraph()->isolate();
281 }
282 
283 }  // namespace compiler
284 }  // namespace internal
285 }  // namespace v8
286