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 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
259 } | 259 } |
260 | 260 |
261 return NoChange(); | 261 return NoChange(); |
262 } | 262 } |
263 | 263 |
264 Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance( | 264 Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance( |
265 Node* node) { | 265 Node* node) { |
266 DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode()); | 266 DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode()); |
267 Node* constructor = NodeProperties::GetValueInput(node, 0); | 267 Node* constructor = NodeProperties::GetValueInput(node, 0); |
268 Node* object = NodeProperties::GetValueInput(node, 1); | 268 Node* object = NodeProperties::GetValueInput(node, 1); |
| 269 Node* context = NodeProperties::GetContextInput(node); |
| 270 Node* frame_state = NodeProperties::GetFrameStateInput(node); |
| 271 Node* effect = NodeProperties::GetEffectInput(node); |
| 272 Node* control = NodeProperties::GetControlInput(node); |
| 273 |
| 274 // Check if the {constructor} is known at compile time. |
| 275 HeapObjectMatcher m(constructor); |
| 276 if (!m.HasValue()) return NoChange(); |
269 | 277 |
270 // Check if the {constructor} is a JSBoundFunction. | 278 // Check if the {constructor} is a JSBoundFunction. |
271 HeapObjectMatcher m(constructor); | 279 if (m.Value()->IsJSBoundFunction()) { |
272 if (m.HasValue() && m.Value()->IsJSBoundFunction()) { | |
273 // OrdinaryHasInstance on bound functions turns into a recursive | 280 // OrdinaryHasInstance on bound functions turns into a recursive |
274 // invocation of the instanceof operator again. | 281 // invocation of the instanceof operator again. |
275 // ES6 section 7.3.19 OrdinaryHasInstance (C, O) step 2. | 282 // ES6 section 7.3.19 OrdinaryHasInstance (C, O) step 2. |
276 Handle<JSBoundFunction> function = Handle<JSBoundFunction>::cast(m.Value()); | 283 Handle<JSBoundFunction> function = Handle<JSBoundFunction>::cast(m.Value()); |
277 Handle<JSReceiver> bound_target_function(function->bound_target_function()); | 284 Handle<JSReceiver> bound_target_function(function->bound_target_function()); |
278 NodeProperties::ReplaceValueInput(node, object, 0); | 285 NodeProperties::ReplaceValueInput(node, object, 0); |
279 NodeProperties::ReplaceValueInput( | 286 NodeProperties::ReplaceValueInput( |
280 node, jsgraph()->HeapConstant(bound_target_function), 1); | 287 node, jsgraph()->HeapConstant(bound_target_function), 1); |
281 NodeProperties::ChangeOp(node, javascript()->InstanceOf()); | 288 NodeProperties::ChangeOp(node, javascript()->InstanceOf()); |
282 Reduction const reduction = ReduceJSInstanceOf(node); | 289 Reduction const reduction = ReduceJSInstanceOf(node); |
283 return reduction.Changed() ? reduction : Changed(node); | 290 return reduction.Changed() ? reduction : Changed(node); |
284 } | 291 } |
285 | 292 |
| 293 // Check if the {constructor} is a JSFunction. |
| 294 if (m.Value()->IsJSFunction()) { |
| 295 // Check if the {function} is a constructor and has an instance "prototype". |
| 296 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); |
| 297 if (function->IsConstructor() && function->has_instance_prototype() && |
| 298 function->prototype()->IsJSReceiver()) { |
| 299 // Ensure that the {function} has a valid initial map, so we can |
| 300 // depend on that for the prototype constant-folding below. |
| 301 JSFunction::EnsureHasInitialMap(function); |
| 302 |
| 303 // Install a code dependency on the {function}s initial map. |
| 304 Handle<Map> initial_map(function->initial_map(), isolate()); |
| 305 dependencies()->AssumeInitialMapCantChange(initial_map); |
| 306 |
| 307 // Determine the {function}s prototype. |
| 308 Handle<JSReceiver> function_prototype = |
| 309 handle(JSReceiver::cast(initial_map->prototype()), isolate()); |
| 310 |
| 311 ZoneHandleSet<Map> object_maps; |
| 312 NodeProperties::InferReceiverMapsResult result = |
| 313 NodeProperties::InferReceiverMaps(object, effect, &object_maps); |
| 314 if (result != NodeProperties::kNoReceiverMaps) { |
| 315 bool all = true; |
| 316 bool none = true; |
| 317 for (size_t i = 0; i < object_maps.size(); ++i) { |
| 318 Handle<Map> object_map = object_maps[i]; |
| 319 if (object_map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) { |
| 320 all = none = false; |
| 321 break; |
| 322 } |
| 323 if (result == NodeProperties::kUnreliableReceiverMaps) { |
| 324 if (!object_map->is_stable()) { |
| 325 all = none = false; |
| 326 break; |
| 327 } |
| 328 } |
| 329 for (PrototypeIterator j(object_maps[i]);; j.Advance()) { |
| 330 if (j.IsAtEnd()) { |
| 331 all = false; |
| 332 break; |
| 333 } |
| 334 Handle<JSReceiver> const current = |
| 335 PrototypeIterator::GetCurrent<JSReceiver>(j); |
| 336 if (current.is_identical_to(function_prototype)) { |
| 337 none = false; |
| 338 break; |
| 339 } |
| 340 if (!current->map()->is_stable() || |
| 341 current->map()->IsSpecialReceiverMap()) { |
| 342 all = none = false; |
| 343 break; |
| 344 } |
| 345 } |
| 346 } |
| 347 |
| 348 DCHECK_IMPLIES(all, !none); |
| 349 DCHECK_IMPLIES(none, !all); |
| 350 |
| 351 if (all || none) { |
| 352 for (size_t i = 0; i < object_maps.size(); ++i) { |
| 353 if (result == NodeProperties::kUnreliableReceiverMaps) { |
| 354 dependencies()->AssumeMapStable(object_maps[i]); |
| 355 } |
| 356 for (PrototypeIterator j(object_maps[i]); !j.IsAtEnd(); |
| 357 j.Advance()) { |
| 358 Handle<JSReceiver> const current = |
| 359 PrototypeIterator::GetCurrent<JSReceiver>(j); |
| 360 if (current.is_identical_to(function_prototype)) break; |
| 361 dependencies()->AssumeMapStable(handle(current->map())); |
| 362 } |
| 363 } |
| 364 Node* value = jsgraph()->BooleanConstant(all); |
| 365 ReplaceWithValue(node, value, effect, control); |
| 366 return Replace(value); |
| 367 } |
| 368 } |
| 369 |
| 370 Node* prototype = jsgraph()->Constant(function_prototype); |
| 371 |
| 372 Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), object); |
| 373 Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| 374 check0, control); |
| 375 |
| 376 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); |
| 377 Node* etrue0 = effect; |
| 378 Node* vtrue0 = jsgraph()->FalseConstant(); |
| 379 |
| 380 control = graph()->NewNode(common()->IfFalse(), branch0); |
| 381 |
| 382 // Loop through the {object}s prototype chain looking for the {prototype}. |
| 383 Node* loop = control = |
| 384 graph()->NewNode(common()->Loop(2), control, control); |
| 385 Node* eloop = effect = |
| 386 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); |
| 387 Node* vloop = object = |
| 388 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
| 389 object, object, loop); |
| 390 |
| 391 // Load the {object} map and instance type. |
| 392 Node* object_map = effect = |
| 393 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
| 394 object, effect, control); |
| 395 Node* object_instance_type = effect = graph()->NewNode( |
| 396 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), |
| 397 object_map, effect, control); |
| 398 |
| 399 // Check if the {object} is a special receiver, because for special |
| 400 // receivers, i.e. proxies or API objects that need access checks, |
| 401 // we have to use the %HasInPrototypeChain runtime function instead. |
| 402 Node* check1 = graph()->NewNode( |
| 403 simplified()->NumberLessThanOrEqual(), object_instance_type, |
| 404 jsgraph()->Constant(LAST_SPECIAL_RECEIVER_TYPE)); |
| 405 Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| 406 check1, control); |
| 407 |
| 408 control = graph()->NewNode(common()->IfFalse(), branch1); |
| 409 |
| 410 Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); |
| 411 Node* etrue1 = effect; |
| 412 Node* vtrue1; |
| 413 |
| 414 // Check if the {object} is not a receiver at all. |
| 415 Node* check10 = |
| 416 graph()->NewNode(simplified()->NumberLessThan(), object_instance_type, |
| 417 jsgraph()->Constant(FIRST_JS_RECEIVER_TYPE)); |
| 418 Node* branch10 = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
| 419 check10, if_true1); |
| 420 |
| 421 // A primitive value cannot match the {prototype} we're looking for. |
| 422 if_true1 = graph()->NewNode(common()->IfTrue(), branch10); |
| 423 vtrue1 = jsgraph()->FalseConstant(); |
| 424 |
| 425 Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch10); |
| 426 Node* efalse1 = etrue1; |
| 427 Node* vfalse1; |
| 428 { |
| 429 // Slow path, need to call the %HasInPrototypeChain runtime function. |
| 430 vfalse1 = efalse1 = if_false1 = graph()->NewNode( |
| 431 javascript()->CallRuntime(Runtime::kHasInPrototypeChain), object, |
| 432 prototype, context, frame_state, efalse1, if_false1); |
| 433 |
| 434 // Replace any potential {IfException} uses of {node} to catch |
| 435 // exceptions from this %HasInPrototypeChain runtime call instead. |
| 436 Node* on_exception = nullptr; |
| 437 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { |
| 438 NodeProperties::ReplaceControlInput(on_exception, vfalse1); |
| 439 NodeProperties::ReplaceEffectInput(on_exception, efalse1); |
| 440 if_false1 = graph()->NewNode(common()->IfSuccess(), vfalse1); |
| 441 Revisit(on_exception); |
| 442 } |
| 443 } |
| 444 |
| 445 // Load the {object} prototype. |
| 446 Node* object_prototype = effect = graph()->NewNode( |
| 447 simplified()->LoadField(AccessBuilder::ForMapPrototype()), object_map, |
| 448 effect, control); |
| 449 |
| 450 // Check if we reached the end of {object}s prototype chain. |
| 451 Node* check2 = |
| 452 graph()->NewNode(simplified()->ReferenceEqual(), object_prototype, |
| 453 jsgraph()->NullConstant()); |
| 454 Node* branch2 = graph()->NewNode(common()->Branch(), check2, control); |
| 455 |
| 456 Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2); |
| 457 Node* etrue2 = effect; |
| 458 Node* vtrue2 = jsgraph()->FalseConstant(); |
| 459 |
| 460 control = graph()->NewNode(common()->IfFalse(), branch2); |
| 461 |
| 462 // Check if we reached the {prototype}. |
| 463 Node* check3 = graph()->NewNode(simplified()->ReferenceEqual(), |
| 464 object_prototype, prototype); |
| 465 Node* branch3 = graph()->NewNode(common()->Branch(), check3, control); |
| 466 |
| 467 Node* if_true3 = graph()->NewNode(common()->IfTrue(), branch3); |
| 468 Node* etrue3 = effect; |
| 469 Node* vtrue3 = jsgraph()->TrueConstant(); |
| 470 |
| 471 control = graph()->NewNode(common()->IfFalse(), branch3); |
| 472 |
| 473 // Close the loop. |
| 474 vloop->ReplaceInput(1, object_prototype); |
| 475 eloop->ReplaceInput(1, effect); |
| 476 loop->ReplaceInput(1, control); |
| 477 |
| 478 control = graph()->NewNode(common()->Merge(5), if_true0, if_true1, |
| 479 if_true2, if_true3, if_false1); |
| 480 effect = graph()->NewNode(common()->EffectPhi(5), etrue0, etrue1, etrue2, |
| 481 etrue3, efalse1, control); |
| 482 |
| 483 // Morph the {node} into an appropriate Phi. |
| 484 ReplaceWithValue(node, node, effect, control); |
| 485 node->ReplaceInput(0, vtrue0); |
| 486 node->ReplaceInput(1, vtrue1); |
| 487 node->ReplaceInput(2, vtrue2); |
| 488 node->ReplaceInput(3, vtrue3); |
| 489 node->ReplaceInput(4, vfalse1); |
| 490 node->ReplaceInput(5, control); |
| 491 node->TrimInputCount(6); |
| 492 NodeProperties::ChangeOp( |
| 493 node, common()->Phi(MachineRepresentation::kTagged, 5)); |
| 494 return Changed(node); |
| 495 } |
| 496 } |
| 497 |
286 return NoChange(); | 498 return NoChange(); |
287 } | 499 } |
288 | 500 |
289 Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) { | 501 Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) { |
290 DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode()); | 502 DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode()); |
291 ContextAccess const& access = ContextAccessOf(node->op()); | 503 ContextAccess const& access = ContextAccessOf(node->op()); |
292 // Specialize JSLoadContext(NATIVE_CONTEXT_INDEX) to the known native | 504 // Specialize JSLoadContext(NATIVE_CONTEXT_INDEX) to the known native |
293 // context (if any), so we can constant-fold those fields, which is | 505 // context (if any), so we can constant-fold those fields, which is |
294 // safe, since the NATIVE_CONTEXT_INDEX slot is always immutable. | 506 // safe, since the NATIVE_CONTEXT_INDEX slot is always immutable. |
295 if (access.index() == Context::NATIVE_CONTEXT_INDEX) { | 507 if (access.index() == Context::NATIVE_CONTEXT_INDEX) { |
(...skipping 2054 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2350 return jsgraph()->javascript(); | 2562 return jsgraph()->javascript(); |
2351 } | 2563 } |
2352 | 2564 |
2353 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { | 2565 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { |
2354 return jsgraph()->simplified(); | 2566 return jsgraph()->simplified(); |
2355 } | 2567 } |
2356 | 2568 |
2357 } // namespace compiler | 2569 } // namespace compiler |
2358 } // namespace internal | 2570 } // namespace internal |
2359 } // namespace v8 | 2571 } // namespace v8 |
OLD | NEW |