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/code-factory.h" | 7 #include "src/code-factory.h" |
8 #include "src/code-stubs.h" | 8 #include "src/code-stubs.h" |
9 #include "src/compilation-dependencies.h" | 9 #include "src/compilation-dependencies.h" |
10 #include "src/compiler/js-graph.h" | 10 #include "src/compiler/js-graph.h" |
11 #include "src/compiler/linkage.h" | 11 #include "src/compiler/linkage.h" |
12 #include "src/compiler/node-matchers.h" | 12 #include "src/compiler/node-matchers.h" |
13 #include "src/compiler/simplified-operator.h" | 13 #include "src/compiler/simplified-operator.h" |
14 #include "src/feedback-vector-inl.h" | 14 #include "src/feedback-vector-inl.h" |
| 15 #include "src/ic/call-optimization.h" |
15 #include "src/objects-inl.h" | 16 #include "src/objects-inl.h" |
16 | 17 |
17 namespace v8 { | 18 namespace v8 { |
18 namespace internal { | 19 namespace internal { |
19 namespace compiler { | 20 namespace compiler { |
20 | 21 |
21 Reduction JSCallReducer::Reduce(Node* node) { | 22 Reduction JSCallReducer::Reduce(Node* node) { |
22 switch (node->opcode()) { | 23 switch (node->opcode()) { |
23 case IrOpcode::kJSConstruct: | 24 case IrOpcode::kJSConstruct: |
24 return ReduceJSConstruct(node); | 25 return ReduceJSConstruct(node); |
(...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
265 node->ReplaceInput(1, object); | 266 node->ReplaceInput(1, object); |
266 node->ReplaceInput(2, context); | 267 node->ReplaceInput(2, context); |
267 node->ReplaceInput(3, frame_state); | 268 node->ReplaceInput(3, frame_state); |
268 node->ReplaceInput(4, effect); | 269 node->ReplaceInput(4, effect); |
269 node->ReplaceInput(5, control); | 270 node->ReplaceInput(5, control); |
270 node->TrimInputCount(6); | 271 node->TrimInputCount(6); |
271 NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance()); | 272 NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance()); |
272 return Changed(node); | 273 return Changed(node); |
273 } | 274 } |
274 | 275 |
275 namespace { | |
276 | |
277 bool CanInlineApiCall(Isolate* isolate, Node* node, | |
278 Handle<FunctionTemplateInfo> function_template_info) { | |
279 DCHECK(node->opcode() == IrOpcode::kJSCall); | |
280 if (V8_UNLIKELY(FLAG_runtime_stats)) return false; | |
281 if (function_template_info->call_code()->IsUndefined(isolate)) { | |
282 return false; | |
283 } | |
284 CallParameters const& params = CallParametersOf(node->op()); | |
285 // CallApiCallbackStub expects the target in a register, so we count it out, | |
286 // and counts the receiver as an implicit argument, so we count the receiver | |
287 // out too. | |
288 int const argc = static_cast<int>(params.arity()) - 2; | |
289 if (argc > CallApiCallbackStub::kArgMax || !params.feedback().IsValid()) { | |
290 return false; | |
291 } | |
292 HeapObjectMatcher receiver(NodeProperties::GetValueInput(node, 1)); | |
293 if (!receiver.HasValue()) { | |
294 return false; | |
295 } | |
296 return receiver.Value()->IsUndefined(isolate) || | |
297 (receiver.Value()->map()->IsJSObjectMap() && | |
298 !receiver.Value()->map()->is_access_check_needed()); | |
299 } | |
300 | |
301 } // namespace | |
302 | |
303 JSCallReducer::HolderLookup JSCallReducer::LookupHolder( | |
304 Handle<JSObject> object, | |
305 Handle<FunctionTemplateInfo> function_template_info, | |
306 Handle<JSObject>* holder) { | |
307 DCHECK(object->map()->IsJSObjectMap()); | |
308 Handle<Map> object_map(object->map()); | |
309 Handle<FunctionTemplateInfo> expected_receiver_type; | |
310 if (!function_template_info->signature()->IsUndefined(isolate())) { | |
311 expected_receiver_type = | |
312 handle(FunctionTemplateInfo::cast(function_template_info->signature())); | |
313 } | |
314 if (expected_receiver_type.is_null() || | |
315 expected_receiver_type->IsTemplateFor(*object_map)) { | |
316 *holder = Handle<JSObject>::null(); | |
317 return kHolderIsReceiver; | |
318 } | |
319 while (object_map->has_hidden_prototype()) { | |
320 Handle<JSObject> prototype(JSObject::cast(object_map->prototype())); | |
321 object_map = handle(prototype->map()); | |
322 if (expected_receiver_type->IsTemplateFor(*object_map)) { | |
323 *holder = prototype; | |
324 return kHolderFound; | |
325 } | |
326 } | |
327 return kHolderNotFound; | |
328 } | |
329 | |
330 Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object) { | 276 Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object) { |
331 Node* effect = NodeProperties::GetEffectInput(node); | 277 Node* effect = NodeProperties::GetEffectInput(node); |
332 | 278 |
333 // Try to determine the {object} map. | 279 // Try to determine the {object} map. |
334 ZoneHandleSet<Map> object_maps; | 280 ZoneHandleSet<Map> object_maps; |
335 NodeProperties::InferReceiverMapsResult result = | 281 NodeProperties::InferReceiverMapsResult result = |
336 NodeProperties::InferReceiverMaps(object, effect, &object_maps); | 282 NodeProperties::InferReceiverMaps(object, effect, &object_maps); |
337 if (result != NodeProperties::kNoReceiverMaps) { | 283 if (result != NodeProperties::kNoReceiverMaps) { |
338 Handle<Map> candidate_map( | 284 Handle<Map> candidate_map( |
339 object_maps[0]->GetPrototypeChainRootMap(isolate())); | 285 object_maps[0]->GetPrototypeChainRootMap(isolate())); |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
391 // ES6 section 26.1.7 Reflect.getPrototypeOf ( target ) | 337 // ES6 section 26.1.7 Reflect.getPrototypeOf ( target ) |
392 Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) { | 338 Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) { |
393 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); | 339 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
394 Node* target = (node->op()->ValueInputCount() >= 3) | 340 Node* target = (node->op()->ValueInputCount() >= 3) |
395 ? NodeProperties::GetValueInput(node, 2) | 341 ? NodeProperties::GetValueInput(node, 2) |
396 : jsgraph()->UndefinedConstant(); | 342 : jsgraph()->UndefinedConstant(); |
397 return ReduceObjectGetPrototype(node, target); | 343 return ReduceObjectGetPrototype(node, target); |
398 } | 344 } |
399 | 345 |
400 Reduction JSCallReducer::ReduceCallApiFunction( | 346 Reduction JSCallReducer::ReduceCallApiFunction( |
401 Node* node, Node* target, | 347 Node* node, Handle<FunctionTemplateInfo> function_template_info) { |
402 Handle<FunctionTemplateInfo> function_template_info) { | 348 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
403 Isolate* isolate = this->isolate(); | 349 CallParameters const& p = CallParametersOf(node->op()); |
404 CHECK(!isolate->serializer_enabled()); | 350 int const argc = static_cast<int>(p.arity()) - 2; |
405 HeapObjectMatcher m(target); | 351 Node* receiver = (p.convert_mode() == ConvertReceiverMode::kNullOrUndefined) |
406 DCHECK(m.HasValue() && m.Value()->IsJSFunction()); | 352 ? jsgraph()->HeapConstant(global_proxy()) |
407 if (!CanInlineApiCall(isolate, node, function_template_info)) { | 353 : NodeProperties::GetValueInput(node, 1); |
408 return NoChange(); | 354 Node* effect = NodeProperties::GetEffectInput(node); |
409 } | |
410 Handle<CallHandlerInfo> call_handler_info( | |
411 handle(CallHandlerInfo::cast(function_template_info->call_code()))); | |
412 Handle<Object> data(call_handler_info->data(), isolate); | |
413 | 355 |
414 Node* receiver_node = NodeProperties::GetValueInput(node, 1); | 356 // CallApiCallbackStub expects the target in a register, so we count it out, |
415 CallParameters const& params = CallParametersOf(node->op()); | 357 // and counts the receiver as an implicit argument, so we count the receiver |
| 358 // out too. |
| 359 if (argc > CallApiCallbackStub::kArgMax) return NoChange(); |
416 | 360 |
417 Handle<HeapObject> receiver = HeapObjectMatcher(receiver_node).Value(); | 361 // Infer the {receiver} maps, and check if we can inline the API function |
418 bool const receiver_is_undefined = receiver->IsUndefined(isolate); | 362 // callback based on those. |
419 if (receiver_is_undefined) { | 363 ZoneHandleSet<Map> receiver_maps; |
420 receiver = handle(Handle<JSFunction>::cast(m.Value())->global_proxy()); | 364 NodeProperties::InferReceiverMapsResult result = |
421 } else { | 365 NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps); |
422 DCHECK(receiver->map()->IsJSObjectMap() && | 366 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
423 !receiver->map()->is_access_check_needed()); | 367 for (size_t i = 0; i < receiver_maps.size(); ++i) { |
| 368 Handle<Map> receiver_map = receiver_maps[i]; |
| 369 if (!receiver_map->IsJSObjectMap() || |
| 370 (!function_template_info->accept_any_receiver() && |
| 371 receiver_map->is_access_check_needed())) { |
| 372 return NoChange(); |
| 373 } |
| 374 // In case of unreliable {receiver} information, the {receiver_maps} |
| 375 // must all be stable in order to consume the information. |
| 376 if (result == NodeProperties::kUnreliableReceiverMaps) { |
| 377 if (!receiver_map->is_stable()) return NoChange(); |
| 378 } |
424 } | 379 } |
425 | 380 |
426 Handle<JSObject> holder; | 381 // See if we can constant-fold the compatible receiver checks. |
427 HolderLookup lookup = LookupHolder(Handle<JSObject>::cast(receiver), | 382 CallOptimization call_optimization(function_template_info); |
428 function_template_info, &holder); | 383 if (!call_optimization.is_simple_api_call()) return NoChange(); |
429 if (lookup == kHolderNotFound) return NoChange(); | 384 CallOptimization::HolderLookup lookup; |
430 if (receiver_is_undefined) { | 385 Handle<JSObject> api_holder = |
431 receiver_node = jsgraph()->HeapConstant(receiver); | 386 call_optimization.LookupHolderOfExpectedType(receiver_maps[0], &lookup); |
432 NodeProperties::ReplaceValueInput(node, receiver_node, 1); | 387 if (lookup == CallOptimization::kHolderNotFound) return NoChange(); |
| 388 for (size_t i = 1; i < receiver_maps.size(); ++i) { |
| 389 CallOptimization::HolderLookup lookupi; |
| 390 Handle<JSObject> holder = call_optimization.LookupHolderOfExpectedType( |
| 391 receiver_maps[i], &lookupi); |
| 392 if (lookup != lookupi) return NoChange(); |
| 393 if (!api_holder.is_identical_to(holder)) return NoChange(); |
433 } | 394 } |
434 Node* holder_node = | |
435 lookup == kHolderFound ? jsgraph()->HeapConstant(holder) : receiver_node; | |
436 | 395 |
437 Zone* zone = graph()->zone(); | 396 // Install stability dependencies for unreliable {receiver_maps}. |
438 // Same as CanInlineApiCall: exclude the target (which goes in a register) and | 397 if (result == NodeProperties::kUnreliableReceiverMaps) { |
439 // the receiver (which is implicitly counted by CallApiCallbackStub) from the | 398 for (size_t i = 0; i < receiver_maps.size(); ++i) { |
440 // arguments count. | 399 dependencies()->AssumeMapStable(receiver_maps[i]); |
441 int const argc = static_cast<int>(params.arity() - 2); | 400 } |
442 CallApiCallbackStub stub(isolate, argc, false); | 401 } |
| 402 |
| 403 // CallApiCallbackStub's register arguments: code, target, call data, holder, |
| 404 // function address. |
| 405 // TODO(turbofan): Consider introducing a JSCallApiCallback operator for |
| 406 // this and lower it during JSGenericLowering, and unify this with the |
| 407 // JSNativeContextSpecialization::InlineApiCall method a bit. |
| 408 Handle<CallHandlerInfo> call_handler_info( |
| 409 CallHandlerInfo::cast(function_template_info->call_code()), isolate()); |
| 410 Handle<Object> data(call_handler_info->data(), isolate()); |
| 411 CallApiCallbackStub stub(isolate(), argc, false); |
443 CallInterfaceDescriptor cid = stub.GetCallInterfaceDescriptor(); | 412 CallInterfaceDescriptor cid = stub.GetCallInterfaceDescriptor(); |
444 CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor( | 413 CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor( |
445 isolate, zone, cid, | 414 isolate(), graph()->zone(), cid, |
446 cid.GetStackParameterCount() + argc + 1 /* implicit receiver */, | 415 cid.GetStackParameterCount() + argc + 1 /* implicit receiver */, |
447 CallDescriptor::kNeedsFrameState, Operator::kNoProperties, | 416 CallDescriptor::kNeedsFrameState, Operator::kNoProperties, |
448 MachineType::AnyTagged(), 1); | 417 MachineType::AnyTagged(), 1); |
449 ApiFunction api_function(v8::ToCData<Address>(call_handler_info->callback())); | 418 ApiFunction api_function(v8::ToCData<Address>(call_handler_info->callback())); |
| 419 Node* holder = lookup == CallOptimization::kHolderFound |
| 420 ? jsgraph()->HeapConstant(api_holder) |
| 421 : receiver; |
450 ExternalReference function_reference( | 422 ExternalReference function_reference( |
451 &api_function, ExternalReference::DIRECT_API_CALL, isolate); | 423 &api_function, ExternalReference::DIRECT_API_CALL, isolate()); |
452 | 424 node->InsertInput(graph()->zone(), 0, |
453 // CallApiCallbackStub's register arguments: code, target, call data, holder, | 425 jsgraph()->HeapConstant(stub.GetCode())); |
454 // function address. | 426 node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(data)); |
455 node->InsertInput(zone, 0, jsgraph()->HeapConstant(stub.GetCode())); | 427 node->InsertInput(graph()->zone(), 3, holder); |
456 node->InsertInput(zone, 2, jsgraph()->Constant(data)); | 428 node->InsertInput(graph()->zone(), 4, |
457 node->InsertInput(zone, 3, holder_node); | 429 jsgraph()->ExternalConstant(function_reference)); |
458 node->InsertInput(zone, 4, jsgraph()->ExternalConstant(function_reference)); | 430 node->ReplaceInput(5, receiver); |
459 NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); | 431 NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); |
460 return Changed(node); | 432 return Changed(node); |
461 } | 433 } |
462 | 434 |
463 Reduction JSCallReducer::ReduceSpreadCall(Node* node, int arity) { | 435 Reduction JSCallReducer::ReduceSpreadCall(Node* node, int arity) { |
464 DCHECK(node->opcode() == IrOpcode::kJSCallWithSpread || | 436 DCHECK(node->opcode() == IrOpcode::kJSCallWithSpread || |
465 node->opcode() == IrOpcode::kJSConstructWithSpread); | 437 node->opcode() == IrOpcode::kJSConstructWithSpread); |
466 | 438 |
467 // Do check to make sure we can actually avoid iteration. | 439 // Do check to make sure we can actually avoid iteration. |
468 if (!isolate()->initial_array_iterator_prototype_map()->is_stable()) { | 440 if (!isolate()->initial_array_iterator_prototype_map()->is_stable()) { |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
605 return ReduceReflectGetPrototypeOf(node); | 577 return ReduceReflectGetPrototypeOf(node); |
606 default: | 578 default: |
607 break; | 579 break; |
608 } | 580 } |
609 | 581 |
610 // Check for the Array constructor. | 582 // Check for the Array constructor. |
611 if (*function == function->native_context()->array_function()) { | 583 if (*function == function->native_context()->array_function()) { |
612 return ReduceArrayConstructor(node); | 584 return ReduceArrayConstructor(node); |
613 } | 585 } |
614 | 586 |
615 if (shared->IsApiFunction()) { | 587 if (!FLAG_runtime_stats && shared->IsApiFunction()) { |
616 return ReduceCallApiFunction( | 588 Handle<FunctionTemplateInfo> function_template_info( |
617 node, target, | 589 FunctionTemplateInfo::cast(shared->function_data()), isolate()); |
618 handle(FunctionTemplateInfo::cast(shared->function_data()))); | 590 return ReduceCallApiFunction(node, function_template_info); |
619 } | 591 } |
620 } else if (m.Value()->IsJSBoundFunction()) { | 592 } else if (m.Value()->IsJSBoundFunction()) { |
621 Handle<JSBoundFunction> function = | 593 Handle<JSBoundFunction> function = |
622 Handle<JSBoundFunction>::cast(m.Value()); | 594 Handle<JSBoundFunction>::cast(m.Value()); |
623 Handle<JSReceiver> bound_target_function( | 595 Handle<JSReceiver> bound_target_function( |
624 function->bound_target_function(), isolate()); | 596 function->bound_target_function(), isolate()); |
625 Handle<Object> bound_this(function->bound_this(), isolate()); | 597 Handle<Object> bound_this(function->bound_this(), isolate()); |
626 Handle<FixedArray> bound_arguments(function->bound_arguments(), | 598 Handle<FixedArray> bound_arguments(function->bound_arguments(), |
627 isolate()); | 599 isolate()); |
628 CallParameters const& p = CallParametersOf(node->op()); | 600 CallParameters const& p = CallParametersOf(node->op()); |
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
859 | 831 |
860 return ReduceSpreadCall(node, arity); | 832 return ReduceSpreadCall(node, arity); |
861 } | 833 } |
862 | 834 |
863 Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } | 835 Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } |
864 | 836 |
865 Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } | 837 Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } |
866 | 838 |
867 Factory* JSCallReducer::factory() const { return isolate()->factory(); } | 839 Factory* JSCallReducer::factory() const { return isolate()->factory(); } |
868 | 840 |
| 841 Handle<JSGlobalProxy> JSCallReducer::global_proxy() const { |
| 842 return handle(JSGlobalProxy::cast(native_context()->global_proxy()), |
| 843 isolate()); |
| 844 } |
| 845 |
869 CommonOperatorBuilder* JSCallReducer::common() const { | 846 CommonOperatorBuilder* JSCallReducer::common() const { |
870 return jsgraph()->common(); | 847 return jsgraph()->common(); |
871 } | 848 } |
872 | 849 |
873 JSOperatorBuilder* JSCallReducer::javascript() const { | 850 JSOperatorBuilder* JSCallReducer::javascript() const { |
874 return jsgraph()->javascript(); | 851 return jsgraph()->javascript(); |
875 } | 852 } |
876 | 853 |
877 SimplifiedOperatorBuilder* JSCallReducer::simplified() const { | 854 SimplifiedOperatorBuilder* JSCallReducer::simplified() const { |
878 return jsgraph()->simplified(); | 855 return jsgraph()->simplified(); |
879 } | 856 } |
880 | 857 |
881 } // namespace compiler | 858 } // namespace compiler |
882 } // namespace internal | 859 } // namespace internal |
883 } // namespace v8 | 860 } // namespace v8 |
OLD | NEW |