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 3 // found in the LICENSE file. 4 5 #include "src/compiler/js-call-reducer.h" 6 7 #include "src/code-factory.h" 8 #include "src/code-stubs.h" 9 #include "src/compilation-dependencies.h" 10 #include "src/compiler/js-graph.h" 11 #include "src/compiler/linkage.h" 12 #include "src/compiler/node-matchers.h" 13 #include "src/compiler/simplified-operator.h" 14 #include "src/feedback-vector-inl.h" 15 #include "src/objects-inl.h" 16 17 namespace v8 { 18 namespace internal { 19 namespace compiler { 20 21 Reduction JSCallReducer::Reduce(Node* node) { 22 switch (node->opcode()) { 23 case IrOpcode::kJSConstruct: 24 return ReduceJSConstruct(node); 25 case IrOpcode::kJSConstructWithSpread: 26 return ReduceJSConstructWithSpread(node); 27 case IrOpcode::kJSCall: 28 return ReduceJSCall(node); 29 case IrOpcode::kJSCallWithSpread: 30 return ReduceJSCallWithSpread(node); 31 default: 32 break; 33 } 34 return NoChange(); 35 } 36 37 38 // ES6 section 22.1.1 The Array Constructor 39 Reduction JSCallReducer::ReduceArrayConstructor(Node* node) { 40 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 41 Node* target = NodeProperties::GetValueInput(node, 0); 42 CallParameters const& p = CallParametersOf(node->op()); 43 44 // Check if we have an allocation site from the CallIC. 45 Handle<AllocationSite> site; 46 if (p.feedback().IsValid()) { 47 CallICNexus nexus(p.feedback().vector(), p.feedback().slot()); 48 Handle<Object> feedback(nexus.GetFeedback(), isolate()); 49 if (feedback->IsAllocationSite()) { 50 site = Handle<AllocationSite>::cast(feedback); 51 } 52 } 53 54 // Turn the {node} into a {JSCreateArray} call. 55 DCHECK_LE(2u, p.arity()); 56 size_t const arity = p.arity() - 2; 57 NodeProperties::ReplaceValueInput(node, target, 0); 58 NodeProperties::ReplaceValueInput(node, target, 1); 59 // TODO(bmeurer): We might need to propagate the tail call mode to 60 // the JSCreateArray operator, because an Array call in tail call 61 // position must always properly consume the parent stack frame. 62 NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site)); 63 return Changed(node); 64 } 65 66 67 // ES6 section 20.1.1 The Number Constructor 68 Reduction JSCallReducer::ReduceNumberConstructor(Node* node) { 69 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 70 CallParameters const& p = CallParametersOf(node->op()); 71 72 // Turn the {node} into a {JSToNumber} call. 73 DCHECK_LE(2u, p.arity()); 74 Node* value = (p.arity() == 2) ? jsgraph()->ZeroConstant() 75 : NodeProperties::GetValueInput(node, 2); 76 NodeProperties::ReplaceValueInputs(node, value); 77 NodeProperties::ChangeOp(node, javascript()->ToNumber()); 78 return Changed(node); 79 } 80 81 82 // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray ) 83 Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { 84 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 85 Node* target = NodeProperties::GetValueInput(node, 0); 86 CallParameters const& p = CallParametersOf(node->op()); 87 // Tail calls to Function.prototype.apply are not properly supported 88 // down the pipeline, so we disable this optimization completely for 89 // tail calls (for now). 90 if (p.tail_call_mode() == TailCallMode::kAllow) return NoChange(); 91 Handle<JSFunction> apply = 92 Handle<JSFunction>::cast(HeapObjectMatcher(target).Value()); 93 size_t arity = p.arity(); 94 DCHECK_LE(2u, arity); 95 ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny; 96 if (arity == 2) { 97 // Neither thisArg nor argArray was provided. 98 convert_mode = ConvertReceiverMode::kNullOrUndefined; 99 node->ReplaceInput(0, node->InputAt(1)); 100 node->ReplaceInput(1, jsgraph()->UndefinedConstant()); 101 } else if (arity == 3) { 102 // The argArray was not provided, just remove the {target}. 103 node->RemoveInput(0); 104 --arity; 105 } else if (arity == 4) { 106 // Check if argArray is an arguments object, and {node} is the only value 107 // user of argArray (except for value uses in frame states). 108 Node* arg_array = NodeProperties::GetValueInput(node, 3); 109 if (arg_array->opcode() != IrOpcode::kJSCreateArguments) return NoChange(); 110 for (Edge edge : arg_array->use_edges()) { 111 if (edge.from()->opcode() == IrOpcode::kStateValues) continue; 112 if (!NodeProperties::IsValueEdge(edge)) continue; 113 if (edge.from() == node) continue; 114 return NoChange(); 115 } 116 // Check if the arguments can be handled in the fast case (i.e. we don't 117 // have aliased sloppy arguments), and compute the {start_index} for 118 // rest parameters. 119 CreateArgumentsType const type = CreateArgumentsTypeOf(arg_array->op()); 120 Node* frame_state = NodeProperties::GetFrameStateInput(arg_array); 121 FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); 122 int formal_parameter_count; 123 int start_index = 0; 124 { 125 Handle<SharedFunctionInfo> shared; 126 if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); 127 formal_parameter_count = shared->internal_formal_parameter_count(); 128 } 129 if (type == CreateArgumentsType::kMappedArguments) { 130 // Mapped arguments (sloppy mode) cannot be handled if they are aliased. 131 if (formal_parameter_count != 0) return NoChange(); 132 } else if (type == CreateArgumentsType::kRestParameter) { 133 start_index = formal_parameter_count; 134 } 135 // Check if are applying to inlined arguments or to the arguments of 136 // the outermost function. 137 Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput); 138 if (outer_state->opcode() != IrOpcode::kFrameState) { 139 // TODO(jarin,bmeurer): Support the NewUnmappedArgumentsElement and 140 // NewRestParameterElements in the EscapeAnalysis and Deoptimizer 141 // instead, then we don't need this hack. 142 // Only works with zero formal parameters because of lacking deoptimizer 143 // support. 144 if (type != CreateArgumentsType::kRestParameter && 145 formal_parameter_count == 0) { 146 // There are no other uses of the {arg_array} except in StateValues, 147 // so we just replace {arg_array} with a marker for the Deoptimizer 148 // that this refers to the arguments object. 149 Node* arguments = graph()->NewNode(common()->ArgumentsObjectState()); 150 ReplaceWithValue(arg_array, arguments); 151 } 152 153 // Reduce {node} to a JSCallForwardVarargs operation, which just 154 // re-pushes the incoming arguments and calls the {target}. 155 node->RemoveInput(0); // Function.prototype.apply 156 node->RemoveInput(2); // arguments 157 NodeProperties::ChangeOp(node, javascript()->CallForwardVarargs( 158 start_index, p.tail_call_mode())); 159 return Changed(node); 160 } 161 // Get to the actual frame state from which to extract the arguments; 162 // we can only optimize this in case the {node} was already inlined into 163 // some other function (and same for the {arg_array}). 164 FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state); 165 if (outer_info.type() == FrameStateType::kArgumentsAdaptor) { 166 // Need to take the parameters from the arguments adaptor. 167 frame_state = outer_state; 168 } 169 // Remove the argArray input from the {node}. 170 node->RemoveInput(static_cast<int>(--arity)); 171 // Add the actual parameters to the {node}, skipping the receiver, 172 // starting from {start_index}. 173 Node* const parameters = frame_state->InputAt(kFrameStateParametersInput); 174 for (int i = start_index + 1; i < parameters->InputCount(); ++i) { 175 node->InsertInput(graph()->zone(), static_cast<int>(arity), 176 parameters->InputAt(i)); 177 ++arity; 178 } 179 // Drop the {target} from the {node}. 180 node->RemoveInput(0); 181 --arity; 182 } else { 183 return NoChange(); 184 } 185 // Change {node} to the new {JSCall} operator. 186 NodeProperties::ChangeOp( 187 node, 188 javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode, 189 p.tail_call_mode())); 190 // Change context of {node} to the Function.prototype.apply context, 191 // to ensure any exception is thrown in the correct context. 192 NodeProperties::ReplaceContextInput( 193 node, jsgraph()->HeapConstant(handle(apply->context(), isolate()))); 194 // Try to further reduce the JSCall {node}. 195 Reduction const reduction = ReduceJSCall(node); 196 return reduction.Changed() ? reduction : Changed(node); 197 } 198 199 200 // ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args) 201 Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) { 202 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 203 CallParameters const& p = CallParametersOf(node->op()); 204 Handle<JSFunction> call = Handle<JSFunction>::cast( 205 HeapObjectMatcher(NodeProperties::GetValueInput(node, 0)).Value()); 206 // Change context of {node} to the Function.prototype.call context, 207 // to ensure any exception is thrown in the correct context. 208 NodeProperties::ReplaceContextInput( 209 node, jsgraph()->HeapConstant(handle(call->context(), isolate()))); 210 // Remove the target from {node} and use the receiver as target instead, and 211 // the thisArg becomes the new target. If thisArg was not provided, insert 212 // undefined instead. 213 size_t arity = p.arity(); 214 DCHECK_LE(2u, arity); 215 ConvertReceiverMode convert_mode; 216 if (arity == 2) { 217 // The thisArg was not provided, use undefined as receiver. 218 convert_mode = ConvertReceiverMode::kNullOrUndefined; 219 node->ReplaceInput(0, node->InputAt(1)); 220 node->ReplaceInput(1, jsgraph()->UndefinedConstant()); 221 } else { 222 // Just remove the target, which is the first value input. 223 convert_mode = ConvertReceiverMode::kAny; 224 node->RemoveInput(0); 225 --arity; 226 } 227 NodeProperties::ChangeOp( 228 node, 229 javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode, 230 p.tail_call_mode())); 231 // Try to further reduce the JSCall {node}. 232 Reduction const reduction = ReduceJSCall(node); 233 return reduction.Changed() ? reduction : Changed(node); 234 } 235 236 // ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] (V) 237 Reduction JSCallReducer::ReduceFunctionPrototypeHasInstance(Node* node) { 238 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 239 Node* receiver = NodeProperties::GetValueInput(node, 1); 240 Node* object = (node->op()->ValueInputCount() >= 3) 241 ? NodeProperties::GetValueInput(node, 2) 242 : jsgraph()->UndefinedConstant(); 243 Node* context = NodeProperties::GetContextInput(node); 244 Node* frame_state = NodeProperties::GetFrameStateInput(node); 245 Node* effect = NodeProperties::GetEffectInput(node); 246 Node* control = NodeProperties::GetControlInput(node); 247 248 // TODO(turbofan): If JSOrdinaryToInstance raises an exception, the 249 // stack trace doesn't contain the @@hasInstance call; we have the 250 // corresponding bug in the baseline case. Some massaging of the frame 251 // state would be necessary here. 252 253 // Morph this {node} into a JSOrdinaryHasInstance node. 254 node->ReplaceInput(0, receiver); 255 node->ReplaceInput(1, object); 256 node->ReplaceInput(2, context); 257 node->ReplaceInput(3, frame_state); 258 node->ReplaceInput(4, effect); 259 node->ReplaceInput(5, control); 260 node->TrimInputCount(6); 261 NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance()); 262 return Changed(node); 263 } 264 265 namespace { 266 267 bool CanInlineApiCall(Isolate* isolate, Node* node, 268 Handle<FunctionTemplateInfo> function_template_info) { 269 DCHECK(node->opcode() == IrOpcode::kJSCall); 270 if (V8_UNLIKELY(FLAG_runtime_stats)) return false; 271 if (function_template_info->call_code()->IsUndefined(isolate)) { 272 return false; 273 } 274 CallParameters const& params = CallParametersOf(node->op()); 275 // CallApiCallbackStub expects the target in a register, so we count it out, 276 // and counts the receiver as an implicit argument, so we count the receiver 277 // out too. 278 int const argc = static_cast<int>(params.arity()) - 2; 279 if (argc > CallApiCallbackStub::kArgMax || !params.feedback().IsValid()) { 280 return false; 281 } 282 HeapObjectMatcher receiver(NodeProperties::GetValueInput(node, 1)); 283 if (!receiver.HasValue()) { 284 return false; 285 } 286 return receiver.Value()->IsUndefined(isolate) || 287 (receiver.Value()->map()->IsJSObjectMap() && 288 !receiver.Value()->map()->is_access_check_needed()); 289 } 290 291 } // namespace 292 293 JSCallReducer::HolderLookup JSCallReducer::LookupHolder( 294 Handle<JSObject> object, 295 Handle<FunctionTemplateInfo> function_template_info, 296 Handle<JSObject>* holder) { 297 DCHECK(object->map()->IsJSObjectMap()); 298 Handle<Map> object_map(object->map()); 299 Handle<FunctionTemplateInfo> expected_receiver_type; 300 if (!function_template_info->signature()->IsUndefined(isolate())) { 301 expected_receiver_type = 302 handle(FunctionTemplateInfo::cast(function_template_info->signature())); 303 } 304 if (expected_receiver_type.is_null() || 305 expected_receiver_type->IsTemplateFor(*object_map)) { 306 *holder = Handle<JSObject>::null(); 307 return kHolderIsReceiver; 308 } 309 while (object_map->has_hidden_prototype()) { 310 Handle<JSObject> prototype(JSObject::cast(object_map->prototype())); 311 object_map = handle(prototype->map()); 312 if (expected_receiver_type->IsTemplateFor(*object_map)) { 313 *holder = prototype; 314 return kHolderFound; 315 } 316 } 317 return kHolderNotFound; 318 } 319 320 // ES6 section B.2.2.1.1 get Object.prototype.__proto__ 321 Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) { 322 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 323 Node* receiver = NodeProperties::GetValueInput(node, 1); 324 Node* effect = NodeProperties::GetEffectInput(node); 325 326 // Try to determine the {receiver} map. 327 ZoneHandleSet<Map> receiver_maps; 328 NodeProperties::InferReceiverMapsResult result = 329 NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps); 330 if (result == NodeProperties::kReliableReceiverMaps) { 331 Handle<Map> candidate_map( 332 receiver_maps[0]->GetPrototypeChainRootMap(isolate())); 333 Handle<Object> candidate_prototype(candidate_map->prototype(), isolate()); 334 335 // Check if we can constant-fold the {candidate_prototype}. 336 for (size_t i = 0; i < receiver_maps.size(); ++i) { 337 Handle<Map> const receiver_map( 338 receiver_maps[i]->GetPrototypeChainRootMap(isolate())); 339 if (receiver_map->IsJSProxyMap() || 340 receiver_map->has_hidden_prototype() || 341 receiver_map->is_access_check_needed() || 342 receiver_map->prototype() != *candidate_prototype) { 343 return NoChange(); 344 } 345 } 346 Node* value = jsgraph()->Constant(candidate_prototype); 347 ReplaceWithValue(node, value); 348 return Replace(value); 349 } 350 351 return NoChange(); 352 } 353 354 Reduction JSCallReducer::ReduceCallApiFunction( 355 Node* node, Node* target, 356 Handle<FunctionTemplateInfo> function_template_info) { 357 Isolate* isolate = this->isolate(); 358 CHECK(!isolate->serializer_enabled()); 359 HeapObjectMatcher m(target); 360 DCHECK(m.HasValue() && m.Value()->IsJSFunction()); 361 if (!CanInlineApiCall(isolate, node, function_template_info)) { 362 return NoChange(); 363 } 364 Handle<CallHandlerInfo> call_handler_info( 365 handle(CallHandlerInfo::cast(function_template_info->call_code()))); 366 Handle<Object> data(call_handler_info->data(), isolate); 367 368 Node* receiver_node = NodeProperties::GetValueInput(node, 1); 369 CallParameters const& params = CallParametersOf(node->op()); 370 371 Handle<HeapObject> receiver = HeapObjectMatcher(receiver_node).Value(); 372 bool const receiver_is_undefined = receiver->IsUndefined(isolate); 373 if (receiver_is_undefined) { 374 receiver = handle(Handle<JSFunction>::cast(m.Value())->global_proxy()); 375 } else { 376 DCHECK(receiver->map()->IsJSObjectMap() && 377 !receiver->map()->is_access_check_needed()); 378 } 379 380 Handle<JSObject> holder; 381 HolderLookup lookup = LookupHolder(Handle<JSObject>::cast(receiver), 382 function_template_info, &holder); 383 if (lookup == kHolderNotFound) return NoChange(); 384 if (receiver_is_undefined) { 385 receiver_node = jsgraph()->HeapConstant(receiver); 386 NodeProperties::ReplaceValueInput(node, receiver_node, 1); 387 } 388 Node* holder_node = 389 lookup == kHolderFound ? jsgraph()->HeapConstant(holder) : receiver_node; 390 391 Zone* zone = graph()->zone(); 392 // Same as CanInlineApiCall: exclude the target (which goes in a register) and 393 // the receiver (which is implicitly counted by CallApiCallbackStub) from the 394 // arguments count. 395 int const argc = static_cast<int>(params.arity() - 2); 396 CallApiCallbackStub stub(isolate, argc, data->IsUndefined(isolate), false); 397 CallInterfaceDescriptor cid = stub.GetCallInterfaceDescriptor(); 398 CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor( 399 isolate, zone, cid, 400 cid.GetStackParameterCount() + argc + 1 /* implicit receiver */, 401 CallDescriptor::kNeedsFrameState, Operator::kNoProperties, 402 MachineType::AnyTagged(), 1); 403 ApiFunction api_function(v8::ToCData<Address>(call_handler_info->callback())); 404 ExternalReference function_reference( 405 &api_function, ExternalReference::DIRECT_API_CALL, isolate); 406 407 // CallApiCallbackStub's register arguments: code, target, call data, holder, 408 // function address. 409 node->InsertInput(zone, 0, jsgraph()->HeapConstant(stub.GetCode())); 410 node->InsertInput(zone, 2, jsgraph()->Constant(data)); 411 node->InsertInput(zone, 3, holder_node); 412 node->InsertInput(zone, 4, jsgraph()->ExternalConstant(function_reference)); 413 NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); 414 return Changed(node); 415 } 416 417 Reduction JSCallReducer::ReduceSpreadCall(Node* node, int arity) { 418 DCHECK(node->opcode() == IrOpcode::kJSCallWithSpread || 419 node->opcode() == IrOpcode::kJSConstructWithSpread); 420 421 // Do check to make sure we can actually avoid iteration. 422 if (!isolate()->initial_array_iterator_prototype_map()->is_stable()) { 423 return NoChange(); 424 } 425 426 Node* spread = NodeProperties::GetValueInput(node, arity); 427 428 // Check if spread is an arguments object, and {node} is the only value user 429 // of spread (except for value uses in frame states). 430 if (spread->opcode() != IrOpcode::kJSCreateArguments) return NoChange(); 431 for (Edge edge : spread->use_edges()) { 432 if (edge.from()->opcode() == IrOpcode::kStateValues) continue; 433 if (!NodeProperties::IsValueEdge(edge)) continue; 434 if (edge.from() == node) continue; 435 return NoChange(); 436 } 437 438 // Get to the actual frame state from which to extract the arguments; 439 // we can only optimize this in case the {node} was already inlined into 440 // some other function (and same for the {spread}). 441 CreateArgumentsType type = CreateArgumentsTypeOf(spread->op()); 442 Node* frame_state = NodeProperties::GetFrameStateInput(spread); 443 Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput); 444 if (outer_state->opcode() != IrOpcode::kFrameState) return NoChange(); 445 FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state); 446 if (outer_info.type() == FrameStateType::kArgumentsAdaptor) { 447 // Need to take the parameters from the arguments adaptor. 448 frame_state = outer_state; 449 } 450 FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); 451 int start_index = 0; 452 if (type == CreateArgumentsType::kMappedArguments) { 453 // Mapped arguments (sloppy mode) cannot be handled if they are aliased. 454 Handle<SharedFunctionInfo> shared; 455 if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); 456 if (shared->internal_formal_parameter_count() != 0) return NoChange(); 457 } else if (type == CreateArgumentsType::kRestParameter) { 458 Handle<SharedFunctionInfo> shared; 459 if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); 460 start_index = shared->internal_formal_parameter_count(); 461 462 // Only check the array iterator protector when we have a rest object. 463 if (!isolate()->IsArrayIteratorLookupChainIntact()) return NoChange(); 464 // Add a code dependency on the array iterator protector. 465 dependencies()->AssumePropertyCell(factory()->array_iterator_protector()); 466 } 467 468 dependencies()->AssumeMapStable( 469 isolate()->initial_array_iterator_prototype_map()); 470 471 node->RemoveInput(arity--); 472 473 // Add the actual parameters to the {node}, skipping the receiver. 474 Node* const parameters = frame_state->InputAt(kFrameStateParametersInput); 475 for (int i = start_index + 1; i < state_info.parameter_count(); ++i) { 476 node->InsertInput(graph()->zone(), static_cast<int>(++arity), 477 parameters->InputAt(i)); 478 } 479 480 if (node->opcode() == IrOpcode::kJSCallWithSpread) { 481 NodeProperties::ChangeOp( 482 node, javascript()->Call(arity + 1, 7, VectorSlotPair())); 483 } else { 484 NodeProperties::ChangeOp( 485 node, javascript()->Construct(arity + 2, 7, VectorSlotPair())); 486 } 487 return Changed(node); 488 } 489 490 Reduction JSCallReducer::ReduceJSCall(Node* node) { 491 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 492 CallParameters const& p = CallParametersOf(node->op()); 493 Node* target = NodeProperties::GetValueInput(node, 0); 494 Node* control = NodeProperties::GetControlInput(node); 495 Node* effect = NodeProperties::GetEffectInput(node); 496 497 // Try to specialize JSCall {node}s with constant {target}s. 498 HeapObjectMatcher m(target); 499 if (m.HasValue()) { 500 if (m.Value()->IsJSFunction()) { 501 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); 502 Handle<SharedFunctionInfo> shared(function->shared(), isolate()); 503 504 // Raise a TypeError if the {target} is a "classConstructor". 505 if (IsClassConstructor(shared->kind())) { 506 NodeProperties::ReplaceValueInputs(node, target); 507 NodeProperties::ChangeOp( 508 node, javascript()->CallRuntime( 509 Runtime::kThrowConstructorNonCallableError, 1)); 510 return Changed(node); 511 } 512 513 // Don't inline cross native context. 514 if (function->native_context() != *native_context()) return NoChange(); 515 516 // Check for known builtin functions. 517 switch (shared->code()->builtin_index()) { 518 case Builtins::kFunctionPrototypeApply: 519 return ReduceFunctionPrototypeApply(node); 520 case Builtins::kFunctionPrototypeCall: 521 return ReduceFunctionPrototypeCall(node); 522 case Builtins::kFunctionPrototypeHasInstance: 523 return ReduceFunctionPrototypeHasInstance(node); 524 case Builtins::kNumberConstructor: 525 return ReduceNumberConstructor(node); 526 case Builtins::kObjectPrototypeGetProto: 527 return ReduceObjectPrototypeGetProto(node); 528 default: 529 break; 530 } 531 532 // Check for the Array constructor. 533 if (*function == function->native_context()->array_function()) { 534 return ReduceArrayConstructor(node); 535 } 536 537 if (shared->IsApiFunction()) { 538 return ReduceCallApiFunction( 539 node, target, 540 handle(FunctionTemplateInfo::cast(shared->function_data()))); 541 } 542 } else if (m.Value()->IsJSBoundFunction()) { 543 Handle<JSBoundFunction> function = 544 Handle<JSBoundFunction>::cast(m.Value()); 545 Handle<JSReceiver> bound_target_function( 546 function->bound_target_function(), isolate()); 547 Handle<Object> bound_this(function->bound_this(), isolate()); 548 Handle<FixedArray> bound_arguments(function->bound_arguments(), 549 isolate()); 550 CallParameters const& p = CallParametersOf(node->op()); 551 ConvertReceiverMode const convert_mode = 552 (bound_this->IsNullOrUndefined(isolate())) 553 ? ConvertReceiverMode::kNullOrUndefined 554 : ConvertReceiverMode::kNotNullOrUndefined; 555 size_t arity = p.arity(); 556 DCHECK_LE(2u, arity); 557 // Patch {node} to use [[BoundTargetFunction]] and [[BoundThis]]. 558 NodeProperties::ReplaceValueInput( 559 node, jsgraph()->Constant(bound_target_function), 0); 560 NodeProperties::ReplaceValueInput(node, jsgraph()->Constant(bound_this), 561 1); 562 // Insert the [[BoundArguments]] for {node}. 563 for (int i = 0; i < bound_arguments->length(); ++i) { 564 node->InsertInput( 565 graph()->zone(), i + 2, 566 jsgraph()->Constant(handle(bound_arguments->get(i), isolate()))); 567 arity++; 568 } 569 NodeProperties::ChangeOp( 570 node, 571 javascript()->Call(arity, p.frequency(), VectorSlotPair(), 572 convert_mode, p.tail_call_mode())); 573 // Try to further reduce the JSCall {node}. 574 Reduction const reduction = ReduceJSCall(node); 575 return reduction.Changed() ? reduction : Changed(node); 576 } 577 578 // Don't mess with other {node}s that have a constant {target}. 579 // TODO(bmeurer): Also support proxies here. 580 return NoChange(); 581 } 582 583 // Extract feedback from the {node} using the CallICNexus. 584 if (!p.feedback().IsValid()) return NoChange(); 585 CallICNexus nexus(p.feedback().vector(), p.feedback().slot()); 586 if (nexus.IsUninitialized()) { 587 // TODO(turbofan): Tail-calling to a CallIC stub is not supported. 588 if (p.tail_call_mode() == TailCallMode::kAllow) return NoChange(); 589 590 // Insert a CallIC here to collect feedback for uninitialized calls. 591 int const arg_count = static_cast<int>(p.arity() - 2); 592 Callable callable = CodeFactory::CallIC(isolate(), p.convert_mode()); 593 CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState; 594 CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( 595 isolate(), graph()->zone(), callable.descriptor(), arg_count + 1, 596 flags); 597 Node* stub_code = jsgraph()->HeapConstant(callable.code()); 598 Node* stub_arity = jsgraph()->Constant(arg_count); 599 Node* slot_index = 600 jsgraph()->Constant(FeedbackVector::GetIndex(p.feedback().slot())); 601 Node* feedback_vector = jsgraph()->HeapConstant(p.feedback().vector()); 602 node->InsertInput(graph()->zone(), 0, stub_code); 603 node->InsertInput(graph()->zone(), 2, stub_arity); 604 node->InsertInput(graph()->zone(), 3, slot_index); 605 node->InsertInput(graph()->zone(), 4, feedback_vector); 606 NodeProperties::ChangeOp(node, common()->Call(desc)); 607 return Changed(node); 608 } 609 610 // Not much we can do if deoptimization support is disabled. 611 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); 612 613 Handle<Object> feedback(nexus.GetFeedback(), isolate()); 614 if (feedback->IsAllocationSite()) { 615 // Retrieve the Array function from the {node}. 616 Node* array_function = jsgraph()->HeapConstant( 617 handle(native_context()->array_function(), isolate())); 618 619 // Check that the {target} is still the {array_function}. 620 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target, 621 array_function); 622 effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control); 623 624 // Turn the {node} into a {JSCreateArray} call. 625 NodeProperties::ReplaceValueInput(node, array_function, 0); 626 NodeProperties::ReplaceEffectInput(node, effect); 627 return ReduceArrayConstructor(node); 628 } else if (feedback->IsWeakCell()) { 629 Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback); 630 if (cell->value()->IsJSFunction()) { 631 Node* target_function = 632 jsgraph()->Constant(handle(cell->value(), isolate())); 633 634 // Check that the {target} is still the {target_function}. 635 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target, 636 target_function); 637 effect = 638 graph()->NewNode(simplified()->CheckIf(), check, effect, control); 639 640 // Specialize the JSCall node to the {target_function}. 641 NodeProperties::ReplaceValueInput(node, target_function, 0); 642 NodeProperties::ReplaceEffectInput(node, effect); 643 644 // Try to further reduce the JSCall {node}. 645 Reduction const reduction = ReduceJSCall(node); 646 return reduction.Changed() ? reduction : Changed(node); 647 } 648 } 649 return NoChange(); 650 } 651 652 Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) { 653 DCHECK_EQ(IrOpcode::kJSCallWithSpread, node->opcode()); 654 CallWithSpreadParameters const& p = CallWithSpreadParametersOf(node->op()); 655 DCHECK_LE(3u, p.arity()); 656 int arity = static_cast<int>(p.arity() - 1); 657 658 return ReduceSpreadCall(node, arity); 659 } 660 661 Reduction JSCallReducer::ReduceJSConstruct(Node* node) { 662 DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode()); 663 ConstructParameters const& p = ConstructParametersOf(node->op()); 664 DCHECK_LE(2u, p.arity()); 665 int const arity = static_cast<int>(p.arity() - 2); 666 Node* target = NodeProperties::GetValueInput(node, 0); 667 Node* new_target = NodeProperties::GetValueInput(node, arity + 1); 668 Node* effect = NodeProperties::GetEffectInput(node); 669 Node* control = NodeProperties::GetControlInput(node); 670 671 // Try to specialize JSConstruct {node}s with constant {target}s. 672 HeapObjectMatcher m(target); 673 if (m.HasValue()) { 674 if (m.Value()->IsJSFunction()) { 675 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); 676 677 // Raise a TypeError if the {target} is not a constructor. 678 if (!function->IsConstructor()) { 679 NodeProperties::ReplaceValueInputs(node, target); 680 NodeProperties::ChangeOp( 681 node, javascript()->CallRuntime( 682 Runtime::kThrowConstructedNonConstructable)); 683 return Changed(node); 684 } 685 686 // Don't inline cross native context. 687 if (function->native_context() != *native_context()) return NoChange(); 688 689 // Check for the ArrayConstructor. 690 if (*function == function->native_context()->array_function()) { 691 // Check if we have an allocation site. 692 Handle<AllocationSite> site; 693 if (p.feedback().IsValid()) { 694 CallICNexus nexus(p.feedback().vector(), p.feedback().slot()); 695 Handle<Object> feedback(nexus.GetFeedback(), isolate()); 696 if (feedback->IsAllocationSite()) { 697 site = Handle<AllocationSite>::cast(feedback); 698 } 699 } 700 701 // Turn the {node} into a {JSCreateArray} call. 702 for (int i = arity; i > 0; --i) { 703 NodeProperties::ReplaceValueInput( 704 node, NodeProperties::GetValueInput(node, i), i + 1); 705 } 706 NodeProperties::ReplaceValueInput(node, new_target, 1); 707 NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site)); 708 return Changed(node); 709 } 710 } 711 712 // Don't mess with other {node}s that have a constant {target}. 713 // TODO(bmeurer): Also support optimizing bound functions and proxies here. 714 return NoChange(); 715 } 716 717 // Not much we can do if deoptimization support is disabled. 718 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); 719 720 if (!p.feedback().IsValid()) return NoChange(); 721 CallICNexus nexus(p.feedback().vector(), p.feedback().slot()); 722 Handle<Object> feedback(nexus.GetFeedback(), isolate()); 723 if (feedback->IsAllocationSite()) { 724 // The feedback is an AllocationSite, which means we have called the 725 // Array function and collected transition (and pretenuring) feedback 726 // for the resulting arrays. This has to be kept in sync with the 727 // implementation of the CallConstructStub. 728 Handle<AllocationSite> site = Handle<AllocationSite>::cast(feedback); 729 730 // Retrieve the Array function from the {node}. 731 Node* array_function = jsgraph()->HeapConstant( 732 handle(native_context()->array_function(), isolate())); 733 734 // Check that the {target} is still the {array_function}. 735 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target, 736 array_function); 737 effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control); 738 739 // Turn the {node} into a {JSCreateArray} call. 740 NodeProperties::ReplaceEffectInput(node, effect); 741 for (int i = arity; i > 0; --i) { 742 NodeProperties::ReplaceValueInput( 743 node, NodeProperties::GetValueInput(node, i), i + 1); 744 } 745 NodeProperties::ReplaceValueInput(node, new_target, 1); 746 NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site)); 747 return Changed(node); 748 } else if (feedback->IsWeakCell()) { 749 Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback); 750 if (cell->value()->IsJSFunction()) { 751 Node* target_function = 752 jsgraph()->Constant(handle(cell->value(), isolate())); 753 754 // Check that the {target} is still the {target_function}. 755 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target, 756 target_function); 757 effect = 758 graph()->NewNode(simplified()->CheckIf(), check, effect, control); 759 760 // Specialize the JSConstruct node to the {target_function}. 761 NodeProperties::ReplaceValueInput(node, target_function, 0); 762 NodeProperties::ReplaceEffectInput(node, effect); 763 if (target == new_target) { 764 NodeProperties::ReplaceValueInput(node, target_function, arity + 1); 765 } 766 767 // Try to further reduce the JSConstruct {node}. 768 Reduction const reduction = ReduceJSConstruct(node); 769 return reduction.Changed() ? reduction : Changed(node); 770 } 771 } 772 773 return NoChange(); 774 } 775 776 Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) { 777 DCHECK_EQ(IrOpcode::kJSConstructWithSpread, node->opcode()); 778 ConstructWithSpreadParameters const& p = 779 ConstructWithSpreadParametersOf(node->op()); 780 DCHECK_LE(3u, p.arity()); 781 int arity = static_cast<int>(p.arity() - 2); 782 783 return ReduceSpreadCall(node, arity); 784 } 785 786 Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } 787 788 Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } 789 790 Factory* JSCallReducer::factory() const { return isolate()->factory(); } 791 792 CommonOperatorBuilder* JSCallReducer::common() const { 793 return jsgraph()->common(); 794 } 795 796 JSOperatorBuilder* JSCallReducer::javascript() const { 797 return jsgraph()->javascript(); 798 } 799 800 SimplifiedOperatorBuilder* JSCallReducer::simplified() const { 801 return jsgraph()->simplified(); 802 } 803 804 } // namespace compiler 805 } // namespace internal 806 } // namespace v8 807