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
Reduce(Node * node)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
ReduceParameter(Node * node)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
SimplifyJSLoadContext(Node * node,Node * new_context,size_t new_depth)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
SimplifyJSStoreContext(Node * node,Node * new_context,size_t new_depth)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
IsContextParameter(Node * node)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.
GetSpecializationContext(JSHeapBroker * broker,Node * node,size_t * distance,Maybe<OuterContext> maybe_outer)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
ReduceJSLoadContext(Node * node)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(js_heap_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 for (; depth > 0; --depth) {
148 concrete = concrete.previous().value();
149 }
150
151 if (!access.immutable()) {
152 // We found the requested context object but since the context slot is
153 // mutable we can only partially reduce the load.
154 return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
155 }
156
157 // This will hold the final value, if we can figure it out.
158 base::Optional<ObjectRef> maybe_value;
159
160 maybe_value = concrete.get(static_cast<int>(access.index()));
161 if (maybe_value.has_value() && !maybe_value->IsSmi()) {
162 // Even though the context slot is immutable, the context might have escaped
163 // before the function to which it belongs has initialized the slot.
164 // We must be conservative and check if the value in the slot is currently
165 // the hole or undefined. Only if it is neither of these, can we be sure
166 // that it won't change anymore.
167 OddballType oddball_type = maybe_value->oddball_type();
168 if (oddball_type == OddballType::kUndefined ||
169 oddball_type == OddballType::kHole) {
170 maybe_value.reset();
171 }
172 }
173
174 if (!maybe_value.has_value()) {
175 return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
176 }
177
178 // Success. The context load can be replaced with the constant.
179 // TODO(titzer): record the specialization for sharing code across
180 // multiple contexts that have the same value in the corresponding context
181 // slot.
182 Node* constant = jsgraph_->Constant(*maybe_value);
183 ReplaceWithValue(node, constant);
184 return Replace(constant);
185 }
186
187
ReduceJSStoreContext(Node * node)188 Reduction JSContextSpecialization::ReduceJSStoreContext(Node* node) {
189 DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
190
191 const ContextAccess& access = ContextAccessOf(node->op());
192 size_t depth = access.depth();
193
194 // First walk up the context chain in the graph until we reduce the depth to 0
195 // or hit a node that does not have a CreateXYZContext operator.
196 Node* context = NodeProperties::GetOuterContext(node, &depth);
197
198 base::Optional<ContextRef> maybe_concrete =
199 GetSpecializationContext(js_heap_broker(), context, &depth, outer());
200 if (!maybe_concrete.has_value()) {
201 // We do not have a concrete context object, so we can only partially reduce
202 // the load by folding-in the outer context node.
203 return SimplifyJSStoreContext(node, context, depth);
204 }
205
206 // Now walk up the concrete context chain for the remaining depth.
207 ContextRef concrete = maybe_concrete.value();
208 for (; depth > 0; --depth) {
209 concrete = concrete.previous().value();
210 }
211
212 return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
213 }
214
215
isolate() const216 Isolate* JSContextSpecialization::isolate() const {
217 return jsgraph()->isolate();
218 }
219
220 } // namespace compiler
221 } // namespace internal
222 } // namespace v8
223