• 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(MakeRef(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   return ParameterIndexOf(node->op()) ==
93          StartNode{NodeProperties::GetValueInput(node, 0)}
94              .ContextParameterIndex_MaybeNonStandardLayout();
95 }
96 
97 // Given a context {node} and the {distance} from that context to the target
98 // context (which we want to read from or store to), try to return a
99 // specialization context.  If successful, update {distance} to whatever
100 // distance remains from the specialization context.
GetSpecializationContext(JSHeapBroker * broker,Node * node,size_t * distance,Maybe<OuterContext> maybe_outer)101 base::Optional<ContextRef> GetSpecializationContext(
102     JSHeapBroker* broker, Node* node, size_t* distance,
103     Maybe<OuterContext> maybe_outer) {
104   switch (node->opcode()) {
105     case IrOpcode::kHeapConstant: {
106       // TODO(jgruber,chromium:1209798): Using kAssumeMemoryFence works around
107       // the fact that the graph stores handles (and not refs). The assumption
108       // is that any handle inserted into the graph is safe to read; but we
109       // don't preserve the reason why it is safe to read. Thus we must
110       // over-approximate here and assume the existence of a memory fence. In
111       // the future, we should consider having the graph store ObjectRefs or
112       // ObjectData pointer instead, which would make new ref construction here
113       // unnecessary.
114       HeapObjectRef object =
115           MakeRefAssumeMemoryFence(broker, HeapConstantOf(node->op()));
116       if (object.IsContext()) return object.AsContext();
117       break;
118     }
119     case IrOpcode::kParameter: {
120       OuterContext outer;
121       if (maybe_outer.To(&outer) && IsContextParameter(node) &&
122           *distance >= outer.distance) {
123         *distance -= outer.distance;
124         return MakeRef(broker, outer.context);
125       }
126       break;
127     }
128     default:
129       break;
130   }
131   return base::Optional<ContextRef>();
132 }
133 
134 }  // anonymous namespace
135 
ReduceJSLoadContext(Node * node)136 Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
137   DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
138 
139   const ContextAccess& access = ContextAccessOf(node->op());
140   size_t depth = access.depth();
141 
142   // First walk up the context chain in the graph as far as possible.
143   Node* context = NodeProperties::GetOuterContext(node, &depth);
144 
145   base::Optional<ContextRef> maybe_concrete =
146       GetSpecializationContext(broker(), context, &depth, outer());
147   if (!maybe_concrete.has_value()) {
148     // We do not have a concrete context object, so we can only partially reduce
149     // the load by folding-in the outer context node.
150     return SimplifyJSLoadContext(node, context, depth);
151   }
152 
153   // Now walk up the concrete context chain for the remaining depth.
154   ContextRef concrete = maybe_concrete.value();
155   concrete = concrete.previous(&depth);
156   if (depth > 0) {
157     TRACE_BROKER_MISSING(broker(), "previous value for context " << concrete);
158     return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
159   }
160 
161   if (!access.immutable()) {
162     // We found the requested context object but since the context slot is
163     // mutable we can only partially reduce the load.
164     return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
165   }
166 
167   // This will hold the final value, if we can figure it out.
168   base::Optional<ObjectRef> maybe_value;
169   maybe_value = concrete.get(static_cast<int>(access.index()));
170 
171   if (!maybe_value.has_value()) {
172     TRACE_BROKER_MISSING(broker(), "slot value " << access.index()
173                                                  << " for context "
174                                                  << concrete);
175     return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
176   }
177 
178   if (!maybe_value->IsSmi()) {
179     // Even though the context slot is immutable, the context might have escaped
180     // before the function to which it belongs has initialized the slot.
181     // We must be conservative and check if the value in the slot is currently
182     // the hole or undefined. Only if it is neither of these, can we be sure
183     // that it won't change anymore.
184     OddballType oddball_type = maybe_value->AsHeapObject().map().oddball_type();
185     if (oddball_type == OddballType::kUndefined ||
186         oddball_type == OddballType::kHole) {
187       return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
188     }
189   }
190 
191   // Success. The context load can be replaced with the constant.
192   Node* constant = jsgraph_->Constant(*maybe_value);
193   ReplaceWithValue(node, constant);
194   return Replace(constant);
195 }
196 
197 
ReduceJSStoreContext(Node * node)198 Reduction JSContextSpecialization::ReduceJSStoreContext(Node* node) {
199   DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
200 
201   const ContextAccess& access = ContextAccessOf(node->op());
202   size_t depth = access.depth();
203 
204   // First walk up the context chain in the graph until we reduce the depth to 0
205   // or hit a node that does not have a CreateXYZContext operator.
206   Node* context = NodeProperties::GetOuterContext(node, &depth);
207 
208   base::Optional<ContextRef> maybe_concrete =
209       GetSpecializationContext(broker(), context, &depth, outer());
210   if (!maybe_concrete.has_value()) {
211     // We do not have a concrete context object, so we can only partially reduce
212     // the load by folding-in the outer context node.
213     return SimplifyJSStoreContext(node, context, depth);
214   }
215 
216   // Now walk up the concrete context chain for the remaining depth.
217   ContextRef concrete = maybe_concrete.value();
218   concrete = concrete.previous(&depth);
219   if (depth > 0) {
220     TRACE_BROKER_MISSING(broker(), "previous value for context " << concrete);
221     return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
222   }
223 
224   return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
225 }
226 
GetModuleContext(JSHeapBroker * broker,Node * node,Maybe<OuterContext> maybe_context)227 base::Optional<ContextRef> GetModuleContext(JSHeapBroker* broker, Node* node,
228                                             Maybe<OuterContext> maybe_context) {
229   size_t depth = std::numeric_limits<size_t>::max();
230   Node* context = NodeProperties::GetOuterContext(node, &depth);
231 
232   auto find_context = [](ContextRef c) {
233     while (c.map().instance_type() != MODULE_CONTEXT_TYPE) {
234       size_t depth = 1;
235       c = c.previous(&depth);
236       CHECK_EQ(depth, 0);
237     }
238     return c;
239   };
240 
241   switch (context->opcode()) {
242     case IrOpcode::kHeapConstant: {
243       // TODO(jgruber,chromium:1209798): Using kAssumeMemoryFence works around
244       // the fact that the graph stores handles (and not refs). The assumption
245       // is that any handle inserted into the graph is safe to read; but we
246       // don't preserve the reason why it is safe to read. Thus we must
247       // over-approximate here and assume the existence of a memory fence. In
248       // the future, we should consider having the graph store ObjectRefs or
249       // ObjectData pointer instead, which would make new ref construction here
250       // unnecessary.
251       HeapObjectRef object =
252           MakeRefAssumeMemoryFence(broker, HeapConstantOf(context->op()));
253       if (object.IsContext()) {
254         return find_context(object.AsContext());
255       }
256       break;
257     }
258     case IrOpcode::kParameter: {
259       OuterContext outer;
260       if (maybe_context.To(&outer) && IsContextParameter(context)) {
261         return find_context(MakeRef(broker, outer.context));
262       }
263       break;
264     }
265     default:
266       break;
267   }
268 
269   return base::Optional<ContextRef>();
270 }
271 
ReduceJSGetImportMeta(Node * node)272 Reduction JSContextSpecialization::ReduceJSGetImportMeta(Node* node) {
273   base::Optional<ContextRef> maybe_context =
274       GetModuleContext(broker(), node, outer());
275   if (!maybe_context.has_value()) return NoChange();
276 
277   ContextRef context = maybe_context.value();
278   base::Optional<ObjectRef> module = context.get(Context::EXTENSION_INDEX);
279   if (!module.has_value()) return NoChange();
280   base::Optional<ObjectRef> import_meta =
281       module->AsSourceTextModule().import_meta();
282   if (!import_meta.has_value()) return NoChange();
283   if (!import_meta->IsJSObject()) {
284     DCHECK(import_meta->IsTheHole());
285     // The import.meta object has not yet been created. Let JSGenericLowering
286     // replace the operator with a runtime call.
287     return NoChange();
288   }
289 
290   Node* import_meta_const = jsgraph()->Constant(*import_meta);
291   ReplaceWithValue(node, import_meta_const);
292   return Changed(import_meta_const);
293 }
294 
isolate() const295 Isolate* JSContextSpecialization::isolate() const {
296   return jsgraph()->isolate();
297 }
298 
299 }  // namespace compiler
300 }  // namespace internal
301 }  // namespace v8
302