| OLD | NEW |
| 1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/compiler/js-native-context-specialization.h" | 5 #include "src/compiler/js-native-context-specialization.h" |
| 6 | 6 |
| 7 #include "src/accessors.h" | 7 #include "src/accessors.h" |
| 8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
| 9 #include "src/compilation-dependencies.h" | 9 #include "src/compilation-dependencies.h" |
| 10 #include "src/compiler/access-builder.h" | 10 #include "src/compiler/access-builder.h" |
| (...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 267 } | 267 } |
| 268 | 268 |
| 269 return NoChange(); | 269 return NoChange(); |
| 270 } | 270 } |
| 271 | 271 |
| 272 Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance( | 272 Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance( |
| 273 Node* node) { | 273 Node* node) { |
| 274 DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode()); | 274 DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode()); |
| 275 Node* constructor = NodeProperties::GetValueInput(node, 0); | 275 Node* constructor = NodeProperties::GetValueInput(node, 0); |
| 276 Node* object = NodeProperties::GetValueInput(node, 1); | 276 Node* object = NodeProperties::GetValueInput(node, 1); |
| 277 Node* context = NodeProperties::GetContextInput(node); | |
| 278 Node* frame_state = NodeProperties::GetFrameStateInput(node); | |
| 279 Node* effect = NodeProperties::GetEffectInput(node); | |
| 280 Node* control = NodeProperties::GetControlInput(node); | |
| 281 | 277 |
| 282 // Check if the {constructor} is known at compile time. | 278 // Check if the {constructor} is known at compile time. |
| 283 HeapObjectMatcher m(constructor); | 279 HeapObjectMatcher m(constructor); |
| 284 if (!m.HasValue()) return NoChange(); | 280 if (!m.HasValue()) return NoChange(); |
| 285 | 281 |
| 286 // Check if the {constructor} is a JSBoundFunction. | 282 // Check if the {constructor} is a JSBoundFunction. |
| 287 if (m.Value()->IsJSBoundFunction()) { | 283 if (m.Value()->IsJSBoundFunction()) { |
| 288 // OrdinaryHasInstance on bound functions turns into a recursive | 284 // OrdinaryHasInstance on bound functions turns into a recursive |
| 289 // invocation of the instanceof operator again. | 285 // invocation of the instanceof operator again. |
| 290 // ES6 section 7.3.19 OrdinaryHasInstance (C, O) step 2. | 286 // ES6 section 7.3.19 OrdinaryHasInstance (C, O) step 2. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 304 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); | 300 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); |
| 305 if (function->IsConstructor() && function->has_instance_prototype() && | 301 if (function->IsConstructor() && function->has_instance_prototype() && |
| 306 function->prototype()->IsJSReceiver()) { | 302 function->prototype()->IsJSReceiver()) { |
| 307 // Ensure that the {function} has a valid initial map, so we can | 303 // Ensure that the {function} has a valid initial map, so we can |
| 308 // depend on that for the prototype constant-folding below. | 304 // depend on that for the prototype constant-folding below. |
| 309 JSFunction::EnsureHasInitialMap(function); | 305 JSFunction::EnsureHasInitialMap(function); |
| 310 | 306 |
| 311 // Install a code dependency on the {function}s initial map. | 307 // Install a code dependency on the {function}s initial map. |
| 312 Handle<Map> initial_map(function->initial_map(), isolate()); | 308 Handle<Map> initial_map(function->initial_map(), isolate()); |
| 313 dependencies()->AssumeInitialMapCantChange(initial_map); | 309 dependencies()->AssumeInitialMapCantChange(initial_map); |
| 314 Handle<JSReceiver> function_prototype = | 310 Node* prototype = |
| 315 handle(JSReceiver::cast(initial_map->prototype()), isolate()); | 311 jsgraph()->Constant(handle(initial_map->prototype(), isolate())); |
| 316 | 312 |
| 317 // Check if we can constant-fold the prototype chain walk | 313 // Lower the {node} to JSHasInPrototypeChain. |
| 318 // for the given {object} and the {function_prototype}. | 314 NodeProperties::ReplaceValueInput(node, object, 0); |
| 319 InferHasInPrototypeChainResult result = | 315 NodeProperties::ReplaceValueInput(node, prototype, 1); |
| 320 InferHasInPrototypeChain(object, effect, function_prototype); | 316 NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain()); |
| 321 if (result != kMayBeInPrototypeChain) { | |
| 322 Node* value = jsgraph()->BooleanConstant(result == kIsInPrototypeChain); | |
| 323 ReplaceWithValue(node, value, effect, control); | |
| 324 return Replace(value); | |
| 325 } | |
| 326 | |
| 327 Node* prototype = jsgraph()->Constant(function_prototype); | |
| 328 | |
| 329 Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), object); | |
| 330 Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kFalse), | |
| 331 check0, control); | |
| 332 | |
| 333 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); | |
| 334 Node* etrue0 = effect; | |
| 335 Node* vtrue0 = jsgraph()->FalseConstant(); | |
| 336 | |
| 337 control = graph()->NewNode(common()->IfFalse(), branch0); | |
| 338 | |
| 339 // Loop through the {object}s prototype chain looking for the {prototype}. | |
| 340 Node* loop = control = | |
| 341 graph()->NewNode(common()->Loop(2), control, control); | |
| 342 Node* eloop = effect = | |
| 343 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); | |
| 344 Node* vloop = object = | |
| 345 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | |
| 346 object, object, loop); | |
| 347 | |
| 348 // Load the {object} map and instance type. | |
| 349 Node* object_map = effect = | |
| 350 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | |
| 351 object, effect, control); | |
| 352 Node* object_instance_type = effect = graph()->NewNode( | |
| 353 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), | |
| 354 object_map, effect, control); | |
| 355 | |
| 356 // Check if the {object} is a special receiver, because for special | |
| 357 // receivers, i.e. proxies or API objects that need access checks, | |
| 358 // we have to use the %HasInPrototypeChain runtime function instead. | |
| 359 Node* check1 = graph()->NewNode( | |
| 360 simplified()->NumberLessThanOrEqual(), object_instance_type, | |
| 361 jsgraph()->Constant(LAST_SPECIAL_RECEIVER_TYPE)); | |
| 362 Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), | |
| 363 check1, control); | |
| 364 | |
| 365 control = graph()->NewNode(common()->IfFalse(), branch1); | |
| 366 | |
| 367 Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); | |
| 368 Node* etrue1 = effect; | |
| 369 Node* vtrue1; | |
| 370 | |
| 371 // Check if the {object} is not a receiver at all. | |
| 372 Node* check10 = | |
| 373 graph()->NewNode(simplified()->NumberLessThan(), object_instance_type, | |
| 374 jsgraph()->Constant(FIRST_JS_RECEIVER_TYPE)); | |
| 375 Node* branch10 = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 376 check10, if_true1); | |
| 377 | |
| 378 // A primitive value cannot match the {prototype} we're looking for. | |
| 379 if_true1 = graph()->NewNode(common()->IfTrue(), branch10); | |
| 380 vtrue1 = jsgraph()->FalseConstant(); | |
| 381 | |
| 382 Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch10); | |
| 383 Node* efalse1 = etrue1; | |
| 384 Node* vfalse1; | |
| 385 { | |
| 386 // Slow path, need to call the %HasInPrototypeChain runtime function. | |
| 387 vfalse1 = efalse1 = if_false1 = graph()->NewNode( | |
| 388 javascript()->CallRuntime(Runtime::kHasInPrototypeChain), object, | |
| 389 prototype, context, frame_state, efalse1, if_false1); | |
| 390 | |
| 391 // Replace any potential {IfException} uses of {node} to catch | |
| 392 // exceptions from this %HasInPrototypeChain runtime call instead. | |
| 393 Node* on_exception = nullptr; | |
| 394 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { | |
| 395 NodeProperties::ReplaceControlInput(on_exception, vfalse1); | |
| 396 NodeProperties::ReplaceEffectInput(on_exception, efalse1); | |
| 397 if_false1 = graph()->NewNode(common()->IfSuccess(), vfalse1); | |
| 398 Revisit(on_exception); | |
| 399 } | |
| 400 } | |
| 401 | |
| 402 // Load the {object} prototype. | |
| 403 Node* object_prototype = effect = graph()->NewNode( | |
| 404 simplified()->LoadField(AccessBuilder::ForMapPrototype()), object_map, | |
| 405 effect, control); | |
| 406 | |
| 407 // Check if we reached the end of {object}s prototype chain. | |
| 408 Node* check2 = | |
| 409 graph()->NewNode(simplified()->ReferenceEqual(), object_prototype, | |
| 410 jsgraph()->NullConstant()); | |
| 411 Node* branch2 = graph()->NewNode(common()->Branch(), check2, control); | |
| 412 | |
| 413 Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2); | |
| 414 Node* etrue2 = effect; | |
| 415 Node* vtrue2 = jsgraph()->FalseConstant(); | |
| 416 | |
| 417 control = graph()->NewNode(common()->IfFalse(), branch2); | |
| 418 | |
| 419 // Check if we reached the {prototype}. | |
| 420 Node* check3 = graph()->NewNode(simplified()->ReferenceEqual(), | |
| 421 object_prototype, prototype); | |
| 422 Node* branch3 = graph()->NewNode(common()->Branch(), check3, control); | |
| 423 | |
| 424 Node* if_true3 = graph()->NewNode(common()->IfTrue(), branch3); | |
| 425 Node* etrue3 = effect; | |
| 426 Node* vtrue3 = jsgraph()->TrueConstant(); | |
| 427 | |
| 428 control = graph()->NewNode(common()->IfFalse(), branch3); | |
| 429 | |
| 430 // Close the loop. | |
| 431 vloop->ReplaceInput(1, object_prototype); | |
| 432 eloop->ReplaceInput(1, effect); | |
| 433 loop->ReplaceInput(1, control); | |
| 434 | |
| 435 control = graph()->NewNode(common()->Merge(5), if_true0, if_true1, | |
| 436 if_true2, if_true3, if_false1); | |
| 437 effect = graph()->NewNode(common()->EffectPhi(5), etrue0, etrue1, etrue2, | |
| 438 etrue3, efalse1, control); | |
| 439 | |
| 440 // Morph the {node} into an appropriate Phi. | |
| 441 ReplaceWithValue(node, node, effect, control); | |
| 442 node->ReplaceInput(0, vtrue0); | |
| 443 node->ReplaceInput(1, vtrue1); | |
| 444 node->ReplaceInput(2, vtrue2); | |
| 445 node->ReplaceInput(3, vtrue3); | |
| 446 node->ReplaceInput(4, vfalse1); | |
| 447 node->ReplaceInput(5, control); | |
| 448 node->TrimInputCount(6); | |
| 449 NodeProperties::ChangeOp( | |
| 450 node, common()->Phi(MachineRepresentation::kTagged, 5)); | |
| 451 return Changed(node); | 317 return Changed(node); |
| 452 } | 318 } |
| 453 } | 319 } |
| 454 | 320 |
| 455 return NoChange(); | 321 return NoChange(); |
| 456 } | 322 } |
| 457 | 323 |
| 458 Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) { | 324 Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) { |
| 459 DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode()); | 325 DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode()); |
| 460 ContextAccess const& access = ContextAccessOf(node->op()); | 326 ContextAccess const& access = ContextAccessOf(node->op()); |
| (...skipping 1977 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2438 // Install code dependencies on the prototype maps. | 2304 // Install code dependencies on the prototype maps. |
| 2439 for (Handle<Map> map : receiver_maps) { | 2305 for (Handle<Map> map : receiver_maps) { |
| 2440 dependencies()->AssumePrototypeMapsStable(map, initial_object_prototype); | 2306 dependencies()->AssumePrototypeMapsStable(map, initial_object_prototype); |
| 2441 } | 2307 } |
| 2442 | 2308 |
| 2443 // Install code dependency on the array protector cell. | 2309 // Install code dependency on the array protector cell. |
| 2444 dependencies()->AssumePropertyCell(factory()->array_protector()); | 2310 dependencies()->AssumePropertyCell(factory()->array_protector()); |
| 2445 return true; | 2311 return true; |
| 2446 } | 2312 } |
| 2447 | 2313 |
| 2448 JSNativeContextSpecialization::InferHasInPrototypeChainResult | |
| 2449 JSNativeContextSpecialization::InferHasInPrototypeChain( | |
| 2450 Node* receiver, Node* effect, Handle<JSReceiver> prototype) { | |
| 2451 ZoneHandleSet<Map> receiver_maps; | |
| 2452 NodeProperties::InferReceiverMapsResult result = | |
| 2453 NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps); | |
| 2454 if (result == NodeProperties::kNoReceiverMaps) return kMayBeInPrototypeChain; | |
| 2455 | |
| 2456 // Check if either all or none of the {receiver_maps} have the given | |
| 2457 // {prototype} in their prototype chain. | |
| 2458 bool all = true; | |
| 2459 bool none = true; | |
| 2460 for (size_t i = 0; i < receiver_maps.size(); ++i) { | |
| 2461 Handle<Map> receiver_map = receiver_maps[i]; | |
| 2462 if (receiver_map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) { | |
| 2463 return kMayBeInPrototypeChain; | |
| 2464 } | |
| 2465 if (result == NodeProperties::kUnreliableReceiverMaps) { | |
| 2466 // In case of an unreliable {result} we need to ensure that all | |
| 2467 // {receiver_maps} are stable, because otherwise we cannot trust | |
| 2468 // the {receiver_maps} information, since arbitrary side-effects | |
| 2469 // may have happened. | |
| 2470 if (!receiver_map->is_stable()) { | |
| 2471 return kMayBeInPrototypeChain; | |
| 2472 } | |
| 2473 } | |
| 2474 for (PrototypeIterator j(receiver_map);; j.Advance()) { | |
| 2475 if (j.IsAtEnd()) { | |
| 2476 all = false; | |
| 2477 break; | |
| 2478 } | |
| 2479 Handle<JSReceiver> const current = | |
| 2480 PrototypeIterator::GetCurrent<JSReceiver>(j); | |
| 2481 if (current.is_identical_to(prototype)) { | |
| 2482 none = false; | |
| 2483 break; | |
| 2484 } | |
| 2485 if (!current->map()->is_stable() || | |
| 2486 current->map()->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) { | |
| 2487 return kMayBeInPrototypeChain; | |
| 2488 } | |
| 2489 } | |
| 2490 } | |
| 2491 DCHECK_IMPLIES(all, !none); | |
| 2492 DCHECK_IMPLIES(none, !all); | |
| 2493 | |
| 2494 if (all) return kIsInPrototypeChain; | |
| 2495 if (none) return kIsNotInPrototypeChain; | |
| 2496 return kMayBeInPrototypeChain; | |
| 2497 } | |
| 2498 | |
| 2499 bool JSNativeContextSpecialization::ExtractReceiverMaps( | 2314 bool JSNativeContextSpecialization::ExtractReceiverMaps( |
| 2500 Node* receiver, Node* effect, FeedbackNexus const& nexus, | 2315 Node* receiver, Node* effect, FeedbackNexus const& nexus, |
| 2501 MapHandles* receiver_maps) { | 2316 MapHandles* receiver_maps) { |
| 2502 DCHECK_EQ(0, receiver_maps->size()); | 2317 DCHECK_EQ(0, receiver_maps->size()); |
| 2503 // See if we can infer a concrete type for the {receiver}. | 2318 // See if we can infer a concrete type for the {receiver}. |
| 2504 if (InferReceiverMaps(receiver, effect, receiver_maps)) { | 2319 if (InferReceiverMaps(receiver, effect, receiver_maps)) { |
| 2505 // We can assume that the {receiver} still has the infered {receiver_maps}. | 2320 // We can assume that the {receiver} still has the infered {receiver_maps}. |
| 2506 return true; | 2321 return true; |
| 2507 } | 2322 } |
| 2508 // Try to extract some maps from the {nexus}. | 2323 // Try to extract some maps from the {nexus}. |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2611 return jsgraph()->javascript(); | 2426 return jsgraph()->javascript(); |
| 2612 } | 2427 } |
| 2613 | 2428 |
| 2614 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { | 2429 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { |
| 2615 return jsgraph()->simplified(); | 2430 return jsgraph()->simplified(); |
| 2616 } | 2431 } |
| 2617 | 2432 |
| 2618 } // namespace compiler | 2433 } // namespace compiler |
| 2619 } // namespace internal | 2434 } // namespace internal |
| 2620 } // namespace v8 | 2435 } // namespace v8 |
| OLD | NEW |