Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: src/compiler/js-call-reducer.cc

Issue 2839953002: [turbofan] Optimize API function calls based on inferred receiver maps. (Closed)
Patch Set: REBASE Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/compiler/js-call-reducer.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
OLDNEW
« no previous file with comments | « src/compiler/js-call-reducer.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698