| 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 |