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 |