OLD | NEW |
1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/compiler/js-call-reducer.h" | 5 #include "src/compiler/js-call-reducer.h" |
6 | 6 |
7 #include "src/compiler/js-graph.h" | 7 #include "src/compiler/js-graph.h" |
8 #include "src/compiler/node-matchers.h" | 8 #include "src/compiler/node-matchers.h" |
9 #include "src/compiler/simplified-operator.h" | |
10 #include "src/objects-inl.h" | 9 #include "src/objects-inl.h" |
11 #include "src/type-feedback-vector-inl.h" | 10 #include "src/type-feedback-vector-inl.h" |
12 | 11 |
13 namespace v8 { | 12 namespace v8 { |
14 namespace internal { | 13 namespace internal { |
15 namespace compiler { | 14 namespace compiler { |
16 | 15 |
17 namespace { | 16 namespace { |
18 | 17 |
19 VectorSlotPair CallCountFeedback(VectorSlotPair p) { | 18 VectorSlotPair CallCountFeedback(VectorSlotPair p) { |
(...skipping 13 matching lines...) Expand all Loading... |
33 CallICNexus nexus(vector, slot); | 32 CallICNexus nexus(vector, slot); |
34 nexus.ConfigureMegamorphic(call_count); | 33 nexus.ConfigureMegamorphic(call_count); |
35 return VectorSlotPair(vector, slot); | 34 return VectorSlotPair(vector, slot); |
36 } | 35 } |
37 | 36 |
38 } // namespace | 37 } // namespace |
39 | 38 |
40 | 39 |
41 Reduction JSCallReducer::Reduce(Node* node) { | 40 Reduction JSCallReducer::Reduce(Node* node) { |
42 switch (node->opcode()) { | 41 switch (node->opcode()) { |
| 42 case IrOpcode::kJSCallConstruct: |
| 43 return ReduceJSCallConstruct(node); |
43 case IrOpcode::kJSCallFunction: | 44 case IrOpcode::kJSCallFunction: |
44 return ReduceJSCallFunction(node); | 45 return ReduceJSCallFunction(node); |
45 default: | 46 default: |
46 break; | 47 break; |
47 } | 48 } |
48 return NoChange(); | 49 return NoChange(); |
49 } | 50 } |
50 | 51 |
51 | 52 |
| 53 // ES6 section 22.1.1 The Array Constructor |
| 54 Reduction JSCallReducer::ReduceArrayConstructor(Node* node) { |
| 55 Node* target = NodeProperties::GetValueInput(node, 0); |
| 56 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); |
| 57 CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); |
| 58 |
| 59 // Check if we have an allocation site from the CallIC. |
| 60 Handle<AllocationSite> site; |
| 61 if (p.feedback().IsValid()) { |
| 62 CallICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
| 63 Handle<Object> feedback(nexus.GetFeedback(), isolate()); |
| 64 if (feedback->IsAllocationSite()) { |
| 65 site = Handle<AllocationSite>::cast(feedback); |
| 66 } |
| 67 } |
| 68 |
| 69 // Turn the {node} into a {JSCreateArray} call. |
| 70 DCHECK_LE(2u, p.arity()); |
| 71 size_t const arity = p.arity() - 2; |
| 72 NodeProperties::ReplaceValueInput(node, target, 0); |
| 73 NodeProperties::ReplaceValueInput(node, target, 1); |
| 74 NodeProperties::RemoveFrameStateInput(node, 1); |
| 75 // TODO(bmeurer): We might need to propagate the tail call mode to |
| 76 // the JSCreateArray operator, because an Array call in tail call |
| 77 // position must always properly consume the parent stack frame. |
| 78 NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site)); |
| 79 return Changed(node); |
| 80 } |
| 81 |
| 82 |
52 // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray ) | 83 // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray ) |
53 Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { | 84 Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { |
54 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); | 85 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); |
55 Node* target = NodeProperties::GetValueInput(node, 0); | 86 Node* target = NodeProperties::GetValueInput(node, 0); |
56 CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); | 87 CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); |
57 Handle<JSFunction> apply = | 88 Handle<JSFunction> apply = |
58 Handle<JSFunction>::cast(HeapObjectMatcher(target).Value()); | 89 Handle<JSFunction>::cast(HeapObjectMatcher(target).Value()); |
59 size_t arity = p.arity(); | 90 size_t arity = p.arity(); |
60 DCHECK_LE(2u, arity); | 91 DCHECK_LE(2u, arity); |
61 ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny; | 92 ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny; |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
163 // Try to further reduce the JSCallFunction {node}. | 194 // Try to further reduce the JSCallFunction {node}. |
164 Reduction const reduction = ReduceJSCallFunction(node); | 195 Reduction const reduction = ReduceJSCallFunction(node); |
165 return reduction.Changed() ? reduction : Changed(node); | 196 return reduction.Changed() ? reduction : Changed(node); |
166 } | 197 } |
167 | 198 |
168 | 199 |
169 Reduction JSCallReducer::ReduceJSCallFunction(Node* node) { | 200 Reduction JSCallReducer::ReduceJSCallFunction(Node* node) { |
170 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); | 201 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); |
171 CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); | 202 CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); |
172 Node* target = NodeProperties::GetValueInput(node, 0); | 203 Node* target = NodeProperties::GetValueInput(node, 0); |
| 204 Node* context = NodeProperties::GetContextInput(node); |
173 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); | 205 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); |
174 Node* control = NodeProperties::GetControlInput(node); | 206 Node* control = NodeProperties::GetControlInput(node); |
175 Node* effect = NodeProperties::GetEffectInput(node); | 207 Node* effect = NodeProperties::GetEffectInput(node); |
176 | 208 |
177 // Try to specialize JSCallFunction {node}s with constant {target}s. | 209 // Try to specialize JSCallFunction {node}s with constant {target}s. |
178 HeapObjectMatcher m(target); | 210 HeapObjectMatcher m(target); |
179 if (m.HasValue()) { | 211 if (m.HasValue()) { |
180 if (m.Value()->IsJSFunction()) { | 212 if (m.Value()->IsJSFunction()) { |
181 Handle<SharedFunctionInfo> shared( | 213 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); |
182 Handle<JSFunction>::cast(m.Value())->shared(), isolate()); | 214 Handle<SharedFunctionInfo> shared(function->shared(), isolate()); |
183 | 215 |
184 // Raise a TypeError if the {target} is a "classConstructor". | 216 // Raise a TypeError if the {target} is a "classConstructor". |
185 if (IsClassConstructor(shared->kind())) { | 217 if (IsClassConstructor(shared->kind())) { |
186 NodeProperties::RemoveFrameStateInput(node, 0); | 218 NodeProperties::RemoveFrameStateInput(node, 0); |
187 NodeProperties::RemoveValueInputs(node); | 219 NodeProperties::RemoveValueInputs(node); |
188 NodeProperties::ChangeOp( | 220 NodeProperties::ChangeOp( |
189 node, javascript()->CallRuntime( | 221 node, javascript()->CallRuntime( |
190 Runtime::kThrowConstructorNonCallableError, 0)); | 222 Runtime::kThrowConstructorNonCallableError, 0)); |
191 return Changed(node); | 223 return Changed(node); |
192 } | 224 } |
193 | 225 |
194 // Check for known builtin functions. | 226 // Check for known builtin functions. |
195 if (shared->HasBuiltinFunctionId()) { | 227 if (shared->HasBuiltinFunctionId()) { |
196 switch (shared->builtin_function_id()) { | 228 switch (shared->builtin_function_id()) { |
197 case kFunctionApply: | 229 case kFunctionApply: |
198 return ReduceFunctionPrototypeApply(node); | 230 return ReduceFunctionPrototypeApply(node); |
199 case kFunctionCall: | 231 case kFunctionCall: |
200 return ReduceFunctionPrototypeCall(node); | 232 return ReduceFunctionPrototypeCall(node); |
201 default: | 233 default: |
202 break; | 234 break; |
203 } | 235 } |
204 } | 236 } |
| 237 |
| 238 // Check for the ArrayConstructor. |
| 239 if (*function == function->native_context()->array_function()) { |
| 240 return ReduceArrayConstructor(node); |
| 241 } |
205 } | 242 } |
| 243 |
206 // Don't mess with other {node}s that have a constant {target}. | 244 // Don't mess with other {node}s that have a constant {target}. |
207 // TODO(bmeurer): Also support optimizing bound functions and proxies here. | 245 // TODO(bmeurer): Also support optimizing bound functions and proxies here. |
208 return NoChange(); | 246 return NoChange(); |
209 } | 247 } |
210 | 248 |
211 // Not much we can do if deoptimization support is disabled. | 249 // Not much we can do if deoptimization support is disabled. |
212 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | 250 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); |
213 | 251 |
214 // Extract feedback from the {node} using the CallICNexus. | 252 // Extract feedback from the {node} using the CallICNexus. |
215 if (!p.feedback().IsValid()) return NoChange(); | 253 if (!p.feedback().IsValid()) return NoChange(); |
216 CallICNexus nexus(p.feedback().vector(), p.feedback().slot()); | 254 CallICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
217 Handle<Object> feedback(nexus.GetFeedback(), isolate()); | 255 Handle<Object> feedback(nexus.GetFeedback(), isolate()); |
218 if (feedback->IsWeakCell()) { | 256 if (feedback->IsAllocationSite()) { |
| 257 // Retrieve the Array function from the {node}. |
| 258 Node* array_function; |
| 259 Handle<Context> native_context; |
| 260 if (GetNativeContext(node).ToHandle(&native_context)) { |
| 261 array_function = jsgraph()->HeapConstant( |
| 262 handle(native_context->array_function(), isolate())); |
| 263 } else { |
| 264 Node* global_object = effect = graph()->NewNode( |
| 265 javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true), |
| 266 context, context, effect); |
| 267 Node* native_context = effect = graph()->NewNode( |
| 268 javascript()->LoadNativeContext(), global_object, context, effect); |
| 269 array_function = effect = graph()->NewNode( |
| 270 javascript()->LoadContext(0, Context::ARRAY_FUNCTION_INDEX, true), |
| 271 native_context, native_context, effect); |
| 272 } |
| 273 |
| 274 // Check that the {target} is still the {array_function}. |
| 275 Node* check = effect = |
| 276 graph()->NewNode(javascript()->StrictEqual(), target, array_function, |
| 277 context, effect, control); |
| 278 Node* branch = |
| 279 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); |
| 280 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
| 281 Node* deoptimize = |
| 282 graph()->NewNode(common()->Deoptimize(), frame_state, effect, if_false); |
| 283 // TODO(bmeurer): This should be on the AdvancedReducer somehow. |
| 284 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); |
| 285 control = graph()->NewNode(common()->IfTrue(), branch); |
| 286 |
| 287 // Turn the {node} into a {JSCreateArray} call. |
| 288 NodeProperties::ReplaceValueInput(node, array_function, 0); |
| 289 NodeProperties::ReplaceEffectInput(node, effect); |
| 290 NodeProperties::ReplaceControlInput(node, control); |
| 291 return ReduceArrayConstructor(node); |
| 292 } else if (feedback->IsWeakCell()) { |
219 Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback); | 293 Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback); |
220 if (cell->value()->IsJSFunction()) { | 294 if (cell->value()->IsJSFunction()) { |
| 295 Node* target_function = |
| 296 jsgraph()->Constant(handle(cell->value(), isolate())); |
| 297 |
221 // Check that the {target} is still the {target_function}. | 298 // Check that the {target} is still the {target_function}. |
222 Node* target_function = jsgraph()->HeapConstant( | 299 Node* check = effect = |
223 handle(JSFunction::cast(cell->value()), isolate())); | 300 graph()->NewNode(javascript()->StrictEqual(), target, target_function, |
224 Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), | 301 context, effect, control); |
225 target, target_function); | |
226 Node* branch = | 302 Node* branch = |
227 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); | 303 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); |
228 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); | 304 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
229 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, | 305 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, |
230 effect, if_false); | 306 effect, if_false); |
231 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | 307 // TODO(bmeurer): This should be on the AdvancedReducer somehow. |
232 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | 308 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); |
233 control = graph()->NewNode(common()->IfTrue(), branch); | 309 control = graph()->NewNode(common()->IfTrue(), branch); |
234 | 310 |
235 // Specialize the JSCallFunction node to the {target_function}. | 311 // Specialize the JSCallFunction node to the {target_function}. |
236 NodeProperties::ReplaceValueInput(node, target_function, 0); | 312 NodeProperties::ReplaceValueInput(node, target_function, 0); |
| 313 NodeProperties::ReplaceEffectInput(node, effect); |
237 NodeProperties::ReplaceControlInput(node, control); | 314 NodeProperties::ReplaceControlInput(node, control); |
238 | 315 |
239 // Try to further reduce the JSCallFunction {node}. | 316 // Try to further reduce the JSCallFunction {node}. |
240 Reduction const reduction = ReduceJSCallFunction(node); | 317 Reduction const reduction = ReduceJSCallFunction(node); |
241 return reduction.Changed() ? reduction : Changed(node); | 318 return reduction.Changed() ? reduction : Changed(node); |
242 } | 319 } |
243 } | 320 } |
244 return NoChange(); | 321 return NoChange(); |
245 } | 322 } |
246 | 323 |
247 | 324 |
| 325 Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) { |
| 326 DCHECK_EQ(IrOpcode::kJSCallConstruct, node->opcode()); |
| 327 CallConstructParameters const& p = CallConstructParametersOf(node->op()); |
| 328 DCHECK_LE(2u, p.arity()); |
| 329 int const arity = static_cast<int>(p.arity() - 2); |
| 330 Node* target = NodeProperties::GetValueInput(node, 0); |
| 331 Node* new_target = NodeProperties::GetValueInput(node, arity + 1); |
| 332 |
| 333 // Try to specialize JSCallConstruct {node}s with constant {target}s. |
| 334 HeapObjectMatcher m(target); |
| 335 if (m.HasValue()) { |
| 336 if (m.Value()->IsJSFunction()) { |
| 337 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); |
| 338 |
| 339 // Raise a TypeError if the {target} is not a constructor. |
| 340 if (!function->IsConstructor()) { |
| 341 NodeProperties::ReplaceValueInputs(node, target); |
| 342 NodeProperties::ChangeOp( |
| 343 node, |
| 344 javascript()->CallRuntime(Runtime::kThrowCalledNonCallable, 1)); |
| 345 return Changed(node); |
| 346 } |
| 347 |
| 348 // Check for the ArrayConstructor. |
| 349 if (*function == function->native_context()->array_function()) { |
| 350 // Check if we have an allocation site. |
| 351 Handle<AllocationSite> site; |
| 352 if (p.feedback().IsValid()) { |
| 353 Handle<Object> feedback( |
| 354 p.feedback().vector()->Get(p.feedback().slot()), isolate()); |
| 355 if (feedback->IsAllocationSite()) { |
| 356 site = Handle<AllocationSite>::cast(feedback); |
| 357 } |
| 358 } |
| 359 |
| 360 // Turn the {node} into a {JSCreateArray} call. |
| 361 for (int i = arity; i > 0; --i) { |
| 362 NodeProperties::ReplaceValueInput( |
| 363 node, NodeProperties::GetValueInput(node, i), i + 1); |
| 364 } |
| 365 NodeProperties::ReplaceValueInput(node, new_target, 1); |
| 366 NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site)); |
| 367 return Changed(node); |
| 368 } |
| 369 } |
| 370 |
| 371 // Don't mess with other {node}s that have a constant {target}. |
| 372 // TODO(bmeurer): Also support optimizing bound functions and proxies here. |
| 373 return NoChange(); |
| 374 } |
| 375 |
| 376 return NoChange(); |
| 377 } |
| 378 |
| 379 |
| 380 MaybeHandle<Context> JSCallReducer::GetNativeContext(Node* node) { |
| 381 Node* const context = NodeProperties::GetContextInput(node); |
| 382 return NodeProperties::GetSpecializationNativeContext(context, |
| 383 native_context()); |
| 384 } |
| 385 |
| 386 |
248 Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } | 387 Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } |
249 | 388 |
250 | 389 |
251 Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } | 390 Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } |
252 | 391 |
253 | 392 |
254 CommonOperatorBuilder* JSCallReducer::common() const { | 393 CommonOperatorBuilder* JSCallReducer::common() const { |
255 return jsgraph()->common(); | 394 return jsgraph()->common(); |
256 } | 395 } |
257 | 396 |
258 | 397 |
259 JSOperatorBuilder* JSCallReducer::javascript() const { | 398 JSOperatorBuilder* JSCallReducer::javascript() const { |
260 return jsgraph()->javascript(); | 399 return jsgraph()->javascript(); |
261 } | 400 } |
262 | 401 |
263 | |
264 SimplifiedOperatorBuilder* JSCallReducer::simplified() const { | |
265 return jsgraph()->simplified(); | |
266 } | |
267 | |
268 } // namespace compiler | 402 } // namespace compiler |
269 } // namespace internal | 403 } // namespace internal |
270 } // namespace v8 | 404 } // namespace v8 |
OLD | NEW |