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/api-inl.h" 8 #include "src/builtins/builtins-promise-gen.h" 9 #include "src/builtins/builtins-utils.h" 10 #include "src/code-factory.h" 11 #include "src/code-stubs.h" 12 #include "src/compiler/access-builder.h" 13 #include "src/compiler/access-info.h" 14 #include "src/compiler/allocation-builder.h" 15 #include "src/compiler/compilation-dependencies.h" 16 #include "src/compiler/js-graph.h" 17 #include "src/compiler/linkage.h" 18 #include "src/compiler/node-matchers.h" 19 #include "src/compiler/property-access-builder.h" 20 #include "src/compiler/simplified-operator.h" 21 #include "src/compiler/type-cache.h" 22 #include "src/feedback-vector-inl.h" 23 #include "src/ic/call-optimization.h" 24 #include "src/objects-inl.h" 25 #include "src/objects/arguments-inl.h" 26 #include "src/objects/js-array-buffer-inl.h" 27 #include "src/objects/js-array-inl.h" 28 #include "src/vector-slot-pair.h" 29 30 namespace v8 { 31 namespace internal { 32 namespace compiler { 33 34 Reduction JSCallReducer::ReduceMathUnary(Node* node, const Operator* op) { 35 CallParameters const& p = CallParametersOf(node->op()); 36 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 37 return NoChange(); 38 } 39 if (node->op()->ValueInputCount() < 3) { 40 Node* value = jsgraph()->NaNConstant(); 41 ReplaceWithValue(node, value); 42 return Replace(value); 43 } 44 45 Node* effect = NodeProperties::GetEffectInput(node); 46 Node* control = NodeProperties::GetControlInput(node); 47 Node* input = NodeProperties::GetValueInput(node, 2); 48 49 input = effect = 50 graph()->NewNode(simplified()->SpeculativeToNumber( 51 NumberOperationHint::kNumberOrOddball, p.feedback()), 52 input, effect, control); 53 Node* value = graph()->NewNode(op, input); 54 ReplaceWithValue(node, value, effect); 55 return Replace(value); 56 } 57 58 Reduction JSCallReducer::ReduceMathBinary(Node* node, const Operator* op) { 59 CallParameters const& p = CallParametersOf(node->op()); 60 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 61 return NoChange(); 62 } 63 if (node->op()->ValueInputCount() < 3) { 64 Node* value = jsgraph()->NaNConstant(); 65 ReplaceWithValue(node, value); 66 return Replace(value); 67 } 68 Node* effect = NodeProperties::GetEffectInput(node); 69 Node* control = NodeProperties::GetControlInput(node); 70 71 Node* left = NodeProperties::GetValueInput(node, 2); 72 Node* right = node->op()->ValueInputCount() > 3 73 ? NodeProperties::GetValueInput(node, 3) 74 : jsgraph()->NaNConstant(); 75 left = effect = 76 graph()->NewNode(simplified()->SpeculativeToNumber( 77 NumberOperationHint::kNumberOrOddball, p.feedback()), 78 left, effect, control); 79 right = effect = 80 graph()->NewNode(simplified()->SpeculativeToNumber( 81 NumberOperationHint::kNumberOrOddball, p.feedback()), 82 right, effect, control); 83 Node* value = graph()->NewNode(op, left, right); 84 ReplaceWithValue(node, value, effect); 85 return Replace(value); 86 } 87 88 // ES6 section 20.2.2.19 Math.imul ( x, y ) 89 Reduction JSCallReducer::ReduceMathImul(Node* node) { 90 CallParameters const& p = CallParametersOf(node->op()); 91 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 92 return NoChange(); 93 } 94 if (node->op()->ValueInputCount() < 3) { 95 Node* value = jsgraph()->ZeroConstant(); 96 ReplaceWithValue(node, value); 97 return Replace(value); 98 } 99 Node* left = NodeProperties::GetValueInput(node, 2); 100 Node* right = node->op()->ValueInputCount() > 3 101 ? NodeProperties::GetValueInput(node, 3) 102 : jsgraph()->ZeroConstant(); 103 Node* effect = NodeProperties::GetEffectInput(node); 104 Node* control = NodeProperties::GetControlInput(node); 105 106 left = effect = 107 graph()->NewNode(simplified()->SpeculativeToNumber( 108 NumberOperationHint::kNumberOrOddball, p.feedback()), 109 left, effect, control); 110 right = effect = 111 graph()->NewNode(simplified()->SpeculativeToNumber( 112 NumberOperationHint::kNumberOrOddball, p.feedback()), 113 right, effect, control); 114 left = graph()->NewNode(simplified()->NumberToUint32(), left); 115 right = graph()->NewNode(simplified()->NumberToUint32(), right); 116 Node* value = graph()->NewNode(simplified()->NumberImul(), left, right); 117 ReplaceWithValue(node, value, effect); 118 return Replace(value); 119 } 120 121 // ES6 section 20.2.2.11 Math.clz32 ( x ) 122 Reduction JSCallReducer::ReduceMathClz32(Node* node) { 123 CallParameters const& p = CallParametersOf(node->op()); 124 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 125 return NoChange(); 126 } 127 if (node->op()->ValueInputCount() < 3) { 128 Node* value = jsgraph()->Constant(32); 129 ReplaceWithValue(node, value); 130 return Replace(value); 131 } 132 Node* input = NodeProperties::GetValueInput(node, 2); 133 Node* effect = NodeProperties::GetEffectInput(node); 134 Node* control = NodeProperties::GetControlInput(node); 135 136 input = effect = 137 graph()->NewNode(simplified()->SpeculativeToNumber( 138 NumberOperationHint::kNumberOrOddball, p.feedback()), 139 input, effect, control); 140 input = graph()->NewNode(simplified()->NumberToUint32(), input); 141 Node* value = graph()->NewNode(simplified()->NumberClz32(), input); 142 ReplaceWithValue(node, value, effect); 143 return Replace(value); 144 } 145 146 // ES6 section 20.2.2.24 Math.max ( value1, value2, ...values ) 147 // ES6 section 20.2.2.25 Math.min ( value1, value2, ...values ) 148 Reduction JSCallReducer::ReduceMathMinMax(Node* node, const Operator* op, 149 Node* empty_value) { 150 CallParameters const& p = CallParametersOf(node->op()); 151 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 152 return NoChange(); 153 } 154 if (node->op()->ValueInputCount() <= 2) { 155 ReplaceWithValue(node, empty_value); 156 return Replace(empty_value); 157 } 158 Node* effect = NodeProperties::GetEffectInput(node); 159 Node* control = NodeProperties::GetControlInput(node); 160 161 Node* value = effect = 162 graph()->NewNode(simplified()->SpeculativeToNumber( 163 NumberOperationHint::kNumberOrOddball, p.feedback()), 164 NodeProperties::GetValueInput(node, 2), effect, control); 165 for (int i = 3; i < node->op()->ValueInputCount(); i++) { 166 Node* input = effect = graph()->NewNode( 167 simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball, 168 p.feedback()), 169 NodeProperties::GetValueInput(node, i), effect, control); 170 value = graph()->NewNode(op, value, input); 171 } 172 173 ReplaceWithValue(node, value, effect); 174 return Replace(value); 175 } 176 177 Reduction JSCallReducer::Reduce(Node* node) { 178 switch (node->opcode()) { 179 case IrOpcode::kJSConstruct: 180 return ReduceJSConstruct(node); 181 case IrOpcode::kJSConstructWithArrayLike: 182 return ReduceJSConstructWithArrayLike(node); 183 case IrOpcode::kJSConstructWithSpread: 184 return ReduceJSConstructWithSpread(node); 185 case IrOpcode::kJSCall: 186 return ReduceJSCall(node); 187 case IrOpcode::kJSCallWithArrayLike: 188 return ReduceJSCallWithArrayLike(node); 189 case IrOpcode::kJSCallWithSpread: 190 return ReduceJSCallWithSpread(node); 191 default: 192 break; 193 } 194 return NoChange(); 195 } 196 197 void JSCallReducer::Finalize() { 198 // TODO(turbofan): This is not the best solution; ideally we would be able 199 // to teach the GraphReducer about arbitrary dependencies between different 200 // nodes, even if they don't show up in the use list of the other node. 201 std::set<Node*> const waitlist = std::move(waitlist_); 202 for (Node* node : waitlist) { 203 if (!node->IsDead()) { 204 Reduction const reduction = Reduce(node); 205 if (reduction.Changed()) { 206 Node* replacement = reduction.replacement(); 207 if (replacement != node) { 208 Replace(node, replacement); 209 } 210 } 211 } 212 } 213 } 214 215 // ES6 section 22.1.1 The Array Constructor 216 Reduction JSCallReducer::ReduceArrayConstructor(Node* node) { 217 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 218 Node* target = NodeProperties::GetValueInput(node, 0); 219 CallParameters const& p = CallParametersOf(node->op()); 220 221 // Turn the {node} into a {JSCreateArray} call. 222 DCHECK_LE(2u, p.arity()); 223 size_t const arity = p.arity() - 2; 224 NodeProperties::ReplaceValueInput(node, target, 0); 225 NodeProperties::ReplaceValueInput(node, target, 1); 226 NodeProperties::ChangeOp( 227 node, javascript()->CreateArray(arity, MaybeHandle<AllocationSite>())); 228 return Changed(node); 229 } 230 231 // ES6 section 19.3.1.1 Boolean ( value ) 232 Reduction JSCallReducer::ReduceBooleanConstructor(Node* node) { 233 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 234 CallParameters const& p = CallParametersOf(node->op()); 235 236 // Replace the {node} with a proper {ToBoolean} operator. 237 DCHECK_LE(2u, p.arity()); 238 Node* value = (p.arity() == 2) ? jsgraph()->UndefinedConstant() 239 : NodeProperties::GetValueInput(node, 2); 240 value = graph()->NewNode(simplified()->ToBoolean(), value); 241 ReplaceWithValue(node, value); 242 return Replace(value); 243 } 244 245 // ES section #sec-object-constructor 246 Reduction JSCallReducer::ReduceObjectConstructor(Node* node) { 247 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 248 CallParameters const& p = CallParametersOf(node->op()); 249 if (p.arity() < 3) return NoChange(); 250 Node* value = (p.arity() >= 3) ? NodeProperties::GetValueInput(node, 2) 251 : jsgraph()->UndefinedConstant(); 252 Node* effect = NodeProperties::GetEffectInput(node); 253 254 // We can fold away the Object(x) call if |x| is definitely not a primitive. 255 if (NodeProperties::CanBePrimitive(isolate(), value, effect)) { 256 if (!NodeProperties::CanBeNullOrUndefined(isolate(), value, effect)) { 257 // Turn the {node} into a {JSToObject} call if we know that 258 // the {value} cannot be null or undefined. 259 NodeProperties::ReplaceValueInputs(node, value); 260 NodeProperties::ChangeOp(node, javascript()->ToObject()); 261 return Changed(node); 262 } 263 } else { 264 ReplaceWithValue(node, value); 265 return Replace(value); 266 } 267 return NoChange(); 268 } 269 270 // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray ) 271 Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { 272 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 273 CallParameters const& p = CallParametersOf(node->op()); 274 size_t arity = p.arity(); 275 DCHECK_LE(2u, arity); 276 ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny; 277 if (arity == 2) { 278 // Neither thisArg nor argArray was provided. 279 convert_mode = ConvertReceiverMode::kNullOrUndefined; 280 node->ReplaceInput(0, node->InputAt(1)); 281 node->ReplaceInput(1, jsgraph()->UndefinedConstant()); 282 } else if (arity == 3) { 283 // The argArray was not provided, just remove the {target}. 284 node->RemoveInput(0); 285 --arity; 286 } else { 287 Node* target = NodeProperties::GetValueInput(node, 1); 288 Node* this_argument = NodeProperties::GetValueInput(node, 2); 289 Node* arguments_list = NodeProperties::GetValueInput(node, 3); 290 Node* context = NodeProperties::GetContextInput(node); 291 Node* frame_state = NodeProperties::GetFrameStateInput(node); 292 Node* effect = NodeProperties::GetEffectInput(node); 293 Node* control = NodeProperties::GetControlInput(node); 294 295 // If {arguments_list} cannot be null or undefined, we don't need 296 // to expand this {node} to control-flow. 297 if (!NodeProperties::CanBeNullOrUndefined(isolate(), arguments_list, 298 effect)) { 299 // Massage the value inputs appropriately. 300 node->ReplaceInput(0, target); 301 node->ReplaceInput(1, this_argument); 302 node->ReplaceInput(2, arguments_list); 303 while (arity-- > 3) node->RemoveInput(3); 304 305 // Morph the {node} to a {JSCallWithArrayLike}. 306 NodeProperties::ChangeOp(node, 307 javascript()->CallWithArrayLike(p.frequency())); 308 Reduction const reduction = ReduceJSCallWithArrayLike(node); 309 return reduction.Changed() ? reduction : Changed(node); 310 } else { 311 // Check whether {arguments_list} is null. 312 Node* check_null = 313 graph()->NewNode(simplified()->ReferenceEqual(), arguments_list, 314 jsgraph()->NullConstant()); 315 control = graph()->NewNode(common()->Branch(BranchHint::kFalse), 316 check_null, control); 317 Node* if_null = graph()->NewNode(common()->IfTrue(), control); 318 control = graph()->NewNode(common()->IfFalse(), control); 319 320 // Check whether {arguments_list} is undefined. 321 Node* check_undefined = 322 graph()->NewNode(simplified()->ReferenceEqual(), arguments_list, 323 jsgraph()->UndefinedConstant()); 324 control = graph()->NewNode(common()->Branch(BranchHint::kFalse), 325 check_undefined, control); 326 Node* if_undefined = graph()->NewNode(common()->IfTrue(), control); 327 control = graph()->NewNode(common()->IfFalse(), control); 328 329 // Lower to {JSCallWithArrayLike} if {arguments_list} is neither null 330 // nor undefined. 331 Node* effect0 = effect; 332 Node* control0 = control; 333 Node* value0 = effect0 = control0 = graph()->NewNode( 334 javascript()->CallWithArrayLike(p.frequency()), target, this_argument, 335 arguments_list, context, frame_state, effect0, control0); 336 337 // Lower to {JSCall} if {arguments_list} is either null or undefined. 338 Node* effect1 = effect; 339 Node* control1 = 340 graph()->NewNode(common()->Merge(2), if_null, if_undefined); 341 Node* value1 = effect1 = control1 = 342 graph()->NewNode(javascript()->Call(2), target, this_argument, 343 context, frame_state, effect1, control1); 344 345 // Rewire potential exception edges. 346 Node* if_exception = nullptr; 347 if (NodeProperties::IsExceptionalCall(node, &if_exception)) { 348 // Create appropriate {IfException} and {IfSuccess} nodes. 349 Node* if_exception0 = 350 graph()->NewNode(common()->IfException(), control0, effect0); 351 control0 = graph()->NewNode(common()->IfSuccess(), control0); 352 Node* if_exception1 = 353 graph()->NewNode(common()->IfException(), control1, effect1); 354 control1 = graph()->NewNode(common()->IfSuccess(), control1); 355 356 // Join the exception edges. 357 Node* merge = 358 graph()->NewNode(common()->Merge(2), if_exception0, if_exception1); 359 Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0, 360 if_exception1, merge); 361 Node* phi = 362 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 363 if_exception0, if_exception1, merge); 364 ReplaceWithValue(if_exception, phi, ephi, merge); 365 } 366 367 // Join control paths. 368 control = graph()->NewNode(common()->Merge(2), control0, control1); 369 effect = 370 graph()->NewNode(common()->EffectPhi(2), effect0, effect1, control); 371 Node* value = 372 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 373 value0, value1, control); 374 ReplaceWithValue(node, value, effect, control); 375 return Replace(value); 376 } 377 } 378 // Change {node} to the new {JSCall} operator. 379 NodeProperties::ChangeOp( 380 node, 381 javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode)); 382 // Try to further reduce the JSCall {node}. 383 Reduction const reduction = ReduceJSCall(node); 384 return reduction.Changed() ? reduction : Changed(node); 385 } 386 387 // ES section #sec-function.prototype.bind 388 Reduction JSCallReducer::ReduceFunctionPrototypeBind(Node* node) { 389 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 390 // Value inputs to the {node} are as follows: 391 // 392 // - target, which is Function.prototype.bind JSFunction 393 // - receiver, which is the [[BoundTargetFunction]] 394 // - bound_this (optional), which is the [[BoundThis]] 395 // - and all the remaining value inouts are [[BoundArguments]] 396 Node* receiver = NodeProperties::GetValueInput(node, 1); 397 Node* bound_this = (node->op()->ValueInputCount() < 3) 398 ? jsgraph()->UndefinedConstant() 399 : NodeProperties::GetValueInput(node, 2); 400 Node* context = NodeProperties::GetContextInput(node); 401 Node* effect = NodeProperties::GetEffectInput(node); 402 Node* control = NodeProperties::GetControlInput(node); 403 404 // Ensure that the {receiver} is known to be a JSBoundFunction or 405 // a JSFunction with the same [[Prototype]], and all maps we've 406 // seen for the {receiver} so far indicate that {receiver} is 407 // definitely a constructor or not a constructor. 408 ZoneHandleSet<Map> receiver_maps; 409 NodeProperties::InferReceiverMapsResult result = 410 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 411 &receiver_maps); 412 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 413 DCHECK_NE(0, receiver_maps.size()); 414 bool const is_constructor = receiver_maps[0]->is_constructor(); 415 Handle<Object> const prototype(receiver_maps[0]->prototype(), isolate()); 416 for (Handle<Map> const receiver_map : receiver_maps) { 417 // Check for consistency among the {receiver_maps}. 418 STATIC_ASSERT(LAST_TYPE == LAST_FUNCTION_TYPE); 419 if (receiver_map->prototype() != *prototype) return NoChange(); 420 if (receiver_map->is_constructor() != is_constructor) return NoChange(); 421 if (receiver_map->instance_type() < FIRST_FUNCTION_TYPE) return NoChange(); 422 423 // Disallow binding of slow-mode functions. We need to figure out 424 // whether the length and name property are in the original state. 425 if (receiver_map->is_dictionary_map()) return NoChange(); 426 427 // Check whether the length and name properties are still present 428 // as AccessorInfo objects. In that case, their values can be 429 // recomputed even if the actual value of the object changes. 430 // This mirrors the checks done in builtins-function-gen.cc at 431 // runtime otherwise. 432 Handle<DescriptorArray> descriptors(receiver_map->instance_descriptors(), 433 isolate()); 434 if (descriptors->number_of_descriptors() < 2) return NoChange(); 435 if (descriptors->GetKey(JSFunction::kLengthDescriptorIndex) != 436 ReadOnlyRoots(isolate()).length_string()) { 437 return NoChange(); 438 } 439 if (!descriptors->GetStrongValue(JSFunction::kLengthDescriptorIndex) 440 ->IsAccessorInfo()) { 441 return NoChange(); 442 } 443 if (descriptors->GetKey(JSFunction::kNameDescriptorIndex) != 444 ReadOnlyRoots(isolate()).name_string()) { 445 return NoChange(); 446 } 447 if (!descriptors->GetStrongValue(JSFunction::kNameDescriptorIndex) 448 ->IsAccessorInfo()) { 449 return NoChange(); 450 } 451 } 452 453 // Setup the map for the resulting JSBoundFunction with the 454 // correct instance {prototype}. 455 Handle<Map> map( 456 is_constructor 457 ? native_context()->bound_function_with_constructor_map() 458 : native_context()->bound_function_without_constructor_map(), 459 isolate()); 460 if (map->prototype() != *prototype) { 461 map = Map::TransitionToPrototype(isolate(), map, prototype); 462 } 463 464 // Make sure we can rely on the {receiver_maps}. 465 if (result == NodeProperties::kUnreliableReceiverMaps) { 466 effect = graph()->NewNode( 467 simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver, 468 effect, control); 469 } 470 471 // Replace the {node} with a JSCreateBoundFunction. 472 int const arity = std::max(0, node->op()->ValueInputCount() - 3); 473 int const input_count = 2 + arity + 3; 474 Node** inputs = graph()->zone()->NewArray<Node*>(input_count); 475 inputs[0] = receiver; 476 inputs[1] = bound_this; 477 for (int i = 0; i < arity; ++i) { 478 inputs[2 + i] = NodeProperties::GetValueInput(node, 3 + i); 479 } 480 inputs[2 + arity + 0] = context; 481 inputs[2 + arity + 1] = effect; 482 inputs[2 + arity + 2] = control; 483 Node* value = effect = graph()->NewNode( 484 javascript()->CreateBoundFunction(arity, map), input_count, inputs); 485 ReplaceWithValue(node, value, effect, control); 486 return Replace(value); 487 } 488 489 // ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args) 490 Reduction JSCallReducer::ReduceFunctionPrototypeCall(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* effect = NodeProperties::GetEffectInput(node); 495 Node* control = NodeProperties::GetControlInput(node); 496 497 // Change context of {node} to the Function.prototype.call context, 498 // to ensure any exception is thrown in the correct context. 499 Node* context; 500 HeapObjectMatcher m(target); 501 if (m.HasValue()) { 502 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); 503 context = jsgraph()->HeapConstant(handle(function->context(), isolate())); 504 } else { 505 context = effect = graph()->NewNode( 506 simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target, 507 effect, control); 508 } 509 NodeProperties::ReplaceContextInput(node, context); 510 NodeProperties::ReplaceEffectInput(node, effect); 511 512 // Remove the target from {node} and use the receiver as target instead, and 513 // the thisArg becomes the new target. If thisArg was not provided, insert 514 // undefined instead. 515 size_t arity = p.arity(); 516 DCHECK_LE(2u, arity); 517 ConvertReceiverMode convert_mode; 518 if (arity == 2) { 519 // The thisArg was not provided, use undefined as receiver. 520 convert_mode = ConvertReceiverMode::kNullOrUndefined; 521 node->ReplaceInput(0, node->InputAt(1)); 522 node->ReplaceInput(1, jsgraph()->UndefinedConstant()); 523 } else { 524 // Just remove the target, which is the first value input. 525 convert_mode = ConvertReceiverMode::kAny; 526 node->RemoveInput(0); 527 --arity; 528 } 529 NodeProperties::ChangeOp( 530 node, 531 javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode)); 532 // Try to further reduce the JSCall {node}. 533 Reduction const reduction = ReduceJSCall(node); 534 return reduction.Changed() ? reduction : Changed(node); 535 } 536 537 // ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] (V) 538 Reduction JSCallReducer::ReduceFunctionPrototypeHasInstance(Node* node) { 539 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 540 Node* receiver = NodeProperties::GetValueInput(node, 1); 541 Node* object = (node->op()->ValueInputCount() >= 3) 542 ? NodeProperties::GetValueInput(node, 2) 543 : jsgraph()->UndefinedConstant(); 544 Node* context = NodeProperties::GetContextInput(node); 545 Node* frame_state = NodeProperties::GetFrameStateInput(node); 546 Node* effect = NodeProperties::GetEffectInput(node); 547 Node* control = NodeProperties::GetControlInput(node); 548 549 // TODO(turbofan): If JSOrdinaryToInstance raises an exception, the 550 // stack trace doesn't contain the @@hasInstance call; we have the 551 // corresponding bug in the baseline case. Some massaging of the frame 552 // state would be necessary here. 553 554 // Morph this {node} into a JSOrdinaryHasInstance node. 555 node->ReplaceInput(0, receiver); 556 node->ReplaceInput(1, object); 557 node->ReplaceInput(2, context); 558 node->ReplaceInput(3, frame_state); 559 node->ReplaceInput(4, effect); 560 node->ReplaceInput(5, control); 561 node->TrimInputCount(6); 562 NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance()); 563 return Changed(node); 564 } 565 566 Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object) { 567 Node* effect = NodeProperties::GetEffectInput(node); 568 569 // Try to determine the {object} map. 570 ZoneHandleSet<Map> object_maps; 571 NodeProperties::InferReceiverMapsResult result = 572 NodeProperties::InferReceiverMaps(isolate(), object, effect, 573 &object_maps); 574 if (result != NodeProperties::kNoReceiverMaps) { 575 Handle<Map> candidate_map = object_maps[0]; 576 Handle<Object> candidate_prototype(candidate_map->prototype(), isolate()); 577 578 // Check if we can constant-fold the {candidate_prototype}. 579 for (size_t i = 0; i < object_maps.size(); ++i) { 580 Handle<Map> object_map = object_maps[i]; 581 if (object_map->IsSpecialReceiverMap() || 582 object_map->has_hidden_prototype() || 583 object_map->prototype() != *candidate_prototype) { 584 // We exclude special receivers, like JSProxy or API objects that 585 // might require access checks here; we also don't want to deal 586 // with hidden prototypes at this point. 587 return NoChange(); 588 } 589 // The above check also excludes maps for primitive values, which is 590 // important because we are not applying [[ToObject]] here as expected. 591 DCHECK(!object_map->IsPrimitiveMap() && object_map->IsJSReceiverMap()); 592 if (result == NodeProperties::kUnreliableReceiverMaps && 593 !object_map->is_stable()) { 594 return NoChange(); 595 } 596 } 597 if (result == NodeProperties::kUnreliableReceiverMaps) { 598 for (size_t i = 0; i < object_maps.size(); ++i) { 599 dependencies()->DependOnStableMap( 600 MapRef(js_heap_broker(), object_maps[i])); 601 } 602 } 603 Node* value = jsgraph()->Constant(candidate_prototype); 604 ReplaceWithValue(node, value); 605 return Replace(value); 606 } 607 608 return NoChange(); 609 } 610 611 // ES6 section 19.1.2.11 Object.getPrototypeOf ( O ) 612 Reduction JSCallReducer::ReduceObjectGetPrototypeOf(Node* node) { 613 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 614 Node* object = (node->op()->ValueInputCount() >= 3) 615 ? NodeProperties::GetValueInput(node, 2) 616 : jsgraph()->UndefinedConstant(); 617 return ReduceObjectGetPrototype(node, object); 618 } 619 620 // ES section #sec-object.is 621 Reduction JSCallReducer::ReduceObjectIs(Node* node) { 622 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 623 CallParameters const& params = CallParametersOf(node->op()); 624 int const argc = static_cast<int>(params.arity() - 2); 625 Node* lhs = (argc >= 1) ? NodeProperties::GetValueInput(node, 2) 626 : jsgraph()->UndefinedConstant(); 627 Node* rhs = (argc >= 2) ? NodeProperties::GetValueInput(node, 3) 628 : jsgraph()->UndefinedConstant(); 629 Node* value = graph()->NewNode(simplified()->SameValue(), lhs, rhs); 630 ReplaceWithValue(node, value); 631 return Replace(value); 632 } 633 634 // ES6 section B.2.2.1.1 get Object.prototype.__proto__ 635 Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) { 636 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 637 Node* receiver = NodeProperties::GetValueInput(node, 1); 638 return ReduceObjectGetPrototype(node, receiver); 639 } 640 641 // ES #sec-object.prototype.hasownproperty 642 Reduction JSCallReducer::ReduceObjectPrototypeHasOwnProperty(Node* node) { 643 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 644 CallParameters const& params = CallParametersOf(node->op()); 645 int const argc = static_cast<int>(params.arity() - 2); 646 Node* receiver = NodeProperties::GetValueInput(node, 1); 647 Node* name = (argc >= 1) ? NodeProperties::GetValueInput(node, 2) 648 : jsgraph()->UndefinedConstant(); 649 Node* effect = NodeProperties::GetEffectInput(node); 650 Node* control = NodeProperties::GetControlInput(node); 651 652 // We can optimize a call to Object.prototype.hasOwnProperty if it's being 653 // used inside a fast-mode for..in, so for code like this: 654 // 655 // for (name in receiver) { 656 // if (receiver.hasOwnProperty(name)) { 657 // ... 658 // } 659 // } 660 // 661 // If the for..in is in fast-mode, we know that the {receiver} has {name} 662 // as own property, otherwise the enumeration wouldn't include it. The graph 663 // constructed by the BytecodeGraphBuilder in this case looks like this: 664 665 // receiver 666 // ^ ^ 667 // | | 668 // | +-+ 669 // | | 670 // | JSToObject 671 // | ^ 672 // | | 673 // | JSForInNext 674 // | ^ 675 // +----+ | 676 // | | 677 // JSCall[hasOwnProperty] 678 679 // We can constant-fold the {node} to True in this case, and insert 680 // a (potentially redundant) map check to guard the fact that the 681 // {receiver} map didn't change since the dominating JSForInNext. This 682 // map check is only necessary when TurboFan cannot prove that there 683 // is no observable side effect between the {JSForInNext} and the 684 // {JSCall} to Object.prototype.hasOwnProperty. 685 // 686 // Also note that it's safe to look through the {JSToObject}, since the 687 // Object.prototype.hasOwnProperty does an implicit ToObject anyway, and 688 // these operations are not observable. 689 if (name->opcode() == IrOpcode::kJSForInNext) { 690 ForInMode const mode = ForInModeOf(name->op()); 691 if (mode != ForInMode::kGeneric) { 692 Node* object = NodeProperties::GetValueInput(name, 0); 693 Node* cache_type = NodeProperties::GetValueInput(name, 2); 694 if (object->opcode() == IrOpcode::kJSToObject) { 695 object = NodeProperties::GetValueInput(object, 0); 696 } 697 if (object == receiver) { 698 // No need to repeat the map check if we can prove that there's no 699 // observable side effect between {effect} and {name]. 700 if (!NodeProperties::NoObservableSideEffectBetween(effect, name)) { 701 Node* receiver_map = effect = 702 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), 703 receiver, effect, control); 704 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), 705 receiver_map, cache_type); 706 effect = graph()->NewNode( 707 simplified()->CheckIf(DeoptimizeReason::kWrongMap), check, effect, 708 control); 709 } 710 Node* value = jsgraph()->TrueConstant(); 711 ReplaceWithValue(node, value, effect, control); 712 return Replace(value); 713 } 714 } 715 } 716 717 return NoChange(); 718 } 719 720 // ES #sec-object.prototype.isprototypeof 721 Reduction JSCallReducer::ReduceObjectPrototypeIsPrototypeOf(Node* node) { 722 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 723 Node* receiver = NodeProperties::GetValueInput(node, 1); 724 Node* value = node->op()->ValueInputCount() > 2 725 ? NodeProperties::GetValueInput(node, 2) 726 : jsgraph()->UndefinedConstant(); 727 Node* effect = NodeProperties::GetEffectInput(node); 728 729 // Ensure that the {receiver} is known to be a JSReceiver (so that 730 // the ToObject step of Object.prototype.isPrototypeOf is a no-op). 731 ZoneHandleSet<Map> receiver_maps; 732 NodeProperties::InferReceiverMapsResult result = 733 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 734 &receiver_maps); 735 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 736 for (size_t i = 0; i < receiver_maps.size(); ++i) { 737 if (!receiver_maps[i]->IsJSReceiverMap()) return NoChange(); 738 } 739 740 // We don't check whether {value} is a proper JSReceiver here explicitly, 741 // and don't explicitly rule out Primitive {value}s, since all of them 742 // have null as their prototype, so the prototype chain walk inside the 743 // JSHasInPrototypeChain operator immediately aborts and yields false. 744 NodeProperties::ReplaceValueInput(node, value, 0); 745 NodeProperties::ReplaceValueInput(node, receiver, 1); 746 for (int i = node->op()->ValueInputCount(); i-- > 2;) { 747 node->RemoveInput(i); 748 } 749 NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain()); 750 return Changed(node); 751 } 752 753 // ES6 section 26.1.1 Reflect.apply ( target, thisArgument, argumentsList ) 754 Reduction JSCallReducer::ReduceReflectApply(Node* node) { 755 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 756 CallParameters const& p = CallParametersOf(node->op()); 757 int arity = static_cast<int>(p.arity() - 2); 758 DCHECK_LE(0, arity); 759 // Massage value inputs appropriately. 760 node->RemoveInput(0); 761 node->RemoveInput(0); 762 while (arity < 3) { 763 node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant()); 764 } 765 while (arity-- > 3) { 766 node->RemoveInput(arity); 767 } 768 NodeProperties::ChangeOp(node, 769 javascript()->CallWithArrayLike(p.frequency())); 770 Reduction const reduction = ReduceJSCallWithArrayLike(node); 771 return reduction.Changed() ? reduction : Changed(node); 772 } 773 774 // ES6 section 26.1.2 Reflect.construct ( target, argumentsList [, newTarget] ) 775 Reduction JSCallReducer::ReduceReflectConstruct(Node* node) { 776 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 777 CallParameters const& p = CallParametersOf(node->op()); 778 int arity = static_cast<int>(p.arity() - 2); 779 DCHECK_LE(0, arity); 780 // Massage value inputs appropriately. 781 node->RemoveInput(0); 782 node->RemoveInput(0); 783 while (arity < 2) { 784 node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant()); 785 } 786 if (arity < 3) { 787 node->InsertInput(graph()->zone(), arity++, node->InputAt(0)); 788 } 789 while (arity-- > 3) { 790 node->RemoveInput(arity); 791 } 792 NodeProperties::ChangeOp(node, 793 javascript()->ConstructWithArrayLike(p.frequency())); 794 Reduction const reduction = ReduceJSConstructWithArrayLike(node); 795 return reduction.Changed() ? reduction : Changed(node); 796 } 797 798 // ES6 section 26.1.7 Reflect.getPrototypeOf ( target ) 799 Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) { 800 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 801 Node* target = (node->op()->ValueInputCount() >= 3) 802 ? NodeProperties::GetValueInput(node, 2) 803 : jsgraph()->UndefinedConstant(); 804 return ReduceObjectGetPrototype(node, target); 805 } 806 807 // ES6 section #sec-object.create Object.create(proto, properties) 808 Reduction JSCallReducer::ReduceObjectCreate(Node* node) { 809 int arg_count = node->op()->ValueInputCount(); 810 Node* properties = arg_count >= 4 ? NodeProperties::GetValueInput(node, 3) 811 : jsgraph()->UndefinedConstant(); 812 if (properties != jsgraph()->UndefinedConstant()) return NoChange(); 813 814 Node* effect = NodeProperties::GetEffectInput(node); 815 Node* control = NodeProperties::GetControlInput(node); 816 Node* context = NodeProperties::GetContextInput(node); 817 Node* frame_state = NodeProperties::GetFrameStateInput(node); 818 Node* prototype = arg_count >= 3 ? NodeProperties::GetValueInput(node, 2) 819 : jsgraph()->UndefinedConstant(); 820 node->ReplaceInput(0, prototype); 821 node->ReplaceInput(1, context); 822 node->ReplaceInput(2, frame_state); 823 node->ReplaceInput(3, effect); 824 node->ReplaceInput(4, control); 825 node->TrimInputCount(5); 826 NodeProperties::ChangeOp(node, javascript()->CreateObject()); 827 return Changed(node); 828 } 829 830 // ES section #sec-reflect.get 831 Reduction JSCallReducer::ReduceReflectGet(Node* node) { 832 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 833 CallParameters const& p = CallParametersOf(node->op()); 834 int arity = static_cast<int>(p.arity() - 2); 835 if (arity != 2) return NoChange(); 836 Node* target = NodeProperties::GetValueInput(node, 2); 837 Node* key = NodeProperties::GetValueInput(node, 3); 838 Node* context = NodeProperties::GetContextInput(node); 839 Node* frame_state = NodeProperties::GetFrameStateInput(node); 840 Node* effect = NodeProperties::GetEffectInput(node); 841 Node* control = NodeProperties::GetControlInput(node); 842 843 // Check whether {target} is a JSReceiver. 844 Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target); 845 Node* branch = 846 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); 847 848 // Throw an appropriate TypeError if the {target} is not a JSReceiver. 849 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 850 Node* efalse = effect; 851 { 852 if_false = efalse = graph()->NewNode( 853 javascript()->CallRuntime(Runtime::kThrowTypeError, 2), 854 jsgraph()->Constant(MessageTemplate::kCalledOnNonObject), 855 jsgraph()->HeapConstant( 856 factory()->NewStringFromAsciiChecked("Reflect.get")), 857 context, frame_state, efalse, if_false); 858 } 859 860 // Otherwise just use the existing GetPropertyStub. 861 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 862 Node* etrue = effect; 863 Node* vtrue; 864 { 865 Callable callable = 866 Builtins::CallableFor(isolate(), Builtins::kGetProperty); 867 auto call_descriptor = Linkage::GetStubCallDescriptor( 868 graph()->zone(), callable.descriptor(), 0, 869 CallDescriptor::kNeedsFrameState, Operator::kNoProperties); 870 Node* stub_code = jsgraph()->HeapConstant(callable.code()); 871 vtrue = etrue = if_true = 872 graph()->NewNode(common()->Call(call_descriptor), stub_code, target, 873 key, context, frame_state, etrue, if_true); 874 } 875 876 // Rewire potential exception edges. 877 Node* on_exception = nullptr; 878 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { 879 // Create appropriate {IfException} and {IfSuccess} nodes. 880 Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true); 881 if_true = graph()->NewNode(common()->IfSuccess(), if_true); 882 Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false); 883 if_false = graph()->NewNode(common()->IfSuccess(), if_false); 884 885 // Join the exception edges. 886 Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse); 887 Node* ephi = 888 graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge); 889 Node* phi = 890 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 891 extrue, exfalse, merge); 892 ReplaceWithValue(on_exception, phi, ephi, merge); 893 } 894 895 // Connect the throwing path to end. 896 if_false = graph()->NewNode(common()->Throw(), efalse, if_false); 897 NodeProperties::MergeControlToEnd(graph(), common(), if_false); 898 899 // Continue on the regular path. 900 ReplaceWithValue(node, vtrue, etrue, if_true); 901 return Changed(vtrue); 902 } 903 904 // ES section #sec-reflect.has 905 Reduction JSCallReducer::ReduceReflectHas(Node* node) { 906 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 907 CallParameters const& p = CallParametersOf(node->op()); 908 int arity = static_cast<int>(p.arity() - 2); 909 DCHECK_LE(0, arity); 910 Node* target = (arity >= 1) ? NodeProperties::GetValueInput(node, 2) 911 : jsgraph()->UndefinedConstant(); 912 Node* key = (arity >= 2) ? NodeProperties::GetValueInput(node, 3) 913 : jsgraph()->UndefinedConstant(); 914 Node* context = NodeProperties::GetContextInput(node); 915 Node* frame_state = NodeProperties::GetFrameStateInput(node); 916 Node* effect = NodeProperties::GetEffectInput(node); 917 Node* control = NodeProperties::GetControlInput(node); 918 919 // Check whether {target} is a JSReceiver. 920 Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target); 921 Node* branch = 922 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); 923 924 // Throw an appropriate TypeError if the {target} is not a JSReceiver. 925 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 926 Node* efalse = effect; 927 { 928 if_false = efalse = graph()->NewNode( 929 javascript()->CallRuntime(Runtime::kThrowTypeError, 2), 930 jsgraph()->Constant(MessageTemplate::kCalledOnNonObject), 931 jsgraph()->HeapConstant( 932 factory()->NewStringFromAsciiChecked("Reflect.has")), 933 context, frame_state, efalse, if_false); 934 } 935 936 // Otherwise just use the existing {JSHasProperty} logic. 937 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 938 Node* etrue = effect; 939 Node* vtrue; 940 { 941 vtrue = etrue = if_true = 942 graph()->NewNode(javascript()->HasProperty(), target, key, context, 943 frame_state, etrue, if_true); 944 } 945 946 // Rewire potential exception edges. 947 Node* on_exception = nullptr; 948 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { 949 // Create appropriate {IfException} and {IfSuccess} nodes. 950 Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true); 951 if_true = graph()->NewNode(common()->IfSuccess(), if_true); 952 Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false); 953 if_false = graph()->NewNode(common()->IfSuccess(), if_false); 954 955 // Join the exception edges. 956 Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse); 957 Node* ephi = 958 graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge); 959 Node* phi = 960 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 961 extrue, exfalse, merge); 962 ReplaceWithValue(on_exception, phi, ephi, merge); 963 } 964 965 // Connect the throwing path to end. 966 if_false = graph()->NewNode(common()->Throw(), efalse, if_false); 967 NodeProperties::MergeControlToEnd(graph(), common(), if_false); 968 969 // Continue on the regular path. 970 ReplaceWithValue(node, vtrue, etrue, if_true); 971 return Changed(vtrue); 972 } 973 974 bool CanInlineArrayIteratingBuiltin(Isolate* isolate, 975 Handle<Map> receiver_map) { 976 if (!receiver_map->prototype()->IsJSArray()) return false; 977 Handle<JSArray> receiver_prototype(JSArray::cast(receiver_map->prototype()), 978 isolate); 979 return receiver_map->instance_type() == JS_ARRAY_TYPE && 980 IsFastElementsKind(receiver_map->elements_kind()) && 981 isolate->IsNoElementsProtectorIntact() && 982 isolate->IsAnyInitialArrayPrototype(receiver_prototype); 983 } 984 985 Node* JSCallReducer::WireInLoopStart(Node* k, Node** control, Node** effect) { 986 Node* loop = *control = 987 graph()->NewNode(common()->Loop(2), *control, *control); 988 Node* eloop = *effect = 989 graph()->NewNode(common()->EffectPhi(2), *effect, *effect, loop); 990 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); 991 NodeProperties::MergeControlToEnd(graph(), common(), terminate); 992 return graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), k, 993 k, loop); 994 } 995 996 void JSCallReducer::WireInLoopEnd(Node* loop, Node* eloop, Node* vloop, Node* k, 997 Node* control, Node* effect) { 998 loop->ReplaceInput(1, control); 999 vloop->ReplaceInput(1, k); 1000 eloop->ReplaceInput(1, effect); 1001 } 1002 1003 Reduction JSCallReducer::ReduceArrayForEach(Node* node, 1004 Handle<SharedFunctionInfo> shared) { 1005 if (!FLAG_turbo_inline_array_builtins) return NoChange(); 1006 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 1007 CallParameters const& p = CallParametersOf(node->op()); 1008 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 1009 return NoChange(); 1010 } 1011 1012 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); 1013 Node* effect = NodeProperties::GetEffectInput(node); 1014 Node* control = NodeProperties::GetControlInput(node); 1015 Node* context = NodeProperties::GetContextInput(node); 1016 1017 // Try to determine the {receiver} map. 1018 Node* receiver = NodeProperties::GetValueInput(node, 1); 1019 Node* fncallback = node->op()->ValueInputCount() > 2 1020 ? NodeProperties::GetValueInput(node, 2) 1021 : jsgraph()->UndefinedConstant(); 1022 Node* this_arg = node->op()->ValueInputCount() > 3 1023 ? NodeProperties::GetValueInput(node, 3) 1024 : jsgraph()->UndefinedConstant(); 1025 ZoneHandleSet<Map> receiver_maps; 1026 NodeProperties::InferReceiverMapsResult result = 1027 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 1028 &receiver_maps); 1029 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 1030 1031 // By ensuring that {kind} is object or double, we can be polymorphic 1032 // on different elements kinds. 1033 ElementsKind kind = receiver_maps[0]->elements_kind(); 1034 if (IsSmiElementsKind(kind)) { 1035 kind = FastSmiToObjectElementsKind(kind); 1036 } 1037 for (Handle<Map> receiver_map : receiver_maps) { 1038 ElementsKind next_kind = receiver_map->elements_kind(); 1039 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) { 1040 return NoChange(); 1041 } 1042 if (!IsFastElementsKind(next_kind)) { 1043 return NoChange(); 1044 } 1045 if (IsDoubleElementsKind(kind) != IsDoubleElementsKind(next_kind)) { 1046 return NoChange(); 1047 } 1048 if (IsHoleyElementsKind(next_kind)) { 1049 kind = GetHoleyElementsKind(kind); 1050 } 1051 } 1052 1053 // Install code dependencies on the {receiver} prototype maps and the 1054 // global array protector cell. 1055 dependencies()->DependOnProtector( 1056 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); 1057 1058 // If we have unreliable maps, we need a map check. 1059 if (result == NodeProperties::kUnreliableReceiverMaps) { 1060 effect = 1061 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 1062 receiver_maps, p.feedback()), 1063 receiver, effect, control); 1064 } 1065 1066 Node* k = jsgraph()->ZeroConstant(); 1067 1068 Node* original_length = effect = graph()->NewNode( 1069 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, 1070 effect, control); 1071 1072 std::vector<Node*> checkpoint_params( 1073 {receiver, fncallback, this_arg, k, original_length}); 1074 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 1075 1076 // Check whether the given callback function is callable. Note that this has 1077 // to happen outside the loop to make sure we also throw on empty arrays. 1078 Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState( 1079 jsgraph(), shared, Builtins::kArrayForEachLoopLazyDeoptContinuation, 1080 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 1081 outer_frame_state, ContinuationFrameStateMode::LAZY); 1082 Node* check_fail = nullptr; 1083 Node* check_throw = nullptr; 1084 WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect, 1085 &control, &check_fail, &check_throw); 1086 1087 // Start the loop. 1088 Node* vloop = k = WireInLoopStart(k, &control, &effect); 1089 Node *loop = control, *eloop = effect; 1090 checkpoint_params[3] = k; 1091 1092 Node* continue_test = 1093 graph()->NewNode(simplified()->NumberLessThan(), k, original_length); 1094 Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), 1095 continue_test, control); 1096 1097 Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch); 1098 Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch); 1099 control = if_true; 1100 1101 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 1102 jsgraph(), shared, Builtins::kArrayForEachLoopEagerDeoptContinuation, 1103 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 1104 outer_frame_state, ContinuationFrameStateMode::EAGER); 1105 1106 effect = 1107 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); 1108 1109 // Make sure the map hasn't changed during the iteration 1110 effect = 1111 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 1112 receiver_maps, p.feedback()), 1113 receiver, effect, control); 1114 1115 Node* element = 1116 SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); 1117 1118 Node* next_k = 1119 graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant()); 1120 checkpoint_params[3] = next_k; 1121 1122 Node* hole_true = nullptr; 1123 Node* hole_false = nullptr; 1124 Node* effect_true = effect; 1125 1126 if (IsHoleyElementsKind(kind)) { 1127 // Holey elements kind require a hole check and skipping of the element in 1128 // the case of a hole. 1129 Node* check; 1130 if (IsDoubleElementsKind(kind)) { 1131 check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element); 1132 } else { 1133 check = graph()->NewNode(simplified()->ReferenceEqual(), element, 1134 jsgraph()->TheHoleConstant()); 1135 } 1136 Node* branch = 1137 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); 1138 hole_true = graph()->NewNode(common()->IfTrue(), branch); 1139 hole_false = graph()->NewNode(common()->IfFalse(), branch); 1140 control = hole_false; 1141 1142 // The contract is that we don't leak "the hole" into "user JavaScript", 1143 // so we must rename the {element} here to explicitly exclude "the hole" 1144 // from the type of {element}. 1145 element = effect = graph()->NewNode( 1146 common()->TypeGuard(Type::NonInternal()), element, effect, control); 1147 } 1148 1149 frame_state = CreateJavaScriptBuiltinContinuationFrameState( 1150 jsgraph(), shared, Builtins::kArrayForEachLoopLazyDeoptContinuation, 1151 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 1152 outer_frame_state, ContinuationFrameStateMode::LAZY); 1153 1154 control = effect = graph()->NewNode( 1155 javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k, 1156 receiver, context, frame_state, effect, control); 1157 1158 // Rewire potential exception edges. 1159 Node* on_exception = nullptr; 1160 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { 1161 RewirePostCallbackExceptionEdges(check_throw, on_exception, effect, 1162 &check_fail, &control); 1163 } 1164 1165 if (IsHoleyElementsKind(kind)) { 1166 Node* after_call_control = control; 1167 Node* after_call_effect = effect; 1168 control = hole_true; 1169 effect = effect_true; 1170 1171 control = graph()->NewNode(common()->Merge(2), control, after_call_control); 1172 effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect, 1173 control); 1174 } 1175 1176 WireInLoopEnd(loop, eloop, vloop, next_k, control, effect); 1177 1178 control = if_false; 1179 effect = eloop; 1180 1181 // Wire up the branch for the case when IsCallable fails for the callback. 1182 // Since {check_throw} is an unconditional throw, it's impossible to 1183 // return a successful completion. Therefore, we simply connect the successful 1184 // completion to the graph end. 1185 Node* throw_node = 1186 graph()->NewNode(common()->Throw(), check_throw, check_fail); 1187 NodeProperties::MergeControlToEnd(graph(), common(), throw_node); 1188 1189 ReplaceWithValue(node, jsgraph()->UndefinedConstant(), effect, control); 1190 return Replace(jsgraph()->UndefinedConstant()); 1191 } 1192 1193 Reduction JSCallReducer::ReduceArrayReduce(Node* node, 1194 ArrayReduceDirection direction, 1195 Handle<SharedFunctionInfo> shared) { 1196 if (!FLAG_turbo_inline_array_builtins) return NoChange(); 1197 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 1198 CallParameters const& p = CallParametersOf(node->op()); 1199 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 1200 return NoChange(); 1201 } 1202 bool left = direction == ArrayReduceDirection::kLeft; 1203 1204 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); 1205 Node* effect = NodeProperties::GetEffectInput(node); 1206 Node* control = NodeProperties::GetControlInput(node); 1207 Node* context = NodeProperties::GetContextInput(node); 1208 1209 // Try to determine the {receiver} map. 1210 Node* receiver = NodeProperties::GetValueInput(node, 1); 1211 Node* fncallback = node->op()->ValueInputCount() > 2 1212 ? NodeProperties::GetValueInput(node, 2) 1213 : jsgraph()->UndefinedConstant(); 1214 1215 ZoneHandleSet<Map> receiver_maps; 1216 NodeProperties::InferReceiverMapsResult result = 1217 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 1218 &receiver_maps); 1219 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 1220 1221 ElementsKind kind = receiver_maps[0]->elements_kind(); 1222 for (Handle<Map> receiver_map : receiver_maps) { 1223 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) 1224 return NoChange(); 1225 if (!UnionElementsKindUptoSize(&kind, receiver_map->elements_kind())) 1226 return NoChange(); 1227 } 1228 1229 std::function<Node*(Node*)> hole_check = [this, kind](Node* element) { 1230 if (IsDoubleElementsKind(kind)) { 1231 return graph()->NewNode(simplified()->NumberIsFloat64Hole(), element); 1232 } else { 1233 return graph()->NewNode(simplified()->ReferenceEqual(), element, 1234 jsgraph()->TheHoleConstant()); 1235 } 1236 }; 1237 1238 // Install code dependencies on the {receiver} prototype maps and the 1239 // global array protector cell. 1240 dependencies()->DependOnProtector( 1241 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); 1242 1243 // If we have unreliable maps, we need a map check. 1244 if (result == NodeProperties::kUnreliableReceiverMaps) { 1245 effect = 1246 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 1247 receiver_maps, p.feedback()), 1248 receiver, effect, control); 1249 } 1250 1251 Node* original_length = effect = graph()->NewNode( 1252 simplified()->LoadField(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS)), 1253 receiver, effect, control); 1254 1255 Node* initial_index = 1256 left ? jsgraph()->ZeroConstant() 1257 : graph()->NewNode(simplified()->NumberSubtract(), original_length, 1258 jsgraph()->OneConstant()); 1259 const Operator* next_op = 1260 left ? simplified()->NumberAdd() : simplified()->NumberSubtract(); 1261 Node* k = initial_index; 1262 1263 Node* check_frame_state; 1264 { 1265 Builtins::Name builtin_lazy = 1266 left ? Builtins::kArrayReduceLoopLazyDeoptContinuation 1267 : Builtins::kArrayReduceRightLoopLazyDeoptContinuation; 1268 const std::vector<Node*> checkpoint_params( 1269 {receiver, fncallback, k, original_length, 1270 jsgraph()->UndefinedConstant()}); 1271 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 1272 check_frame_state = CreateJavaScriptBuiltinContinuationFrameState( 1273 jsgraph(), shared, builtin_lazy, node->InputAt(0), context, 1274 checkpoint_params.data(), stack_parameters - 1, outer_frame_state, 1275 ContinuationFrameStateMode::LAZY); 1276 } 1277 Node* check_fail = nullptr; 1278 Node* check_throw = nullptr; 1279 // Check whether the given callback function is callable. Note that 1280 // this has to happen outside the loop to make sure we also throw on 1281 // empty arrays. 1282 WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect, 1283 &control, &check_fail, &check_throw); 1284 1285 // Set initial accumulator value 1286 Node* cur = jsgraph()->TheHoleConstant(); 1287 1288 if (node->op()->ValueInputCount() > 3) { 1289 cur = NodeProperties::GetValueInput(node, 3); 1290 } else { 1291 // Find first/last non holey element. In case the search fails, we need a 1292 // deopt continuation. 1293 Builtins::Name builtin_eager = 1294 left ? Builtins::kArrayReducePreLoopEagerDeoptContinuation 1295 : Builtins::kArrayReduceRightPreLoopEagerDeoptContinuation; 1296 const std::vector<Node*> checkpoint_params( 1297 {receiver, fncallback, original_length}); 1298 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 1299 Node* find_first_element_frame_state = 1300 CreateJavaScriptBuiltinContinuationFrameState( 1301 jsgraph(), shared, builtin_eager, node->InputAt(0), context, 1302 checkpoint_params.data(), stack_parameters, outer_frame_state, 1303 ContinuationFrameStateMode::EAGER); 1304 1305 Node* vloop = k = WireInLoopStart(k, &control, &effect); 1306 Node* loop = control; 1307 Node* eloop = effect; 1308 effect = graph()->NewNode(common()->Checkpoint(), 1309 find_first_element_frame_state, effect, control); 1310 Node* continue_test = 1311 left ? graph()->NewNode(simplified()->NumberLessThan(), k, 1312 original_length) 1313 : graph()->NewNode(simplified()->NumberLessThanOrEqual(), 1314 jsgraph()->ZeroConstant(), k); 1315 effect = graph()->NewNode( 1316 simplified()->CheckIf(DeoptimizeReason::kNoInitialElement), 1317 continue_test, effect, control); 1318 1319 cur = SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); 1320 Node* next_k = graph()->NewNode(next_op, k, jsgraph()->OneConstant()); 1321 1322 Node* hole_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), 1323 hole_check(cur), control); 1324 Node* found_el = graph()->NewNode(common()->IfFalse(), hole_branch); 1325 control = found_el; 1326 Node* is_hole = graph()->NewNode(common()->IfTrue(), hole_branch); 1327 1328 WireInLoopEnd(loop, eloop, vloop, next_k, is_hole, effect); 1329 // We did the hole-check, so exclude hole from the type. 1330 cur = effect = graph()->NewNode(common()->TypeGuard(Type::NonInternal()), 1331 cur, effect, control); 1332 k = next_k; 1333 } 1334 1335 // Start the loop. 1336 Node* loop = control = graph()->NewNode(common()->Loop(2), control, control); 1337 Node* eloop = effect = 1338 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); 1339 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); 1340 NodeProperties::MergeControlToEnd(graph(), common(), terminate); 1341 Node* kloop = k = graph()->NewNode( 1342 common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop); 1343 Node* curloop = cur = graph()->NewNode( 1344 common()->Phi(MachineRepresentation::kTagged, 2), cur, cur, loop); 1345 1346 control = loop; 1347 effect = eloop; 1348 1349 Node* continue_test = 1350 left 1351 ? graph()->NewNode(simplified()->NumberLessThan(), k, original_length) 1352 : graph()->NewNode(simplified()->NumberLessThanOrEqual(), 1353 jsgraph()->ZeroConstant(), k); 1354 1355 Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), 1356 continue_test, control); 1357 1358 Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch); 1359 Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch); 1360 control = if_true; 1361 1362 { 1363 Builtins::Name builtin_eager = 1364 left ? Builtins::kArrayReduceLoopEagerDeoptContinuation 1365 : Builtins::kArrayReduceRightLoopEagerDeoptContinuation; 1366 const std::vector<Node*> checkpoint_params( 1367 {receiver, fncallback, k, original_length, curloop}); 1368 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 1369 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 1370 jsgraph(), shared, builtin_eager, node->InputAt(0), context, 1371 checkpoint_params.data(), stack_parameters, outer_frame_state, 1372 ContinuationFrameStateMode::EAGER); 1373 effect = 1374 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); 1375 } 1376 1377 // Make sure the map hasn't changed during the iteration 1378 effect = graph()->NewNode( 1379 simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver, 1380 effect, control); 1381 1382 Node* element = 1383 SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); 1384 1385 Node* next_k = graph()->NewNode(next_op, k, jsgraph()->OneConstant()); 1386 1387 Node* hole_true = nullptr; 1388 Node* hole_false = nullptr; 1389 Node* effect_true = effect; 1390 1391 if (IsHoleyElementsKind(kind)) { 1392 // Holey elements kind require a hole check and skipping of the element in 1393 // the case of a hole. 1394 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), 1395 hole_check(element), control); 1396 hole_true = graph()->NewNode(common()->IfTrue(), branch); 1397 hole_false = graph()->NewNode(common()->IfFalse(), branch); 1398 control = hole_false; 1399 1400 // The contract is that we don't leak "the hole" into "user JavaScript", 1401 // so we must rename the {element} here to explicitly exclude "the hole" 1402 // from the type of {element}. 1403 element = effect = graph()->NewNode( 1404 common()->TypeGuard(Type::NonInternal()), element, effect, control); 1405 } 1406 1407 Node* next_cur; 1408 { 1409 Builtins::Name builtin_lazy = 1410 left ? Builtins::kArrayReduceLoopLazyDeoptContinuation 1411 : Builtins::kArrayReduceRightLoopLazyDeoptContinuation; 1412 const std::vector<Node*> checkpoint_params( 1413 {receiver, fncallback, next_k, original_length, curloop}); 1414 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 1415 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 1416 jsgraph(), shared, builtin_lazy, node->InputAt(0), context, 1417 checkpoint_params.data(), stack_parameters - 1, outer_frame_state, 1418 ContinuationFrameStateMode::LAZY); 1419 1420 next_cur = control = effect = 1421 graph()->NewNode(javascript()->Call(6, p.frequency()), fncallback, 1422 jsgraph()->UndefinedConstant(), cur, element, k, 1423 receiver, context, frame_state, effect, control); 1424 } 1425 1426 // Rewire potential exception edges. 1427 Node* on_exception = nullptr; 1428 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { 1429 RewirePostCallbackExceptionEdges(check_throw, on_exception, effect, 1430 &check_fail, &control); 1431 } 1432 1433 if (IsHoleyElementsKind(kind)) { 1434 Node* after_call_control = control; 1435 Node* after_call_effect = effect; 1436 control = hole_true; 1437 effect = effect_true; 1438 1439 control = graph()->NewNode(common()->Merge(2), control, after_call_control); 1440 effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect, 1441 control); 1442 next_cur = 1443 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), cur, 1444 next_cur, control); 1445 } 1446 1447 k = next_k; 1448 cur = next_cur; 1449 1450 loop->ReplaceInput(1, control); 1451 kloop->ReplaceInput(1, k); 1452 curloop->ReplaceInput(1, cur); 1453 eloop->ReplaceInput(1, effect); 1454 1455 control = if_false; 1456 effect = eloop; 1457 1458 // Wire up the branch for the case when IsCallable fails for the callback. 1459 // Since {check_throw} is an unconditional throw, it's impossible to 1460 // return a successful completion. Therefore, we simply connect the successful 1461 // completion to the graph end. 1462 Node* throw_node = 1463 graph()->NewNode(common()->Throw(), check_throw, check_fail); 1464 NodeProperties::MergeControlToEnd(graph(), common(), throw_node); 1465 1466 ReplaceWithValue(node, curloop, effect, control); 1467 return Replace(curloop); 1468 } 1469 1470 Reduction JSCallReducer::ReduceArrayMap(Node* node, 1471 Handle<SharedFunctionInfo> shared) { 1472 if (!FLAG_turbo_inline_array_builtins) return NoChange(); 1473 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 1474 CallParameters const& p = CallParametersOf(node->op()); 1475 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 1476 return NoChange(); 1477 } 1478 1479 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); 1480 Node* effect = NodeProperties::GetEffectInput(node); 1481 Node* control = NodeProperties::GetControlInput(node); 1482 Node* context = NodeProperties::GetContextInput(node); 1483 1484 // Try to determine the {receiver} map. 1485 Node* receiver = NodeProperties::GetValueInput(node, 1); 1486 Node* fncallback = node->op()->ValueInputCount() > 2 1487 ? NodeProperties::GetValueInput(node, 2) 1488 : jsgraph()->UndefinedConstant(); 1489 Node* this_arg = node->op()->ValueInputCount() > 3 1490 ? NodeProperties::GetValueInput(node, 3) 1491 : jsgraph()->UndefinedConstant(); 1492 ZoneHandleSet<Map> receiver_maps; 1493 NodeProperties::InferReceiverMapsResult result = 1494 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 1495 &receiver_maps); 1496 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 1497 1498 // Ensure that any changes to the Array species constructor cause deopt. 1499 if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange(); 1500 1501 const ElementsKind kind = receiver_maps[0]->elements_kind(); 1502 1503 for (Handle<Map> receiver_map : receiver_maps) { 1504 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) 1505 return NoChange(); 1506 // We can handle different maps, as long as their elements kind are the 1507 // same. 1508 if (receiver_map->elements_kind() != kind) return NoChange(); 1509 } 1510 1511 if (IsHoleyElementsKind(kind)) { 1512 dependencies()->DependOnProtector( 1513 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); 1514 } 1515 1516 dependencies()->DependOnProtector( 1517 PropertyCellRef(js_heap_broker(), factory()->array_species_protector())); 1518 1519 Handle<JSFunction> handle_constructor( 1520 JSFunction::cast( 1521 native_context()->GetInitialJSArrayMap(kind)->GetConstructor()), 1522 isolate()); 1523 Node* array_constructor = jsgraph()->HeapConstant(handle_constructor); 1524 1525 1526 Node* k = jsgraph()->ZeroConstant(); 1527 1528 // If we have unreliable maps, we need a map check. 1529 if (result == NodeProperties::kUnreliableReceiverMaps) { 1530 effect = 1531 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 1532 receiver_maps, p.feedback()), 1533 receiver, effect, control); 1534 } 1535 1536 Node* original_length = effect = graph()->NewNode( 1537 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, 1538 effect, control); 1539 1540 // Even though {JSCreateArray} is not marked as {kNoThrow}, we can elide the 1541 // exceptional projections because it cannot throw with the given parameters. 1542 Node* a = control = effect = graph()->NewNode( 1543 javascript()->CreateArray(1, MaybeHandle<AllocationSite>()), 1544 array_constructor, array_constructor, original_length, context, 1545 outer_frame_state, effect, control); 1546 1547 std::vector<Node*> checkpoint_params( 1548 {receiver, fncallback, this_arg, a, k, original_length}); 1549 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 1550 1551 // Check whether the given callback function is callable. Note that this has 1552 // to happen outside the loop to make sure we also throw on empty arrays. 1553 Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState( 1554 jsgraph(), shared, Builtins::kArrayMapLoopLazyDeoptContinuation, 1555 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 1556 outer_frame_state, ContinuationFrameStateMode::LAZY); 1557 Node* check_fail = nullptr; 1558 Node* check_throw = nullptr; 1559 WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect, 1560 &control, &check_fail, &check_throw); 1561 1562 // Start the loop. 1563 Node* vloop = k = WireInLoopStart(k, &control, &effect); 1564 Node *loop = control, *eloop = effect; 1565 checkpoint_params[4] = k; 1566 1567 Node* continue_test = 1568 graph()->NewNode(simplified()->NumberLessThan(), k, original_length); 1569 Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), 1570 continue_test, control); 1571 1572 Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch); 1573 Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch); 1574 control = if_true; 1575 1576 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 1577 jsgraph(), shared, Builtins::kArrayMapLoopEagerDeoptContinuation, 1578 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 1579 outer_frame_state, ContinuationFrameStateMode::EAGER); 1580 1581 effect = 1582 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); 1583 1584 // Make sure the map hasn't changed during the iteration 1585 effect = 1586 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 1587 receiver_maps, p.feedback()), 1588 receiver, effect, control); 1589 1590 Node* element = 1591 SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); 1592 1593 Node* next_k = 1594 graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant()); 1595 1596 Node* hole_true = nullptr; 1597 Node* hole_false = nullptr; 1598 Node* effect_true = effect; 1599 1600 if (IsHoleyElementsKind(kind)) { 1601 // Holey elements kind require a hole check and skipping of the element in 1602 // the case of a hole. 1603 Node* check; 1604 if (IsDoubleElementsKind(kind)) { 1605 check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element); 1606 } else { 1607 check = graph()->NewNode(simplified()->ReferenceEqual(), element, 1608 jsgraph()->TheHoleConstant()); 1609 } 1610 Node* branch = 1611 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); 1612 hole_true = graph()->NewNode(common()->IfTrue(), branch); 1613 hole_false = graph()->NewNode(common()->IfFalse(), branch); 1614 control = hole_false; 1615 1616 // The contract is that we don't leak "the hole" into "user JavaScript", 1617 // so we must rename the {element} here to explicitly exclude "the hole" 1618 // from the type of {element}. 1619 element = effect = graph()->NewNode( 1620 common()->TypeGuard(Type::NonInternal()), element, effect, control); 1621 } 1622 1623 // This frame state is dealt with by hand in 1624 // ArrayMapLoopLazyDeoptContinuation. 1625 frame_state = CreateJavaScriptBuiltinContinuationFrameState( 1626 jsgraph(), shared, Builtins::kArrayMapLoopLazyDeoptContinuation, 1627 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 1628 outer_frame_state, ContinuationFrameStateMode::LAZY); 1629 1630 Node* callback_value = control = effect = graph()->NewNode( 1631 javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k, 1632 receiver, context, frame_state, effect, control); 1633 1634 // Rewire potential exception edges. 1635 Node* on_exception = nullptr; 1636 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { 1637 RewirePostCallbackExceptionEdges(check_throw, on_exception, effect, 1638 &check_fail, &control); 1639 } 1640 1641 // The array {a} should be HOLEY_SMI_ELEMENTS because we'd only come into this 1642 // loop if the input array length is non-zero, and "new Array({x > 0})" always 1643 // produces a HOLEY array. 1644 Handle<Map> double_map(Map::cast(native_context()->get( 1645 Context::ArrayMapIndex(HOLEY_DOUBLE_ELEMENTS))), 1646 isolate()); 1647 Handle<Map> fast_map( 1648 Map::cast(native_context()->get(Context::ArrayMapIndex(HOLEY_ELEMENTS))), 1649 isolate()); 1650 effect = graph()->NewNode( 1651 simplified()->TransitionAndStoreElement(double_map, fast_map), a, k, 1652 callback_value, effect, control); 1653 1654 if (IsHoleyElementsKind(kind)) { 1655 Node* after_call_and_store_control = control; 1656 Node* after_call_and_store_effect = effect; 1657 control = hole_true; 1658 effect = effect_true; 1659 1660 control = graph()->NewNode(common()->Merge(2), control, 1661 after_call_and_store_control); 1662 effect = graph()->NewNode(common()->EffectPhi(2), effect, 1663 after_call_and_store_effect, control); 1664 } 1665 1666 WireInLoopEnd(loop, eloop, vloop, next_k, control, effect); 1667 1668 control = if_false; 1669 effect = eloop; 1670 1671 // Wire up the branch for the case when IsCallable fails for the callback. 1672 // Since {check_throw} is an unconditional throw, it's impossible to 1673 // return a successful completion. Therefore, we simply connect the successful 1674 // completion to the graph end. 1675 Node* throw_node = 1676 graph()->NewNode(common()->Throw(), check_throw, check_fail); 1677 NodeProperties::MergeControlToEnd(graph(), common(), throw_node); 1678 1679 ReplaceWithValue(node, a, effect, control); 1680 return Replace(a); 1681 } 1682 1683 Reduction JSCallReducer::ReduceArrayFilter(Node* node, 1684 Handle<SharedFunctionInfo> shared) { 1685 if (!FLAG_turbo_inline_array_builtins) return NoChange(); 1686 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 1687 CallParameters const& p = CallParametersOf(node->op()); 1688 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 1689 return NoChange(); 1690 } 1691 1692 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); 1693 Node* effect = NodeProperties::GetEffectInput(node); 1694 Node* control = NodeProperties::GetControlInput(node); 1695 Node* context = NodeProperties::GetContextInput(node); 1696 // Try to determine the {receiver} map. 1697 Node* receiver = NodeProperties::GetValueInput(node, 1); 1698 Node* fncallback = node->op()->ValueInputCount() > 2 1699 ? NodeProperties::GetValueInput(node, 2) 1700 : jsgraph()->UndefinedConstant(); 1701 Node* this_arg = node->op()->ValueInputCount() > 3 1702 ? NodeProperties::GetValueInput(node, 3) 1703 : jsgraph()->UndefinedConstant(); 1704 ZoneHandleSet<Map> receiver_maps; 1705 NodeProperties::InferReceiverMapsResult result = 1706 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 1707 &receiver_maps); 1708 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 1709 1710 // And ensure that any changes to the Array species constructor cause deopt. 1711 if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange(); 1712 1713 const ElementsKind kind = receiver_maps[0]->elements_kind(); 1714 // The output array is packed (filter doesn't visit holes). 1715 const ElementsKind packed_kind = GetPackedElementsKind(kind); 1716 1717 for (Handle<Map> receiver_map : receiver_maps) { 1718 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) { 1719 return NoChange(); 1720 } 1721 // We can handle different maps, as long as their elements kind are the 1722 // same. 1723 if (receiver_map->elements_kind() != kind) return NoChange(); 1724 } 1725 1726 if (IsHoleyElementsKind(kind)) { 1727 dependencies()->DependOnProtector( 1728 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); 1729 } 1730 1731 dependencies()->DependOnProtector( 1732 PropertyCellRef(js_heap_broker(), factory()->array_species_protector())); 1733 1734 Handle<Map> initial_map( 1735 Map::cast(native_context()->GetInitialJSArrayMap(packed_kind)), 1736 isolate()); 1737 1738 Node* k = jsgraph()->ZeroConstant(); 1739 Node* to = jsgraph()->ZeroConstant(); 1740 1741 // If we have unreliable maps, we need a map check. 1742 if (result == NodeProperties::kUnreliableReceiverMaps) { 1743 effect = 1744 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 1745 receiver_maps, p.feedback()), 1746 receiver, effect, control); 1747 } 1748 1749 Node* a; // Construct the output array. 1750 { 1751 AllocationBuilder ab(jsgraph(), effect, control); 1752 ab.Allocate(initial_map->instance_size(), NOT_TENURED, Type::Array()); 1753 ab.Store(AccessBuilder::ForMap(), initial_map); 1754 Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant(); 1755 ab.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), empty_fixed_array); 1756 ab.Store(AccessBuilder::ForJSObjectElements(), empty_fixed_array); 1757 ab.Store(AccessBuilder::ForJSArrayLength(packed_kind), 1758 jsgraph()->ZeroConstant()); 1759 for (int i = 0; i < initial_map->GetInObjectProperties(); ++i) { 1760 ab.Store(AccessBuilder::ForJSObjectInObjectProperty( 1761 MapRef(js_heap_broker(), initial_map), i), 1762 jsgraph()->UndefinedConstant()); 1763 } 1764 a = effect = ab.Finish(); 1765 } 1766 1767 Node* original_length = effect = graph()->NewNode( 1768 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, 1769 effect, control); 1770 1771 // Check whether the given callback function is callable. Note that this has 1772 // to happen outside the loop to make sure we also throw on empty arrays. 1773 Node* check_fail = nullptr; 1774 Node* check_throw = nullptr; 1775 { 1776 // This frame state doesn't ever call the deopt continuation, it's only 1777 // necessary to specifiy a continuation in order to handle the exceptional 1778 // case. We don't have all the values available to completely fill out 1779 // checkpoint_params yet, but that's okay because it'll never be called. 1780 // Therefore, "to" is mentioned twice, once standing in for the k_value 1781 // value. 1782 std::vector<Node*> checkpoint_params( 1783 {receiver, fncallback, this_arg, a, k, original_length, to, to}); 1784 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 1785 1786 Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState( 1787 jsgraph(), shared, Builtins::kArrayFilterLoopLazyDeoptContinuation, 1788 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 1789 outer_frame_state, ContinuationFrameStateMode::LAZY); 1790 WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, 1791 effect, &control, &check_fail, &check_throw); 1792 } 1793 1794 // Start the loop. 1795 Node* vloop = k = WireInLoopStart(k, &control, &effect); 1796 Node *loop = control, *eloop = effect; 1797 Node* v_to_loop = to = graph()->NewNode( 1798 common()->Phi(MachineRepresentation::kTaggedSigned, 2), to, to, loop); 1799 1800 Node* continue_test = 1801 graph()->NewNode(simplified()->NumberLessThan(), k, original_length); 1802 Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), 1803 continue_test, control); 1804 1805 Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch); 1806 Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch); 1807 control = if_true; 1808 1809 { 1810 std::vector<Node*> checkpoint_params( 1811 {receiver, fncallback, this_arg, a, k, original_length, to}); 1812 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 1813 1814 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 1815 jsgraph(), shared, Builtins::kArrayFilterLoopEagerDeoptContinuation, 1816 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 1817 outer_frame_state, ContinuationFrameStateMode::EAGER); 1818 1819 effect = 1820 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); 1821 } 1822 1823 // Make sure the map hasn't changed during the iteration. 1824 effect = 1825 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 1826 receiver_maps, p.feedback()), 1827 receiver, effect, control); 1828 1829 Node* element = 1830 SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); 1831 1832 Node* next_k = 1833 graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant()); 1834 1835 Node* hole_true = nullptr; 1836 Node* hole_false = nullptr; 1837 Node* effect_true = effect; 1838 Node* hole_true_vto = to; 1839 1840 if (IsHoleyElementsKind(kind)) { 1841 // Holey elements kind require a hole check and skipping of the element in 1842 // the case of a hole. 1843 Node* check; 1844 if (IsDoubleElementsKind(kind)) { 1845 check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element); 1846 } else { 1847 check = graph()->NewNode(simplified()->ReferenceEqual(), element, 1848 jsgraph()->TheHoleConstant()); 1849 } 1850 Node* branch = 1851 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); 1852 hole_true = graph()->NewNode(common()->IfTrue(), branch); 1853 hole_false = graph()->NewNode(common()->IfFalse(), branch); 1854 control = hole_false; 1855 1856 // The contract is that we don't leak "the hole" into "user JavaScript", 1857 // so we must rename the {element} here to explicitly exclude "the hole" 1858 // from the type of {element}. 1859 element = effect = graph()->NewNode( 1860 common()->TypeGuard(Type::NonInternal()), element, effect, control); 1861 } 1862 1863 Node* callback_value = nullptr; 1864 { 1865 // This frame state is dealt with by hand in 1866 // Builtins::kArrayFilterLoopLazyDeoptContinuation. 1867 std::vector<Node*> checkpoint_params( 1868 {receiver, fncallback, this_arg, a, k, original_length, element, to}); 1869 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 1870 1871 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 1872 jsgraph(), shared, Builtins::kArrayFilterLoopLazyDeoptContinuation, 1873 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 1874 outer_frame_state, ContinuationFrameStateMode::LAZY); 1875 1876 callback_value = control = effect = graph()->NewNode( 1877 javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k, 1878 receiver, context, frame_state, effect, control); 1879 } 1880 1881 // Rewire potential exception edges. 1882 Node* on_exception = nullptr; 1883 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { 1884 RewirePostCallbackExceptionEdges(check_throw, on_exception, effect, 1885 &check_fail, &control); 1886 } 1887 1888 // We need an eager frame state for right after the callback function 1889 // returned, just in case an attempt to grow the output array fails. 1890 // 1891 // Note that we are intentionally reusing the 1892 // Builtins::kArrayFilterLoopLazyDeoptContinuation as an *eager* entry 1893 // point in this case. This is safe, because re-evaluating a [ToBoolean] 1894 // coercion is safe. 1895 { 1896 std::vector<Node*> checkpoint_params({receiver, fncallback, this_arg, a, k, 1897 original_length, element, to, 1898 callback_value}); 1899 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 1900 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 1901 jsgraph(), shared, Builtins::kArrayFilterLoopLazyDeoptContinuation, 1902 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 1903 outer_frame_state, ContinuationFrameStateMode::EAGER); 1904 1905 effect = 1906 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); 1907 } 1908 1909 // We have to coerce callback_value to boolean, and only store the element in 1910 // a if it's true. The checkpoint above protects against the case that 1911 // growing {a} fails. 1912 to = DoFilterPostCallbackWork(packed_kind, &control, &effect, a, to, element, 1913 callback_value); 1914 1915 if (IsHoleyElementsKind(kind)) { 1916 Node* after_call_control = control; 1917 Node* after_call_effect = effect; 1918 control = hole_true; 1919 effect = effect_true; 1920 1921 control = graph()->NewNode(common()->Merge(2), control, after_call_control); 1922 effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect, 1923 control); 1924 to = 1925 graph()->NewNode(common()->Phi(MachineRepresentation::kTaggedSigned, 2), 1926 hole_true_vto, to, control); 1927 } 1928 1929 WireInLoopEnd(loop, eloop, vloop, next_k, control, effect); 1930 v_to_loop->ReplaceInput(1, to); 1931 1932 control = if_false; 1933 effect = eloop; 1934 1935 // Wire up the branch for the case when IsCallable fails for the callback. 1936 // Since {check_throw} is an unconditional throw, it's impossible to 1937 // return a successful completion. Therefore, we simply connect the successful 1938 // completion to the graph end. 1939 Node* throw_node = 1940 graph()->NewNode(common()->Throw(), check_throw, check_fail); 1941 NodeProperties::MergeControlToEnd(graph(), common(), throw_node); 1942 1943 ReplaceWithValue(node, a, effect, control); 1944 return Replace(a); 1945 } 1946 1947 Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant, 1948 Handle<SharedFunctionInfo> shared) { 1949 if (!FLAG_turbo_inline_array_builtins) return NoChange(); 1950 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 1951 CallParameters const& p = CallParametersOf(node->op()); 1952 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 1953 return NoChange(); 1954 } 1955 1956 Builtins::Name eager_continuation_builtin; 1957 Builtins::Name lazy_continuation_builtin; 1958 Builtins::Name after_callback_lazy_continuation_builtin; 1959 if (variant == ArrayFindVariant::kFind) { 1960 eager_continuation_builtin = Builtins::kArrayFindLoopEagerDeoptContinuation; 1961 lazy_continuation_builtin = Builtins::kArrayFindLoopLazyDeoptContinuation; 1962 after_callback_lazy_continuation_builtin = 1963 Builtins::kArrayFindLoopAfterCallbackLazyDeoptContinuation; 1964 } else { 1965 DCHECK_EQ(ArrayFindVariant::kFindIndex, variant); 1966 eager_continuation_builtin = 1967 Builtins::kArrayFindIndexLoopEagerDeoptContinuation; 1968 lazy_continuation_builtin = 1969 Builtins::kArrayFindIndexLoopLazyDeoptContinuation; 1970 after_callback_lazy_continuation_builtin = 1971 Builtins::kArrayFindIndexLoopAfterCallbackLazyDeoptContinuation; 1972 } 1973 1974 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); 1975 Node* effect = NodeProperties::GetEffectInput(node); 1976 Node* control = NodeProperties::GetControlInput(node); 1977 Node* context = NodeProperties::GetContextInput(node); 1978 1979 // Try to determine the {receiver} map. 1980 Node* receiver = NodeProperties::GetValueInput(node, 1); 1981 Node* fncallback = node->op()->ValueInputCount() > 2 1982 ? NodeProperties::GetValueInput(node, 2) 1983 : jsgraph()->UndefinedConstant(); 1984 Node* this_arg = node->op()->ValueInputCount() > 3 1985 ? NodeProperties::GetValueInput(node, 3) 1986 : jsgraph()->UndefinedConstant(); 1987 ZoneHandleSet<Map> receiver_maps; 1988 NodeProperties::InferReceiverMapsResult result = 1989 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 1990 &receiver_maps); 1991 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 1992 1993 const ElementsKind kind = receiver_maps[0]->elements_kind(); 1994 1995 for (Handle<Map> receiver_map : receiver_maps) { 1996 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) 1997 return NoChange(); 1998 // We can handle different maps, as long as their elements kind are the 1999 // same. 2000 if (receiver_map->elements_kind() != kind) return NoChange(); 2001 } 2002 2003 // Install code dependencies on the {receiver} prototype maps and the 2004 // global array protector cell. 2005 dependencies()->DependOnProtector( 2006 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); 2007 2008 // If we have unreliable maps, we need a map check. 2009 if (result == NodeProperties::kUnreliableReceiverMaps) { 2010 effect = 2011 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 2012 receiver_maps, p.feedback()), 2013 receiver, effect, control); 2014 } 2015 2016 Node* k = jsgraph()->ZeroConstant(); 2017 2018 Node* original_length = effect = graph()->NewNode( 2019 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, 2020 effect, control); 2021 2022 std::vector<Node*> checkpoint_params( 2023 {receiver, fncallback, this_arg, k, original_length}); 2024 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 2025 2026 // Check whether the given callback function is callable. Note that this has 2027 // to happen outside the loop to make sure we also throw on empty arrays. 2028 Node* check_fail = nullptr; 2029 Node* check_throw = nullptr; 2030 { 2031 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 2032 jsgraph(), shared, lazy_continuation_builtin, node->InputAt(0), context, 2033 &checkpoint_params[0], stack_parameters, outer_frame_state, 2034 ContinuationFrameStateMode::LAZY); 2035 WireInCallbackIsCallableCheck(fncallback, context, frame_state, effect, 2036 &control, &check_fail, &check_throw); 2037 } 2038 2039 // Start the loop. 2040 Node* vloop = k = WireInLoopStart(k, &control, &effect); 2041 Node *loop = control, *eloop = effect; 2042 checkpoint_params[3] = k; 2043 2044 // Check if we've iterated past the last element of the array. 2045 Node* if_false = nullptr; 2046 { 2047 Node* continue_test = 2048 graph()->NewNode(simplified()->NumberLessThan(), k, original_length); 2049 Node* continue_branch = graph()->NewNode( 2050 common()->Branch(BranchHint::kTrue), continue_test, control); 2051 control = graph()->NewNode(common()->IfTrue(), continue_branch); 2052 if_false = graph()->NewNode(common()->IfFalse(), continue_branch); 2053 } 2054 2055 // Check the map hasn't changed during the iteration. 2056 { 2057 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 2058 jsgraph(), shared, eager_continuation_builtin, node->InputAt(0), 2059 context, &checkpoint_params[0], stack_parameters, outer_frame_state, 2060 ContinuationFrameStateMode::EAGER); 2061 2062 effect = 2063 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); 2064 2065 effect = 2066 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 2067 receiver_maps, p.feedback()), 2068 receiver, effect, control); 2069 } 2070 2071 // Load k-th element from receiver. 2072 Node* element = 2073 SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); 2074 2075 // Increment k for the next iteration. 2076 Node* next_k = checkpoint_params[3] = 2077 graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant()); 2078 2079 // Replace holes with undefined. 2080 if (kind == HOLEY_DOUBLE_ELEMENTS) { 2081 // TODO(7409): avoid deopt if not all uses of value are truncated. 2082 CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole; 2083 element = effect = 2084 graph()->NewNode(simplified()->CheckFloat64Hole(mode, p.feedback()), 2085 element, effect, control); 2086 } else if (IsHoleyElementsKind(kind)) { 2087 element = 2088 graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), element); 2089 } 2090 2091 Node* if_found_return_value = 2092 (variant == ArrayFindVariant::kFind) ? element : k; 2093 2094 // Call the callback. 2095 Node* callback_value = nullptr; 2096 { 2097 std::vector<Node*> call_checkpoint_params({receiver, fncallback, this_arg, 2098 next_k, original_length, 2099 if_found_return_value}); 2100 const int call_stack_parameters = 2101 static_cast<int>(call_checkpoint_params.size()); 2102 2103 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 2104 jsgraph(), shared, after_callback_lazy_continuation_builtin, 2105 node->InputAt(0), context, &call_checkpoint_params[0], 2106 call_stack_parameters, outer_frame_state, 2107 ContinuationFrameStateMode::LAZY); 2108 2109 callback_value = control = effect = graph()->NewNode( 2110 javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k, 2111 receiver, context, frame_state, effect, control); 2112 } 2113 2114 // Rewire potential exception edges. 2115 Node* on_exception = nullptr; 2116 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { 2117 RewirePostCallbackExceptionEdges(check_throw, on_exception, effect, 2118 &check_fail, &control); 2119 } 2120 2121 // Check whether the given callback function returned a truthy value. 2122 Node* boolean_result = 2123 graph()->NewNode(simplified()->ToBoolean(), callback_value); 2124 Node* efound_branch = effect; 2125 Node* found_branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), 2126 boolean_result, control); 2127 Node* if_found = graph()->NewNode(common()->IfTrue(), found_branch); 2128 Node* if_notfound = graph()->NewNode(common()->IfFalse(), found_branch); 2129 control = if_notfound; 2130 2131 // Close the loop. 2132 WireInLoopEnd(loop, eloop, vloop, next_k, control, effect); 2133 2134 control = graph()->NewNode(common()->Merge(2), if_found, if_false); 2135 effect = 2136 graph()->NewNode(common()->EffectPhi(2), efound_branch, eloop, control); 2137 2138 Node* if_not_found_value = (variant == ArrayFindVariant::kFind) 2139 ? jsgraph()->UndefinedConstant() 2140 : jsgraph()->MinusOneConstant(); 2141 Node* return_value = 2142 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 2143 if_found_return_value, if_not_found_value, control); 2144 2145 // Wire up the branch for the case when IsCallable fails for the callback. 2146 // Since {check_throw} is an unconditional throw, it's impossible to 2147 // return a successful completion. Therefore, we simply connect the successful 2148 // completion to the graph end. 2149 Node* throw_node = 2150 graph()->NewNode(common()->Throw(), check_throw, check_fail); 2151 NodeProperties::MergeControlToEnd(graph(), common(), throw_node); 2152 2153 ReplaceWithValue(node, return_value, effect, control); 2154 return Replace(return_value); 2155 } 2156 2157 Node* JSCallReducer::DoFilterPostCallbackWork(ElementsKind kind, Node** control, 2158 Node** effect, Node* a, Node* to, 2159 Node* element, 2160 Node* callback_value) { 2161 Node* boolean_result = 2162 graph()->NewNode(simplified()->ToBoolean(), callback_value); 2163 2164 Node* check_boolean_result = 2165 graph()->NewNode(simplified()->ReferenceEqual(), boolean_result, 2166 jsgraph()->TrueConstant()); 2167 Node* boolean_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), 2168 check_boolean_result, *control); 2169 2170 Node* if_true = graph()->NewNode(common()->IfTrue(), boolean_branch); 2171 Node* etrue = *effect; 2172 Node* vtrue; 2173 { 2174 // Load the elements backing store of the {receiver}. 2175 Node* elements = etrue = graph()->NewNode( 2176 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), a, etrue, 2177 if_true); 2178 2179 DCHECK(TypeCache::Get().kFixedDoubleArrayLengthType.Is( 2180 TypeCache::Get().kFixedArrayLengthType)); 2181 Node* checked_to = etrue = graph()->NewNode( 2182 common()->TypeGuard(TypeCache::Get().kFixedArrayLengthType), to, etrue, 2183 if_true); 2184 Node* elements_length = etrue = graph()->NewNode( 2185 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), elements, 2186 etrue, if_true); 2187 2188 GrowFastElementsMode mode = 2189 IsDoubleElementsKind(kind) ? GrowFastElementsMode::kDoubleElements 2190 : GrowFastElementsMode::kSmiOrObjectElements; 2191 elements = etrue = graph()->NewNode( 2192 simplified()->MaybeGrowFastElements(mode, VectorSlotPair()), a, 2193 elements, checked_to, elements_length, etrue, if_true); 2194 2195 // Update the length of {a}. 2196 Node* new_length_a = graph()->NewNode(simplified()->NumberAdd(), checked_to, 2197 jsgraph()->OneConstant()); 2198 2199 etrue = graph()->NewNode( 2200 simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), a, 2201 new_length_a, etrue, if_true); 2202 2203 // Append the value to the {elements}. 2204 etrue = graph()->NewNode( 2205 simplified()->StoreElement(AccessBuilder::ForFixedArrayElement(kind)), 2206 elements, checked_to, element, etrue, if_true); 2207 2208 vtrue = new_length_a; 2209 } 2210 2211 Node* if_false = graph()->NewNode(common()->IfFalse(), boolean_branch); 2212 Node* efalse = *effect; 2213 Node* vfalse = to; 2214 2215 *control = graph()->NewNode(common()->Merge(2), if_true, if_false); 2216 *effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, *control); 2217 to = graph()->NewNode(common()->Phi(MachineRepresentation::kTaggedSigned, 2), 2218 vtrue, vfalse, *control); 2219 return to; 2220 } 2221 2222 void JSCallReducer::WireInCallbackIsCallableCheck( 2223 Node* fncallback, Node* context, Node* check_frame_state, Node* effect, 2224 Node** control, Node** check_fail, Node** check_throw) { 2225 Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), fncallback); 2226 Node* check_branch = 2227 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, *control); 2228 *check_fail = graph()->NewNode(common()->IfFalse(), check_branch); 2229 *check_throw = *check_fail = graph()->NewNode( 2230 javascript()->CallRuntime(Runtime::kThrowTypeError, 2), 2231 jsgraph()->Constant(MessageTemplate::kCalledNonCallable), fncallback, 2232 context, check_frame_state, effect, *check_fail); 2233 *control = graph()->NewNode(common()->IfTrue(), check_branch); 2234 } 2235 2236 void JSCallReducer::RewirePostCallbackExceptionEdges(Node* check_throw, 2237 Node* on_exception, 2238 Node* effect, 2239 Node** check_fail, 2240 Node** control) { 2241 // Create appropriate {IfException} and {IfSuccess} nodes. 2242 Node* if_exception0 = 2243 graph()->NewNode(common()->IfException(), check_throw, *check_fail); 2244 *check_fail = graph()->NewNode(common()->IfSuccess(), *check_fail); 2245 Node* if_exception1 = 2246 graph()->NewNode(common()->IfException(), effect, *control); 2247 *control = graph()->NewNode(common()->IfSuccess(), *control); 2248 2249 // Join the exception edges. 2250 Node* merge = 2251 graph()->NewNode(common()->Merge(2), if_exception0, if_exception1); 2252 Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0, 2253 if_exception1, merge); 2254 Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 2255 if_exception0, if_exception1, merge); 2256 ReplaceWithValue(on_exception, phi, ephi, merge); 2257 } 2258 2259 Node* JSCallReducer::SafeLoadElement(ElementsKind kind, Node* receiver, 2260 Node* control, Node** effect, Node** k, 2261 const VectorSlotPair& feedback) { 2262 // Make sure that the access is still in bounds, since the callback could have 2263 // changed the array's size. 2264 Node* length = *effect = graph()->NewNode( 2265 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, 2266 *effect, control); 2267 *k = *effect = graph()->NewNode(simplified()->CheckBounds(feedback), *k, 2268 length, *effect, control); 2269 2270 // Reload the elements pointer before calling the callback, since the previous 2271 // callback might have resized the array causing the elements buffer to be 2272 // re-allocated. 2273 Node* elements = *effect = graph()->NewNode( 2274 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver, 2275 *effect, control); 2276 2277 Node* element = *effect = graph()->NewNode( 2278 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement( 2279 kind, LoadSensitivity::kCritical)), 2280 elements, *k, *effect, control); 2281 return element; 2282 } 2283 2284 Reduction JSCallReducer::ReduceArrayEvery(Node* node, 2285 Handle<SharedFunctionInfo> shared) { 2286 if (!FLAG_turbo_inline_array_builtins) return NoChange(); 2287 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 2288 CallParameters const& p = CallParametersOf(node->op()); 2289 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 2290 return NoChange(); 2291 } 2292 2293 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); 2294 Node* effect = NodeProperties::GetEffectInput(node); 2295 Node* control = NodeProperties::GetControlInput(node); 2296 Node* context = NodeProperties::GetContextInput(node); 2297 // Try to determine the {receiver} map. 2298 Node* receiver = NodeProperties::GetValueInput(node, 1); 2299 Node* fncallback = node->op()->ValueInputCount() > 2 2300 ? NodeProperties::GetValueInput(node, 2) 2301 : jsgraph()->UndefinedConstant(); 2302 Node* this_arg = node->op()->ValueInputCount() > 3 2303 ? NodeProperties::GetValueInput(node, 3) 2304 : jsgraph()->UndefinedConstant(); 2305 ZoneHandleSet<Map> receiver_maps; 2306 NodeProperties::InferReceiverMapsResult result = 2307 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 2308 &receiver_maps); 2309 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 2310 2311 // And ensure that any changes to the Array species constructor cause deopt. 2312 if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange(); 2313 2314 const ElementsKind kind = receiver_maps[0]->elements_kind(); 2315 2316 for (Handle<Map> receiver_map : receiver_maps) { 2317 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) 2318 return NoChange(); 2319 // We can handle different maps, as long as their elements kind are the 2320 // same. 2321 if (receiver_map->elements_kind() != kind) return NoChange(); 2322 } 2323 2324 if (IsHoleyElementsKind(kind)) { 2325 dependencies()->DependOnProtector( 2326 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); 2327 } 2328 2329 dependencies()->DependOnProtector( 2330 PropertyCellRef(js_heap_broker(), factory()->array_species_protector())); 2331 2332 // If we have unreliable maps, we need a map check. 2333 if (result == NodeProperties::kUnreliableReceiverMaps) { 2334 effect = 2335 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 2336 receiver_maps, p.feedback()), 2337 receiver, effect, control); 2338 } 2339 2340 Node* k = jsgraph()->ZeroConstant(); 2341 2342 // Make sure the map hasn't changed before we construct the output array. 2343 effect = graph()->NewNode( 2344 simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver, 2345 effect, control); 2346 2347 Node* original_length = effect = graph()->NewNode( 2348 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, 2349 effect, control); 2350 2351 // Check whether the given callback function is callable. Note that this has 2352 // to happen outside the loop to make sure we also throw on empty arrays. 2353 Node* check_fail = nullptr; 2354 Node* check_throw = nullptr; 2355 { 2356 // This frame state doesn't ever call the deopt continuation, it's only 2357 // necessary to specifiy a continuation in order to handle the exceptional 2358 // case. 2359 std::vector<Node*> checkpoint_params( 2360 {receiver, fncallback, this_arg, k, original_length}); 2361 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 2362 2363 Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState( 2364 jsgraph(), shared, Builtins::kArrayEveryLoopLazyDeoptContinuation, 2365 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 2366 outer_frame_state, ContinuationFrameStateMode::LAZY); 2367 WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, 2368 effect, &control, &check_fail, &check_throw); 2369 } 2370 2371 // Start the loop. 2372 Node* vloop = k = WireInLoopStart(k, &control, &effect); 2373 Node *loop = control, *eloop = effect; 2374 2375 Node* continue_test = 2376 graph()->NewNode(simplified()->NumberLessThan(), k, original_length); 2377 Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), 2378 continue_test, control); 2379 2380 Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch); 2381 Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch); 2382 control = if_true; 2383 2384 { 2385 std::vector<Node*> checkpoint_params( 2386 {receiver, fncallback, this_arg, k, original_length}); 2387 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 2388 2389 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 2390 jsgraph(), shared, Builtins::kArrayEveryLoopEagerDeoptContinuation, 2391 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 2392 outer_frame_state, ContinuationFrameStateMode::EAGER); 2393 2394 effect = 2395 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); 2396 } 2397 2398 // Make sure the map hasn't changed during the iteration. 2399 effect = 2400 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 2401 receiver_maps, p.feedback()), 2402 receiver, effect, control); 2403 2404 Node* element = 2405 SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); 2406 2407 Node* next_k = 2408 graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant()); 2409 2410 Node* hole_true = nullptr; 2411 Node* hole_false = nullptr; 2412 Node* effect_true = effect; 2413 2414 if (IsHoleyElementsKind(kind)) { 2415 // Holey elements kind require a hole check and skipping of the element in 2416 // the case of a hole. 2417 Node* check; 2418 if (IsDoubleElementsKind(kind)) { 2419 check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element); 2420 } else { 2421 check = graph()->NewNode(simplified()->ReferenceEqual(), element, 2422 jsgraph()->TheHoleConstant()); 2423 } 2424 Node* branch = 2425 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); 2426 hole_true = graph()->NewNode(common()->IfTrue(), branch); 2427 hole_false = graph()->NewNode(common()->IfFalse(), branch); 2428 control = hole_false; 2429 2430 // The contract is that we don't leak "the hole" into "user JavaScript", 2431 // so we must rename the {element} here to explicitly exclude "the hole" 2432 // from the type of {element}. 2433 element = effect = graph()->NewNode( 2434 common()->TypeGuard(Type::NonInternal()), element, effect, control); 2435 } 2436 2437 Node* callback_value = nullptr; 2438 { 2439 // This frame state is dealt with by hand in 2440 // Builtins::kArrayEveryLoopLazyDeoptContinuation. 2441 std::vector<Node*> checkpoint_params( 2442 {receiver, fncallback, this_arg, k, original_length}); 2443 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 2444 2445 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 2446 jsgraph(), shared, Builtins::kArrayEveryLoopLazyDeoptContinuation, 2447 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 2448 outer_frame_state, ContinuationFrameStateMode::LAZY); 2449 2450 callback_value = control = effect = graph()->NewNode( 2451 javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k, 2452 receiver, context, frame_state, effect, control); 2453 } 2454 2455 // Rewire potential exception edges. 2456 Node* on_exception = nullptr; 2457 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { 2458 RewirePostCallbackExceptionEdges(check_throw, on_exception, effect, 2459 &check_fail, &control); 2460 } 2461 2462 // We have to coerce callback_value to boolean. 2463 Node* if_false_callback; 2464 Node* efalse_callback; 2465 { 2466 Node* boolean_result = 2467 graph()->NewNode(simplified()->ToBoolean(), callback_value); 2468 Node* check_boolean_result = 2469 graph()->NewNode(simplified()->ReferenceEqual(), boolean_result, 2470 jsgraph()->TrueConstant()); 2471 Node* boolean_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), 2472 check_boolean_result, control); 2473 if_false_callback = graph()->NewNode(common()->IfFalse(), boolean_branch); 2474 efalse_callback = effect; 2475 2476 // Nothing to do in the true case. 2477 control = graph()->NewNode(common()->IfTrue(), boolean_branch); 2478 } 2479 2480 if (IsHoleyElementsKind(kind)) { 2481 Node* after_call_control = control; 2482 Node* after_call_effect = effect; 2483 control = hole_true; 2484 effect = effect_true; 2485 2486 control = graph()->NewNode(common()->Merge(2), control, after_call_control); 2487 effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect, 2488 control); 2489 } 2490 2491 WireInLoopEnd(loop, eloop, vloop, next_k, control, effect); 2492 2493 control = graph()->NewNode(common()->Merge(2), if_false, if_false_callback); 2494 effect = 2495 graph()->NewNode(common()->EffectPhi(2), eloop, efalse_callback, control); 2496 Node* return_value = graph()->NewNode( 2497 common()->Phi(MachineRepresentation::kTagged, 2), 2498 jsgraph()->TrueConstant(), jsgraph()->FalseConstant(), control); 2499 2500 // Wire up the branch for the case when IsCallable fails for the callback. 2501 // Since {check_throw} is an unconditional throw, it's impossible to 2502 // return a successful completion. Therefore, we simply connect the successful 2503 // completion to the graph end. 2504 Node* throw_node = 2505 graph()->NewNode(common()->Throw(), check_throw, check_fail); 2506 NodeProperties::MergeControlToEnd(graph(), common(), throw_node); 2507 2508 ReplaceWithValue(node, return_value, effect, control); 2509 return Replace(return_value); 2510 } 2511 2512 namespace { 2513 2514 // Returns the correct Callable for Array's indexOf based on the receiver's 2515 // |elements_kind| and |isolate|. Assumes that |elements_kind| is a fast one. 2516 Callable GetCallableForArrayIndexOf(ElementsKind elements_kind, 2517 Isolate* isolate) { 2518 switch (elements_kind) { 2519 case PACKED_SMI_ELEMENTS: 2520 case HOLEY_SMI_ELEMENTS: 2521 case PACKED_ELEMENTS: 2522 case HOLEY_ELEMENTS: 2523 return Builtins::CallableFor(isolate, Builtins::kArrayIndexOfSmiOrObject); 2524 case PACKED_DOUBLE_ELEMENTS: 2525 return Builtins::CallableFor(isolate, 2526 Builtins::kArrayIndexOfPackedDoubles); 2527 default: 2528 DCHECK_EQ(HOLEY_DOUBLE_ELEMENTS, elements_kind); 2529 return Builtins::CallableFor(isolate, 2530 Builtins::kArrayIndexOfHoleyDoubles); 2531 } 2532 } 2533 2534 // Returns the correct Callable for Array's includes based on the receiver's 2535 // |elements_kind| and |isolate|. Assumes that |elements_kind| is a fast one. 2536 Callable GetCallableForArrayIncludes(ElementsKind elements_kind, 2537 Isolate* isolate) { 2538 switch (elements_kind) { 2539 case PACKED_SMI_ELEMENTS: 2540 case HOLEY_SMI_ELEMENTS: 2541 case PACKED_ELEMENTS: 2542 case HOLEY_ELEMENTS: 2543 return Builtins::CallableFor(isolate, 2544 Builtins::kArrayIncludesSmiOrObject); 2545 case PACKED_DOUBLE_ELEMENTS: 2546 return Builtins::CallableFor(isolate, 2547 Builtins::kArrayIncludesPackedDoubles); 2548 default: 2549 DCHECK_EQ(HOLEY_DOUBLE_ELEMENTS, elements_kind); 2550 return Builtins::CallableFor(isolate, 2551 Builtins::kArrayIncludesHoleyDoubles); 2552 } 2553 } 2554 2555 } // namespace 2556 2557 // For search_variant == kIndexOf: 2558 // ES6 Array.prototype.indexOf(searchElement[, fromIndex]) 2559 // #sec-array.prototype.indexof 2560 // For search_variant == kIncludes: 2561 // ES7 Array.prototype.inludes(searchElement[, fromIndex]) 2562 // #sec-array.prototype.includes 2563 Reduction JSCallReducer::ReduceArrayIndexOfIncludes( 2564 SearchVariant search_variant, Node* node) { 2565 CallParameters const& p = CallParametersOf(node->op()); 2566 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 2567 return NoChange(); 2568 } 2569 2570 Handle<Map> receiver_map; 2571 if (!NodeProperties::GetMapWitness(isolate(), node).ToHandle(&receiver_map)) 2572 return NoChange(); 2573 2574 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) 2575 return NoChange(); 2576 2577 if (IsHoleyElementsKind(receiver_map->elements_kind())) { 2578 dependencies()->DependOnProtector( 2579 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); 2580 } 2581 2582 Callable const callable = 2583 search_variant == SearchVariant::kIndexOf 2584 ? GetCallableForArrayIndexOf(receiver_map->elements_kind(), isolate()) 2585 : GetCallableForArrayIncludes(receiver_map->elements_kind(), 2586 isolate()); 2587 CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( 2588 graph()->zone(), callable.descriptor(), 0, CallDescriptor::kNoFlags, 2589 Operator::kEliminatable); 2590 // The stub expects the following arguments: the receiver array, its elements, 2591 // the search_element, the array length, and the index to start searching 2592 // from. 2593 Node* receiver = NodeProperties::GetValueInput(node, 1); 2594 Node* effect = NodeProperties::GetEffectInput(node); 2595 Node* control = NodeProperties::GetControlInput(node); 2596 Node* elements = effect = graph()->NewNode( 2597 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver, 2598 effect, control); 2599 Node* search_element = (node->op()->ValueInputCount() >= 3) 2600 ? NodeProperties::GetValueInput(node, 2) 2601 : jsgraph()->UndefinedConstant(); 2602 Node* length = effect = graph()->NewNode( 2603 simplified()->LoadField( 2604 AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())), 2605 receiver, effect, control); 2606 Node* new_from_index = jsgraph()->ZeroConstant(); 2607 if (node->op()->ValueInputCount() >= 4) { 2608 Node* from_index = NodeProperties::GetValueInput(node, 3); 2609 from_index = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), 2610 from_index, effect, control); 2611 // If the index is negative, it means the offset from the end and therefore 2612 // needs to be added to the length. If the result is still negative, it 2613 // needs to be clamped to 0. 2614 new_from_index = graph()->NewNode( 2615 common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse), 2616 graph()->NewNode(simplified()->NumberLessThan(), from_index, 2617 jsgraph()->ZeroConstant()), 2618 graph()->NewNode( 2619 simplified()->NumberMax(), 2620 graph()->NewNode(simplified()->NumberAdd(), length, from_index), 2621 jsgraph()->ZeroConstant()), 2622 from_index); 2623 } 2624 2625 Node* context = NodeProperties::GetContextInput(node); 2626 Node* replacement_node = effect = graph()->NewNode( 2627 common()->Call(desc), jsgraph()->HeapConstant(callable.code()), elements, 2628 search_element, length, new_from_index, context, effect); 2629 ReplaceWithValue(node, replacement_node, effect); 2630 return Replace(replacement_node); 2631 } 2632 2633 Reduction JSCallReducer::ReduceArraySome(Node* node, 2634 Handle<SharedFunctionInfo> shared) { 2635 if (!FLAG_turbo_inline_array_builtins) return NoChange(); 2636 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 2637 CallParameters const& p = CallParametersOf(node->op()); 2638 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 2639 return NoChange(); 2640 } 2641 2642 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); 2643 Node* effect = NodeProperties::GetEffectInput(node); 2644 Node* control = NodeProperties::GetControlInput(node); 2645 Node* context = NodeProperties::GetContextInput(node); 2646 // Try to determine the {receiver} map. 2647 Node* receiver = NodeProperties::GetValueInput(node, 1); 2648 Node* fncallback = node->op()->ValueInputCount() > 2 2649 ? NodeProperties::GetValueInput(node, 2) 2650 : jsgraph()->UndefinedConstant(); 2651 Node* this_arg = node->op()->ValueInputCount() > 3 2652 ? NodeProperties::GetValueInput(node, 3) 2653 : jsgraph()->UndefinedConstant(); 2654 ZoneHandleSet<Map> receiver_maps; 2655 NodeProperties::InferReceiverMapsResult result = 2656 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 2657 &receiver_maps); 2658 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 2659 2660 // And ensure that any changes to the Array species constructor cause deopt. 2661 if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange(); 2662 2663 if (receiver_maps.size() == 0) return NoChange(); 2664 2665 const ElementsKind kind = receiver_maps[0]->elements_kind(); 2666 2667 for (Handle<Map> receiver_map : receiver_maps) { 2668 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) 2669 return NoChange(); 2670 // We can handle different maps, as long as their elements kind are the 2671 // same. 2672 if (receiver_map->elements_kind() != kind) return NoChange(); 2673 } 2674 2675 if (IsHoleyElementsKind(kind)) { 2676 dependencies()->DependOnProtector( 2677 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); 2678 } 2679 2680 dependencies()->DependOnProtector( 2681 PropertyCellRef(js_heap_broker(), factory()->array_species_protector())); 2682 2683 Node* k = jsgraph()->ZeroConstant(); 2684 2685 // If we have unreliable maps, we need a map check. 2686 if (result == NodeProperties::kUnreliableReceiverMaps) { 2687 effect = 2688 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 2689 receiver_maps, p.feedback()), 2690 receiver, effect, control); 2691 } 2692 2693 // Make sure the map hasn't changed before we construct the output array. 2694 effect = graph()->NewNode( 2695 simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver, 2696 effect, control); 2697 2698 Node* original_length = effect = graph()->NewNode( 2699 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, 2700 effect, control); 2701 2702 // Check whether the given callback function is callable. Note that this has 2703 // to happen outside the loop to make sure we also throw on empty arrays. 2704 Node* check_fail = nullptr; 2705 Node* check_throw = nullptr; 2706 { 2707 // This frame state doesn't ever call the deopt continuation, it's only 2708 // necessary to specifiy a continuation in order to handle the exceptional 2709 // case. 2710 std::vector<Node*> checkpoint_params( 2711 {receiver, fncallback, this_arg, k, original_length}); 2712 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 2713 2714 Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState( 2715 jsgraph(), shared, Builtins::kArraySomeLoopLazyDeoptContinuation, 2716 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 2717 outer_frame_state, ContinuationFrameStateMode::LAZY); 2718 WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, 2719 effect, &control, &check_fail, &check_throw); 2720 } 2721 2722 // Start the loop. 2723 Node* loop = control = graph()->NewNode(common()->Loop(2), control, control); 2724 Node* eloop = effect = 2725 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); 2726 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); 2727 NodeProperties::MergeControlToEnd(graph(), common(), terminate); 2728 Node* vloop = k = graph()->NewNode( 2729 common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop); 2730 2731 Node* continue_test = 2732 graph()->NewNode(simplified()->NumberLessThan(), k, original_length); 2733 Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), 2734 continue_test, control); 2735 2736 Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch); 2737 Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch); 2738 control = if_true; 2739 2740 { 2741 std::vector<Node*> checkpoint_params( 2742 {receiver, fncallback, this_arg, k, original_length}); 2743 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 2744 2745 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 2746 jsgraph(), shared, Builtins::kArraySomeLoopEagerDeoptContinuation, 2747 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 2748 outer_frame_state, ContinuationFrameStateMode::EAGER); 2749 2750 effect = 2751 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); 2752 } 2753 2754 // Make sure the map hasn't changed during the iteration. 2755 effect = 2756 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 2757 receiver_maps, p.feedback()), 2758 receiver, effect, control); 2759 2760 Node* element = 2761 SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); 2762 2763 Node* next_k = 2764 graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant()); 2765 2766 Node* hole_true = nullptr; 2767 Node* hole_false = nullptr; 2768 Node* effect_true = effect; 2769 2770 if (IsHoleyElementsKind(kind)) { 2771 // Holey elements kind require a hole check and skipping of the element in 2772 // the case of a hole. 2773 Node* check; 2774 if (IsDoubleElementsKind(kind)) { 2775 check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element); 2776 } else { 2777 check = graph()->NewNode(simplified()->ReferenceEqual(), element, 2778 jsgraph()->TheHoleConstant()); 2779 } 2780 Node* branch = 2781 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); 2782 hole_true = graph()->NewNode(common()->IfTrue(), branch); 2783 hole_false = graph()->NewNode(common()->IfFalse(), branch); 2784 control = hole_false; 2785 2786 // The contract is that we don't leak "the hole" into "user JavaScript", 2787 // so we must rename the {element} here to explicitly exclude "the hole" 2788 // from the type of {element}. 2789 element = effect = graph()->NewNode( 2790 common()->TypeGuard(Type::NonInternal()), element, effect, control); 2791 } 2792 2793 Node* callback_value = nullptr; 2794 { 2795 // This frame state is dealt with by hand in 2796 // Builtins::kArrayEveryLoopLazyDeoptContinuation. 2797 std::vector<Node*> checkpoint_params( 2798 {receiver, fncallback, this_arg, k, original_length}); 2799 const int stack_parameters = static_cast<int>(checkpoint_params.size()); 2800 2801 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 2802 jsgraph(), shared, Builtins::kArraySomeLoopLazyDeoptContinuation, 2803 node->InputAt(0), context, &checkpoint_params[0], stack_parameters, 2804 outer_frame_state, ContinuationFrameStateMode::LAZY); 2805 2806 callback_value = control = effect = graph()->NewNode( 2807 javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k, 2808 receiver, context, frame_state, effect, control); 2809 } 2810 2811 // Rewire potential exception edges. 2812 Node* on_exception = nullptr; 2813 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { 2814 RewirePostCallbackExceptionEdges(check_throw, on_exception, effect, 2815 &check_fail, &control); 2816 } 2817 2818 // We have to coerce callback_value to boolean. 2819 Node* if_true_callback; 2820 Node* etrue_callback; 2821 { 2822 Node* boolean_result = 2823 graph()->NewNode(simplified()->ToBoolean(), callback_value); 2824 Node* check_boolean_result = 2825 graph()->NewNode(simplified()->ReferenceEqual(), boolean_result, 2826 jsgraph()->TrueConstant()); 2827 Node* boolean_branch = graph()->NewNode( 2828 common()->Branch(BranchHint::kFalse), check_boolean_result, control); 2829 if_true_callback = graph()->NewNode(common()->IfTrue(), boolean_branch); 2830 etrue_callback = effect; 2831 2832 // Nothing to do in the false case. 2833 control = graph()->NewNode(common()->IfFalse(), boolean_branch); 2834 } 2835 2836 if (IsHoleyElementsKind(kind)) { 2837 Node* after_call_control = control; 2838 Node* after_call_effect = effect; 2839 control = hole_true; 2840 effect = effect_true; 2841 2842 control = graph()->NewNode(common()->Merge(2), control, after_call_control); 2843 effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect, 2844 control); 2845 } 2846 2847 loop->ReplaceInput(1, control); 2848 vloop->ReplaceInput(1, next_k); 2849 eloop->ReplaceInput(1, effect); 2850 2851 control = graph()->NewNode(common()->Merge(2), if_false, if_true_callback); 2852 effect = 2853 graph()->NewNode(common()->EffectPhi(2), eloop, etrue_callback, control); 2854 Node* return_value = graph()->NewNode( 2855 common()->Phi(MachineRepresentation::kTagged, 2), 2856 jsgraph()->FalseConstant(), jsgraph()->TrueConstant(), control); 2857 2858 // Wire up the branch for the case when IsCallable fails for the callback. 2859 // Since {check_throw} is an unconditional throw, it's impossible to 2860 // return a successful completion. Therefore, we simply connect the successful 2861 // completion to the graph end. 2862 Node* throw_node = 2863 graph()->NewNode(common()->Throw(), check_throw, check_fail); 2864 NodeProperties::MergeControlToEnd(graph(), common(), throw_node); 2865 2866 ReplaceWithValue(node, return_value, effect, control); 2867 return Replace(return_value); 2868 } 2869 2870 Reduction JSCallReducer::ReduceCallApiFunction( 2871 Node* node, Handle<SharedFunctionInfo> shared) { 2872 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 2873 CallParameters const& p = CallParametersOf(node->op()); 2874 int const argc = static_cast<int>(p.arity()) - 2; 2875 Node* target = NodeProperties::GetValueInput(node, 0); 2876 Node* receiver = (p.convert_mode() == ConvertReceiverMode::kNullOrUndefined) 2877 ? jsgraph()->HeapConstant(global_proxy()) 2878 : NodeProperties::GetValueInput(node, 1); 2879 Node* effect = NodeProperties::GetEffectInput(node); 2880 Node* control = NodeProperties::GetControlInput(node); 2881 2882 Handle<FunctionTemplateInfo> function_template_info( 2883 FunctionTemplateInfo::cast(shared->function_data()), isolate()); 2884 2885 // CallApiCallbackStub expects the target in a register, so we count it out, 2886 // and counts the receiver as an implicit argument, so we count the receiver 2887 // out too. 2888 if (argc > CallApiCallbackStub::kArgMax) return NoChange(); 2889 2890 // Infer the {receiver} maps, and check if we can inline the API function 2891 // callback based on those. 2892 ZoneHandleSet<Map> receiver_maps; 2893 NodeProperties::InferReceiverMapsResult result = 2894 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 2895 &receiver_maps); 2896 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 2897 for (size_t i = 0; i < receiver_maps.size(); ++i) { 2898 Handle<Map> receiver_map = receiver_maps[i]; 2899 if (!receiver_map->IsJSObjectMap() || 2900 (!function_template_info->accept_any_receiver() && 2901 receiver_map->is_access_check_needed())) { 2902 return NoChange(); 2903 } 2904 // In case of unreliable {receiver} information, the {receiver_maps} 2905 // must all be stable in order to consume the information. 2906 if (result == NodeProperties::kUnreliableReceiverMaps) { 2907 if (!receiver_map->is_stable()) return NoChange(); 2908 } 2909 } 2910 2911 // See if we can constant-fold the compatible receiver checks. 2912 CallOptimization call_optimization(isolate(), function_template_info); 2913 if (!call_optimization.is_simple_api_call()) return NoChange(); 2914 CallOptimization::HolderLookup lookup; 2915 Handle<JSObject> api_holder = 2916 call_optimization.LookupHolderOfExpectedType(receiver_maps[0], &lookup); 2917 if (lookup == CallOptimization::kHolderNotFound) return NoChange(); 2918 for (size_t i = 1; i < receiver_maps.size(); ++i) { 2919 CallOptimization::HolderLookup lookupi; 2920 Handle<JSObject> holder = call_optimization.LookupHolderOfExpectedType( 2921 receiver_maps[i], &lookupi); 2922 if (lookup != lookupi) return NoChange(); 2923 if (!api_holder.is_identical_to(holder)) return NoChange(); 2924 } 2925 2926 // Install stability dependencies for unreliable {receiver_maps}. 2927 if (result == NodeProperties::kUnreliableReceiverMaps) { 2928 for (size_t i = 0; i < receiver_maps.size(); ++i) { 2929 dependencies()->DependOnStableMap( 2930 MapRef(js_heap_broker(), receiver_maps[i])); 2931 } 2932 } 2933 2934 // Load the {target}s context. 2935 Node* context = effect = graph()->NewNode( 2936 simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target, 2937 effect, control); 2938 2939 // CallApiCallbackStub's register arguments: code, target, call data, holder, 2940 // function address. 2941 // TODO(turbofan): Consider introducing a JSCallApiCallback operator for 2942 // this and lower it during JSGenericLowering, and unify this with the 2943 // JSNativeContextSpecialization::InlineApiCall method a bit. 2944 Handle<CallHandlerInfo> call_handler_info( 2945 CallHandlerInfo::cast(function_template_info->call_code()), isolate()); 2946 Handle<Object> data(call_handler_info->data(), isolate()); 2947 Callable call_api_callback = CodeFactory::CallApiCallback(isolate(), argc); 2948 CallInterfaceDescriptor cid = call_api_callback.descriptor(); 2949 auto call_descriptor = Linkage::GetStubCallDescriptor( 2950 graph()->zone(), cid, 2951 cid.GetStackParameterCount() + argc + 1 /* implicit receiver */, 2952 CallDescriptor::kNeedsFrameState); 2953 ApiFunction api_function(v8::ToCData<Address>(call_handler_info->callback())); 2954 Node* holder = lookup == CallOptimization::kHolderFound 2955 ? jsgraph()->HeapConstant(api_holder) 2956 : receiver; 2957 ExternalReference function_reference = ExternalReference::Create( 2958 &api_function, ExternalReference::DIRECT_API_CALL); 2959 node->InsertInput(graph()->zone(), 0, 2960 jsgraph()->HeapConstant(call_api_callback.code())); 2961 node->ReplaceInput(1, context); 2962 node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(data)); 2963 node->InsertInput(graph()->zone(), 3, holder); 2964 node->InsertInput(graph()->zone(), 4, 2965 jsgraph()->ExternalConstant(function_reference)); 2966 node->ReplaceInput(5, receiver); 2967 node->RemoveInput(6 + argc); // Remove context input. 2968 node->ReplaceInput(7 + argc, effect); // Update effect input. 2969 NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); 2970 return Changed(node); 2971 } 2972 2973 namespace { 2974 2975 // Check whether elements aren't mutated; we play it extremely safe here by 2976 // explicitly checking that {node} is only used by {LoadField} or {LoadElement}. 2977 bool IsSafeArgumentsElements(Node* node) { 2978 for (Edge const edge : node->use_edges()) { 2979 if (!NodeProperties::IsValueEdge(edge)) continue; 2980 if (edge.from()->opcode() != IrOpcode::kLoadField && 2981 edge.from()->opcode() != IrOpcode::kLoadElement) { 2982 return false; 2983 } 2984 } 2985 return true; 2986 } 2987 2988 } // namespace 2989 2990 Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( 2991 Node* node, int arity, CallFrequency const& frequency, 2992 VectorSlotPair const& feedback) { 2993 DCHECK(node->opcode() == IrOpcode::kJSCallWithArrayLike || 2994 node->opcode() == IrOpcode::kJSCallWithSpread || 2995 node->opcode() == IrOpcode::kJSConstructWithArrayLike || 2996 node->opcode() == IrOpcode::kJSConstructWithSpread); 2997 2998 // In case of a call/construct with spread, we need to 2999 // ensure that it's safe to avoid the actual iteration. 3000 if ((node->opcode() == IrOpcode::kJSCallWithSpread || 3001 node->opcode() == IrOpcode::kJSConstructWithSpread) && 3002 !isolate()->IsArrayIteratorLookupChainIntact()) { 3003 return NoChange(); 3004 } 3005 3006 // Check if {arguments_list} is an arguments object, and {node} is the only 3007 // value user of {arguments_list} (except for value uses in frame states). 3008 Node* arguments_list = NodeProperties::GetValueInput(node, arity); 3009 if (arguments_list->opcode() != IrOpcode::kJSCreateArguments) { 3010 return NoChange(); 3011 } 3012 for (Edge edge : arguments_list->use_edges()) { 3013 if (!NodeProperties::IsValueEdge(edge)) continue; 3014 Node* const user = edge.from(); 3015 switch (user->opcode()) { 3016 case IrOpcode::kCheckMaps: 3017 case IrOpcode::kFrameState: 3018 case IrOpcode::kStateValues: 3019 case IrOpcode::kReferenceEqual: 3020 case IrOpcode::kReturn: 3021 // Ignore safe uses that definitely don't mess with the arguments. 3022 continue; 3023 case IrOpcode::kLoadField: { 3024 DCHECK_EQ(arguments_list, user->InputAt(0)); 3025 FieldAccess const& access = FieldAccessOf(user->op()); 3026 if (access.offset == JSArray::kLengthOffset) { 3027 // Ignore uses for arguments#length. 3028 STATIC_ASSERT(JSArray::kLengthOffset == 3029 JSArgumentsObject::kLengthOffset); 3030 continue; 3031 } else if (access.offset == JSObject::kElementsOffset) { 3032 // Ignore safe uses for arguments#elements. 3033 if (IsSafeArgumentsElements(user)) continue; 3034 } 3035 break; 3036 } 3037 case IrOpcode::kJSCallWithArrayLike: 3038 // Ignore uses as argumentsList input to calls with array like. 3039 if (user->InputAt(2) == arguments_list) continue; 3040 break; 3041 case IrOpcode::kJSConstructWithArrayLike: 3042 // Ignore uses as argumentsList input to calls with array like. 3043 if (user->InputAt(1) == arguments_list) continue; 3044 break; 3045 case IrOpcode::kJSCallWithSpread: { 3046 // Ignore uses as spread input to calls with spread. 3047 CallParameters p = CallParametersOf(user->op()); 3048 int const arity = static_cast<int>(p.arity() - 1); 3049 if (user->InputAt(arity) == arguments_list) continue; 3050 break; 3051 } 3052 case IrOpcode::kJSConstructWithSpread: { 3053 // Ignore uses as spread input to construct with spread. 3054 ConstructParameters p = ConstructParametersOf(user->op()); 3055 int const arity = static_cast<int>(p.arity() - 2); 3056 if (user->InputAt(arity) == arguments_list) continue; 3057 break; 3058 } 3059 default: 3060 break; 3061 } 3062 // We cannot currently reduce the {node} to something better than what 3063 // it already is, but we might be able to do something about the {node} 3064 // later, so put it on the waitlist and try again during finalization. 3065 waitlist_.insert(node); 3066 return NoChange(); 3067 } 3068 3069 // Get to the actual frame state from which to extract the arguments; 3070 // we can only optimize this in case the {node} was already inlined into 3071 // some other function (and same for the {arguments_list}). 3072 CreateArgumentsType const type = CreateArgumentsTypeOf(arguments_list->op()); 3073 Node* frame_state = NodeProperties::GetFrameStateInput(arguments_list); 3074 FrameStateInfo state_info = FrameStateInfoOf(frame_state->op()); 3075 int start_index = 0; 3076 // Determine the formal parameter count; 3077 Handle<SharedFunctionInfo> shared; 3078 if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); 3079 int formal_parameter_count = shared->internal_formal_parameter_count(); 3080 if (type == CreateArgumentsType::kMappedArguments) { 3081 // Mapped arguments (sloppy mode) that are aliased can only be handled 3082 // here if there's no side-effect between the {node} and the {arg_array}. 3083 // TODO(turbofan): Further relax this constraint. 3084 if (formal_parameter_count != 0) { 3085 Node* effect = NodeProperties::GetEffectInput(node); 3086 if (!NodeProperties::NoObservableSideEffectBetween(effect, 3087 arguments_list)) { 3088 return NoChange(); 3089 } 3090 } 3091 } else if (type == CreateArgumentsType::kRestParameter) { 3092 start_index = formal_parameter_count; 3093 } 3094 3095 // For call/construct with spread, we need to also install a code 3096 // dependency on the array iterator lookup protector cell to ensure 3097 // that no one messed with the %ArrayIteratorPrototype%.next method. 3098 if (node->opcode() == IrOpcode::kJSCallWithSpread || 3099 node->opcode() == IrOpcode::kJSConstructWithSpread) { 3100 dependencies()->DependOnProtector(PropertyCellRef( 3101 js_heap_broker(), factory()->array_iterator_protector())); 3102 } 3103 3104 // Remove the {arguments_list} input from the {node}. 3105 node->RemoveInput(arity--); 3106 // Check if are spreading to inlined arguments or to the arguments of 3107 // the outermost function. 3108 Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput); 3109 if (outer_state->opcode() != IrOpcode::kFrameState) { 3110 Operator const* op = 3111 (node->opcode() == IrOpcode::kJSCallWithArrayLike || 3112 node->opcode() == IrOpcode::kJSCallWithSpread) 3113 ? javascript()->CallForwardVarargs(arity + 1, start_index) 3114 : javascript()->ConstructForwardVarargs(arity + 2, start_index); 3115 NodeProperties::ChangeOp(node, op); 3116 return Changed(node); 3117 } 3118 // Get to the actual frame state from which to extract the arguments; 3119 // we can only optimize this in case the {node} was already inlined into 3120 // some other function (and same for the {arg_array}). 3121 FrameStateInfo outer_info = FrameStateInfoOf(outer_state->op()); 3122 if (outer_info.type() == FrameStateType::kArgumentsAdaptor) { 3123 // Need to take the parameters from the arguments adaptor. 3124 frame_state = outer_state; 3125 } 3126 // Add the actual parameters to the {node}, skipping the receiver. 3127 Node* const parameters = frame_state->InputAt(kFrameStateParametersInput); 3128 for (int i = start_index + 1; i < parameters->InputCount(); ++i) { 3129 node->InsertInput(graph()->zone(), static_cast<int>(++arity), 3130 parameters->InputAt(i)); 3131 } 3132 3133 if (node->opcode() == IrOpcode::kJSCallWithArrayLike || 3134 node->opcode() == IrOpcode::kJSCallWithSpread) { 3135 NodeProperties::ChangeOp( 3136 node, javascript()->Call(arity + 1, frequency, feedback)); 3137 Reduction const reduction = ReduceJSCall(node); 3138 return reduction.Changed() ? reduction : Changed(node); 3139 } else { 3140 NodeProperties::ChangeOp( 3141 node, javascript()->Construct(arity + 2, frequency, feedback)); 3142 Node* new_target = NodeProperties::GetValueInput(node, arity + 1); 3143 Node* frame_state = NodeProperties::GetFrameStateInput(node); 3144 Node* context = NodeProperties::GetContextInput(node); 3145 Node* effect = NodeProperties::GetEffectInput(node); 3146 Node* control = NodeProperties::GetControlInput(node); 3147 3148 // Check whether the given new target value is a constructor function. The 3149 // replacement {JSConstruct} operator only checks the passed target value 3150 // but relies on the new target value to be implicitly valid. 3151 Node* check = 3152 graph()->NewNode(simplified()->ObjectIsConstructor(), new_target); 3153 Node* check_branch = 3154 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); 3155 Node* check_fail = graph()->NewNode(common()->IfFalse(), check_branch); 3156 Node* check_throw = check_fail = 3157 graph()->NewNode(javascript()->CallRuntime(Runtime::kThrowTypeError, 2), 3158 jsgraph()->Constant(MessageTemplate::kNotConstructor), 3159 new_target, context, frame_state, effect, check_fail); 3160 control = graph()->NewNode(common()->IfTrue(), check_branch); 3161 NodeProperties::ReplaceControlInput(node, control); 3162 3163 // Rewire potential exception edges. 3164 Node* on_exception = nullptr; 3165 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { 3166 // Create appropriate {IfException} and {IfSuccess} nodes. 3167 Node* if_exception = 3168 graph()->NewNode(common()->IfException(), check_throw, check_fail); 3169 check_fail = graph()->NewNode(common()->IfSuccess(), check_fail); 3170 3171 // Join the exception edges. 3172 Node* merge = 3173 graph()->NewNode(common()->Merge(2), if_exception, on_exception); 3174 Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception, 3175 on_exception, merge); 3176 Node* phi = 3177 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 3178 if_exception, on_exception, merge); 3179 ReplaceWithValue(on_exception, phi, ephi, merge); 3180 merge->ReplaceInput(1, on_exception); 3181 ephi->ReplaceInput(1, on_exception); 3182 phi->ReplaceInput(1, on_exception); 3183 } 3184 3185 // The above %ThrowTypeError runtime call is an unconditional throw, making 3186 // it impossible to return a successful completion in this case. We simply 3187 // connect the successful completion to the graph end. 3188 Node* throw_node = 3189 graph()->NewNode(common()->Throw(), check_throw, check_fail); 3190 NodeProperties::MergeControlToEnd(graph(), common(), throw_node); 3191 3192 Reduction const reduction = ReduceJSConstruct(node); 3193 return reduction.Changed() ? reduction : Changed(node); 3194 } 3195 } 3196 3197 namespace { 3198 3199 bool ShouldUseCallICFeedback(Node* node) { 3200 HeapObjectMatcher m(node); 3201 if (m.HasValue() || m.IsJSCreateClosure()) { 3202 // Don't use CallIC feedback when we know the function 3203 // being called, i.e. either know the closure itself or 3204 // at least the SharedFunctionInfo. 3205 return false; 3206 } else if (m.IsPhi()) { 3207 // Protect against endless loops here. 3208 Node* control = NodeProperties::GetControlInput(node); 3209 if (control->opcode() == IrOpcode::kLoop) return false; 3210 // Check if {node} is a Phi of nodes which shouldn't 3211 // use CallIC feedback (not looking through loops). 3212 int const value_input_count = m.node()->op()->ValueInputCount(); 3213 for (int n = 0; n < value_input_count; ++n) { 3214 if (ShouldUseCallICFeedback(node->InputAt(n))) return true; 3215 } 3216 return false; 3217 } 3218 return true; 3219 } 3220 3221 } // namespace 3222 3223 Reduction JSCallReducer::ReduceJSCall(Node* node) { 3224 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 3225 CallParameters const& p = CallParametersOf(node->op()); 3226 Node* target = NodeProperties::GetValueInput(node, 0); 3227 Node* control = NodeProperties::GetControlInput(node); 3228 Node* effect = NodeProperties::GetEffectInput(node); 3229 size_t arity = p.arity(); 3230 DCHECK_LE(2u, arity); 3231 3232 // Try to specialize JSCall {node}s with constant {target}s. 3233 HeapObjectMatcher m(target); 3234 if (m.HasValue()) { 3235 if (m.Value()->IsJSFunction()) { 3236 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); 3237 3238 // Don't inline cross native context. 3239 if (function->native_context() != *native_context()) return NoChange(); 3240 3241 return ReduceJSCall(node, handle(function->shared(), isolate())); 3242 } else if (m.Value()->IsJSBoundFunction()) { 3243 Handle<JSBoundFunction> function = 3244 Handle<JSBoundFunction>::cast(m.Value()); 3245 Handle<JSReceiver> bound_target_function( 3246 function->bound_target_function(), isolate()); 3247 Handle<Object> bound_this(function->bound_this(), isolate()); 3248 Handle<FixedArray> bound_arguments(function->bound_arguments(), 3249 isolate()); 3250 ConvertReceiverMode const convert_mode = 3251 (bound_this->IsNullOrUndefined(isolate())) 3252 ? ConvertReceiverMode::kNullOrUndefined 3253 : ConvertReceiverMode::kNotNullOrUndefined; 3254 // Patch {node} to use [[BoundTargetFunction]] and [[BoundThis]]. 3255 NodeProperties::ReplaceValueInput( 3256 node, jsgraph()->Constant(bound_target_function), 0); 3257 NodeProperties::ReplaceValueInput(node, jsgraph()->Constant(bound_this), 3258 1); 3259 // Insert the [[BoundArguments]] for {node}. 3260 for (int i = 0; i < bound_arguments->length(); ++i) { 3261 node->InsertInput( 3262 graph()->zone(), i + 2, 3263 jsgraph()->Constant(handle(bound_arguments->get(i), isolate()))); 3264 arity++; 3265 } 3266 NodeProperties::ChangeOp( 3267 node, javascript()->Call(arity, p.frequency(), VectorSlotPair(), 3268 convert_mode)); 3269 // Try to further reduce the JSCall {node}. 3270 Reduction const reduction = ReduceJSCall(node); 3271 return reduction.Changed() ? reduction : Changed(node); 3272 } 3273 3274 // Don't mess with other {node}s that have a constant {target}. 3275 // TODO(bmeurer): Also support proxies here. 3276 return NoChange(); 3277 } 3278 3279 // If {target} is the result of a JSCreateClosure operation, we can 3280 // just immediately try to inline based on the SharedFunctionInfo, 3281 // since TurboFan generally doesn't inline cross-context, and hence 3282 // the {target} must have the same native context as the call site. 3283 if (target->opcode() == IrOpcode::kJSCreateClosure) { 3284 CreateClosureParameters const& p = CreateClosureParametersOf(target->op()); 3285 return ReduceJSCall(node, p.shared_info()); 3286 } 3287 3288 // If {target} is the result of a JSCreateBoundFunction operation, 3289 // we can just fold the construction and call the bound target 3290 // function directly instead. 3291 if (target->opcode() == IrOpcode::kJSCreateBoundFunction) { 3292 Node* bound_target_function = NodeProperties::GetValueInput(target, 0); 3293 Node* bound_this = NodeProperties::GetValueInput(target, 1); 3294 int const bound_arguments_length = 3295 static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity()); 3296 3297 // Patch the {node} to use [[BoundTargetFunction]] and [[BoundThis]]. 3298 NodeProperties::ReplaceValueInput(node, bound_target_function, 0); 3299 NodeProperties::ReplaceValueInput(node, bound_this, 1); 3300 3301 // Insert the [[BoundArguments]] for {node}. 3302 for (int i = 0; i < bound_arguments_length; ++i) { 3303 Node* value = NodeProperties::GetValueInput(target, 2 + i); 3304 node->InsertInput(graph()->zone(), 2 + i, value); 3305 arity++; 3306 } 3307 3308 // Update the JSCall operator on {node}. 3309 ConvertReceiverMode const convert_mode = 3310 NodeProperties::CanBeNullOrUndefined(isolate(), bound_this, effect) 3311 ? ConvertReceiverMode::kAny 3312 : ConvertReceiverMode::kNotNullOrUndefined; 3313 NodeProperties::ChangeOp( 3314 node, javascript()->Call(arity, p.frequency(), VectorSlotPair(), 3315 convert_mode)); 3316 3317 // Try to further reduce the JSCall {node}. 3318 Reduction const reduction = ReduceJSCall(node); 3319 return reduction.Changed() ? reduction : Changed(node); 3320 } 3321 3322 // Extract feedback from the {node} using the FeedbackNexus. 3323 if (!p.feedback().IsValid()) return NoChange(); 3324 FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); 3325 if (nexus.IsUninitialized()) { 3326 if (flags() & kBailoutOnUninitialized) { 3327 // Introduce a SOFT deopt if the call {node} wasn't executed so far. 3328 return ReduceSoftDeoptimize( 3329 node, DeoptimizeReason::kInsufficientTypeFeedbackForCall); 3330 } 3331 return NoChange(); 3332 } 3333 3334 HeapObject* heap_object; 3335 if (nexus.GetFeedback()->ToWeakHeapObject(&heap_object)) { 3336 Handle<HeapObject> feedback(heap_object, isolate()); 3337 // Check if we want to use CallIC feedback here. 3338 if (!ShouldUseCallICFeedback(target)) return NoChange(); 3339 3340 if (feedback->IsCallable()) { 3341 Node* target_function = jsgraph()->Constant(feedback); 3342 3343 // Check that the {target} is still the {target_function}. 3344 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target, 3345 target_function); 3346 effect = graph()->NewNode( 3347 simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check, 3348 effect, control); 3349 3350 // Specialize the JSCall node to the {target_function}. 3351 NodeProperties::ReplaceValueInput(node, target_function, 0); 3352 NodeProperties::ReplaceEffectInput(node, effect); 3353 3354 // Try to further reduce the JSCall {node}. 3355 Reduction const reduction = ReduceJSCall(node); 3356 return reduction.Changed() ? reduction : Changed(node); 3357 } 3358 } 3359 return NoChange(); 3360 } 3361 3362 Reduction JSCallReducer::ReduceJSCall(Node* node, 3363 Handle<SharedFunctionInfo> shared) { 3364 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 3365 Node* target = NodeProperties::GetValueInput(node, 0); 3366 3367 // Do not reduce calls to functions with break points. 3368 if (shared->HasBreakInfo()) return NoChange(); 3369 3370 // Raise a TypeError if the {target} is a "classConstructor". 3371 if (IsClassConstructor(shared->kind())) { 3372 NodeProperties::ReplaceValueInputs(node, target); 3373 NodeProperties::ChangeOp( 3374 node, javascript()->CallRuntime( 3375 Runtime::kThrowConstructorNonCallableError, 1)); 3376 return Changed(node); 3377 } 3378 3379 // Check for known builtin functions. 3380 3381 int builtin_id = 3382 shared->HasBuiltinId() ? shared->builtin_id() : Builtins::kNoBuiltinId; 3383 switch (builtin_id) { 3384 case Builtins::kArrayConstructor: 3385 return ReduceArrayConstructor(node); 3386 case Builtins::kBooleanConstructor: 3387 return ReduceBooleanConstructor(node); 3388 case Builtins::kFunctionPrototypeApply: 3389 return ReduceFunctionPrototypeApply(node); 3390 case Builtins::kFastFunctionPrototypeBind: 3391 return ReduceFunctionPrototypeBind(node); 3392 case Builtins::kFunctionPrototypeCall: 3393 return ReduceFunctionPrototypeCall(node); 3394 case Builtins::kFunctionPrototypeHasInstance: 3395 return ReduceFunctionPrototypeHasInstance(node); 3396 case Builtins::kObjectConstructor: 3397 return ReduceObjectConstructor(node); 3398 case Builtins::kObjectCreate: 3399 return ReduceObjectCreate(node); 3400 case Builtins::kObjectGetPrototypeOf: 3401 return ReduceObjectGetPrototypeOf(node); 3402 case Builtins::kObjectIs: 3403 return ReduceObjectIs(node); 3404 case Builtins::kObjectPrototypeGetProto: 3405 return ReduceObjectPrototypeGetProto(node); 3406 case Builtins::kObjectPrototypeHasOwnProperty: 3407 return ReduceObjectPrototypeHasOwnProperty(node); 3408 case Builtins::kObjectPrototypeIsPrototypeOf: 3409 return ReduceObjectPrototypeIsPrototypeOf(node); 3410 case Builtins::kReflectApply: 3411 return ReduceReflectApply(node); 3412 case Builtins::kReflectConstruct: 3413 return ReduceReflectConstruct(node); 3414 case Builtins::kReflectGet: 3415 return ReduceReflectGet(node); 3416 case Builtins::kReflectGetPrototypeOf: 3417 return ReduceReflectGetPrototypeOf(node); 3418 case Builtins::kReflectHas: 3419 return ReduceReflectHas(node); 3420 case Builtins::kArrayForEach: 3421 return ReduceArrayForEach(node, shared); 3422 case Builtins::kArrayMap: 3423 return ReduceArrayMap(node, shared); 3424 case Builtins::kArrayFilter: 3425 return ReduceArrayFilter(node, shared); 3426 case Builtins::kArrayReduce: 3427 return ReduceArrayReduce(node, ArrayReduceDirection::kLeft, shared); 3428 case Builtins::kArrayReduceRight: 3429 return ReduceArrayReduce(node, ArrayReduceDirection::kRight, shared); 3430 case Builtins::kArrayPrototypeFind: 3431 return ReduceArrayFind(node, ArrayFindVariant::kFind, shared); 3432 case Builtins::kArrayPrototypeFindIndex: 3433 return ReduceArrayFind(node, ArrayFindVariant::kFindIndex, shared); 3434 case Builtins::kArrayEvery: 3435 return ReduceArrayEvery(node, shared); 3436 case Builtins::kArrayIndexOf: 3437 return ReduceArrayIndexOfIncludes(SearchVariant::kIndexOf, node); 3438 case Builtins::kArrayIncludes: 3439 return ReduceArrayIndexOfIncludes(SearchVariant::kIncludes, node); 3440 case Builtins::kArraySome: 3441 return ReduceArraySome(node, shared); 3442 case Builtins::kArrayPrototypePush: 3443 return ReduceArrayPrototypePush(node); 3444 case Builtins::kArrayPrototypePop: 3445 return ReduceArrayPrototypePop(node); 3446 case Builtins::kArrayPrototypeShift: 3447 return ReduceArrayPrototypeShift(node); 3448 case Builtins::kArrayPrototypeSlice: 3449 return ReduceArrayPrototypeSlice(node); 3450 case Builtins::kArrayPrototypeEntries: 3451 return ReduceArrayIterator(node, IterationKind::kEntries); 3452 case Builtins::kArrayPrototypeKeys: 3453 return ReduceArrayIterator(node, IterationKind::kKeys); 3454 case Builtins::kArrayPrototypeValues: 3455 return ReduceArrayIterator(node, IterationKind::kValues); 3456 case Builtins::kArrayIteratorPrototypeNext: 3457 return ReduceArrayIteratorPrototypeNext(node); 3458 case Builtins::kArrayIsArray: 3459 return ReduceArrayIsArray(node); 3460 case Builtins::kArrayBufferIsView: 3461 return ReduceArrayBufferIsView(node); 3462 case Builtins::kDataViewPrototypeGetByteLength: 3463 return ReduceArrayBufferViewAccessor( 3464 node, JS_DATA_VIEW_TYPE, 3465 AccessBuilder::ForJSArrayBufferViewByteLength()); 3466 case Builtins::kDataViewPrototypeGetByteOffset: 3467 return ReduceArrayBufferViewAccessor( 3468 node, JS_DATA_VIEW_TYPE, 3469 AccessBuilder::ForJSArrayBufferViewByteOffset()); 3470 case Builtins::kDataViewPrototypeGetUint8: 3471 return ReduceDataViewPrototypeGet(node, 3472 ExternalArrayType::kExternalUint8Array); 3473 case Builtins::kDataViewPrototypeGetInt8: 3474 return ReduceDataViewPrototypeGet(node, 3475 ExternalArrayType::kExternalInt8Array); 3476 case Builtins::kDataViewPrototypeGetUint16: 3477 return ReduceDataViewPrototypeGet( 3478 node, ExternalArrayType::kExternalUint16Array); 3479 case Builtins::kDataViewPrototypeGetInt16: 3480 return ReduceDataViewPrototypeGet(node, 3481 ExternalArrayType::kExternalInt16Array); 3482 case Builtins::kDataViewPrototypeGetUint32: 3483 return ReduceDataViewPrototypeGet( 3484 node, ExternalArrayType::kExternalUint32Array); 3485 case Builtins::kDataViewPrototypeGetInt32: 3486 return ReduceDataViewPrototypeGet(node, 3487 ExternalArrayType::kExternalInt32Array); 3488 case Builtins::kDataViewPrototypeGetFloat32: 3489 return ReduceDataViewPrototypeGet( 3490 node, ExternalArrayType::kExternalFloat32Array); 3491 case Builtins::kDataViewPrototypeGetFloat64: 3492 return ReduceDataViewPrototypeGet( 3493 node, ExternalArrayType::kExternalFloat64Array); 3494 case Builtins::kDataViewPrototypeSetUint8: 3495 return ReduceDataViewPrototypeSet(node, 3496 ExternalArrayType::kExternalUint8Array); 3497 case Builtins::kDataViewPrototypeSetInt8: 3498 return ReduceDataViewPrototypeSet(node, 3499 ExternalArrayType::kExternalInt8Array); 3500 case Builtins::kDataViewPrototypeSetUint16: 3501 return ReduceDataViewPrototypeSet( 3502 node, ExternalArrayType::kExternalUint16Array); 3503 case Builtins::kDataViewPrototypeSetInt16: 3504 return ReduceDataViewPrototypeSet(node, 3505 ExternalArrayType::kExternalInt16Array); 3506 case Builtins::kDataViewPrototypeSetUint32: 3507 return ReduceDataViewPrototypeSet( 3508 node, ExternalArrayType::kExternalUint32Array); 3509 case Builtins::kDataViewPrototypeSetInt32: 3510 return ReduceDataViewPrototypeSet(node, 3511 ExternalArrayType::kExternalInt32Array); 3512 case Builtins::kDataViewPrototypeSetFloat32: 3513 return ReduceDataViewPrototypeSet( 3514 node, ExternalArrayType::kExternalFloat32Array); 3515 case Builtins::kDataViewPrototypeSetFloat64: 3516 return ReduceDataViewPrototypeSet( 3517 node, ExternalArrayType::kExternalFloat64Array); 3518 case Builtins::kTypedArrayPrototypeByteLength: 3519 return ReduceArrayBufferViewAccessor( 3520 node, JS_TYPED_ARRAY_TYPE, 3521 AccessBuilder::ForJSArrayBufferViewByteLength()); 3522 case Builtins::kTypedArrayPrototypeByteOffset: 3523 return ReduceArrayBufferViewAccessor( 3524 node, JS_TYPED_ARRAY_TYPE, 3525 AccessBuilder::ForJSArrayBufferViewByteOffset()); 3526 case Builtins::kTypedArrayPrototypeLength: 3527 return ReduceArrayBufferViewAccessor( 3528 node, JS_TYPED_ARRAY_TYPE, AccessBuilder::ForJSTypedArrayLength()); 3529 case Builtins::kTypedArrayPrototypeToStringTag: 3530 return ReduceTypedArrayPrototypeToStringTag(node); 3531 case Builtins::kMathAbs: 3532 return ReduceMathUnary(node, simplified()->NumberAbs()); 3533 case Builtins::kMathAcos: 3534 return ReduceMathUnary(node, simplified()->NumberAcos()); 3535 case Builtins::kMathAcosh: 3536 return ReduceMathUnary(node, simplified()->NumberAcosh()); 3537 case Builtins::kMathAsin: 3538 return ReduceMathUnary(node, simplified()->NumberAsin()); 3539 case Builtins::kMathAsinh: 3540 return ReduceMathUnary(node, simplified()->NumberAsinh()); 3541 case Builtins::kMathAtan: 3542 return ReduceMathUnary(node, simplified()->NumberAtan()); 3543 case Builtins::kMathAtanh: 3544 return ReduceMathUnary(node, simplified()->NumberAtanh()); 3545 case Builtins::kMathCbrt: 3546 return ReduceMathUnary(node, simplified()->NumberCbrt()); 3547 case Builtins::kMathCeil: 3548 return ReduceMathUnary(node, simplified()->NumberCeil()); 3549 case Builtins::kMathCos: 3550 return ReduceMathUnary(node, simplified()->NumberCos()); 3551 case Builtins::kMathCosh: 3552 return ReduceMathUnary(node, simplified()->NumberCosh()); 3553 case Builtins::kMathExp: 3554 return ReduceMathUnary(node, simplified()->NumberExp()); 3555 case Builtins::kMathExpm1: 3556 return ReduceMathUnary(node, simplified()->NumberExpm1()); 3557 case Builtins::kMathFloor: 3558 return ReduceMathUnary(node, simplified()->NumberFloor()); 3559 case Builtins::kMathFround: 3560 return ReduceMathUnary(node, simplified()->NumberFround()); 3561 case Builtins::kMathLog: 3562 return ReduceMathUnary(node, simplified()->NumberLog()); 3563 case Builtins::kMathLog1p: 3564 return ReduceMathUnary(node, simplified()->NumberLog1p()); 3565 case Builtins::kMathLog10: 3566 return ReduceMathUnary(node, simplified()->NumberLog10()); 3567 case Builtins::kMathLog2: 3568 return ReduceMathUnary(node, simplified()->NumberLog2()); 3569 case Builtins::kMathRound: 3570 return ReduceMathUnary(node, simplified()->NumberRound()); 3571 case Builtins::kMathSign: 3572 return ReduceMathUnary(node, simplified()->NumberSign()); 3573 case Builtins::kMathSin: 3574 return ReduceMathUnary(node, simplified()->NumberSin()); 3575 case Builtins::kMathSinh: 3576 return ReduceMathUnary(node, simplified()->NumberSinh()); 3577 case Builtins::kMathSqrt: 3578 return ReduceMathUnary(node, simplified()->NumberSqrt()); 3579 case Builtins::kMathTan: 3580 return ReduceMathUnary(node, simplified()->NumberTan()); 3581 case Builtins::kMathTanh: 3582 return ReduceMathUnary(node, simplified()->NumberTanh()); 3583 case Builtins::kMathTrunc: 3584 return ReduceMathUnary(node, simplified()->NumberTrunc()); 3585 case Builtins::kMathAtan2: 3586 return ReduceMathBinary(node, simplified()->NumberAtan2()); 3587 case Builtins::kMathPow: 3588 return ReduceMathBinary(node, simplified()->NumberPow()); 3589 case Builtins::kMathClz32: 3590 return ReduceMathClz32(node); 3591 case Builtins::kMathImul: 3592 return ReduceMathImul(node); 3593 case Builtins::kMathMax: 3594 return ReduceMathMinMax(node, simplified()->NumberMax(), 3595 jsgraph()->Constant(-V8_INFINITY)); 3596 case Builtins::kMathMin: 3597 return ReduceMathMinMax(node, simplified()->NumberMin(), 3598 jsgraph()->Constant(V8_INFINITY)); 3599 case Builtins::kNumberIsFinite: 3600 return ReduceNumberIsFinite(node); 3601 case Builtins::kNumberIsInteger: 3602 return ReduceNumberIsInteger(node); 3603 case Builtins::kNumberIsSafeInteger: 3604 return ReduceNumberIsSafeInteger(node); 3605 case Builtins::kNumberIsNaN: 3606 return ReduceNumberIsNaN(node); 3607 case Builtins::kNumberParseInt: 3608 return ReduceNumberParseInt(node); 3609 case Builtins::kGlobalIsFinite: 3610 return ReduceGlobalIsFinite(node); 3611 case Builtins::kGlobalIsNaN: 3612 return ReduceGlobalIsNaN(node); 3613 case Builtins::kMapPrototypeGet: 3614 return ReduceMapPrototypeGet(node); 3615 case Builtins::kMapPrototypeHas: 3616 return ReduceMapPrototypeHas(node); 3617 case Builtins::kRegExpPrototypeTest: 3618 return ReduceRegExpPrototypeTest(node); 3619 case Builtins::kReturnReceiver: 3620 return ReduceReturnReceiver(node); 3621 case Builtins::kStringPrototypeIndexOf: 3622 return ReduceStringPrototypeIndexOf(node); 3623 case Builtins::kStringPrototypeCharAt: 3624 return ReduceStringPrototypeCharAt(node); 3625 case Builtins::kStringPrototypeCharCodeAt: 3626 return ReduceStringPrototypeStringAt(simplified()->StringCharCodeAt(), 3627 node); 3628 case Builtins::kStringPrototypeCodePointAt: 3629 return ReduceStringPrototypeStringAt( 3630 simplified()->StringCodePointAt(UnicodeEncoding::UTF32), node); 3631 case Builtins::kStringPrototypeSubstring: 3632 return ReduceStringPrototypeSubstring(node); 3633 case Builtins::kStringPrototypeSlice: 3634 return ReduceStringPrototypeSlice(node); 3635 case Builtins::kStringPrototypeSubstr: 3636 return ReduceStringPrototypeSubstr(node); 3637 #ifdef V8_INTL_SUPPORT 3638 case Builtins::kStringPrototypeToLowerCaseIntl: 3639 return ReduceStringPrototypeToLowerCaseIntl(node); 3640 case Builtins::kStringPrototypeToUpperCaseIntl: 3641 return ReduceStringPrototypeToUpperCaseIntl(node); 3642 #endif // V8_INTL_SUPPORT 3643 case Builtins::kStringFromCharCode: 3644 return ReduceStringFromCharCode(node); 3645 case Builtins::kStringFromCodePoint: 3646 return ReduceStringFromCodePoint(node); 3647 case Builtins::kStringPrototypeIterator: 3648 return ReduceStringPrototypeIterator(node); 3649 case Builtins::kStringIteratorPrototypeNext: 3650 return ReduceStringIteratorPrototypeNext(node); 3651 case Builtins::kStringPrototypeConcat: 3652 return ReduceStringPrototypeConcat(node, shared); 3653 case Builtins::kTypedArrayPrototypeEntries: 3654 return ReduceArrayIterator(node, IterationKind::kEntries); 3655 case Builtins::kTypedArrayPrototypeKeys: 3656 return ReduceArrayIterator(node, IterationKind::kKeys); 3657 case Builtins::kTypedArrayPrototypeValues: 3658 return ReduceArrayIterator(node, IterationKind::kValues); 3659 case Builtins::kAsyncFunctionPromiseCreate: 3660 return ReduceAsyncFunctionPromiseCreate(node); 3661 case Builtins::kAsyncFunctionPromiseRelease: 3662 return ReduceAsyncFunctionPromiseRelease(node); 3663 case Builtins::kPromiseInternalConstructor: 3664 return ReducePromiseInternalConstructor(node); 3665 case Builtins::kPromiseInternalReject: 3666 return ReducePromiseInternalReject(node); 3667 case Builtins::kPromiseInternalResolve: 3668 return ReducePromiseInternalResolve(node); 3669 case Builtins::kPromisePrototypeCatch: 3670 return ReducePromisePrototypeCatch(node); 3671 case Builtins::kPromisePrototypeFinally: 3672 return ReducePromisePrototypeFinally(node); 3673 case Builtins::kPromisePrototypeThen: 3674 return ReducePromisePrototypeThen(node); 3675 case Builtins::kMapPrototypeEntries: 3676 return ReduceCollectionIteration(node, CollectionKind::kMap, 3677 IterationKind::kEntries); 3678 case Builtins::kMapPrototypeKeys: 3679 return ReduceCollectionIteration(node, CollectionKind::kMap, 3680 IterationKind::kKeys); 3681 case Builtins::kMapPrototypeGetSize: 3682 return ReduceCollectionPrototypeSize(node, CollectionKind::kMap); 3683 case Builtins::kMapPrototypeValues: 3684 return ReduceCollectionIteration(node, CollectionKind::kMap, 3685 IterationKind::kValues); 3686 case Builtins::kMapIteratorPrototypeNext: 3687 return ReduceCollectionIteratorPrototypeNext( 3688 node, OrderedHashMap::kEntrySize, factory()->empty_ordered_hash_map(), 3689 FIRST_MAP_ITERATOR_TYPE, LAST_MAP_ITERATOR_TYPE); 3690 case Builtins::kSetPrototypeEntries: 3691 return ReduceCollectionIteration(node, CollectionKind::kSet, 3692 IterationKind::kEntries); 3693 case Builtins::kSetPrototypeGetSize: 3694 return ReduceCollectionPrototypeSize(node, CollectionKind::kSet); 3695 case Builtins::kSetPrototypeValues: 3696 return ReduceCollectionIteration(node, CollectionKind::kSet, 3697 IterationKind::kValues); 3698 case Builtins::kSetIteratorPrototypeNext: 3699 return ReduceCollectionIteratorPrototypeNext( 3700 node, OrderedHashSet::kEntrySize, factory()->empty_ordered_hash_set(), 3701 FIRST_SET_ITERATOR_TYPE, LAST_SET_ITERATOR_TYPE); 3702 case Builtins::kDatePrototypeGetTime: 3703 return ReduceDatePrototypeGetTime(node); 3704 case Builtins::kDateNow: 3705 return ReduceDateNow(node); 3706 case Builtins::kNumberConstructor: 3707 return ReduceNumberConstructor(node); 3708 default: 3709 break; 3710 } 3711 3712 if (!FLAG_runtime_stats && shared->IsApiFunction()) { 3713 return ReduceCallApiFunction(node, shared); 3714 } 3715 return NoChange(); 3716 } 3717 3718 Reduction JSCallReducer::ReduceJSCallWithArrayLike(Node* node) { 3719 DCHECK_EQ(IrOpcode::kJSCallWithArrayLike, node->opcode()); 3720 CallFrequency frequency = CallFrequencyOf(node->op()); 3721 VectorSlotPair feedback; 3722 return ReduceCallOrConstructWithArrayLikeOrSpread(node, 2, frequency, 3723 feedback); 3724 } 3725 3726 Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) { 3727 DCHECK_EQ(IrOpcode::kJSCallWithSpread, node->opcode()); 3728 CallParameters const& p = CallParametersOf(node->op()); 3729 DCHECK_LE(3u, p.arity()); 3730 int arity = static_cast<int>(p.arity() - 1); 3731 CallFrequency frequency = p.frequency(); 3732 VectorSlotPair feedback = p.feedback(); 3733 return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency, 3734 feedback); 3735 } 3736 3737 Reduction JSCallReducer::ReduceJSConstruct(Node* node) { 3738 DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode()); 3739 ConstructParameters const& p = ConstructParametersOf(node->op()); 3740 DCHECK_LE(2u, p.arity()); 3741 int arity = static_cast<int>(p.arity() - 2); 3742 Node* target = NodeProperties::GetValueInput(node, 0); 3743 Node* new_target = NodeProperties::GetValueInput(node, arity + 1); 3744 Node* effect = NodeProperties::GetEffectInput(node); 3745 Node* control = NodeProperties::GetControlInput(node); 3746 3747 // Extract feedback from the {node} using the FeedbackNexus. 3748 if (p.feedback().IsValid()) { 3749 FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); 3750 if (nexus.IsUninitialized()) { 3751 if (flags() & kBailoutOnUninitialized) { 3752 // Introduce a SOFT deopt if the construct {node} wasn't executed so 3753 // far. 3754 return ReduceSoftDeoptimize( 3755 node, DeoptimizeReason::kInsufficientTypeFeedbackForConstruct); 3756 } 3757 return NoChange(); 3758 } 3759 3760 HeapObject* feedback_object; 3761 if (nexus.GetFeedback()->ToStrongHeapObject(&feedback_object) && 3762 feedback_object->IsAllocationSite()) { 3763 // The feedback is an AllocationSite, which means we have called the 3764 // Array function and collected transition (and pretenuring) feedback 3765 // for the resulting arrays. This has to be kept in sync with the 3766 // implementation in Ignition. 3767 Handle<AllocationSite> site(AllocationSite::cast(feedback_object), 3768 isolate()); 3769 3770 // Retrieve the Array function from the {node}. 3771 Node* array_function = jsgraph()->HeapConstant( 3772 handle(native_context()->array_function(), isolate())); 3773 3774 // Check that the {target} is still the {array_function}. 3775 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target, 3776 array_function); 3777 effect = graph()->NewNode( 3778 simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check, 3779 effect, control); 3780 3781 // Turn the {node} into a {JSCreateArray} call. 3782 NodeProperties::ReplaceEffectInput(node, effect); 3783 for (int i = arity; i > 0; --i) { 3784 NodeProperties::ReplaceValueInput( 3785 node, NodeProperties::GetValueInput(node, i), i + 1); 3786 } 3787 NodeProperties::ReplaceValueInput(node, array_function, 1); 3788 NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site)); 3789 return Changed(node); 3790 } else if (nexus.GetFeedback()->ToWeakHeapObject(&feedback_object) && 3791 !HeapObjectMatcher(new_target).HasValue()) { 3792 Handle<HeapObject> object(feedback_object, isolate()); 3793 if (object->IsConstructor()) { 3794 Node* new_target_feedback = jsgraph()->Constant(object); 3795 3796 // Check that the {new_target} is still the {new_target_feedback}. 3797 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), 3798 new_target, new_target_feedback); 3799 effect = graph()->NewNode( 3800 simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check, 3801 effect, control); 3802 3803 // Specialize the JSConstruct node to the {new_target_feedback}. 3804 NodeProperties::ReplaceValueInput(node, new_target_feedback, arity + 1); 3805 NodeProperties::ReplaceEffectInput(node, effect); 3806 if (target == new_target) { 3807 NodeProperties::ReplaceValueInput(node, new_target_feedback, 0); 3808 } 3809 3810 // Try to further reduce the JSConstruct {node}. 3811 Reduction const reduction = ReduceJSConstruct(node); 3812 return reduction.Changed() ? reduction : Changed(node); 3813 } 3814 } 3815 } 3816 3817 // Try to specialize JSConstruct {node}s with constant {target}s. 3818 HeapObjectMatcher m(target); 3819 if (m.HasValue()) { 3820 // Raise a TypeError if the {target} is not a constructor. 3821 if (!m.Value()->IsConstructor()) { 3822 NodeProperties::ReplaceValueInputs(node, target); 3823 NodeProperties::ChangeOp(node, 3824 javascript()->CallRuntime( 3825 Runtime::kThrowConstructedNonConstructable)); 3826 return Changed(node); 3827 } 3828 3829 if (m.Value()->IsJSFunction()) { 3830 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); 3831 3832 // Do not reduce constructors with break points. 3833 if (function->shared()->HasBreakInfo()) return NoChange(); 3834 3835 // Don't inline cross native context. 3836 if (function->native_context() != *native_context()) return NoChange(); 3837 3838 // Check for known builtin functions. 3839 int builtin_id = function->shared()->HasBuiltinId() 3840 ? function->shared()->builtin_id() 3841 : Builtins::kNoBuiltinId; 3842 switch (builtin_id) { 3843 case Builtins::kArrayConstructor: { 3844 // TODO(bmeurer): Deal with Array subclasses here. 3845 Handle<AllocationSite> site; 3846 // Turn the {node} into a {JSCreateArray} call. 3847 for (int i = arity; i > 0; --i) { 3848 NodeProperties::ReplaceValueInput( 3849 node, NodeProperties::GetValueInput(node, i), i + 1); 3850 } 3851 NodeProperties::ReplaceValueInput(node, new_target, 1); 3852 NodeProperties::ChangeOp(node, 3853 javascript()->CreateArray(arity, site)); 3854 return Changed(node); 3855 } 3856 case Builtins::kObjectConstructor: { 3857 // If no value is passed, we can immediately lower to a simple 3858 // JSCreate and don't need to do any massaging of the {node}. 3859 if (arity == 0) { 3860 NodeProperties::ChangeOp(node, javascript()->Create()); 3861 return Changed(node); 3862 } 3863 3864 // Otherwise we can only lower to JSCreate if we know that 3865 // the value parameter is ignored, which is only the case if 3866 // the {new_target} and {target} are definitely not identical. 3867 HeapObjectMatcher mnew_target(new_target); 3868 if (mnew_target.HasValue() && *mnew_target.Value() != *function) { 3869 // Drop the value inputs. 3870 for (int i = arity; i > 0; --i) node->RemoveInput(i); 3871 NodeProperties::ChangeOp(node, javascript()->Create()); 3872 return Changed(node); 3873 } 3874 break; 3875 } 3876 case Builtins::kPromiseConstructor: 3877 return ReducePromiseConstructor(node); 3878 case Builtins::kTypedArrayConstructor: 3879 return ReduceTypedArrayConstructor( 3880 node, handle(function->shared(), isolate())); 3881 default: 3882 break; 3883 } 3884 } else if (m.Value()->IsJSBoundFunction()) { 3885 Handle<JSBoundFunction> function = 3886 Handle<JSBoundFunction>::cast(m.Value()); 3887 Handle<JSReceiver> bound_target_function( 3888 function->bound_target_function(), isolate()); 3889 Handle<FixedArray> bound_arguments(function->bound_arguments(), 3890 isolate()); 3891 3892 // Patch {node} to use [[BoundTargetFunction]]. 3893 NodeProperties::ReplaceValueInput( 3894 node, jsgraph()->Constant(bound_target_function), 0); 3895 3896 // Patch {node} to use [[BoundTargetFunction]] 3897 // as new.target if {new_target} equals {target}. 3898 NodeProperties::ReplaceValueInput( 3899 node, 3900 graph()->NewNode(common()->Select(MachineRepresentation::kTagged), 3901 graph()->NewNode(simplified()->ReferenceEqual(), 3902 target, new_target), 3903 jsgraph()->Constant(bound_target_function), 3904 new_target), 3905 arity + 1); 3906 3907 // Insert the [[BoundArguments]] for {node}. 3908 for (int i = 0; i < bound_arguments->length(); ++i) { 3909 node->InsertInput( 3910 graph()->zone(), i + 1, 3911 jsgraph()->Constant(handle(bound_arguments->get(i), isolate()))); 3912 arity++; 3913 } 3914 3915 // Update the JSConstruct operator on {node}. 3916 NodeProperties::ChangeOp( 3917 node, 3918 javascript()->Construct(arity + 2, p.frequency(), VectorSlotPair())); 3919 3920 // Try to further reduce the JSConstruct {node}. 3921 Reduction const reduction = ReduceJSConstruct(node); 3922 return reduction.Changed() ? reduction : Changed(node); 3923 } 3924 3925 // TODO(bmeurer): Also support optimizing proxies here. 3926 } 3927 3928 // If {target} is the result of a JSCreateBoundFunction operation, 3929 // we can just fold the construction and construct the bound target 3930 // function directly instead. 3931 if (target->opcode() == IrOpcode::kJSCreateBoundFunction) { 3932 Node* bound_target_function = NodeProperties::GetValueInput(target, 0); 3933 int const bound_arguments_length = 3934 static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity()); 3935 3936 // Patch the {node} to use [[BoundTargetFunction]]. 3937 NodeProperties::ReplaceValueInput(node, bound_target_function, 0); 3938 3939 // Patch {node} to use [[BoundTargetFunction]] 3940 // as new.target if {new_target} equals {target}. 3941 NodeProperties::ReplaceValueInput( 3942 node, 3943 graph()->NewNode(common()->Select(MachineRepresentation::kTagged), 3944 graph()->NewNode(simplified()->ReferenceEqual(), 3945 target, new_target), 3946 bound_target_function, new_target), 3947 arity + 1); 3948 3949 // Insert the [[BoundArguments]] for {node}. 3950 for (int i = 0; i < bound_arguments_length; ++i) { 3951 Node* value = NodeProperties::GetValueInput(target, 2 + i); 3952 node->InsertInput(graph()->zone(), 1 + i, value); 3953 arity++; 3954 } 3955 3956 // Update the JSConstruct operator on {node}. 3957 NodeProperties::ChangeOp( 3958 node, 3959 javascript()->Construct(arity + 2, p.frequency(), VectorSlotPair())); 3960 3961 // Try to further reduce the JSConstruct {node}. 3962 Reduction const reduction = ReduceJSConstruct(node); 3963 return reduction.Changed() ? reduction : Changed(node); 3964 } 3965 3966 return NoChange(); 3967 } 3968 3969 // ES #sec-string.prototype.indexof 3970 Reduction JSCallReducer::ReduceStringPrototypeIndexOf(Node* node) { 3971 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 3972 CallParameters const& p = CallParametersOf(node->op()); 3973 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 3974 return NoChange(); 3975 } 3976 3977 Node* effect = NodeProperties::GetEffectInput(node); 3978 Node* control = NodeProperties::GetControlInput(node); 3979 if (node->op()->ValueInputCount() >= 3) { 3980 Node* receiver = NodeProperties::GetValueInput(node, 1); 3981 Node* new_receiver = effect = graph()->NewNode( 3982 simplified()->CheckString(p.feedback()), receiver, effect, control); 3983 3984 Node* search_string = NodeProperties::GetValueInput(node, 2); 3985 Node* new_search_string = effect = 3986 graph()->NewNode(simplified()->CheckString(p.feedback()), search_string, 3987 effect, control); 3988 3989 Node* new_position = jsgraph()->ZeroConstant(); 3990 if (node->op()->ValueInputCount() >= 4) { 3991 Node* position = NodeProperties::GetValueInput(node, 3); 3992 new_position = effect = graph()->NewNode( 3993 simplified()->CheckSmi(p.feedback()), position, effect, control); 3994 } 3995 3996 NodeProperties::ReplaceEffectInput(node, effect); 3997 RelaxEffectsAndControls(node); 3998 node->ReplaceInput(0, new_receiver); 3999 node->ReplaceInput(1, new_search_string); 4000 node->ReplaceInput(2, new_position); 4001 node->TrimInputCount(3); 4002 NodeProperties::ChangeOp(node, simplified()->StringIndexOf()); 4003 return Changed(node); 4004 } 4005 return NoChange(); 4006 } 4007 4008 // ES #sec-string.prototype.substring 4009 Reduction JSCallReducer::ReduceStringPrototypeSubstring(Node* node) { 4010 if (node->op()->ValueInputCount() < 3) return NoChange(); 4011 CallParameters const& p = CallParametersOf(node->op()); 4012 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 4013 return NoChange(); 4014 } 4015 4016 Node* effect = NodeProperties::GetEffectInput(node); 4017 Node* control = NodeProperties::GetControlInput(node); 4018 Node* receiver = NodeProperties::GetValueInput(node, 1); 4019 Node* start = NodeProperties::GetValueInput(node, 2); 4020 Node* end = node->op()->ValueInputCount() > 3 4021 ? NodeProperties::GetValueInput(node, 3) 4022 : jsgraph()->UndefinedConstant(); 4023 4024 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()), 4025 receiver, effect, control); 4026 4027 start = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), start, 4028 effect, control); 4029 4030 Node* length = graph()->NewNode(simplified()->StringLength(), receiver); 4031 4032 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), end, 4033 jsgraph()->UndefinedConstant()); 4034 Node* branch = 4035 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); 4036 4037 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 4038 Node* etrue = effect; 4039 Node* vtrue = length; 4040 4041 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 4042 Node* efalse = effect; 4043 Node* vfalse = efalse = graph()->NewNode(simplified()->CheckSmi(p.feedback()), 4044 end, efalse, if_false); 4045 4046 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 4047 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 4048 end = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 4049 vtrue, vfalse, control); 4050 Node* finalStart = 4051 graph()->NewNode(simplified()->NumberMin(), 4052 graph()->NewNode(simplified()->NumberMax(), start, 4053 jsgraph()->ZeroConstant()), 4054 length); 4055 Node* finalEnd = 4056 graph()->NewNode(simplified()->NumberMin(), 4057 graph()->NewNode(simplified()->NumberMax(), end, 4058 jsgraph()->ZeroConstant()), 4059 length); 4060 4061 Node* from = 4062 graph()->NewNode(simplified()->NumberMin(), finalStart, finalEnd); 4063 Node* to = graph()->NewNode(simplified()->NumberMax(), finalStart, finalEnd); 4064 4065 Node* value = effect = graph()->NewNode(simplified()->StringSubstring(), 4066 receiver, from, to, effect, control); 4067 ReplaceWithValue(node, value, effect, control); 4068 return Replace(value); 4069 } 4070 4071 // ES #sec-string.prototype.slice 4072 Reduction JSCallReducer::ReduceStringPrototypeSlice(Node* node) { 4073 if (node->op()->ValueInputCount() < 3) return NoChange(); 4074 CallParameters const& p = CallParametersOf(node->op()); 4075 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 4076 return NoChange(); 4077 } 4078 4079 Node* effect = NodeProperties::GetEffectInput(node); 4080 Node* control = NodeProperties::GetControlInput(node); 4081 Node* receiver = NodeProperties::GetValueInput(node, 1); 4082 Node* start = NodeProperties::GetValueInput(node, 2); 4083 Node* end = node->op()->ValueInputCount() > 3 4084 ? NodeProperties::GetValueInput(node, 3) 4085 : jsgraph()->UndefinedConstant(); 4086 4087 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()), 4088 receiver, effect, control); 4089 4090 start = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), start, 4091 effect, control); 4092 4093 Node* length = graph()->NewNode(simplified()->StringLength(), receiver); 4094 4095 // Replace {end} argument with {length} if it is undefined. 4096 { 4097 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), end, 4098 jsgraph()->UndefinedConstant()); 4099 4100 Node* branch = 4101 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); 4102 4103 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 4104 Node* etrue = effect; 4105 Node* vtrue = length; 4106 4107 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 4108 Node* efalse = effect; 4109 Node* vfalse = efalse = graph()->NewNode( 4110 simplified()->CheckSmi(p.feedback()), end, efalse, if_false); 4111 4112 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 4113 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 4114 end = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 4115 vtrue, vfalse, control); 4116 } 4117 4118 Node* from = graph()->NewNode( 4119 common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse), 4120 graph()->NewNode(simplified()->NumberLessThan(), start, 4121 jsgraph()->ZeroConstant()), 4122 graph()->NewNode( 4123 simplified()->NumberMax(), 4124 graph()->NewNode(simplified()->NumberAdd(), length, start), 4125 jsgraph()->ZeroConstant()), 4126 graph()->NewNode(simplified()->NumberMin(), start, length)); 4127 // {from} is always in non-negative Smi range, but our typer cannot 4128 // figure that out yet. 4129 from = effect = graph()->NewNode(common()->TypeGuard(Type::UnsignedSmall()), 4130 from, effect, control); 4131 4132 Node* to = graph()->NewNode( 4133 common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse), 4134 graph()->NewNode(simplified()->NumberLessThan(), end, 4135 jsgraph()->ZeroConstant()), 4136 graph()->NewNode(simplified()->NumberMax(), 4137 graph()->NewNode(simplified()->NumberAdd(), length, end), 4138 jsgraph()->ZeroConstant()), 4139 graph()->NewNode(simplified()->NumberMin(), end, length)); 4140 // {to} is always in non-negative Smi range, but our typer cannot 4141 // figure that out yet. 4142 to = effect = graph()->NewNode(common()->TypeGuard(Type::UnsignedSmall()), to, 4143 effect, control); 4144 4145 Node* result_string = nullptr; 4146 // Return empty string if {from} is smaller than {to}. 4147 { 4148 Node* check = graph()->NewNode(simplified()->NumberLessThan(), from, to); 4149 4150 Node* branch = 4151 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); 4152 4153 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 4154 Node* etrue = effect; 4155 Node* vtrue = etrue = graph()->NewNode(simplified()->StringSubstring(), 4156 receiver, from, to, etrue, if_true); 4157 4158 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 4159 Node* efalse = effect; 4160 Node* vfalse = jsgraph()->EmptyStringConstant(); 4161 4162 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 4163 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 4164 result_string = 4165 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 4166 vtrue, vfalse, control); 4167 } 4168 4169 ReplaceWithValue(node, result_string, effect, control); 4170 return Replace(result_string); 4171 } 4172 4173 // ES #sec-string.prototype.substr 4174 Reduction JSCallReducer::ReduceStringPrototypeSubstr(Node* node) { 4175 if (node->op()->ValueInputCount() < 3) return NoChange(); 4176 CallParameters const& p = CallParametersOf(node->op()); 4177 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 4178 return NoChange(); 4179 } 4180 4181 Node* effect = NodeProperties::GetEffectInput(node); 4182 Node* control = NodeProperties::GetControlInput(node); 4183 Node* receiver = NodeProperties::GetValueInput(node, 1); 4184 Node* start = NodeProperties::GetValueInput(node, 2); 4185 Node* end = node->op()->ValueInputCount() > 3 4186 ? NodeProperties::GetValueInput(node, 3) 4187 : jsgraph()->UndefinedConstant(); 4188 4189 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()), 4190 receiver, effect, control); 4191 4192 start = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), start, 4193 effect, control); 4194 4195 Node* length = graph()->NewNode(simplified()->StringLength(), receiver); 4196 4197 // Replace {end} argument with {length} if it is undefined. 4198 { 4199 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), end, 4200 jsgraph()->UndefinedConstant()); 4201 Node* branch = 4202 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); 4203 4204 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 4205 Node* etrue = effect; 4206 Node* vtrue = length; 4207 4208 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 4209 Node* efalse = effect; 4210 Node* vfalse = efalse = graph()->NewNode( 4211 simplified()->CheckSmi(p.feedback()), end, efalse, if_false); 4212 4213 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 4214 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 4215 end = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 4216 vtrue, vfalse, control); 4217 } 4218 4219 Node* initStart = graph()->NewNode( 4220 common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse), 4221 graph()->NewNode(simplified()->NumberLessThan(), start, 4222 jsgraph()->ZeroConstant()), 4223 graph()->NewNode( 4224 simplified()->NumberMax(), 4225 graph()->NewNode(simplified()->NumberAdd(), length, start), 4226 jsgraph()->ZeroConstant()), 4227 start); 4228 // The select above guarantees that initStart is non-negative, but 4229 // our typer can't figure that out yet. 4230 initStart = effect = graph()->NewNode( 4231 common()->TypeGuard(Type::UnsignedSmall()), initStart, effect, control); 4232 4233 Node* resultLength = graph()->NewNode( 4234 simplified()->NumberMin(), 4235 graph()->NewNode(simplified()->NumberMax(), end, 4236 jsgraph()->ZeroConstant()), 4237 graph()->NewNode(simplified()->NumberSubtract(), length, initStart)); 4238 4239 // The the select below uses {resultLength} only if {resultLength > 0}, 4240 // but our typer can't figure that out yet. 4241 Node* to = effect = graph()->NewNode( 4242 common()->TypeGuard(Type::UnsignedSmall()), 4243 graph()->NewNode(simplified()->NumberAdd(), initStart, resultLength), 4244 effect, control); 4245 4246 Node* result_string = nullptr; 4247 // Return empty string if {from} is smaller than {to}. 4248 { 4249 Node* check = graph()->NewNode(simplified()->NumberLessThan(), 4250 jsgraph()->ZeroConstant(), resultLength); 4251 4252 Node* branch = 4253 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); 4254 4255 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 4256 Node* etrue = effect; 4257 Node* vtrue = etrue = 4258 graph()->NewNode(simplified()->StringSubstring(), receiver, initStart, 4259 to, etrue, if_true); 4260 4261 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 4262 Node* efalse = effect; 4263 Node* vfalse = jsgraph()->EmptyStringConstant(); 4264 4265 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 4266 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 4267 result_string = 4268 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 4269 vtrue, vfalse, control); 4270 } 4271 4272 ReplaceWithValue(node, result_string, effect, control); 4273 return Replace(result_string); 4274 } 4275 4276 Reduction JSCallReducer::ReduceJSConstructWithArrayLike(Node* node) { 4277 DCHECK_EQ(IrOpcode::kJSConstructWithArrayLike, node->opcode()); 4278 CallFrequency frequency = CallFrequencyOf(node->op()); 4279 VectorSlotPair feedback; 4280 return ReduceCallOrConstructWithArrayLikeOrSpread(node, 1, frequency, 4281 feedback); 4282 } 4283 4284 Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) { 4285 DCHECK_EQ(IrOpcode::kJSConstructWithSpread, node->opcode()); 4286 ConstructParameters const& p = ConstructParametersOf(node->op()); 4287 DCHECK_LE(3u, p.arity()); 4288 int arity = static_cast<int>(p.arity() - 2); 4289 CallFrequency frequency = p.frequency(); 4290 VectorSlotPair feedback = p.feedback(); 4291 return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency, 4292 feedback); 4293 } 4294 4295 Reduction JSCallReducer::ReduceReturnReceiver(Node* node) { 4296 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 4297 Node* receiver = NodeProperties::GetValueInput(node, 1); 4298 ReplaceWithValue(node, receiver); 4299 return Replace(receiver); 4300 } 4301 4302 Reduction JSCallReducer::ReduceSoftDeoptimize(Node* node, 4303 DeoptimizeReason reason) { 4304 Node* effect = NodeProperties::GetEffectInput(node); 4305 Node* control = NodeProperties::GetControlInput(node); 4306 Node* frame_state = NodeProperties::FindFrameStateBefore(node); 4307 Node* deoptimize = graph()->NewNode( 4308 common()->Deoptimize(DeoptimizeKind::kSoft, reason, VectorSlotPair()), 4309 frame_state, effect, control); 4310 // TODO(bmeurer): This should be on the AdvancedReducer somehow. 4311 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); 4312 Revisit(graph()->end()); 4313 node->TrimInputCount(0); 4314 NodeProperties::ChangeOp(node, common()->Dead()); 4315 return Changed(node); 4316 } 4317 4318 namespace { 4319 4320 // TODO(turbofan): This was copied from old compiler, might be too restrictive. 4321 bool IsReadOnlyLengthDescriptor(Isolate* isolate, Handle<Map> jsarray_map) { 4322 DCHECK(!jsarray_map->is_dictionary_map()); 4323 Handle<Name> length_string = isolate->factory()->length_string(); 4324 DescriptorArray* descriptors = jsarray_map->instance_descriptors(); 4325 int number = descriptors->Search(*length_string, *jsarray_map); 4326 DCHECK_NE(DescriptorArray::kNotFound, number); 4327 return descriptors->GetDetails(number).IsReadOnly(); 4328 } 4329 4330 // TODO(turbofan): This was copied from old compiler, might be too restrictive. 4331 bool CanInlineArrayResizeOperation(Isolate* isolate, Handle<Map> receiver_map) { 4332 if (!receiver_map->prototype()->IsJSArray()) return false; 4333 Handle<JSArray> receiver_prototype(JSArray::cast(receiver_map->prototype()), 4334 isolate); 4335 return receiver_map->instance_type() == JS_ARRAY_TYPE && 4336 IsFastElementsKind(receiver_map->elements_kind()) && 4337 !receiver_map->is_dictionary_map() && receiver_map->is_extensible() && 4338 isolate->IsAnyInitialArrayPrototype(receiver_prototype) && 4339 !IsReadOnlyLengthDescriptor(isolate, receiver_map); 4340 } 4341 4342 } // namespace 4343 4344 // ES6 section 22.1.3.18 Array.prototype.push ( ) 4345 Reduction JSCallReducer::ReduceArrayPrototypePush(Node* node) { 4346 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 4347 CallParameters const& p = CallParametersOf(node->op()); 4348 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 4349 return NoChange(); 4350 } 4351 4352 if (!isolate()->IsNoElementsProtectorIntact()) return NoChange(); 4353 4354 int const num_values = node->op()->ValueInputCount() - 2; 4355 Node* receiver = NodeProperties::GetValueInput(node, 1); 4356 Node* effect = NodeProperties::GetEffectInput(node); 4357 Node* control = NodeProperties::GetControlInput(node); 4358 4359 // Try to determine the {receiver} map(s). 4360 ZoneHandleSet<Map> receiver_maps; 4361 NodeProperties::InferReceiverMapsResult result = 4362 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 4363 &receiver_maps); 4364 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 4365 DCHECK_NE(0, receiver_maps.size()); 4366 4367 ElementsKind kind = receiver_maps[0]->elements_kind(); 4368 4369 for (Handle<Map> receiver_map : receiver_maps) { 4370 if (!CanInlineArrayResizeOperation(isolate(), receiver_map)) 4371 return NoChange(); 4372 if (!UnionElementsKindUptoPackedness(&kind, receiver_map->elements_kind())) 4373 return NoChange(); 4374 } 4375 4376 // Install code dependencies on the {receiver} global array protector cell. 4377 dependencies()->DependOnProtector( 4378 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); 4379 4380 // If the {receiver_maps} information is not reliable, we need 4381 // to check that the {receiver} still has one of these maps. 4382 if (result == NodeProperties::kUnreliableReceiverMaps) { 4383 effect = 4384 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 4385 receiver_maps, p.feedback()), 4386 receiver, effect, control); 4387 } 4388 4389 // Collect the value inputs to push. 4390 std::vector<Node*> values(num_values); 4391 for (int i = 0; i < num_values; ++i) { 4392 values[i] = NodeProperties::GetValueInput(node, 2 + i); 4393 } 4394 4395 for (auto& value : values) { 4396 if (IsSmiElementsKind(kind)) { 4397 value = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), 4398 value, effect, control); 4399 } else if (IsDoubleElementsKind(kind)) { 4400 value = effect = graph()->NewNode(simplified()->CheckNumber(p.feedback()), 4401 value, effect, control); 4402 // Make sure we do not store signaling NaNs into double arrays. 4403 value = graph()->NewNode(simplified()->NumberSilenceNaN(), value); 4404 } 4405 } 4406 4407 // Load the "length" property of the {receiver}. 4408 Node* length = effect = graph()->NewNode( 4409 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, 4410 effect, control); 4411 Node* value = length; 4412 4413 // Check if we have any {values} to push. 4414 if (num_values > 0) { 4415 // Compute the resulting "length" of the {receiver}. 4416 Node* new_length = value = graph()->NewNode( 4417 simplified()->NumberAdd(), length, jsgraph()->Constant(num_values)); 4418 4419 // Load the elements backing store of the {receiver}. 4420 Node* elements = effect = graph()->NewNode( 4421 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver, 4422 effect, control); 4423 Node* elements_length = effect = graph()->NewNode( 4424 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), elements, 4425 effect, control); 4426 4427 GrowFastElementsMode mode = 4428 IsDoubleElementsKind(kind) ? GrowFastElementsMode::kDoubleElements 4429 : GrowFastElementsMode::kSmiOrObjectElements; 4430 elements = effect = graph()->NewNode( 4431 simplified()->MaybeGrowFastElements(mode, p.feedback()), receiver, 4432 elements, 4433 graph()->NewNode(simplified()->NumberAdd(), length, 4434 jsgraph()->Constant(num_values - 1)), 4435 elements_length, effect, control); 4436 4437 // Update the JSArray::length field. Since this is observable, 4438 // there must be no other check after this. 4439 effect = graph()->NewNode( 4440 simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), 4441 receiver, new_length, effect, control); 4442 4443 // Append the {values} to the {elements}. 4444 for (int i = 0; i < num_values; ++i) { 4445 Node* value = values[i]; 4446 Node* index = graph()->NewNode(simplified()->NumberAdd(), length, 4447 jsgraph()->Constant(i)); 4448 effect = graph()->NewNode( 4449 simplified()->StoreElement(AccessBuilder::ForFixedArrayElement(kind)), 4450 elements, index, value, effect, control); 4451 } 4452 } 4453 4454 ReplaceWithValue(node, value, effect, control); 4455 return Replace(value); 4456 } 4457 4458 // ES6 section 22.1.3.17 Array.prototype.pop ( ) 4459 Reduction JSCallReducer::ReduceArrayPrototypePop(Node* node) { 4460 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 4461 CallParameters const& p = CallParametersOf(node->op()); 4462 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 4463 return NoChange(); 4464 } 4465 4466 if (!isolate()->IsNoElementsProtectorIntact()) return NoChange(); 4467 4468 Node* receiver = NodeProperties::GetValueInput(node, 1); 4469 Node* effect = NodeProperties::GetEffectInput(node); 4470 Node* control = NodeProperties::GetControlInput(node); 4471 4472 ZoneHandleSet<Map> receiver_maps; 4473 NodeProperties::InferReceiverMapsResult result = 4474 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 4475 &receiver_maps); 4476 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 4477 DCHECK_NE(0, receiver_maps.size()); 4478 4479 ElementsKind kind = receiver_maps[0]->elements_kind(); 4480 for (Handle<Map> receiver_map : receiver_maps) { 4481 if (!CanInlineArrayResizeOperation(isolate(), receiver_map)) 4482 return NoChange(); 4483 // TODO(turbofan): Extend this to also handle fast holey double elements 4484 // once we got the hole NaN mess sorted out in TurboFan/V8. 4485 if (receiver_map->elements_kind() == HOLEY_DOUBLE_ELEMENTS) 4486 return NoChange(); 4487 if (!UnionElementsKindUptoSize(&kind, receiver_map->elements_kind())) 4488 return NoChange(); 4489 } 4490 4491 // Install code dependencies on the {receiver} global array protector cell. 4492 dependencies()->DependOnProtector( 4493 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); 4494 4495 // If the {receiver_maps} information is not reliable, we need 4496 // to check that the {receiver} still has one of these maps. 4497 if (result == NodeProperties::kUnreliableReceiverMaps) { 4498 effect = 4499 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 4500 receiver_maps, p.feedback()), 4501 receiver, effect, control); 4502 } 4503 4504 // Load the "length" property of the {receiver}. 4505 Node* length = effect = graph()->NewNode( 4506 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, 4507 effect, control); 4508 4509 // Check if the {receiver} has any elements. 4510 Node* check = graph()->NewNode(simplified()->NumberEqual(), length, 4511 jsgraph()->ZeroConstant()); 4512 Node* branch = 4513 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); 4514 4515 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 4516 Node* etrue = effect; 4517 Node* vtrue = jsgraph()->UndefinedConstant(); 4518 4519 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 4520 Node* efalse = effect; 4521 Node* vfalse; 4522 { 4523 // TODO(tebbi): We should trim the backing store if the capacity is too 4524 // big, as implemented in elements.cc:ElementsAccessorBase::SetLengthImpl. 4525 4526 // Load the elements backing store from the {receiver}. 4527 Node* elements = efalse = graph()->NewNode( 4528 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver, 4529 efalse, if_false); 4530 4531 // Ensure that we aren't popping from a copy-on-write backing store. 4532 if (IsSmiOrObjectElementsKind(kind)) { 4533 elements = efalse = 4534 graph()->NewNode(simplified()->EnsureWritableFastElements(), receiver, 4535 elements, efalse, if_false); 4536 } 4537 4538 // Compute the new {length}. 4539 length = graph()->NewNode(simplified()->NumberSubtract(), length, 4540 jsgraph()->OneConstant()); 4541 4542 // Store the new {length} to the {receiver}. 4543 efalse = graph()->NewNode( 4544 simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), 4545 receiver, length, efalse, if_false); 4546 4547 // Load the last entry from the {elements}. 4548 vfalse = efalse = graph()->NewNode( 4549 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)), 4550 elements, length, efalse, if_false); 4551 4552 // Store a hole to the element we just removed from the {receiver}. 4553 efalse = graph()->NewNode( 4554 simplified()->StoreElement( 4555 AccessBuilder::ForFixedArrayElement(GetHoleyElementsKind(kind))), 4556 elements, length, jsgraph()->TheHoleConstant(), efalse, if_false); 4557 } 4558 4559 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 4560 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 4561 Node* value = graph()->NewNode( 4562 common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control); 4563 4564 // Convert the hole to undefined. Do this last, so that we can optimize 4565 // conversion operator via some smart strength reduction in many cases. 4566 if (IsHoleyElementsKind(kind)) { 4567 value = 4568 graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value); 4569 } 4570 4571 ReplaceWithValue(node, value, effect, control); 4572 return Replace(value); 4573 } 4574 4575 // ES6 section 22.1.3.22 Array.prototype.shift ( ) 4576 Reduction JSCallReducer::ReduceArrayPrototypeShift(Node* node) { 4577 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 4578 CallParameters const& p = CallParametersOf(node->op()); 4579 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 4580 return NoChange(); 4581 } 4582 4583 if (!isolate()->IsNoElementsProtectorIntact()) return NoChange(); 4584 Node* target = NodeProperties::GetValueInput(node, 0); 4585 Node* receiver = NodeProperties::GetValueInput(node, 1); 4586 Node* context = NodeProperties::GetContextInput(node); 4587 Node* frame_state = NodeProperties::GetFrameStateInput(node); 4588 Node* effect = NodeProperties::GetEffectInput(node); 4589 Node* control = NodeProperties::GetControlInput(node); 4590 4591 ZoneHandleSet<Map> receiver_maps; 4592 NodeProperties::InferReceiverMapsResult result = 4593 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 4594 &receiver_maps); 4595 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 4596 DCHECK_NE(0, receiver_maps.size()); 4597 4598 ElementsKind kind = receiver_maps[0]->elements_kind(); 4599 for (Handle<Map> receiver_map : receiver_maps) { 4600 if (!CanInlineArrayResizeOperation(isolate(), receiver_map)) 4601 return NoChange(); 4602 // TODO(turbofan): Extend this to also handle fast holey double elements 4603 // once we got the hole NaN mess sorted out in TurboFan/V8. 4604 if (receiver_map->elements_kind() == HOLEY_DOUBLE_ELEMENTS) 4605 return NoChange(); 4606 if (!UnionElementsKindUptoSize(&kind, receiver_map->elements_kind())) 4607 return NoChange(); 4608 } 4609 4610 // Install code dependencies on the {receiver} global array protector cell. 4611 dependencies()->DependOnProtector( 4612 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); 4613 4614 // If the {receiver_maps} information is not reliable, we need 4615 // to check that the {receiver} still has one of these maps. 4616 if (result == NodeProperties::kUnreliableReceiverMaps) { 4617 effect = 4618 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 4619 receiver_maps, p.feedback()), 4620 receiver, effect, control); 4621 } 4622 4623 // Load length of the {receiver}. 4624 Node* length = effect = graph()->NewNode( 4625 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, 4626 effect, control); 4627 4628 // Return undefined if {receiver} has no elements. 4629 Node* check0 = graph()->NewNode(simplified()->NumberEqual(), length, 4630 jsgraph()->ZeroConstant()); 4631 Node* branch0 = 4632 graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control); 4633 4634 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); 4635 Node* etrue0 = effect; 4636 Node* vtrue0 = jsgraph()->UndefinedConstant(); 4637 4638 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); 4639 Node* efalse0 = effect; 4640 Node* vfalse0; 4641 { 4642 // Check if we should take the fast-path. 4643 Node* check1 = 4644 graph()->NewNode(simplified()->NumberLessThanOrEqual(), length, 4645 jsgraph()->Constant(JSArray::kMaxCopyElements)); 4646 Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue), 4647 check1, if_false0); 4648 4649 Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); 4650 Node* etrue1 = efalse0; 4651 Node* vtrue1; 4652 { 4653 Node* elements = etrue1 = graph()->NewNode( 4654 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), 4655 receiver, etrue1, if_true1); 4656 4657 // Load the first element here, which we return below. 4658 vtrue1 = etrue1 = graph()->NewNode( 4659 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)), 4660 elements, jsgraph()->ZeroConstant(), etrue1, if_true1); 4661 4662 // Ensure that we aren't shifting a copy-on-write backing store. 4663 if (IsSmiOrObjectElementsKind(kind)) { 4664 elements = etrue1 = 4665 graph()->NewNode(simplified()->EnsureWritableFastElements(), 4666 receiver, elements, etrue1, if_true1); 4667 } 4668 4669 // Shift the remaining {elements} by one towards the start. 4670 Node* loop = graph()->NewNode(common()->Loop(2), if_true1, if_true1); 4671 Node* eloop = 4672 graph()->NewNode(common()->EffectPhi(2), etrue1, etrue1, loop); 4673 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); 4674 NodeProperties::MergeControlToEnd(graph(), common(), terminate); 4675 Node* index = graph()->NewNode( 4676 common()->Phi(MachineRepresentation::kTagged, 2), 4677 jsgraph()->OneConstant(), 4678 jsgraph()->Constant(JSArray::kMaxCopyElements - 1), loop); 4679 4680 { 4681 Node* check2 = 4682 graph()->NewNode(simplified()->NumberLessThan(), index, length); 4683 Node* branch2 = graph()->NewNode(common()->Branch(), check2, loop); 4684 4685 if_true1 = graph()->NewNode(common()->IfFalse(), branch2); 4686 etrue1 = eloop; 4687 4688 Node* control = graph()->NewNode(common()->IfTrue(), branch2); 4689 Node* effect = etrue1; 4690 4691 ElementAccess const access = AccessBuilder::ForFixedArrayElement(kind); 4692 Node* value = effect = 4693 graph()->NewNode(simplified()->LoadElement(access), elements, index, 4694 effect, control); 4695 effect = 4696 graph()->NewNode(simplified()->StoreElement(access), elements, 4697 graph()->NewNode(simplified()->NumberSubtract(), 4698 index, jsgraph()->OneConstant()), 4699 value, effect, control); 4700 4701 loop->ReplaceInput(1, control); 4702 eloop->ReplaceInput(1, effect); 4703 index->ReplaceInput(1, 4704 graph()->NewNode(simplified()->NumberAdd(), index, 4705 jsgraph()->OneConstant())); 4706 } 4707 4708 // Compute the new {length}. 4709 length = graph()->NewNode(simplified()->NumberSubtract(), length, 4710 jsgraph()->OneConstant()); 4711 4712 // Store the new {length} to the {receiver}. 4713 etrue1 = graph()->NewNode( 4714 simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), 4715 receiver, length, etrue1, if_true1); 4716 4717 // Store a hole to the element we just removed from the {receiver}. 4718 etrue1 = graph()->NewNode( 4719 simplified()->StoreElement( 4720 AccessBuilder::ForFixedArrayElement(GetHoleyElementsKind(kind))), 4721 elements, length, jsgraph()->TheHoleConstant(), etrue1, if_true1); 4722 } 4723 4724 Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); 4725 Node* efalse1 = efalse0; 4726 Node* vfalse1; 4727 { 4728 // Call the generic C++ implementation. 4729 const int builtin_index = Builtins::kArrayShift; 4730 auto call_descriptor = Linkage::GetCEntryStubCallDescriptor( 4731 graph()->zone(), 1, BuiltinArguments::kNumExtraArgsWithReceiver, 4732 Builtins::name(builtin_index), node->op()->properties(), 4733 CallDescriptor::kNeedsFrameState); 4734 Node* stub_code = 4735 jsgraph()->CEntryStubConstant(1, kDontSaveFPRegs, kArgvOnStack, true); 4736 Address builtin_entry = Builtins::CppEntryOf(builtin_index); 4737 Node* entry = 4738 jsgraph()->ExternalConstant(ExternalReference::Create(builtin_entry)); 4739 Node* argc = 4740 jsgraph()->Constant(BuiltinArguments::kNumExtraArgsWithReceiver); 4741 if_false1 = efalse1 = vfalse1 = 4742 graph()->NewNode(common()->Call(call_descriptor), stub_code, receiver, 4743 jsgraph()->PaddingConstant(), argc, target, 4744 jsgraph()->UndefinedConstant(), entry, argc, context, 4745 frame_state, efalse1, if_false1); 4746 } 4747 4748 if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); 4749 efalse0 = 4750 graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0); 4751 vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 4752 vtrue1, vfalse1, if_false0); 4753 } 4754 4755 control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); 4756 effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control); 4757 Node* value = 4758 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 4759 vtrue0, vfalse0, control); 4760 4761 // Convert the hole to undefined. Do this last, so that we can optimize 4762 // conversion operator via some smart strength reduction in many cases. 4763 if (IsHoleyElementsKind(kind)) { 4764 value = 4765 graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value); 4766 } 4767 4768 ReplaceWithValue(node, value, effect, control); 4769 return Replace(value); 4770 } 4771 4772 // ES6 section 22.1.3.23 Array.prototype.slice ( ) 4773 Reduction JSCallReducer::ReduceArrayPrototypeSlice(Node* node) { 4774 if (!FLAG_turbo_inline_array_builtins) return NoChange(); 4775 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 4776 CallParameters const& p = CallParametersOf(node->op()); 4777 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 4778 return NoChange(); 4779 } 4780 4781 int arity = static_cast<int>(p.arity() - 2); 4782 // Here we only optimize for cloning, that is when slice is called 4783 // without arguments, or with a single argument that is the constant 0. 4784 if (arity >= 2) return NoChange(); 4785 if (arity == 1) { 4786 NumberMatcher m(NodeProperties::GetValueInput(node, 2)); 4787 if (!m.HasValue()) return NoChange(); 4788 if (m.Value() != 0) return NoChange(); 4789 } 4790 4791 Node* receiver = NodeProperties::GetValueInput(node, 1); 4792 Node* effect = NodeProperties::GetEffectInput(node); 4793 Node* control = NodeProperties::GetControlInput(node); 4794 4795 // Try to determine the {receiver} map. 4796 ZoneHandleSet<Map> receiver_maps; 4797 NodeProperties::InferReceiverMapsResult result = 4798 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 4799 &receiver_maps); 4800 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 4801 4802 // Ensure that any changes to the Array species constructor cause deopt. 4803 if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange(); 4804 dependencies()->DependOnProtector( 4805 PropertyCellRef(js_heap_broker(), factory()->array_species_protector())); 4806 4807 bool can_be_holey = false; 4808 // Check that the maps are of JSArray (and more) 4809 for (Handle<Map> receiver_map : receiver_maps) { 4810 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) 4811 return NoChange(); 4812 4813 if (IsHoleyElementsKind(receiver_map->elements_kind())) can_be_holey = true; 4814 } 4815 4816 // Install code dependency on the array protector for holey arrays. 4817 if (can_be_holey) { 4818 dependencies()->DependOnProtector( 4819 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); 4820 } 4821 4822 // If we have unreliable maps, we need a map check. 4823 // This is actually redundant due to how JSNativeContextSpecialization 4824 // reduces the load of slice, but we do it here nevertheless for consistency 4825 // and robustness. 4826 if (result == NodeProperties::kUnreliableReceiverMaps) { 4827 effect = 4828 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 4829 receiver_maps, p.feedback()), 4830 receiver, effect, control); 4831 } 4832 4833 Node* context = NodeProperties::GetContextInput(node); 4834 4835 Callable callable = 4836 Builtins::CallableFor(isolate(), Builtins::kCloneFastJSArray); 4837 auto call_descriptor = Linkage::GetStubCallDescriptor( 4838 graph()->zone(), callable.descriptor(), 0, CallDescriptor::kNoFlags, 4839 Operator::kNoThrow | Operator::kNoDeopt); 4840 4841 // Calls to Builtins::kCloneFastJSArray produce COW arrays 4842 // if the original array is COW 4843 Node* clone = effect = graph()->NewNode( 4844 common()->Call(call_descriptor), jsgraph()->HeapConstant(callable.code()), 4845 receiver, context, effect, control); 4846 4847 ReplaceWithValue(node, clone, effect, control); 4848 return Replace(clone); 4849 } 4850 4851 // ES6 section 22.1.2.2 Array.isArray ( arg ) 4852 Reduction JSCallReducer::ReduceArrayIsArray(Node* node) { 4853 // We certainly know that undefined is not an array. 4854 if (node->op()->ValueInputCount() < 3) { 4855 Node* value = jsgraph()->FalseConstant(); 4856 ReplaceWithValue(node, value); 4857 return Replace(value); 4858 } 4859 4860 Node* effect = NodeProperties::GetEffectInput(node); 4861 Node* control = NodeProperties::GetControlInput(node); 4862 Node* context = NodeProperties::GetContextInput(node); 4863 Node* frame_state = NodeProperties::GetFrameStateInput(node); 4864 Node* object = NodeProperties::GetValueInput(node, 2); 4865 node->ReplaceInput(0, object); 4866 node->ReplaceInput(1, context); 4867 node->ReplaceInput(2, frame_state); 4868 node->ReplaceInput(3, effect); 4869 node->ReplaceInput(4, control); 4870 node->TrimInputCount(5); 4871 NodeProperties::ChangeOp(node, javascript()->ObjectIsArray()); 4872 return Changed(node); 4873 } 4874 4875 Reduction JSCallReducer::ReduceArrayIterator(Node* node, IterationKind kind) { 4876 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 4877 Node* receiver = NodeProperties::GetValueInput(node, 1); 4878 Node* context = NodeProperties::GetContextInput(node); 4879 Node* effect = NodeProperties::GetEffectInput(node); 4880 Node* control = NodeProperties::GetControlInput(node); 4881 4882 // Check if we know that {receiver} is a valid JSReceiver. 4883 ZoneHandleSet<Map> receiver_maps; 4884 NodeProperties::InferReceiverMapsResult result = 4885 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 4886 &receiver_maps); 4887 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 4888 DCHECK_NE(0, receiver_maps.size()); 4889 for (Handle<Map> receiver_map : receiver_maps) { 4890 if (!receiver_map->IsJSReceiverMap()) return NoChange(); 4891 } 4892 4893 // Morph the {node} into a JSCreateArrayIterator with the given {kind}. 4894 RelaxControls(node); 4895 node->ReplaceInput(0, receiver); 4896 node->ReplaceInput(1, context); 4897 node->ReplaceInput(2, effect); 4898 node->ReplaceInput(3, control); 4899 node->TrimInputCount(4); 4900 NodeProperties::ChangeOp(node, javascript()->CreateArrayIterator(kind)); 4901 return Changed(node); 4902 } 4903 4904 namespace { 4905 4906 bool InferIteratedObjectMaps(Isolate* isolate, Node* iterator, 4907 ZoneHandleSet<Map>* iterated_object_maps) { 4908 DCHECK_EQ(IrOpcode::kJSCreateArrayIterator, iterator->opcode()); 4909 Node* iterated_object = NodeProperties::GetValueInput(iterator, 0); 4910 Node* effect = NodeProperties::GetEffectInput(iterator); 4911 4912 NodeProperties::InferReceiverMapsResult result = 4913 NodeProperties::InferReceiverMaps(isolate, iterated_object, effect, 4914 iterated_object_maps); 4915 return result != NodeProperties::kNoReceiverMaps; 4916 } 4917 4918 } // namespace 4919 4920 // ES #sec-%arrayiteratorprototype%.next 4921 Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) { 4922 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 4923 CallParameters const& p = CallParametersOf(node->op()); 4924 Node* iterator = NodeProperties::GetValueInput(node, 1); 4925 Node* context = NodeProperties::GetContextInput(node); 4926 Node* effect = NodeProperties::GetEffectInput(node); 4927 Node* control = NodeProperties::GetControlInput(node); 4928 4929 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 4930 return NoChange(); 4931 } 4932 4933 // Check if the {iterator} is a JSCreateArrayIterator. 4934 if (iterator->opcode() != IrOpcode::kJSCreateArrayIterator) return NoChange(); 4935 IterationKind const iteration_kind = 4936 CreateArrayIteratorParametersOf(iterator->op()).kind(); 4937 4938 // Try to infer the [[IteratedObject]] maps from the {iterator}. 4939 ZoneHandleSet<Map> iterated_object_maps; 4940 if (!InferIteratedObjectMaps(isolate(), iterator, &iterated_object_maps)) { 4941 return NoChange(); 4942 } 4943 DCHECK_NE(0, iterated_object_maps.size()); 4944 4945 // Check that various {iterated_object_maps} have compatible elements kinds. 4946 ElementsKind elements_kind = iterated_object_maps[0]->elements_kind(); 4947 if (IsFixedTypedArrayElementsKind(elements_kind)) { 4948 // TurboFan doesn't support loading from BigInt typed arrays yet. 4949 if (elements_kind == BIGUINT64_ELEMENTS || 4950 elements_kind == BIGINT64_ELEMENTS) { 4951 return NoChange(); 4952 } 4953 for (Handle<Map> iterated_object_map : iterated_object_maps) { 4954 if (iterated_object_map->elements_kind() != elements_kind) { 4955 return NoChange(); 4956 } 4957 } 4958 } else { 4959 for (Handle<Map> iterated_object_map : iterated_object_maps) { 4960 if (!CanInlineArrayIteratingBuiltin(isolate(), iterated_object_map)) { 4961 return NoChange(); 4962 } 4963 if (!UnionElementsKindUptoSize(&elements_kind, 4964 iterated_object_map->elements_kind())) { 4965 return NoChange(); 4966 } 4967 } 4968 } 4969 4970 // Install code dependency on the array protector for holey arrays. 4971 if (IsHoleyElementsKind(elements_kind)) { 4972 dependencies()->DependOnProtector( 4973 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); 4974 } 4975 4976 // Load the (current) {iterated_object} from the {iterator}. 4977 Node* iterated_object = effect = 4978 graph()->NewNode(simplified()->LoadField( 4979 AccessBuilder::ForJSArrayIteratorIteratedObject()), 4980 iterator, effect, control); 4981 4982 // Ensure that the {iterated_object} map didn't change. 4983 effect = graph()->NewNode( 4984 simplified()->CheckMaps(CheckMapsFlag::kNone, iterated_object_maps, 4985 p.feedback()), 4986 iterated_object, effect, control); 4987 4988 if (IsFixedTypedArrayElementsKind(elements_kind)) { 4989 // See if we can skip the neutering check. 4990 if (isolate()->IsArrayBufferNeuteringIntact()) { 4991 // Add a code dependency so we are deoptimized in case an ArrayBuffer 4992 // gets neutered. 4993 dependencies()->DependOnProtector(PropertyCellRef( 4994 js_heap_broker(), factory()->array_buffer_neutering_protector())); 4995 } else { 4996 // Deoptimize if the array buffer was neutered. 4997 Node* buffer = effect = graph()->NewNode( 4998 simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), 4999 iterated_object, effect, control); 5000 5001 Node* check = effect = graph()->NewNode( 5002 simplified()->ArrayBufferWasNeutered(), buffer, effect, control); 5003 check = graph()->NewNode(simplified()->BooleanNot(), check); 5004 // TODO(bmeurer): Pass p.feedback(), or better introduce 5005 // CheckArrayBufferNotNeutered? 5006 effect = graph()->NewNode( 5007 simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasNeutered), 5008 check, effect, control); 5009 } 5010 } 5011 5012 // Load the [[NextIndex]] from the {iterator} and leverage the fact 5013 // that we definitely know that it's in Unsigned32 range since the 5014 // {iterated_object} is either a JSArray or a JSTypedArray. For the 5015 // latter case we even know that it's a Smi in UnsignedSmall range. 5016 FieldAccess index_access = AccessBuilder::ForJSArrayIteratorNextIndex(); 5017 if (IsFixedTypedArrayElementsKind(elements_kind)) { 5018 index_access.type = TypeCache::Get().kJSTypedArrayLengthType; 5019 index_access.machine_type = MachineType::TaggedSigned(); 5020 index_access.write_barrier_kind = kNoWriteBarrier; 5021 } else { 5022 index_access.type = TypeCache::Get().kJSArrayLengthType; 5023 } 5024 Node* index = effect = graph()->NewNode(simplified()->LoadField(index_access), 5025 iterator, effect, control); 5026 5027 // Load the elements of the {iterated_object}. While it feels 5028 // counter-intuitive to place the elements pointer load before 5029 // the condition below, as it might not be needed (if the {index} 5030 // is out of bounds for the {iterated_object}), it's better this 5031 // way as it allows the LoadElimination to eliminate redundant 5032 // reloads of the elements pointer. 5033 Node* elements = effect = graph()->NewNode( 5034 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), 5035 iterated_object, effect, control); 5036 5037 // Load the length of the {iterated_object}. Due to the map checks we 5038 // already know something about the length here, which we can leverage 5039 // to generate Word32 operations below without additional checking. 5040 FieldAccess length_access = 5041 IsFixedTypedArrayElementsKind(elements_kind) 5042 ? AccessBuilder::ForJSTypedArrayLength() 5043 : AccessBuilder::ForJSArrayLength(elements_kind); 5044 Node* length = effect = graph()->NewNode( 5045 simplified()->LoadField(length_access), iterated_object, effect, control); 5046 5047 // Check whether {index} is within the valid range for the {iterated_object}. 5048 Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, length); 5049 Node* branch = 5050 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); 5051 5052 Node* done_true; 5053 Node* value_true; 5054 Node* etrue = effect; 5055 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 5056 { 5057 // We know that the {index} is range of the {length} now. 5058 index = etrue = graph()->NewNode( 5059 common()->TypeGuard( 5060 Type::Range(0.0, length_access.type.Max() - 1.0, graph()->zone())), 5061 index, etrue, if_true); 5062 5063 done_true = jsgraph()->FalseConstant(); 5064 if (iteration_kind == IterationKind::kKeys) { 5065 // Just return the {index}. 5066 value_true = index; 5067 } else { 5068 DCHECK(iteration_kind == IterationKind::kEntries || 5069 iteration_kind == IterationKind::kValues); 5070 5071 if (IsFixedTypedArrayElementsKind(elements_kind)) { 5072 Node* base_ptr = etrue = graph()->NewNode( 5073 simplified()->LoadField( 5074 AccessBuilder::ForFixedTypedArrayBaseBasePointer()), 5075 elements, etrue, if_true); 5076 Node* external_ptr = etrue = graph()->NewNode( 5077 simplified()->LoadField( 5078 AccessBuilder::ForFixedTypedArrayBaseExternalPointer()), 5079 elements, etrue, if_true); 5080 5081 ExternalArrayType array_type = kExternalInt8Array; 5082 switch (elements_kind) { 5083 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \ 5084 case TYPE##_ELEMENTS: \ 5085 array_type = kExternal##Type##Array; \ 5086 break; 5087 TYPED_ARRAYS(TYPED_ARRAY_CASE) 5088 default: 5089 UNREACHABLE(); 5090 #undef TYPED_ARRAY_CASE 5091 } 5092 5093 Node* buffer = etrue = 5094 graph()->NewNode(simplified()->LoadField( 5095 AccessBuilder::ForJSArrayBufferViewBuffer()), 5096 iterated_object, etrue, if_true); 5097 5098 value_true = etrue = 5099 graph()->NewNode(simplified()->LoadTypedElement(array_type), buffer, 5100 base_ptr, external_ptr, index, etrue, if_true); 5101 } else { 5102 value_true = etrue = graph()->NewNode( 5103 simplified()->LoadElement( 5104 AccessBuilder::ForFixedArrayElement(elements_kind)), 5105 elements, index, etrue, if_true); 5106 5107 // Convert hole to undefined if needed. 5108 if (elements_kind == HOLEY_ELEMENTS || 5109 elements_kind == HOLEY_SMI_ELEMENTS) { 5110 value_true = graph()->NewNode( 5111 simplified()->ConvertTaggedHoleToUndefined(), value_true); 5112 } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) { 5113 // TODO(6587): avoid deopt if not all uses of value are truncated. 5114 CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole; 5115 value_true = etrue = graph()->NewNode( 5116 simplified()->CheckFloat64Hole(mode, p.feedback()), value_true, 5117 etrue, if_true); 5118 } 5119 } 5120 5121 if (iteration_kind == IterationKind::kEntries) { 5122 // Allocate elements for key/value pair 5123 value_true = etrue = 5124 graph()->NewNode(javascript()->CreateKeyValueArray(), index, 5125 value_true, context, etrue); 5126 } else { 5127 DCHECK_EQ(IterationKind::kValues, iteration_kind); 5128 } 5129 } 5130 5131 // Increment the [[NextIndex]] field in the {iterator}. The TypeGuards 5132 // above guarantee that the {next_index} is in the UnsignedSmall range. 5133 Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index, 5134 jsgraph()->OneConstant()); 5135 etrue = graph()->NewNode(simplified()->StoreField(index_access), iterator, 5136 next_index, etrue, if_true); 5137 } 5138 5139 Node* done_false; 5140 Node* value_false; 5141 Node* efalse = effect; 5142 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 5143 { 5144 // iterator.[[NextIndex]] >= array.length, stop iterating. 5145 done_false = jsgraph()->TrueConstant(); 5146 value_false = jsgraph()->UndefinedConstant(); 5147 5148 if (!IsFixedTypedArrayElementsKind(elements_kind)) { 5149 // Mark the {iterator} as exhausted by setting the [[NextIndex]] to a 5150 // value that will never pass the length check again (aka the maximum 5151 // value possible for the specific iterated object). Note that this is 5152 // different from what the specification says, which is changing the 5153 // [[IteratedObject]] field to undefined, but that makes it difficult 5154 // to eliminate the map checks and "length" accesses in for..of loops. 5155 // 5156 // This is not necessary for JSTypedArray's, since the length of those 5157 // cannot change later and so if we were ever out of bounds for them 5158 // we will stay out-of-bounds forever. 5159 Node* end_index = jsgraph()->Constant(index_access.type.Max()); 5160 efalse = graph()->NewNode(simplified()->StoreField(index_access), 5161 iterator, end_index, efalse, if_false); 5162 } 5163 } 5164 5165 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 5166 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 5167 Node* value = 5168 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 5169 value_true, value_false, control); 5170 Node* done = 5171 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 5172 done_true, done_false, control); 5173 5174 // Create IteratorResult object. 5175 value = effect = graph()->NewNode(javascript()->CreateIterResultObject(), 5176 value, done, context, effect); 5177 ReplaceWithValue(node, value, effect, control); 5178 return Replace(value); 5179 } 5180 5181 // ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos ) 5182 // ES6 section 21.1.3.3 String.prototype.codePointAt ( pos ) 5183 Reduction JSCallReducer::ReduceStringPrototypeStringAt( 5184 const Operator* string_access_operator, Node* node) { 5185 DCHECK(string_access_operator->opcode() == IrOpcode::kStringCharCodeAt || 5186 string_access_operator->opcode() == IrOpcode::kStringCodePointAt); 5187 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 5188 CallParameters const& p = CallParametersOf(node->op()); 5189 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5190 return NoChange(); 5191 } 5192 5193 Node* receiver = NodeProperties::GetValueInput(node, 1); 5194 Node* index = node->op()->ValueInputCount() >= 3 5195 ? NodeProperties::GetValueInput(node, 2) 5196 : jsgraph()->ZeroConstant(); 5197 Node* effect = NodeProperties::GetEffectInput(node); 5198 Node* control = NodeProperties::GetControlInput(node); 5199 5200 // Ensure that the {receiver} is actually a String. 5201 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()), 5202 receiver, effect, control); 5203 5204 // Determine the {receiver} length. 5205 Node* receiver_length = 5206 graph()->NewNode(simplified()->StringLength(), receiver); 5207 5208 // Check that the {index} is within range. 5209 index = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), 5210 index, receiver_length, effect, control); 5211 5212 // Return the character from the {receiver} as single character string. 5213 Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index); 5214 Node* value = effect = graph()->NewNode(string_access_operator, receiver, 5215 masked_index, effect, control); 5216 5217 ReplaceWithValue(node, value, effect, control); 5218 return Replace(value); 5219 } 5220 5221 // ES section 21.1.3.1 String.prototype.charAt ( pos ) 5222 Reduction JSCallReducer::ReduceStringPrototypeCharAt(Node* node) { 5223 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 5224 CallParameters const& p = CallParametersOf(node->op()); 5225 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5226 return NoChange(); 5227 } 5228 5229 Node* receiver = NodeProperties::GetValueInput(node, 1); 5230 Node* index = node->op()->ValueInputCount() >= 3 5231 ? NodeProperties::GetValueInput(node, 2) 5232 : jsgraph()->ZeroConstant(); 5233 Node* effect = NodeProperties::GetEffectInput(node); 5234 Node* control = NodeProperties::GetControlInput(node); 5235 5236 // Ensure that the {receiver} is actually a String. 5237 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()), 5238 receiver, effect, control); 5239 5240 // Determine the {receiver} length. 5241 Node* receiver_length = 5242 graph()->NewNode(simplified()->StringLength(), receiver); 5243 5244 // Check that the {index} is within range. 5245 index = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), 5246 index, receiver_length, effect, control); 5247 5248 // Return the character from the {receiver} as single character string. 5249 Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index); 5250 Node* value = effect = 5251 graph()->NewNode(simplified()->StringCharCodeAt(), receiver, masked_index, 5252 effect, control); 5253 value = graph()->NewNode(simplified()->StringFromSingleCharCode(), value); 5254 5255 ReplaceWithValue(node, value, effect, control); 5256 return Replace(value); 5257 } 5258 5259 #ifdef V8_INTL_SUPPORT 5260 5261 Reduction JSCallReducer::ReduceStringPrototypeToLowerCaseIntl(Node* node) { 5262 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 5263 CallParameters const& p = CallParametersOf(node->op()); 5264 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5265 return NoChange(); 5266 } 5267 Node* effect = NodeProperties::GetEffectInput(node); 5268 Node* control = NodeProperties::GetControlInput(node); 5269 5270 Node* receiver = effect = 5271 graph()->NewNode(simplified()->CheckString(p.feedback()), 5272 NodeProperties::GetValueInput(node, 1), effect, control); 5273 5274 NodeProperties::ReplaceEffectInput(node, effect); 5275 RelaxEffectsAndControls(node); 5276 node->ReplaceInput(0, receiver); 5277 node->TrimInputCount(1); 5278 NodeProperties::ChangeOp(node, simplified()->StringToLowerCaseIntl()); 5279 NodeProperties::SetType(node, Type::String()); 5280 return Changed(node); 5281 } 5282 5283 Reduction JSCallReducer::ReduceStringPrototypeToUpperCaseIntl(Node* node) { 5284 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 5285 CallParameters const& p = CallParametersOf(node->op()); 5286 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5287 return NoChange(); 5288 } 5289 Node* effect = NodeProperties::GetEffectInput(node); 5290 Node* control = NodeProperties::GetControlInput(node); 5291 5292 Node* receiver = effect = 5293 graph()->NewNode(simplified()->CheckString(p.feedback()), 5294 NodeProperties::GetValueInput(node, 1), effect, control); 5295 5296 NodeProperties::ReplaceEffectInput(node, effect); 5297 RelaxEffectsAndControls(node); 5298 node->ReplaceInput(0, receiver); 5299 node->TrimInputCount(1); 5300 NodeProperties::ChangeOp(node, simplified()->StringToUpperCaseIntl()); 5301 NodeProperties::SetType(node, Type::String()); 5302 return Changed(node); 5303 } 5304 5305 #endif // V8_INTL_SUPPORT 5306 5307 // ES #sec-string.fromcharcode 5308 Reduction JSCallReducer::ReduceStringFromCharCode(Node* node) { 5309 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 5310 CallParameters const& p = CallParametersOf(node->op()); 5311 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5312 return NoChange(); 5313 } 5314 if (node->op()->ValueInputCount() == 3) { 5315 Node* effect = NodeProperties::GetEffectInput(node); 5316 Node* control = NodeProperties::GetControlInput(node); 5317 Node* input = NodeProperties::GetValueInput(node, 2); 5318 5319 input = effect = graph()->NewNode( 5320 simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball, 5321 p.feedback()), 5322 input, effect, control); 5323 5324 Node* value = 5325 graph()->NewNode(simplified()->StringFromSingleCharCode(), input); 5326 ReplaceWithValue(node, value, effect); 5327 return Replace(value); 5328 } 5329 return NoChange(); 5330 } 5331 5332 // ES #sec-string.fromcodepoint 5333 Reduction JSCallReducer::ReduceStringFromCodePoint(Node* node) { 5334 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 5335 CallParameters const& p = CallParametersOf(node->op()); 5336 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5337 return NoChange(); 5338 } 5339 if (node->op()->ValueInputCount() == 3) { 5340 Node* effect = NodeProperties::GetEffectInput(node); 5341 Node* control = NodeProperties::GetControlInput(node); 5342 Node* input = NodeProperties::GetValueInput(node, 2); 5343 5344 input = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), 5345 input, effect, control); 5346 5347 input = effect = 5348 graph()->NewNode(simplified()->CheckBounds(p.feedback()), input, 5349 jsgraph()->Constant(0x10FFFF + 1), effect, control); 5350 5351 Node* value = graph()->NewNode( 5352 simplified()->StringFromSingleCodePoint(UnicodeEncoding::UTF32), input); 5353 ReplaceWithValue(node, value, effect); 5354 return Replace(value); 5355 } 5356 return NoChange(); 5357 } 5358 5359 Reduction JSCallReducer::ReduceStringPrototypeIterator(Node* node) { 5360 CallParameters const& p = CallParametersOf(node->op()); 5361 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5362 return NoChange(); 5363 } 5364 Node* effect = NodeProperties::GetEffectInput(node); 5365 Node* control = NodeProperties::GetControlInput(node); 5366 Node* receiver = effect = 5367 graph()->NewNode(simplified()->CheckString(p.feedback()), 5368 NodeProperties::GetValueInput(node, 1), effect, control); 5369 Node* iterator = effect = 5370 graph()->NewNode(javascript()->CreateStringIterator(), receiver, 5371 jsgraph()->NoContextConstant(), effect); 5372 ReplaceWithValue(node, iterator, effect, control); 5373 return Replace(iterator); 5374 } 5375 5376 Reduction JSCallReducer::ReduceStringIteratorPrototypeNext(Node* node) { 5377 Node* receiver = NodeProperties::GetValueInput(node, 1); 5378 Node* effect = NodeProperties::GetEffectInput(node); 5379 Node* control = NodeProperties::GetControlInput(node); 5380 Node* context = NodeProperties::GetContextInput(node); 5381 if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect, 5382 JS_STRING_ITERATOR_TYPE)) { 5383 Node* string = effect = graph()->NewNode( 5384 simplified()->LoadField(AccessBuilder::ForJSStringIteratorString()), 5385 receiver, effect, control); 5386 Node* index = effect = graph()->NewNode( 5387 simplified()->LoadField(AccessBuilder::ForJSStringIteratorIndex()), 5388 receiver, effect, control); 5389 Node* length = graph()->NewNode(simplified()->StringLength(), string); 5390 5391 // branch0: if (index < length) 5392 Node* check0 = 5393 graph()->NewNode(simplified()->NumberLessThan(), index, length); 5394 Node* branch0 = 5395 graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control); 5396 5397 Node* etrue0 = effect; 5398 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); 5399 Node* done_true; 5400 Node* vtrue0; 5401 { 5402 done_true = jsgraph()->FalseConstant(); 5403 Node* codepoint = etrue0 = graph()->NewNode( 5404 simplified()->StringCodePointAt(UnicodeEncoding::UTF16), string, 5405 index, etrue0, if_true0); 5406 vtrue0 = graph()->NewNode( 5407 simplified()->StringFromSingleCodePoint(UnicodeEncoding::UTF16), 5408 codepoint); 5409 5410 // Update iterator.[[NextIndex]] 5411 Node* char_length = 5412 graph()->NewNode(simplified()->StringLength(), vtrue0); 5413 index = graph()->NewNode(simplified()->NumberAdd(), index, char_length); 5414 etrue0 = graph()->NewNode( 5415 simplified()->StoreField(AccessBuilder::ForJSStringIteratorIndex()), 5416 receiver, index, etrue0, if_true0); 5417 } 5418 5419 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); 5420 Node* done_false; 5421 Node* vfalse0; 5422 { 5423 vfalse0 = jsgraph()->UndefinedConstant(); 5424 done_false = jsgraph()->TrueConstant(); 5425 } 5426 5427 control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); 5428 effect = graph()->NewNode(common()->EffectPhi(2), etrue0, effect, control); 5429 Node* value = 5430 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 5431 vtrue0, vfalse0, control); 5432 Node* done = 5433 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 5434 done_true, done_false, control); 5435 5436 value = effect = graph()->NewNode(javascript()->CreateIterResultObject(), 5437 value, done, context, effect); 5438 5439 ReplaceWithValue(node, value, effect, control); 5440 return Replace(value); 5441 } 5442 return NoChange(); 5443 } 5444 5445 // ES #sec-string.prototype.concat 5446 Reduction JSCallReducer::ReduceStringPrototypeConcat( 5447 Node* node, Handle<SharedFunctionInfo> shared) { 5448 if (node->op()->ValueInputCount() < 2 || node->op()->ValueInputCount() > 3) { 5449 return NoChange(); 5450 } 5451 CallParameters const& p = CallParametersOf(node->op()); 5452 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5453 return NoChange(); 5454 } 5455 Node* effect = NodeProperties::GetEffectInput(node); 5456 Node* control = NodeProperties::GetControlInput(node); 5457 Node* context = NodeProperties::GetContextInput(node); 5458 Node* receiver = effect = 5459 graph()->NewNode(simplified()->CheckString(p.feedback()), 5460 NodeProperties::GetValueInput(node, 1), effect, control); 5461 5462 if (node->op()->ValueInputCount() < 3) { 5463 ReplaceWithValue(node, receiver, effect, control); 5464 return Replace(receiver); 5465 } 5466 Node* argument = effect = 5467 graph()->NewNode(simplified()->CheckString(p.feedback()), 5468 NodeProperties::GetValueInput(node, 2), effect, control); 5469 5470 Callable const callable = 5471 CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED); 5472 auto call_descriptor = 5473 Linkage::GetStubCallDescriptor(graph()->zone(), callable.descriptor(), 0, 5474 CallDescriptor::kNeedsFrameState, 5475 Operator::kNoDeopt | Operator::kNoWrite); 5476 5477 // TODO(turbofan): Massage the FrameState of the {node} here once we 5478 // have an artificial builtin frame type, so that it looks like the 5479 // exception from StringAdd overflow came from String.prototype.concat 5480 // builtin instead of the calling function. 5481 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); 5482 5483 Node* value = effect = control = graph()->NewNode( 5484 common()->Call(call_descriptor), jsgraph()->HeapConstant(callable.code()), 5485 receiver, argument, context, outer_frame_state, effect, control); 5486 5487 ReplaceWithValue(node, value, effect, control); 5488 return Replace(value); 5489 } 5490 5491 Reduction JSCallReducer::ReduceAsyncFunctionPromiseCreate(Node* node) { 5492 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 5493 Node* context = NodeProperties::GetContextInput(node); 5494 Node* effect = NodeProperties::GetEffectInput(node); 5495 if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange(); 5496 5497 // Install a code dependency on the promise hook protector cell. 5498 dependencies()->DependOnProtector( 5499 PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector())); 5500 5501 // Morph this {node} into a JSCreatePromise node. 5502 RelaxControls(node); 5503 node->ReplaceInput(0, context); 5504 node->ReplaceInput(1, effect); 5505 node->TrimInputCount(2); 5506 NodeProperties::ChangeOp(node, javascript()->CreatePromise()); 5507 return Changed(node); 5508 } 5509 5510 Reduction JSCallReducer::ReduceAsyncFunctionPromiseRelease(Node* node) { 5511 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 5512 if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange(); 5513 5514 dependencies()->DependOnProtector( 5515 PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector())); 5516 5517 // The AsyncFunctionPromiseRelease builtin is a no-op as long as neither 5518 // the debugger is active nor any promise hook has been installed (ever). 5519 Node* value = jsgraph()->UndefinedConstant(); 5520 ReplaceWithValue(node, value); 5521 return Replace(value); 5522 } 5523 5524 Node* JSCallReducer::CreateArtificialFrameState( 5525 Node* node, Node* outer_frame_state, int parameter_count, 5526 BailoutId bailout_id, FrameStateType frame_state_type, 5527 Handle<SharedFunctionInfo> shared) { 5528 const FrameStateFunctionInfo* state_info = 5529 common()->CreateFrameStateFunctionInfo(frame_state_type, 5530 parameter_count + 1, 0, shared); 5531 5532 const Operator* op = common()->FrameState( 5533 bailout_id, OutputFrameStateCombine::Ignore(), state_info); 5534 const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense()); 5535 Node* node0 = graph()->NewNode(op0); 5536 std::vector<Node*> params; 5537 for (int parameter = 0; parameter < parameter_count + 1; ++parameter) { 5538 params.push_back(node->InputAt(1 + parameter)); 5539 } 5540 const Operator* op_param = common()->StateValues( 5541 static_cast<int>(params.size()), SparseInputMask::Dense()); 5542 Node* params_node = graph()->NewNode( 5543 op_param, static_cast<int>(params.size()), ¶ms.front()); 5544 return graph()->NewNode(op, params_node, node0, node0, 5545 jsgraph()->UndefinedConstant(), node->InputAt(0), 5546 outer_frame_state); 5547 } 5548 5549 Reduction JSCallReducer::ReducePromiseConstructor(Node* node) { 5550 DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode()); 5551 ConstructParameters const& p = ConstructParametersOf(node->op()); 5552 int arity = static_cast<int>(p.arity() - 2); 5553 // We only inline when we have the executor. 5554 if (arity < 1) return NoChange(); 5555 Node* target = NodeProperties::GetValueInput(node, 0); 5556 Node* executor = NodeProperties::GetValueInput(node, 1); 5557 Node* new_target = NodeProperties::GetValueInput(node, arity + 1); 5558 5559 Node* context = NodeProperties::GetContextInput(node); 5560 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); 5561 Node* effect = NodeProperties::GetEffectInput(node); 5562 Node* control = NodeProperties::GetControlInput(node); 5563 5564 if (!FLAG_experimental_inline_promise_constructor) return NoChange(); 5565 if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange(); 5566 5567 // Only handle builtins Promises, not subclasses. 5568 if (target != new_target) return NoChange(); 5569 5570 dependencies()->DependOnProtector( 5571 PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector())); 5572 5573 Handle<SharedFunctionInfo> promise_shared( 5574 handle(native_context()->promise_function()->shared(), isolate())); 5575 5576 // Insert a construct stub frame into the chain of frame states. This will 5577 // reconstruct the proper frame when deoptimizing within the constructor. 5578 // For the frame state, we only provide the executor parameter, even if more 5579 // arugments were passed. This is not observable from JS. 5580 DCHECK_EQ(1, promise_shared->internal_formal_parameter_count()); 5581 Node* constructor_frame_state = CreateArtificialFrameState( 5582 node, outer_frame_state, 1, BailoutId::ConstructStubInvoke(), 5583 FrameStateType::kConstructStub, promise_shared); 5584 5585 // The deopt continuation of this frame state is never called; the frame state 5586 // is only necessary to obtain the right stack trace. 5587 const std::vector<Node*> checkpoint_parameters({ 5588 jsgraph()->UndefinedConstant(), /* receiver */ 5589 jsgraph()->UndefinedConstant(), /* promise */ 5590 jsgraph()->UndefinedConstant(), /* reject function */ 5591 jsgraph()->TheHoleConstant() /* exception */ 5592 }); 5593 int checkpoint_parameters_size = 5594 static_cast<int>(checkpoint_parameters.size()); 5595 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 5596 jsgraph(), promise_shared, 5597 Builtins::kPromiseConstructorLazyDeoptContinuation, target, context, 5598 checkpoint_parameters.data(), checkpoint_parameters_size, 5599 constructor_frame_state, ContinuationFrameStateMode::LAZY); 5600 5601 // Check if executor is callable 5602 Node* check_fail = nullptr; 5603 Node* check_throw = nullptr; 5604 WireInCallbackIsCallableCheck(executor, context, frame_state, effect, 5605 &control, &check_fail, &check_throw); 5606 5607 // Create the resulting JSPromise. 5608 Node* promise = effect = 5609 graph()->NewNode(javascript()->CreatePromise(), context, effect); 5610 5611 // 8. CreatePromiseResolvingFunctions 5612 // Allocate a promise context for the closures below. 5613 Node* promise_context = effect = 5614 graph()->NewNode(javascript()->CreateFunctionContext( 5615 handle(native_context()->scope_info(), isolate()), 5616 PromiseBuiltinsAssembler::kPromiseContextLength - 5617 Context::MIN_CONTEXT_SLOTS, 5618 FUNCTION_SCOPE), 5619 context, effect, control); 5620 effect = 5621 graph()->NewNode(simplified()->StoreField(AccessBuilder::ForContextSlot( 5622 PromiseBuiltinsAssembler::kPromiseSlot)), 5623 promise_context, promise, effect, control); 5624 effect = graph()->NewNode( 5625 simplified()->StoreField(AccessBuilder::ForContextSlot( 5626 PromiseBuiltinsAssembler::kAlreadyResolvedSlot)), 5627 promise_context, jsgraph()->FalseConstant(), effect, control); 5628 effect = graph()->NewNode( 5629 simplified()->StoreField(AccessBuilder::ForContextSlot( 5630 PromiseBuiltinsAssembler::kDebugEventSlot)), 5631 promise_context, jsgraph()->TrueConstant(), effect, control); 5632 5633 // Allocate the closure for the resolve case. 5634 Handle<SharedFunctionInfo> resolve_shared( 5635 native_context()->promise_capability_default_resolve_shared_fun(), 5636 isolate()); 5637 Node* resolve = effect = 5638 graph()->NewNode(javascript()->CreateClosure( 5639 resolve_shared, factory()->many_closures_cell(), 5640 handle(resolve_shared->GetCode(), isolate())), 5641 promise_context, effect, control); 5642 5643 // Allocate the closure for the reject case. 5644 Handle<SharedFunctionInfo> reject_shared( 5645 native_context()->promise_capability_default_reject_shared_fun(), 5646 isolate()); 5647 Node* reject = effect = 5648 graph()->NewNode(javascript()->CreateClosure( 5649 reject_shared, factory()->many_closures_cell(), 5650 handle(reject_shared->GetCode(), isolate())), 5651 promise_context, effect, control); 5652 5653 const std::vector<Node*> checkpoint_parameters_continuation( 5654 {jsgraph()->UndefinedConstant() /* receiver */, promise, reject}); 5655 int checkpoint_parameters_continuation_size = 5656 static_cast<int>(checkpoint_parameters_continuation.size()); 5657 // This continuation just returns the created promise and takes care of 5658 // exceptions thrown by the executor. 5659 frame_state = CreateJavaScriptBuiltinContinuationFrameState( 5660 jsgraph(), promise_shared, 5661 Builtins::kPromiseConstructorLazyDeoptContinuation, target, context, 5662 checkpoint_parameters_continuation.data(), 5663 checkpoint_parameters_continuation_size, constructor_frame_state, 5664 ContinuationFrameStateMode::LAZY_WITH_CATCH); 5665 5666 // 9. Call executor with both resolving functions 5667 effect = control = graph()->NewNode( 5668 javascript()->Call(4, p.frequency(), VectorSlotPair(), 5669 ConvertReceiverMode::kNullOrUndefined, 5670 SpeculationMode::kDisallowSpeculation), 5671 executor, jsgraph()->UndefinedConstant(), resolve, reject, context, 5672 frame_state, effect, control); 5673 5674 Node* exception_effect = effect; 5675 Node* exception_control = control; 5676 { 5677 Node* reason = exception_effect = exception_control = graph()->NewNode( 5678 common()->IfException(), exception_control, exception_effect); 5679 // 10a. Call reject if the call to executor threw. 5680 exception_effect = exception_control = graph()->NewNode( 5681 javascript()->Call(3, p.frequency(), VectorSlotPair(), 5682 ConvertReceiverMode::kNullOrUndefined, 5683 SpeculationMode::kDisallowSpeculation), 5684 reject, jsgraph()->UndefinedConstant(), reason, context, frame_state, 5685 exception_effect, exception_control); 5686 5687 // Rewire potential exception edges. 5688 Node* on_exception = nullptr; 5689 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { 5690 RewirePostCallbackExceptionEdges(check_throw, on_exception, 5691 exception_effect, &check_fail, 5692 &exception_control); 5693 } 5694 } 5695 5696 Node* success_effect = effect; 5697 Node* success_control = control; 5698 { 5699 success_control = graph()->NewNode(common()->IfSuccess(), success_control); 5700 } 5701 5702 control = 5703 graph()->NewNode(common()->Merge(2), success_control, exception_control); 5704 effect = graph()->NewNode(common()->EffectPhi(2), success_effect, 5705 exception_effect, control); 5706 5707 // Wire up the branch for the case when IsCallable fails for the executor. 5708 // Since {check_throw} is an unconditional throw, it's impossible to 5709 // return a successful completion. Therefore, we simply connect the successful 5710 // completion to the graph end. 5711 Node* throw_node = 5712 graph()->NewNode(common()->Throw(), check_throw, check_fail); 5713 NodeProperties::MergeControlToEnd(graph(), common(), throw_node); 5714 5715 ReplaceWithValue(node, promise, effect, control); 5716 return Replace(promise); 5717 } 5718 5719 // V8 Extras: v8.createPromise(parent) 5720 Reduction JSCallReducer::ReducePromiseInternalConstructor(Node* node) { 5721 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 5722 Node* context = NodeProperties::GetContextInput(node); 5723 Node* effect = NodeProperties::GetEffectInput(node); 5724 5725 // Check that promises aren't being observed through (debug) hooks. 5726 if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange(); 5727 5728 dependencies()->DependOnProtector( 5729 PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector())); 5730 5731 // Create a new pending promise. 5732 Node* value = effect = 5733 graph()->NewNode(javascript()->CreatePromise(), context, effect); 5734 5735 ReplaceWithValue(node, value, effect); 5736 return Replace(value); 5737 } 5738 5739 // V8 Extras: v8.rejectPromise(promise, reason) 5740 Reduction JSCallReducer::ReducePromiseInternalReject(Node* node) { 5741 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 5742 Node* promise = node->op()->ValueInputCount() >= 2 5743 ? NodeProperties::GetValueInput(node, 2) 5744 : jsgraph()->UndefinedConstant(); 5745 Node* reason = node->op()->ValueInputCount() >= 3 5746 ? NodeProperties::GetValueInput(node, 3) 5747 : jsgraph()->UndefinedConstant(); 5748 Node* debug_event = jsgraph()->TrueConstant(); 5749 Node* frame_state = NodeProperties::GetFrameStateInput(node); 5750 Node* context = NodeProperties::GetContextInput(node); 5751 Node* effect = NodeProperties::GetEffectInput(node); 5752 Node* control = NodeProperties::GetControlInput(node); 5753 5754 // Reject the {promise} using the given {reason}, and trigger debug logic. 5755 Node* value = effect = 5756 graph()->NewNode(javascript()->RejectPromise(), promise, reason, 5757 debug_event, context, frame_state, effect, control); 5758 5759 ReplaceWithValue(node, value, effect, control); 5760 return Replace(value); 5761 } 5762 5763 // V8 Extras: v8.resolvePromise(promise, resolution) 5764 Reduction JSCallReducer::ReducePromiseInternalResolve(Node* node) { 5765 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 5766 Node* promise = node->op()->ValueInputCount() >= 2 5767 ? NodeProperties::GetValueInput(node, 2) 5768 : jsgraph()->UndefinedConstant(); 5769 Node* resolution = node->op()->ValueInputCount() >= 3 5770 ? NodeProperties::GetValueInput(node, 3) 5771 : jsgraph()->UndefinedConstant(); 5772 Node* frame_state = NodeProperties::GetFrameStateInput(node); 5773 Node* context = NodeProperties::GetContextInput(node); 5774 Node* effect = NodeProperties::GetEffectInput(node); 5775 Node* control = NodeProperties::GetControlInput(node); 5776 5777 // Resolve the {promise} using the given {resolution}. 5778 Node* value = effect = 5779 graph()->NewNode(javascript()->ResolvePromise(), promise, resolution, 5780 context, frame_state, effect, control); 5781 5782 ReplaceWithValue(node, value, effect, control); 5783 return Replace(value); 5784 } 5785 5786 // ES section #sec-promise.prototype.catch 5787 Reduction JSCallReducer::ReducePromisePrototypeCatch(Node* node) { 5788 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 5789 CallParameters const& p = CallParametersOf(node->op()); 5790 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5791 return NoChange(); 5792 } 5793 int arity = static_cast<int>(p.arity() - 2); 5794 Node* receiver = NodeProperties::GetValueInput(node, 1); 5795 Node* effect = NodeProperties::GetEffectInput(node); 5796 Node* control = NodeProperties::GetControlInput(node); 5797 5798 // Check that the Promise.then protector is intact. This protector guards 5799 // that all JSPromise instances whose [[Prototype]] is the initial 5800 // %PromisePrototype% yield the initial %PromisePrototype%.then method 5801 // when looking up "then". 5802 if (!isolate()->IsPromiseThenLookupChainIntact()) return NoChange(); 5803 5804 // Check if we know something about {receiver} already. 5805 ZoneHandleSet<Map> receiver_maps; 5806 NodeProperties::InferReceiverMapsResult result = 5807 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 5808 &receiver_maps); 5809 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 5810 DCHECK_NE(0, receiver_maps.size()); 5811 5812 // Check whether all {receiver_maps} are JSPromise maps and 5813 // have the initial Promise.prototype as their [[Prototype]]. 5814 for (Handle<Map> receiver_map : receiver_maps) { 5815 if (!receiver_map->IsJSPromiseMap()) return NoChange(); 5816 if (receiver_map->prototype() != native_context()->promise_prototype()) { 5817 return NoChange(); 5818 } 5819 } 5820 5821 dependencies()->DependOnProtector( 5822 PropertyCellRef(js_heap_broker(), factory()->promise_then_protector())); 5823 5824 // If the {receiver_maps} aren't reliable, we need to repeat the 5825 // map check here, guarded by the CALL_IC. 5826 if (result == NodeProperties::kUnreliableReceiverMaps) { 5827 effect = 5828 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 5829 receiver_maps, p.feedback()), 5830 receiver, effect, control); 5831 } 5832 5833 // Massage the {node} to call "then" instead by first removing all inputs 5834 // following the onRejected parameter, and then filling up the parameters 5835 // to two inputs from the left with undefined. 5836 Node* target = 5837 jsgraph()->Constant(handle(native_context()->promise_then(), isolate())); 5838 NodeProperties::ReplaceValueInput(node, target, 0); 5839 NodeProperties::ReplaceEffectInput(node, effect); 5840 for (; arity > 1; --arity) node->RemoveInput(3); 5841 for (; arity < 2; ++arity) { 5842 node->InsertInput(graph()->zone(), 2, jsgraph()->UndefinedConstant()); 5843 } 5844 NodeProperties::ChangeOp( 5845 node, javascript()->Call(2 + arity, p.frequency(), p.feedback(), 5846 ConvertReceiverMode::kNotNullOrUndefined, 5847 p.speculation_mode())); 5848 Reduction const reduction = ReducePromisePrototypeThen(node); 5849 return reduction.Changed() ? reduction : Changed(node); 5850 } 5851 5852 // ES section #sec-promise.prototype.finally 5853 Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) { 5854 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 5855 CallParameters const& p = CallParametersOf(node->op()); 5856 int arity = static_cast<int>(p.arity() - 2); 5857 Node* receiver = NodeProperties::GetValueInput(node, 1); 5858 Node* on_finally = arity >= 1 ? NodeProperties::GetValueInput(node, 2) 5859 : jsgraph()->UndefinedConstant(); 5860 Node* effect = NodeProperties::GetEffectInput(node); 5861 Node* control = NodeProperties::GetControlInput(node); 5862 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5863 return NoChange(); 5864 } 5865 5866 // Check that promises aren't being observed through (debug) hooks. 5867 if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange(); 5868 5869 // Check that the Promise#then protector is intact. This protector guards 5870 // that all JSPromise instances whose [[Prototype]] is the initial 5871 // %PromisePrototype% yield the initial %PromisePrototype%.then method 5872 // when looking up "then". 5873 if (!isolate()->IsPromiseThenLookupChainIntact()) return NoChange(); 5874 5875 // Also check that the @@species protector is intact, which guards the 5876 // lookup of "constructor" on JSPromise instances, whoch [[Prototype]] is 5877 // the initial %PromisePrototype%, and the Symbol.species lookup on the 5878 // %PromisePrototype%. 5879 if (!isolate()->IsPromiseSpeciesLookupChainIntact()) return NoChange(); 5880 5881 // Check if we know something about {receiver} already. 5882 ZoneHandleSet<Map> receiver_maps; 5883 NodeProperties::InferReceiverMapsResult result = 5884 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 5885 &receiver_maps); 5886 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 5887 DCHECK_NE(0, receiver_maps.size()); 5888 5889 // Check whether all {receiver_maps} are JSPromise maps and 5890 // have the initial Promise.prototype as their [[Prototype]]. 5891 for (Handle<Map> receiver_map : receiver_maps) { 5892 if (!receiver_map->IsJSPromiseMap()) return NoChange(); 5893 if (receiver_map->prototype() != native_context()->promise_prototype()) { 5894 return NoChange(); 5895 } 5896 } 5897 5898 dependencies()->DependOnProtector( 5899 PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector())); 5900 dependencies()->DependOnProtector( 5901 PropertyCellRef(js_heap_broker(), factory()->promise_then_protector())); 5902 dependencies()->DependOnProtector(PropertyCellRef( 5903 js_heap_broker(), factory()->promise_species_protector())); 5904 5905 // If the {receiver_maps} aren't reliable, we need to repeat the 5906 // map check here, guarded by the CALL_IC. 5907 if (result == NodeProperties::kUnreliableReceiverMaps) { 5908 effect = 5909 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 5910 receiver_maps, p.feedback()), 5911 receiver, effect, control); 5912 } 5913 5914 // Check if {on_finally} is callable, and if so wrap it into appropriate 5915 // closures that perform the finalization. 5916 Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), on_finally); 5917 Node* branch = 5918 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); 5919 5920 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 5921 Node* etrue = effect; 5922 Node* catch_true; 5923 Node* then_true; 5924 { 5925 Node* context = jsgraph()->HeapConstant(native_context()); 5926 Node* constructor = jsgraph()->HeapConstant( 5927 handle(native_context()->promise_function(), isolate())); 5928 5929 // Allocate shared context for the closures below. 5930 context = etrue = graph()->NewNode( 5931 javascript()->CreateFunctionContext( 5932 handle(native_context()->scope_info(), isolate()), 5933 PromiseBuiltinsAssembler::kPromiseFinallyContextLength - 5934 Context::MIN_CONTEXT_SLOTS, 5935 FUNCTION_SCOPE), 5936 context, etrue, if_true); 5937 etrue = 5938 graph()->NewNode(simplified()->StoreField(AccessBuilder::ForContextSlot( 5939 PromiseBuiltinsAssembler::kOnFinallySlot)), 5940 context, on_finally, etrue, if_true); 5941 etrue = 5942 graph()->NewNode(simplified()->StoreField(AccessBuilder::ForContextSlot( 5943 PromiseBuiltinsAssembler::kConstructorSlot)), 5944 context, constructor, etrue, if_true); 5945 5946 // Allocate the closure for the reject case. 5947 Handle<SharedFunctionInfo> catch_finally( 5948 native_context()->promise_catch_finally_shared_fun(), isolate()); 5949 catch_true = etrue = 5950 graph()->NewNode(javascript()->CreateClosure( 5951 catch_finally, factory()->many_closures_cell(), 5952 handle(catch_finally->GetCode(), isolate())), 5953 context, etrue, if_true); 5954 5955 // Allocate the closure for the fulfill case. 5956 Handle<SharedFunctionInfo> then_finally( 5957 native_context()->promise_then_finally_shared_fun(), isolate()); 5958 then_true = etrue = 5959 graph()->NewNode(javascript()->CreateClosure( 5960 then_finally, factory()->many_closures_cell(), 5961 handle(then_finally->GetCode(), isolate())), 5962 context, etrue, if_true); 5963 } 5964 5965 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 5966 Node* efalse = effect; 5967 Node* catch_false = on_finally; 5968 Node* then_false = on_finally; 5969 5970 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 5971 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 5972 Node* catch_finally = 5973 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 5974 catch_true, catch_false, control); 5975 Node* then_finally = 5976 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 5977 then_true, then_false, control); 5978 5979 // At this point we definitely know that {receiver} has one of the 5980 // {receiver_maps}, so insert a MapGuard as a hint for the lowering 5981 // of the call to "then" below. 5982 effect = graph()->NewNode(simplified()->MapGuard(receiver_maps), receiver, 5983 effect, control); 5984 5985 // Massage the {node} to call "then" instead by first removing all inputs 5986 // following the onFinally parameter, and then replacing the only parameter 5987 // input with the {on_finally} value. 5988 Node* target = 5989 jsgraph()->Constant(handle(native_context()->promise_then(), isolate())); 5990 NodeProperties::ReplaceValueInput(node, target, 0); 5991 NodeProperties::ReplaceEffectInput(node, effect); 5992 NodeProperties::ReplaceControlInput(node, control); 5993 for (; arity > 2; --arity) node->RemoveInput(2); 5994 for (; arity < 2; ++arity) 5995 node->InsertInput(graph()->zone(), 2, then_finally); 5996 node->ReplaceInput(2, then_finally); 5997 node->ReplaceInput(3, catch_finally); 5998 NodeProperties::ChangeOp( 5999 node, javascript()->Call(2 + arity, p.frequency(), p.feedback(), 6000 ConvertReceiverMode::kNotNullOrUndefined, 6001 p.speculation_mode())); 6002 Reduction const reduction = ReducePromisePrototypeThen(node); 6003 return reduction.Changed() ? reduction : Changed(node); 6004 } 6005 6006 Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) { 6007 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 6008 CallParameters const& p = CallParametersOf(node->op()); 6009 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6010 return NoChange(); 6011 } 6012 6013 Node* receiver = NodeProperties::GetValueInput(node, 1); 6014 Node* on_fulfilled = node->op()->ValueInputCount() > 2 6015 ? NodeProperties::GetValueInput(node, 2) 6016 : jsgraph()->UndefinedConstant(); 6017 Node* on_rejected = node->op()->ValueInputCount() > 3 6018 ? NodeProperties::GetValueInput(node, 3) 6019 : jsgraph()->UndefinedConstant(); 6020 Node* context = NodeProperties::GetContextInput(node); 6021 Node* effect = NodeProperties::GetEffectInput(node); 6022 Node* control = NodeProperties::GetControlInput(node); 6023 Node* frame_state = NodeProperties::GetFrameStateInput(node); 6024 6025 // Check that promises aren't being observed through (debug) hooks. 6026 if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange(); 6027 6028 // Check if the @@species protector is intact. The @@species protector 6029 // guards the "constructor" lookup on all JSPromise instances and the 6030 // initial Promise.prototype, as well as the Symbol.species lookup on 6031 // the Promise constructor. 6032 if (!isolate()->IsPromiseSpeciesLookupChainIntact()) return NoChange(); 6033 6034 // Check if we know something about {receiver} already. 6035 ZoneHandleSet<Map> receiver_maps; 6036 NodeProperties::InferReceiverMapsResult infer_receiver_maps_result = 6037 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 6038 &receiver_maps); 6039 if (infer_receiver_maps_result == NodeProperties::kNoReceiverMaps) { 6040 return NoChange(); 6041 } 6042 DCHECK_NE(0, receiver_maps.size()); 6043 6044 // Check whether all {receiver_maps} are JSPromise maps and 6045 // have the initial Promise.prototype as their [[Prototype]]. 6046 for (Handle<Map> receiver_map : receiver_maps) { 6047 if (!receiver_map->IsJSPromiseMap()) return NoChange(); 6048 if (receiver_map->prototype() != native_context()->promise_prototype()) { 6049 return NoChange(); 6050 } 6051 } 6052 6053 dependencies()->DependOnProtector( 6054 PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector())); 6055 dependencies()->DependOnProtector(PropertyCellRef( 6056 js_heap_broker(), factory()->promise_species_protector())); 6057 6058 // If the {receiver_maps} aren't reliable, we need to repeat the 6059 // map check here, guarded by the CALL_IC. 6060 if (infer_receiver_maps_result == NodeProperties::kUnreliableReceiverMaps) { 6061 effect = 6062 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 6063 receiver_maps, p.feedback()), 6064 receiver, effect, control); 6065 } 6066 6067 // Check that {on_fulfilled} is callable. 6068 on_fulfilled = graph()->NewNode( 6069 common()->Select(MachineRepresentation::kTagged, BranchHint::kTrue), 6070 graph()->NewNode(simplified()->ObjectIsCallable(), on_fulfilled), 6071 on_fulfilled, jsgraph()->UndefinedConstant()); 6072 6073 // Check that {on_rejected} is callable. 6074 on_rejected = graph()->NewNode( 6075 common()->Select(MachineRepresentation::kTagged, BranchHint::kTrue), 6076 graph()->NewNode(simplified()->ObjectIsCallable(), on_rejected), 6077 on_rejected, jsgraph()->UndefinedConstant()); 6078 6079 // Create the resulting JSPromise. 6080 Node* result = effect = 6081 graph()->NewNode(javascript()->CreatePromise(), context, effect); 6082 6083 // Chain {result} onto {receiver}. 6084 result = effect = graph()->NewNode( 6085 javascript()->PerformPromiseThen(), receiver, on_fulfilled, on_rejected, 6086 result, context, frame_state, effect, control); 6087 ReplaceWithValue(node, result, effect, control); 6088 return Replace(result); 6089 } 6090 6091 // ES section #sec-promise.resolve 6092 Reduction JSCallReducer::ReducePromiseResolveTrampoline(Node* node) { 6093 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 6094 Node* receiver = NodeProperties::GetValueInput(node, 1); 6095 Node* value = node->op()->ValueInputCount() > 2 6096 ? NodeProperties::GetValueInput(node, 2) 6097 : jsgraph()->UndefinedConstant(); 6098 Node* context = NodeProperties::GetContextInput(node); 6099 Node* frame_state = NodeProperties::GetFrameStateInput(node); 6100 Node* effect = NodeProperties::GetEffectInput(node); 6101 Node* control = NodeProperties::GetControlInput(node); 6102 6103 // Check if we know something about {receiver} already. 6104 ZoneHandleSet<Map> receiver_maps; 6105 NodeProperties::InferReceiverMapsResult infer_receiver_maps_result = 6106 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 6107 &receiver_maps); 6108 if (infer_receiver_maps_result == NodeProperties::kNoReceiverMaps) { 6109 return NoChange(); 6110 } 6111 DCHECK_NE(0, receiver_maps.size()); 6112 6113 // Only reduce when all {receiver_maps} are JSReceiver maps. 6114 for (Handle<Map> receiver_map : receiver_maps) { 6115 if (!receiver_map->IsJSReceiverMap()) return NoChange(); 6116 } 6117 6118 // Morph the {node} into a JSPromiseResolve operation. 6119 node->ReplaceInput(0, receiver); 6120 node->ReplaceInput(1, value); 6121 node->ReplaceInput(2, context); 6122 node->ReplaceInput(3, frame_state); 6123 node->ReplaceInput(4, effect); 6124 node->ReplaceInput(5, control); 6125 node->TrimInputCount(6); 6126 NodeProperties::ChangeOp(node, javascript()->PromiseResolve()); 6127 return Changed(node); 6128 } 6129 6130 // ES #sec-typedarray-constructors 6131 Reduction JSCallReducer::ReduceTypedArrayConstructor( 6132 Node* node, Handle<SharedFunctionInfo> shared) { 6133 DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode()); 6134 ConstructParameters const& p = ConstructParametersOf(node->op()); 6135 int arity = static_cast<int>(p.arity() - 2); 6136 Node* target = NodeProperties::GetValueInput(node, 0); 6137 Node* arg1 = (arity >= 1) ? NodeProperties::GetValueInput(node, 1) 6138 : jsgraph()->UndefinedConstant(); 6139 Node* arg2 = (arity >= 2) ? NodeProperties::GetValueInput(node, 2) 6140 : jsgraph()->UndefinedConstant(); 6141 Node* arg3 = (arity >= 3) ? NodeProperties::GetValueInput(node, 3) 6142 : jsgraph()->UndefinedConstant(); 6143 Node* new_target = NodeProperties::GetValueInput(node, arity + 1); 6144 Node* context = NodeProperties::GetContextInput(node); 6145 Node* frame_state = NodeProperties::GetFrameStateInput(node); 6146 Node* effect = NodeProperties::GetEffectInput(node); 6147 Node* control = NodeProperties::GetControlInput(node); 6148 6149 // Insert a construct stub frame into the chain of frame states. This will 6150 // reconstruct the proper frame when deoptimizing within the constructor. 6151 frame_state = CreateArtificialFrameState( 6152 node, frame_state, arity, BailoutId::ConstructStubInvoke(), 6153 FrameStateType::kConstructStub, shared); 6154 6155 // This continuation just returns the newly created JSTypedArray. We 6156 // pass the_hole as the receiver, just like the builtin construct stub 6157 // does in this case. 6158 Node* const parameters[] = {jsgraph()->TheHoleConstant()}; 6159 int const num_parameters = static_cast<int>(arraysize(parameters)); 6160 frame_state = CreateJavaScriptBuiltinContinuationFrameState( 6161 jsgraph(), shared, Builtins::kGenericConstructorLazyDeoptContinuation, 6162 target, context, parameters, num_parameters, frame_state, 6163 ContinuationFrameStateMode::LAZY); 6164 6165 Node* result = 6166 graph()->NewNode(javascript()->CreateTypedArray(), target, new_target, 6167 arg1, arg2, arg3, context, frame_state, effect, control); 6168 return Replace(result); 6169 } 6170 6171 // ES #sec-get-%typedarray%.prototype-@@tostringtag 6172 Reduction JSCallReducer::ReduceTypedArrayPrototypeToStringTag(Node* node) { 6173 Node* receiver = NodeProperties::GetValueInput(node, 1); 6174 Node* effect = NodeProperties::GetEffectInput(node); 6175 Node* control = NodeProperties::GetControlInput(node); 6176 6177 NodeVector values(graph()->zone()); 6178 NodeVector effects(graph()->zone()); 6179 NodeVector controls(graph()->zone()); 6180 6181 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); 6182 control = 6183 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); 6184 6185 values.push_back(jsgraph()->UndefinedConstant()); 6186 effects.push_back(effect); 6187 controls.push_back(graph()->NewNode(common()->IfTrue(), control)); 6188 6189 control = graph()->NewNode(common()->IfFalse(), control); 6190 Node* receiver_map = effect = 6191 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), 6192 receiver, effect, control); 6193 Node* receiver_bit_field2 = effect = graph()->NewNode( 6194 simplified()->LoadField(AccessBuilder::ForMapBitField2()), receiver_map, 6195 effect, control); 6196 Node* receiver_elements_kind = graph()->NewNode( 6197 simplified()->NumberShiftRightLogical(), 6198 graph()->NewNode(simplified()->NumberBitwiseAnd(), receiver_bit_field2, 6199 jsgraph()->Constant(Map::ElementsKindBits::kMask)), 6200 jsgraph()->Constant(Map::ElementsKindBits::kShift)); 6201 6202 // Offset the elements kind by FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND, 6203 // so that the branch cascade below is turned into a simple table 6204 // switch by the ControlFlowOptimizer later. 6205 receiver_elements_kind = graph()->NewNode( 6206 simplified()->NumberSubtract(), receiver_elements_kind, 6207 jsgraph()->Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)); 6208 6209 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \ 6210 do { \ 6211 Node* check = graph()->NewNode( \ 6212 simplified()->NumberEqual(), receiver_elements_kind, \ 6213 jsgraph()->Constant(TYPE##_ELEMENTS - \ 6214 FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)); \ 6215 control = graph()->NewNode(common()->Branch(), check, control); \ 6216 values.push_back(jsgraph()->HeapConstant( \ 6217 factory()->InternalizeUtf8String(#Type "Array"))); \ 6218 effects.push_back(effect); \ 6219 controls.push_back(graph()->NewNode(common()->IfTrue(), control)); \ 6220 control = graph()->NewNode(common()->IfFalse(), control); \ 6221 } while (false); 6222 TYPED_ARRAYS(TYPED_ARRAY_CASE) 6223 #undef TYPED_ARRAY_CASE 6224 6225 values.push_back(jsgraph()->UndefinedConstant()); 6226 effects.push_back(effect); 6227 controls.push_back(control); 6228 6229 int const count = static_cast<int>(controls.size()); 6230 control = graph()->NewNode(common()->Merge(count), count, &controls.front()); 6231 effects.push_back(control); 6232 effect = 6233 graph()->NewNode(common()->EffectPhi(count), count + 1, &effects.front()); 6234 values.push_back(control); 6235 Node* value = 6236 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count), 6237 count + 1, &values.front()); 6238 ReplaceWithValue(node, value, effect, control); 6239 return Replace(value); 6240 } 6241 6242 // ES #sec-number.isfinite 6243 Reduction JSCallReducer::ReduceNumberIsFinite(Node* node) { 6244 if (node->op()->ValueInputCount() < 3) { 6245 Node* value = jsgraph()->FalseConstant(); 6246 ReplaceWithValue(node, value); 6247 return Replace(value); 6248 } 6249 Node* input = NodeProperties::GetValueInput(node, 2); 6250 Node* value = graph()->NewNode(simplified()->ObjectIsFiniteNumber(), input); 6251 ReplaceWithValue(node, value); 6252 return Replace(value); 6253 } 6254 6255 // ES #sec-number.isfinite 6256 Reduction JSCallReducer::ReduceNumberIsInteger(Node* node) { 6257 if (node->op()->ValueInputCount() < 3) { 6258 Node* value = jsgraph()->FalseConstant(); 6259 ReplaceWithValue(node, value); 6260 return Replace(value); 6261 } 6262 Node* input = NodeProperties::GetValueInput(node, 2); 6263 Node* value = graph()->NewNode(simplified()->ObjectIsInteger(), input); 6264 ReplaceWithValue(node, value); 6265 return Replace(value); 6266 } 6267 6268 // ES #sec-number.issafeinteger 6269 Reduction JSCallReducer::ReduceNumberIsSafeInteger(Node* node) { 6270 if (node->op()->ValueInputCount() < 3) { 6271 Node* value = jsgraph()->FalseConstant(); 6272 ReplaceWithValue(node, value); 6273 return Replace(value); 6274 } 6275 Node* input = NodeProperties::GetValueInput(node, 2); 6276 Node* value = graph()->NewNode(simplified()->ObjectIsSafeInteger(), input); 6277 ReplaceWithValue(node, value); 6278 return Replace(value); 6279 } 6280 6281 // ES #sec-number.isnan 6282 Reduction JSCallReducer::ReduceNumberIsNaN(Node* node) { 6283 if (node->op()->ValueInputCount() < 3) { 6284 Node* value = jsgraph()->FalseConstant(); 6285 ReplaceWithValue(node, value); 6286 return Replace(value); 6287 } 6288 Node* input = NodeProperties::GetValueInput(node, 2); 6289 Node* value = graph()->NewNode(simplified()->ObjectIsNaN(), input); 6290 ReplaceWithValue(node, value); 6291 return Replace(value); 6292 } 6293 6294 Reduction JSCallReducer::ReduceMapPrototypeGet(Node* node) { 6295 // We only optimize if we have target, receiver and key parameters. 6296 if (node->op()->ValueInputCount() != 3) return NoChange(); 6297 Node* receiver = NodeProperties::GetValueInput(node, 1); 6298 Node* effect = NodeProperties::GetEffectInput(node); 6299 Node* control = NodeProperties::GetControlInput(node); 6300 Node* key = NodeProperties::GetValueInput(node, 2); 6301 6302 if (!NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect, 6303 JS_MAP_TYPE)) 6304 return NoChange(); 6305 6306 Node* table = effect = graph()->NewNode( 6307 simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), receiver, 6308 effect, control); 6309 6310 Node* entry = effect = graph()->NewNode( 6311 simplified()->FindOrderedHashMapEntry(), table, key, effect, control); 6312 6313 Node* check = graph()->NewNode(simplified()->NumberEqual(), entry, 6314 jsgraph()->MinusOneConstant()); 6315 6316 Node* branch = graph()->NewNode(common()->Branch(), check, control); 6317 6318 // Key not found. 6319 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 6320 Node* etrue = effect; 6321 Node* vtrue = jsgraph()->UndefinedConstant(); 6322 6323 // Key found. 6324 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 6325 Node* efalse = effect; 6326 Node* vfalse = efalse = graph()->NewNode( 6327 simplified()->LoadElement(AccessBuilder::ForOrderedHashMapEntryValue()), 6328 table, entry, efalse, if_false); 6329 6330 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 6331 Node* value = graph()->NewNode( 6332 common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control); 6333 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 6334 6335 ReplaceWithValue(node, value, effect, control); 6336 return Replace(value); 6337 } 6338 6339 Reduction JSCallReducer::ReduceMapPrototypeHas(Node* node) { 6340 // We only optimize if we have target, receiver and key parameters. 6341 if (node->op()->ValueInputCount() != 3) return NoChange(); 6342 Node* receiver = NodeProperties::GetValueInput(node, 1); 6343 Node* effect = NodeProperties::GetEffectInput(node); 6344 Node* control = NodeProperties::GetControlInput(node); 6345 Node* key = NodeProperties::GetValueInput(node, 2); 6346 6347 if (!NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect, 6348 JS_MAP_TYPE)) 6349 return NoChange(); 6350 6351 Node* table = effect = graph()->NewNode( 6352 simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), receiver, 6353 effect, control); 6354 6355 Node* index = effect = graph()->NewNode( 6356 simplified()->FindOrderedHashMapEntry(), table, key, effect, control); 6357 6358 Node* value = graph()->NewNode(simplified()->NumberEqual(), index, 6359 jsgraph()->MinusOneConstant()); 6360 value = graph()->NewNode(simplified()->BooleanNot(), value); 6361 6362 ReplaceWithValue(node, value, effect, control); 6363 return Replace(value); 6364 } 6365 6366 namespace { 6367 6368 InstanceType InstanceTypeForCollectionKind(CollectionKind kind) { 6369 switch (kind) { 6370 case CollectionKind::kMap: 6371 return JS_MAP_TYPE; 6372 case CollectionKind::kSet: 6373 return JS_SET_TYPE; 6374 } 6375 UNREACHABLE(); 6376 } 6377 6378 } // namespace 6379 6380 Reduction JSCallReducer::ReduceCollectionIteration( 6381 Node* node, CollectionKind collection_kind, IterationKind iteration_kind) { 6382 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 6383 Node* receiver = NodeProperties::GetValueInput(node, 1); 6384 Node* context = NodeProperties::GetContextInput(node); 6385 Node* effect = NodeProperties::GetEffectInput(node); 6386 Node* control = NodeProperties::GetControlInput(node); 6387 if (NodeProperties::HasInstanceTypeWitness( 6388 isolate(), receiver, effect, 6389 InstanceTypeForCollectionKind(collection_kind))) { 6390 Node* js_create_iterator = effect = graph()->NewNode( 6391 javascript()->CreateCollectionIterator(collection_kind, iteration_kind), 6392 receiver, context, effect, control); 6393 ReplaceWithValue(node, js_create_iterator, effect); 6394 return Replace(js_create_iterator); 6395 } 6396 return NoChange(); 6397 } 6398 6399 Reduction JSCallReducer::ReduceCollectionPrototypeSize( 6400 Node* node, CollectionKind collection_kind) { 6401 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 6402 Node* receiver = NodeProperties::GetValueInput(node, 1); 6403 Node* effect = NodeProperties::GetEffectInput(node); 6404 Node* control = NodeProperties::GetControlInput(node); 6405 if (NodeProperties::HasInstanceTypeWitness( 6406 isolate(), receiver, effect, 6407 InstanceTypeForCollectionKind(collection_kind))) { 6408 Node* table = effect = graph()->NewNode( 6409 simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), 6410 receiver, effect, control); 6411 Node* value = effect = graph()->NewNode( 6412 simplified()->LoadField( 6413 AccessBuilder::ForOrderedHashTableBaseNumberOfElements()), 6414 table, effect, control); 6415 ReplaceWithValue(node, value, effect, control); 6416 return Replace(value); 6417 } 6418 return NoChange(); 6419 } 6420 6421 Reduction JSCallReducer::ReduceCollectionIteratorPrototypeNext( 6422 Node* node, int entry_size, Handle<HeapObject> empty_collection, 6423 InstanceType collection_iterator_instance_type_first, 6424 InstanceType collection_iterator_instance_type_last) { 6425 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 6426 Node* receiver = NodeProperties::GetValueInput(node, 1); 6427 Node* context = NodeProperties::GetContextInput(node); 6428 Node* effect = NodeProperties::GetEffectInput(node); 6429 Node* control = NodeProperties::GetControlInput(node); 6430 6431 // A word of warning to begin with: This whole method might look a bit 6432 // strange at times, but that's mostly because it was carefully handcrafted 6433 // to allow for full escape analysis and scalar replacement of both the 6434 // collection iterator object and the iterator results, including the 6435 // key-value arrays in case of Set/Map entry iteration. 6436 // 6437 // TODO(turbofan): Currently the escape analysis (and the store-load 6438 // forwarding) is unable to eliminate the allocations for the key-value 6439 // arrays in case of Set/Map entry iteration, and we should investigate 6440 // how to update the escape analysis / arrange the graph in a way that 6441 // this becomes possible. 6442 6443 // Infer the {receiver} instance type. 6444 InstanceType receiver_instance_type; 6445 ZoneHandleSet<Map> receiver_maps; 6446 NodeProperties::InferReceiverMapsResult result = 6447 NodeProperties::InferReceiverMaps(isolate(), receiver, effect, 6448 &receiver_maps); 6449 if (result == NodeProperties::kNoReceiverMaps) return NoChange(); 6450 DCHECK_NE(0, receiver_maps.size()); 6451 receiver_instance_type = receiver_maps[0]->instance_type(); 6452 for (size_t i = 1; i < receiver_maps.size(); ++i) { 6453 if (receiver_maps[i]->instance_type() != receiver_instance_type) { 6454 return NoChange(); 6455 } 6456 } 6457 if (receiver_instance_type < collection_iterator_instance_type_first || 6458 receiver_instance_type > collection_iterator_instance_type_last) { 6459 return NoChange(); 6460 } 6461 6462 // Transition the JSCollectionIterator {receiver} if necessary 6463 // (i.e. there were certain mutations while we're iterating). 6464 { 6465 Node* done_loop; 6466 Node* done_eloop; 6467 Node* loop = control = 6468 graph()->NewNode(common()->Loop(2), control, control); 6469 Node* eloop = effect = 6470 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); 6471 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); 6472 NodeProperties::MergeControlToEnd(graph(), common(), terminate); 6473 6474 // Check if reached the final table of the {receiver}. 6475 Node* table = effect = graph()->NewNode( 6476 simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()), 6477 receiver, effect, control); 6478 Node* next_table = effect = 6479 graph()->NewNode(simplified()->LoadField( 6480 AccessBuilder::ForOrderedHashTableBaseNextTable()), 6481 table, effect, control); 6482 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), next_table); 6483 control = 6484 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); 6485 6486 // Abort the {loop} when we reach the final table. 6487 done_loop = graph()->NewNode(common()->IfTrue(), control); 6488 done_eloop = effect; 6489 6490 // Migrate to the {next_table} otherwise. 6491 control = graph()->NewNode(common()->IfFalse(), control); 6492 6493 // Self-heal the {receiver}s index. 6494 Node* index = effect = graph()->NewNode( 6495 simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()), 6496 receiver, effect, control); 6497 Callable const callable = 6498 Builtins::CallableFor(isolate(), Builtins::kOrderedHashTableHealIndex); 6499 auto call_descriptor = Linkage::GetStubCallDescriptor( 6500 graph()->zone(), callable.descriptor(), 0, CallDescriptor::kNoFlags, 6501 Operator::kEliminatable); 6502 index = effect = 6503 graph()->NewNode(common()->Call(call_descriptor), 6504 jsgraph()->HeapConstant(callable.code()), table, index, 6505 jsgraph()->NoContextConstant(), effect); 6506 6507 index = effect = graph()->NewNode( 6508 common()->TypeGuard(TypeCache::Get().kFixedArrayLengthType), index, 6509 effect, control); 6510 6511 // Update the {index} and {table} on the {receiver}. 6512 effect = graph()->NewNode( 6513 simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorIndex()), 6514 receiver, index, effect, control); 6515 effect = graph()->NewNode( 6516 simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorTable()), 6517 receiver, next_table, effect, control); 6518 6519 // Tie the knot. 6520 loop->ReplaceInput(1, control); 6521 eloop->ReplaceInput(1, effect); 6522 6523 control = done_loop; 6524 effect = done_eloop; 6525 } 6526 6527 // Get current index and table from the JSCollectionIterator {receiver}. 6528 Node* index = effect = graph()->NewNode( 6529 simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()), 6530 receiver, effect, control); 6531 Node* table = effect = graph()->NewNode( 6532 simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()), 6533 receiver, effect, control); 6534 6535 // Create the {JSIteratorResult} first to ensure that we always have 6536 // a dominating Allocate node for the allocation folding phase. 6537 Node* iterator_result = effect = graph()->NewNode( 6538 javascript()->CreateIterResultObject(), jsgraph()->UndefinedConstant(), 6539 jsgraph()->TrueConstant(), context, effect); 6540 6541 // Look for the next non-holey key, starting from {index} in the {table}. 6542 Node* controls[2]; 6543 Node* effects[3]; 6544 { 6545 // Compute the currently used capacity. 6546 Node* number_of_buckets = effect = graph()->NewNode( 6547 simplified()->LoadField( 6548 AccessBuilder::ForOrderedHashTableBaseNumberOfBuckets()), 6549 table, effect, control); 6550 Node* number_of_elements = effect = graph()->NewNode( 6551 simplified()->LoadField( 6552 AccessBuilder::ForOrderedHashTableBaseNumberOfElements()), 6553 table, effect, control); 6554 Node* number_of_deleted_elements = effect = graph()->NewNode( 6555 simplified()->LoadField( 6556 AccessBuilder::ForOrderedHashTableBaseNumberOfDeletedElements()), 6557 table, effect, control); 6558 Node* used_capacity = 6559 graph()->NewNode(simplified()->NumberAdd(), number_of_elements, 6560 number_of_deleted_elements); 6561 6562 // Skip holes and update the {index}. 6563 Node* loop = graph()->NewNode(common()->Loop(2), control, control); 6564 Node* eloop = 6565 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); 6566 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); 6567 NodeProperties::MergeControlToEnd(graph(), common(), terminate); 6568 Node* iloop = graph()->NewNode( 6569 common()->Phi(MachineRepresentation::kTagged, 2), index, index, loop); 6570 6571 Node* index = effect = graph()->NewNode( 6572 common()->TypeGuard(TypeCache::Get().kFixedArrayLengthType), iloop, 6573 eloop, control); 6574 { 6575 Node* check0 = graph()->NewNode(simplified()->NumberLessThan(), index, 6576 used_capacity); 6577 Node* branch0 = 6578 graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, loop); 6579 6580 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); 6581 Node* efalse0 = effect; 6582 { 6583 // Mark the {receiver} as exhausted. 6584 efalse0 = graph()->NewNode( 6585 simplified()->StoreField( 6586 AccessBuilder::ForJSCollectionIteratorTable()), 6587 receiver, jsgraph()->HeapConstant(empty_collection), efalse0, 6588 if_false0); 6589 6590 controls[0] = if_false0; 6591 effects[0] = efalse0; 6592 } 6593 6594 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); 6595 Node* etrue0 = effect; 6596 { 6597 // Load the key of the entry. 6598 Node* entry_start_position = graph()->NewNode( 6599 simplified()->NumberAdd(), 6600 graph()->NewNode( 6601 simplified()->NumberAdd(), 6602 graph()->NewNode(simplified()->NumberMultiply(), index, 6603 jsgraph()->Constant(entry_size)), 6604 number_of_buckets), 6605 jsgraph()->Constant(OrderedHashTableBase::kHashTableStartIndex)); 6606 Node* entry_key = etrue0 = graph()->NewNode( 6607 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()), 6608 table, entry_start_position, etrue0, if_true0); 6609 6610 // Advance the index. 6611 index = graph()->NewNode(simplified()->NumberAdd(), index, 6612 jsgraph()->OneConstant()); 6613 6614 Node* check1 = 6615 graph()->NewNode(simplified()->ReferenceEqual(), entry_key, 6616 jsgraph()->TheHoleConstant()); 6617 Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), 6618 check1, if_true0); 6619 6620 { 6621 // Abort loop with resulting value. 6622 Node* control = graph()->NewNode(common()->IfFalse(), branch1); 6623 Node* effect = etrue0; 6624 Node* value = effect = 6625 graph()->NewNode(common()->TypeGuard(Type::NonInternal()), 6626 entry_key, effect, control); 6627 Node* done = jsgraph()->FalseConstant(); 6628 6629 // Advance the index on the {receiver}. 6630 effect = graph()->NewNode( 6631 simplified()->StoreField( 6632 AccessBuilder::ForJSCollectionIteratorIndex()), 6633 receiver, index, effect, control); 6634 6635 // The actual {value} depends on the {receiver} iteration type. 6636 switch (receiver_instance_type) { 6637 case JS_MAP_KEY_ITERATOR_TYPE: 6638 case JS_SET_VALUE_ITERATOR_TYPE: 6639 break; 6640 6641 case JS_SET_KEY_VALUE_ITERATOR_TYPE: 6642 value = effect = 6643 graph()->NewNode(javascript()->CreateKeyValueArray(), value, 6644 value, context, effect); 6645 break; 6646 6647 case JS_MAP_VALUE_ITERATOR_TYPE: 6648 value = effect = graph()->NewNode( 6649 simplified()->LoadElement( 6650 AccessBuilder::ForFixedArrayElement()), 6651 table, 6652 graph()->NewNode( 6653 simplified()->NumberAdd(), entry_start_position, 6654 jsgraph()->Constant(OrderedHashMap::kValueOffset)), 6655 effect, control); 6656 break; 6657 6658 case JS_MAP_KEY_VALUE_ITERATOR_TYPE: 6659 value = effect = graph()->NewNode( 6660 simplified()->LoadElement( 6661 AccessBuilder::ForFixedArrayElement()), 6662 table, 6663 graph()->NewNode( 6664 simplified()->NumberAdd(), entry_start_position, 6665 jsgraph()->Constant(OrderedHashMap::kValueOffset)), 6666 effect, control); 6667 value = effect = 6668 graph()->NewNode(javascript()->CreateKeyValueArray(), 6669 entry_key, value, context, effect); 6670 break; 6671 6672 default: 6673 UNREACHABLE(); 6674 break; 6675 } 6676 6677 // Store final {value} and {done} into the {iterator_result}. 6678 effect = 6679 graph()->NewNode(simplified()->StoreField( 6680 AccessBuilder::ForJSIteratorResultValue()), 6681 iterator_result, value, effect, control); 6682 effect = 6683 graph()->NewNode(simplified()->StoreField( 6684 AccessBuilder::ForJSIteratorResultDone()), 6685 iterator_result, done, effect, control); 6686 6687 controls[1] = control; 6688 effects[1] = effect; 6689 } 6690 6691 // Continue with next loop index. 6692 loop->ReplaceInput(1, graph()->NewNode(common()->IfTrue(), branch1)); 6693 eloop->ReplaceInput(1, etrue0); 6694 iloop->ReplaceInput(1, index); 6695 } 6696 } 6697 6698 control = effects[2] = graph()->NewNode(common()->Merge(2), 2, controls); 6699 effect = graph()->NewNode(common()->EffectPhi(2), 3, effects); 6700 } 6701 6702 // Yield the final {iterator_result}. 6703 ReplaceWithValue(node, iterator_result, effect, control); 6704 return Replace(iterator_result); 6705 } 6706 6707 Reduction JSCallReducer::ReduceArrayBufferIsView(Node* node) { 6708 Node* value = node->op()->ValueInputCount() >= 3 6709 ? NodeProperties::GetValueInput(node, 2) 6710 : jsgraph()->UndefinedConstant(); 6711 RelaxEffectsAndControls(node); 6712 node->ReplaceInput(0, value); 6713 node->TrimInputCount(1); 6714 NodeProperties::ChangeOp(node, simplified()->ObjectIsArrayBufferView()); 6715 return Changed(node); 6716 } 6717 6718 Reduction JSCallReducer::ReduceArrayBufferViewAccessor( 6719 Node* node, InstanceType instance_type, FieldAccess const& access) { 6720 Node* receiver = NodeProperties::GetValueInput(node, 1); 6721 Node* effect = NodeProperties::GetEffectInput(node); 6722 Node* control = NodeProperties::GetControlInput(node); 6723 if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect, 6724 instance_type)) { 6725 // Load the {receiver}s field. 6726 Node* value = effect = graph()->NewNode(simplified()->LoadField(access), 6727 receiver, effect, control); 6728 6729 // See if we can skip the neutering check. 6730 if (isolate()->IsArrayBufferNeuteringIntact()) { 6731 // Add a code dependency so we are deoptimized in case an ArrayBuffer 6732 // gets neutered. 6733 dependencies()->DependOnProtector(PropertyCellRef( 6734 js_heap_broker(), factory()->array_buffer_neutering_protector())); 6735 } else { 6736 // Check if the {receiver}s buffer was neutered. 6737 Node* buffer = effect = graph()->NewNode( 6738 simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), 6739 receiver, effect, control); 6740 Node* check = effect = graph()->NewNode( 6741 simplified()->ArrayBufferWasNeutered(), buffer, effect, control); 6742 6743 // Default to zero if the {receiver}s buffer was neutered. 6744 value = graph()->NewNode( 6745 common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse), 6746 check, jsgraph()->ZeroConstant(), value); 6747 } 6748 6749 ReplaceWithValue(node, value, effect, control); 6750 return Replace(value); 6751 } 6752 return NoChange(); 6753 } 6754 6755 namespace { 6756 uint32_t ExternalArrayElementSize(const ExternalArrayType element_type) { 6757 switch (element_type) { 6758 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \ 6759 case kExternal##Type##Array: \ 6760 DCHECK_LE(sizeof(ctype), 8); \ 6761 return sizeof(ctype); 6762 TYPED_ARRAYS(TYPED_ARRAY_CASE) 6763 default: 6764 UNREACHABLE(); 6765 #undef TYPED_ARRAY_CASE 6766 } 6767 } 6768 } // namespace 6769 6770 Reduction JSCallReducer::ReduceDataViewPrototypeGet( 6771 Node* node, ExternalArrayType element_type) { 6772 uint32_t const element_size = ExternalArrayElementSize(element_type); 6773 CallParameters const& p = CallParametersOf(node->op()); 6774 Node* effect = NodeProperties::GetEffectInput(node); 6775 Node* control = NodeProperties::GetControlInput(node); 6776 Node* receiver = NodeProperties::GetValueInput(node, 1); 6777 6778 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6779 return NoChange(); 6780 } 6781 6782 Node* offset = node->op()->ValueInputCount() > 2 6783 ? NodeProperties::GetValueInput(node, 2) 6784 : jsgraph()->ZeroConstant(); 6785 6786 Node* is_little_endian = node->op()->ValueInputCount() > 3 6787 ? NodeProperties::GetValueInput(node, 3) 6788 : jsgraph()->FalseConstant(); 6789 6790 // Only do stuff if the {receiver} is really a DataView. 6791 if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect, 6792 JS_DATA_VIEW_TYPE)) { 6793 // Check that the {offset} is within range for the {receiver}. 6794 HeapObjectMatcher m(receiver); 6795 if (m.HasValue()) { 6796 // We only deal with DataViews here whose [[ByteLength]] is at least 6797 // {element_size} and less than 2^31-{element_size}. 6798 Handle<JSDataView> dataview = Handle<JSDataView>::cast(m.Value()); 6799 if (dataview->byte_length()->Number() < element_size || 6800 dataview->byte_length()->Number() - element_size > kMaxInt) { 6801 return NoChange(); 6802 } 6803 6804 // The {receiver}s [[ByteOffset]] must be within Unsigned31 range. 6805 if (dataview->byte_offset()->Number() > kMaxInt) { 6806 return NoChange(); 6807 } 6808 6809 // Check that the {offset} is within range of the {byte_length}. 6810 Node* byte_length = jsgraph()->Constant( 6811 dataview->byte_length()->Number() - (element_size - 1)); 6812 offset = effect = 6813 graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset, 6814 byte_length, effect, control); 6815 6816 // Add the [[ByteOffset]] to compute the effective offset. 6817 Node* byte_offset = 6818 jsgraph()->Constant(dataview->byte_offset()->Number()); 6819 offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset); 6820 } else { 6821 // We only deal with DataViews here that have Smi [[ByteLength]]s. 6822 Node* byte_length = effect = 6823 graph()->NewNode(simplified()->LoadField( 6824 AccessBuilder::ForJSArrayBufferViewByteLength()), 6825 receiver, effect, control); 6826 byte_length = effect = graph()->NewNode( 6827 simplified()->CheckSmi(p.feedback()), byte_length, effect, control); 6828 6829 // Check that the {offset} is within range of the {byte_length}. 6830 offset = effect = 6831 graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset, 6832 byte_length, effect, control); 6833 6834 if (element_size > 0) { 6835 // For non-byte accesses we also need to check that the {offset} 6836 // plus the {element_size}-1 fits within the given {byte_length}. 6837 Node* end_offset = 6838 graph()->NewNode(simplified()->NumberAdd(), offset, 6839 jsgraph()->Constant(element_size - 1)); 6840 effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), 6841 end_offset, byte_length, effect, control); 6842 } 6843 6844 // The {receiver}s [[ByteOffset]] also needs to be a (positive) Smi. 6845 Node* byte_offset = effect = 6846 graph()->NewNode(simplified()->LoadField( 6847 AccessBuilder::ForJSArrayBufferViewByteOffset()), 6848 receiver, effect, control); 6849 byte_offset = effect = graph()->NewNode( 6850 simplified()->CheckSmi(p.feedback()), byte_offset, effect, control); 6851 6852 // Compute the buffer index at which we'll read. 6853 offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset); 6854 } 6855 6856 // Coerce {is_little_endian} to boolean. 6857 is_little_endian = 6858 graph()->NewNode(simplified()->ToBoolean(), is_little_endian); 6859 6860 // Get the underlying buffer and check that it has not been neutered. 6861 Node* buffer = effect = graph()->NewNode( 6862 simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), 6863 receiver, effect, control); 6864 6865 if (isolate()->IsArrayBufferNeuteringIntact()) { 6866 // Add a code dependency so we are deoptimized in case an ArrayBuffer 6867 // gets neutered. 6868 dependencies()->DependOnProtector(PropertyCellRef( 6869 js_heap_broker(), factory()->array_buffer_neutering_protector())); 6870 } else { 6871 // If the buffer was neutered, deopt and let the unoptimized code throw. 6872 Node* check_neutered = effect = graph()->NewNode( 6873 simplified()->ArrayBufferWasNeutered(), buffer, effect, control); 6874 check_neutered = 6875 graph()->NewNode(simplified()->BooleanNot(), check_neutered); 6876 effect = graph()->NewNode( 6877 simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasNeutered, 6878 p.feedback()), 6879 check_neutered, effect, control); 6880 } 6881 6882 // Get the buffer's backing store. 6883 Node* backing_store = effect = graph()->NewNode( 6884 simplified()->LoadField(AccessBuilder::ForJSArrayBufferBackingStore()), 6885 buffer, effect, control); 6886 6887 // Perform the load. 6888 Node* value = effect = graph()->NewNode( 6889 simplified()->LoadDataViewElement(element_type), buffer, backing_store, 6890 offset, is_little_endian, effect, control); 6891 6892 // Continue on the regular path. 6893 ReplaceWithValue(node, value, effect, control); 6894 return Changed(value); 6895 } 6896 6897 return NoChange(); 6898 } 6899 6900 Reduction JSCallReducer::ReduceDataViewPrototypeSet( 6901 Node* node, ExternalArrayType element_type) { 6902 uint32_t const element_size = ExternalArrayElementSize(element_type); 6903 CallParameters const& p = CallParametersOf(node->op()); 6904 Node* effect = NodeProperties::GetEffectInput(node); 6905 Node* control = NodeProperties::GetControlInput(node); 6906 Node* receiver = NodeProperties::GetValueInput(node, 1); 6907 6908 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6909 return NoChange(); 6910 } 6911 6912 Node* offset = node->op()->ValueInputCount() > 2 6913 ? NodeProperties::GetValueInput(node, 2) 6914 : jsgraph()->ZeroConstant(); 6915 6916 Node* value = node->op()->ValueInputCount() > 3 6917 ? NodeProperties::GetValueInput(node, 3) 6918 : jsgraph()->ZeroConstant(); 6919 6920 Node* is_little_endian = node->op()->ValueInputCount() > 4 6921 ? NodeProperties::GetValueInput(node, 4) 6922 : jsgraph()->FalseConstant(); 6923 6924 // Only do stuff if the {receiver} is really a DataView. 6925 if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect, 6926 JS_DATA_VIEW_TYPE)) { 6927 // Check that the {offset} is within range for the {receiver}. 6928 HeapObjectMatcher m(receiver); 6929 if (m.HasValue()) { 6930 // We only deal with DataViews here whose [[ByteLength]] is at least 6931 // {element_size} and less than 2^31-{element_size}. 6932 Handle<JSDataView> dataview = Handle<JSDataView>::cast(m.Value()); 6933 if (dataview->byte_length()->Number() < element_size || 6934 dataview->byte_length()->Number() - element_size > kMaxInt) { 6935 return NoChange(); 6936 } 6937 6938 // The {receiver}s [[ByteOffset]] must be within Unsigned31 range. 6939 if (dataview->byte_offset()->Number() > kMaxInt) { 6940 return NoChange(); 6941 } 6942 6943 // Check that the {offset} is within range of the {byte_length}. 6944 Node* byte_length = jsgraph()->Constant( 6945 dataview->byte_length()->Number() - (element_size - 1)); 6946 offset = effect = 6947 graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset, 6948 byte_length, effect, control); 6949 6950 // Add the [[ByteOffset]] to compute the effective offset. 6951 Node* byte_offset = 6952 jsgraph()->Constant(dataview->byte_offset()->Number()); 6953 offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset); 6954 } else { 6955 // We only deal with DataViews here that have Smi [[ByteLength]]s. 6956 Node* byte_length = effect = 6957 graph()->NewNode(simplified()->LoadField( 6958 AccessBuilder::ForJSArrayBufferViewByteLength()), 6959 receiver, effect, control); 6960 byte_length = effect = graph()->NewNode( 6961 simplified()->CheckSmi(p.feedback()), byte_length, effect, control); 6962 6963 // Check that the {offset} is within range of the {byte_length}. 6964 offset = effect = 6965 graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset, 6966 byte_length, effect, control); 6967 6968 if (element_size > 0) { 6969 // For non-byte accesses we also need to check that the {offset} 6970 // plus the {element_size}-1 fits within the given {byte_length}. 6971 Node* end_offset = 6972 graph()->NewNode(simplified()->NumberAdd(), offset, 6973 jsgraph()->Constant(element_size - 1)); 6974 effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), 6975 end_offset, byte_length, effect, control); 6976 } 6977 6978 // The {receiver}s [[ByteOffset]] also needs to be a (positive) Smi. 6979 Node* byte_offset = effect = 6980 graph()->NewNode(simplified()->LoadField( 6981 AccessBuilder::ForJSArrayBufferViewByteOffset()), 6982 receiver, effect, control); 6983 byte_offset = effect = graph()->NewNode( 6984 simplified()->CheckSmi(p.feedback()), byte_offset, effect, control); 6985 6986 // Compute the buffer index at which we'll read. 6987 offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset); 6988 } 6989 6990 // Coerce {is_little_endian} to boolean. 6991 is_little_endian = 6992 graph()->NewNode(simplified()->ToBoolean(), is_little_endian); 6993 6994 // Coerce {value} to Number. 6995 value = effect = graph()->NewNode( 6996 simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball, 6997 p.feedback()), 6998 value, effect, control); 6999 7000 // Get the underlying buffer and check that it has not been neutered. 7001 Node* buffer = effect = graph()->NewNode( 7002 simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), 7003 receiver, effect, control); 7004 7005 if (isolate()->IsArrayBufferNeuteringIntact()) { 7006 // Add a code dependency so we are deoptimized in case an ArrayBuffer 7007 // gets neutered. 7008 dependencies()->DependOnProtector(PropertyCellRef( 7009 js_heap_broker(), factory()->array_buffer_neutering_protector())); 7010 } else { 7011 // If the buffer was neutered, deopt and let the unoptimized code throw. 7012 Node* check_neutered = effect = graph()->NewNode( 7013 simplified()->ArrayBufferWasNeutered(), buffer, effect, control); 7014 check_neutered = 7015 graph()->NewNode(simplified()->BooleanNot(), check_neutered); 7016 effect = graph()->NewNode( 7017 simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasNeutered, 7018 p.feedback()), 7019 check_neutered, effect, control); 7020 } 7021 7022 // Get the buffer's backing store. 7023 Node* backing_store = effect = graph()->NewNode( 7024 simplified()->LoadField(AccessBuilder::ForJSArrayBufferBackingStore()), 7025 buffer, effect, control); 7026 7027 // Perform the store. 7028 effect = graph()->NewNode(simplified()->StoreDataViewElement(element_type), 7029 buffer, backing_store, offset, value, 7030 is_little_endian, effect, control); 7031 7032 Node* value = jsgraph()->UndefinedConstant(); 7033 7034 // Continue on the regular path. 7035 ReplaceWithValue(node, value, effect, control); 7036 return Changed(value); 7037 } 7038 7039 return NoChange(); 7040 } 7041 7042 // ES6 section 18.2.2 isFinite ( number ) 7043 Reduction JSCallReducer::ReduceGlobalIsFinite(Node* node) { 7044 CallParameters const& p = CallParametersOf(node->op()); 7045 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 7046 return NoChange(); 7047 } 7048 if (node->op()->ValueInputCount() < 3) { 7049 Node* value = jsgraph()->FalseConstant(); 7050 ReplaceWithValue(node, value); 7051 return Replace(value); 7052 } 7053 7054 Node* effect = NodeProperties::GetEffectInput(node); 7055 Node* control = NodeProperties::GetControlInput(node); 7056 Node* input = NodeProperties::GetValueInput(node, 2); 7057 7058 input = effect = 7059 graph()->NewNode(simplified()->SpeculativeToNumber( 7060 NumberOperationHint::kNumberOrOddball, p.feedback()), 7061 input, effect, control); 7062 Node* value = graph()->NewNode(simplified()->NumberIsFinite(), input); 7063 ReplaceWithValue(node, value, effect); 7064 return Replace(value); 7065 } 7066 7067 // ES6 section 18.2.3 isNaN ( number ) 7068 Reduction JSCallReducer::ReduceGlobalIsNaN(Node* node) { 7069 CallParameters const& p = CallParametersOf(node->op()); 7070 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 7071 return NoChange(); 7072 } 7073 if (node->op()->ValueInputCount() < 3) { 7074 Node* value = jsgraph()->TrueConstant(); 7075 ReplaceWithValue(node, value); 7076 return Replace(value); 7077 } 7078 7079 Node* effect = NodeProperties::GetEffectInput(node); 7080 Node* control = NodeProperties::GetControlInput(node); 7081 Node* input = NodeProperties::GetValueInput(node, 2); 7082 7083 input = effect = 7084 graph()->NewNode(simplified()->SpeculativeToNumber( 7085 NumberOperationHint::kNumberOrOddball, p.feedback()), 7086 input, effect, control); 7087 Node* value = graph()->NewNode(simplified()->NumberIsNaN(), input); 7088 ReplaceWithValue(node, value, effect); 7089 return Replace(value); 7090 } 7091 7092 // ES6 section 20.3.4.10 Date.prototype.getTime ( ) 7093 Reduction JSCallReducer::ReduceDatePrototypeGetTime(Node* node) { 7094 Node* receiver = NodeProperties::GetValueInput(node, 1); 7095 Node* effect = NodeProperties::GetEffectInput(node); 7096 Node* control = NodeProperties::GetControlInput(node); 7097 if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect, 7098 JS_DATE_TYPE)) { 7099 Node* value = effect = graph()->NewNode( 7100 simplified()->LoadField(AccessBuilder::ForJSDateValue()), receiver, 7101 effect, control); 7102 ReplaceWithValue(node, value, effect, control); 7103 return Replace(value); 7104 } 7105 return NoChange(); 7106 } 7107 7108 // ES6 section 20.3.3.1 Date.now ( ) 7109 Reduction JSCallReducer::ReduceDateNow(Node* node) { 7110 Node* effect = NodeProperties::GetEffectInput(node); 7111 Node* control = NodeProperties::GetControlInput(node); 7112 Node* value = effect = 7113 graph()->NewNode(simplified()->DateNow(), effect, control); 7114 ReplaceWithValue(node, value, effect, control); 7115 return Replace(value); 7116 } 7117 7118 // ES6 section 20.1.2.13 Number.parseInt ( string, radix ) 7119 Reduction JSCallReducer::ReduceNumberParseInt(Node* node) { 7120 // We certainly know that undefined is not an array. 7121 if (node->op()->ValueInputCount() < 3) { 7122 Node* value = jsgraph()->NaNConstant(); 7123 ReplaceWithValue(node, value); 7124 return Replace(value); 7125 } 7126 7127 int arg_count = node->op()->ValueInputCount(); 7128 Node* effect = NodeProperties::GetEffectInput(node); 7129 Node* control = NodeProperties::GetControlInput(node); 7130 Node* context = NodeProperties::GetContextInput(node); 7131 Node* frame_state = NodeProperties::GetFrameStateInput(node); 7132 Node* object = NodeProperties::GetValueInput(node, 2); 7133 Node* radix = arg_count >= 4 ? NodeProperties::GetValueInput(node, 3) 7134 : jsgraph()->UndefinedConstant(); 7135 node->ReplaceInput(0, object); 7136 node->ReplaceInput(1, radix); 7137 node->ReplaceInput(2, context); 7138 node->ReplaceInput(3, frame_state); 7139 node->ReplaceInput(4, effect); 7140 node->ReplaceInput(5, control); 7141 node->TrimInputCount(6); 7142 NodeProperties::ChangeOp(node, javascript()->ParseInt()); 7143 return Changed(node); 7144 } 7145 7146 Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) { 7147 if (FLAG_force_slow_path) return NoChange(); 7148 if (node->op()->ValueInputCount() < 3) return NoChange(); 7149 CallParameters const& p = CallParametersOf(node->op()); 7150 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 7151 return NoChange(); 7152 } 7153 7154 Node* effect = NodeProperties::GetEffectInput(node); 7155 Node* control = NodeProperties::GetControlInput(node); 7156 Node* regexp = NodeProperties::GetValueInput(node, 1); 7157 7158 // Check if we know something about the {regexp}. 7159 ZoneHandleSet<Map> regexp_maps; 7160 NodeProperties::InferReceiverMapsResult result = 7161 NodeProperties::InferReceiverMaps(isolate(), regexp, effect, 7162 ®exp_maps); 7163 7164 bool need_map_check = false; 7165 switch (result) { 7166 case NodeProperties::kNoReceiverMaps: 7167 return NoChange(); 7168 case NodeProperties::kUnreliableReceiverMaps: 7169 need_map_check = true; 7170 break; 7171 case NodeProperties::kReliableReceiverMaps: 7172 break; 7173 } 7174 7175 for (auto map : regexp_maps) { 7176 if (map->instance_type() != JS_REGEXP_TYPE) return NoChange(); 7177 } 7178 7179 // Compute property access info for "exec" on {resolution}. 7180 PropertyAccessInfo ai_exec; 7181 AccessInfoFactory access_info_factory(js_heap_broker(), dependencies(), 7182 native_context(), graph()->zone()); 7183 if (!access_info_factory.ComputePropertyAccessInfo( 7184 MapHandles(regexp_maps.begin(), regexp_maps.end()), 7185 factory()->exec_string(), AccessMode::kLoad, &ai_exec)) { 7186 return NoChange(); 7187 } 7188 // If "exec" has been modified on {regexp}, we can't do anything. 7189 if (!ai_exec.IsDataConstant()) return NoChange(); 7190 Handle<Object> exec_on_proto = ai_exec.constant(); 7191 if (*exec_on_proto != *isolate()->regexp_exec_function()) return NoChange(); 7192 7193 PropertyAccessBuilder access_builder(jsgraph(), js_heap_broker(), 7194 dependencies()); 7195 7196 // Add proper dependencies on the {regexp}s [[Prototype]]s. 7197 Handle<JSObject> holder; 7198 if (ai_exec.holder().ToHandle(&holder)) { 7199 dependencies()->DependOnStablePrototypeChains( 7200 js_heap_broker(), native_context(), ai_exec.receiver_maps(), holder); 7201 } 7202 7203 if (need_map_check) { 7204 effect = 7205 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, 7206 regexp_maps, p.feedback()), 7207 regexp, effect, control); 7208 } 7209 7210 Node* context = NodeProperties::GetContextInput(node); 7211 Node* frame_state = NodeProperties::GetFrameStateInput(node); 7212 Node* search = NodeProperties::GetValueInput(node, 2); 7213 Node* search_string = effect = graph()->NewNode( 7214 simplified()->CheckString(p.feedback()), search, effect, control); 7215 7216 Node* lastIndex = effect = graph()->NewNode( 7217 simplified()->LoadField(AccessBuilder::ForJSRegExpLastIndex()), regexp, 7218 effect, control); 7219 7220 Node* lastIndexSmi = effect = graph()->NewNode( 7221 simplified()->CheckSmi(p.feedback()), lastIndex, effect, control); 7222 7223 Node* is_positive = graph()->NewNode(simplified()->NumberLessThanOrEqual(), 7224 jsgraph()->ZeroConstant(), lastIndexSmi); 7225 7226 effect = graph()->NewNode( 7227 simplified()->CheckIf(DeoptimizeReason::kNotASmi, p.feedback()), 7228 is_positive, effect, control); 7229 7230 node->ReplaceInput(0, regexp); 7231 node->ReplaceInput(1, search_string); 7232 node->ReplaceInput(2, context); 7233 node->ReplaceInput(3, frame_state); 7234 node->ReplaceInput(4, effect); 7235 node->ReplaceInput(5, control); 7236 node->TrimInputCount(6); 7237 NodeProperties::ChangeOp(node, javascript()->RegExpTest()); 7238 return Changed(node); 7239 } 7240 7241 // ES section #sec-number-constructor 7242 Reduction JSCallReducer::ReduceNumberConstructor(Node* node) { 7243 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 7244 CallParameters const& p = CallParametersOf(node->op()); 7245 7246 if (p.arity() <= 2) { 7247 ReplaceWithValue(node, jsgraph()->ZeroConstant()); 7248 } 7249 7250 // We don't have a new.target argument, so we can convert to number, 7251 // but must also convert BigInts. 7252 if (p.arity() == 3) { 7253 Node* target = NodeProperties::GetValueInput(node, 0); 7254 Node* context = NodeProperties::GetContextInput(node); 7255 Node* value = NodeProperties::GetValueInput(node, 2); 7256 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); 7257 Handle<SharedFunctionInfo> number_constructor( 7258 handle(native_context()->number_function()->shared(), isolate())); 7259 7260 const std::vector<Node*> checkpoint_parameters({ 7261 jsgraph()->UndefinedConstant(), /* receiver */ 7262 }); 7263 int checkpoint_parameters_size = 7264 static_cast<int>(checkpoint_parameters.size()); 7265 7266 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( 7267 jsgraph(), number_constructor, 7268 Builtins::kGenericConstructorLazyDeoptContinuation, target, context, 7269 checkpoint_parameters.data(), checkpoint_parameters_size, 7270 outer_frame_state, ContinuationFrameStateMode::LAZY); 7271 7272 NodeProperties::ReplaceValueInputs(node, value); 7273 NodeProperties::ChangeOp(node, javascript()->ToNumberConvertBigInt()); 7274 NodeProperties::ReplaceFrameStateInput(node, frame_state); 7275 return Changed(node); 7276 } 7277 return NoChange(); 7278 } 7279 7280 Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } 7281 7282 Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } 7283 7284 Factory* JSCallReducer::factory() const { return isolate()->factory(); } 7285 7286 Handle<JSGlobalProxy> JSCallReducer::global_proxy() const { 7287 return handle(JSGlobalProxy::cast(native_context()->global_proxy()), 7288 isolate()); 7289 } 7290 7291 CommonOperatorBuilder* JSCallReducer::common() const { 7292 return jsgraph()->common(); 7293 } 7294 7295 JSOperatorBuilder* JSCallReducer::javascript() const { 7296 return jsgraph()->javascript(); 7297 } 7298 7299 SimplifiedOperatorBuilder* JSCallReducer::simplified() const { 7300 return jsgraph()->simplified(); 7301 } 7302 7303 } // namespace compiler 7304 } // namespace internal 7305 } // namespace v8 7306