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 Handle<JSReceiver> function_prototype = |
| 307 handle(JSReceiver::cast(initial_map->prototype()), isolate()); |
| 308 |
| 309 // Check if we can constant-fold the prototype chain walk |
| 310 // for the given {object} and the {function_prototype}. |
| 311 InferHasInPrototypeChainResult result = |
| 312 InferHasInPrototypeChain(object, effect, function_prototype); |
| 313 if (result != kMayBeInPrototypeChain) { |
| 314 Node* value = jsgraph()->BooleanConstant(result == kIsInPrototypeChain); |
| 315 ReplaceWithValue(node, value, effect, control); |
| 316 return Replace(value); |
| 317 } |
| 318 |
| 319 Node* prototype = jsgraph()->Constant(function_prototype); |
| 320 |
| 321 Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), object); |
| 322 Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| 323 check0, control); |
| 324 |
| 325 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); |
| 326 Node* etrue0 = effect; |
| 327 Node* vtrue0 = jsgraph()->FalseConstant(); |
| 328 |
| 329 control = graph()->NewNode(common()->IfFalse(), branch0); |
| 330 |
| 331 // Loop through the {object}s prototype chain looking for the {prototype}. |
| 332 Node* loop = control = |
| 333 graph()->NewNode(common()->Loop(2), control, control); |
| 334 Node* eloop = effect = |
| 335 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); |
| 336 Node* vloop = object = |
| 337 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
| 338 object, object, loop); |
| 339 |
| 340 // Load the {object} map and instance type. |
| 341 Node* object_map = effect = |
| 342 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
| 343 object, effect, control); |
| 344 Node* object_instance_type = effect = graph()->NewNode( |
| 345 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), |
| 346 object_map, effect, control); |
| 347 |
| 348 // Check if the {object} is a special receiver, because for special |
| 349 // receivers, i.e. proxies or API objects that need access checks, |
| 350 // we have to use the %HasInPrototypeChain runtime function instead. |
| 351 Node* check1 = graph()->NewNode( |
| 352 simplified()->NumberLessThanOrEqual(), object_instance_type, |
| 353 jsgraph()->Constant(LAST_SPECIAL_RECEIVER_TYPE)); |
| 354 Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| 355 check1, control); |
| 356 |
| 357 control = graph()->NewNode(common()->IfFalse(), branch1); |
| 358 |
| 359 Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); |
| 360 Node* etrue1 = effect; |
| 361 Node* vtrue1; |
| 362 |
| 363 // Check if the {object} is not a receiver at all. |
| 364 Node* check10 = |
| 365 graph()->NewNode(simplified()->NumberLessThan(), object_instance_type, |
| 366 jsgraph()->Constant(FIRST_JS_RECEIVER_TYPE)); |
| 367 Node* branch10 = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
| 368 check10, if_true1); |
| 369 |
| 370 // A primitive value cannot match the {prototype} we're looking for. |
| 371 if_true1 = graph()->NewNode(common()->IfTrue(), branch10); |
| 372 vtrue1 = jsgraph()->FalseConstant(); |
| 373 |
| 374 Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch10); |
| 375 Node* efalse1 = etrue1; |
| 376 Node* vfalse1; |
| 377 { |
| 378 // Slow path, need to call the %HasInPrototypeChain runtime function. |
| 379 vfalse1 = efalse1 = if_false1 = graph()->NewNode( |
| 380 javascript()->CallRuntime(Runtime::kHasInPrototypeChain), object, |
| 381 prototype, context, frame_state, efalse1, if_false1); |
| 382 |
| 383 // Replace any potential {IfException} uses of {node} to catch |
| 384 // exceptions from this %HasInPrototypeChain runtime call instead. |
| 385 Node* on_exception = nullptr; |
| 386 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { |
| 387 NodeProperties::ReplaceControlInput(on_exception, vfalse1); |
| 388 NodeProperties::ReplaceEffectInput(on_exception, efalse1); |
| 389 if_false1 = graph()->NewNode(common()->IfSuccess(), vfalse1); |
| 390 Revisit(on_exception); |
| 391 } |
| 392 } |
| 393 |
| 394 // Load the {object} prototype. |
| 395 Node* object_prototype = effect = graph()->NewNode( |
| 396 simplified()->LoadField(AccessBuilder::ForMapPrototype()), object_map, |
| 397 effect, control); |
| 398 |
| 399 // Check if we reached the end of {object}s prototype chain. |
| 400 Node* check2 = |
| 401 graph()->NewNode(simplified()->ReferenceEqual(), object_prototype, |
| 402 jsgraph()->NullConstant()); |
| 403 Node* branch2 = graph()->NewNode(common()->Branch(), check2, control); |
| 404 |
| 405 Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2); |
| 406 Node* etrue2 = effect; |
| 407 Node* vtrue2 = jsgraph()->FalseConstant(); |
| 408 |
| 409 control = graph()->NewNode(common()->IfFalse(), branch2); |
| 410 |
| 411 // Check if we reached the {prototype}. |
| 412 Node* check3 = graph()->NewNode(simplified()->ReferenceEqual(), |
| 413 object_prototype, prototype); |
| 414 Node* branch3 = graph()->NewNode(common()->Branch(), check3, control); |
| 415 |
| 416 Node* if_true3 = graph()->NewNode(common()->IfTrue(), branch3); |
| 417 Node* etrue3 = effect; |
| 418 Node* vtrue3 = jsgraph()->TrueConstant(); |
| 419 |
| 420 control = graph()->NewNode(common()->IfFalse(), branch3); |
| 421 |
| 422 // Close the loop. |
| 423 vloop->ReplaceInput(1, object_prototype); |
| 424 eloop->ReplaceInput(1, effect); |
| 425 loop->ReplaceInput(1, control); |
| 426 |
| 427 control = graph()->NewNode(common()->Merge(5), if_true0, if_true1, |
| 428 if_true2, if_true3, if_false1); |
| 429 effect = graph()->NewNode(common()->EffectPhi(5), etrue0, etrue1, etrue2, |
| 430 etrue3, efalse1, control); |
| 431 |
| 432 // Morph the {node} into an appropriate Phi. |
| 433 ReplaceWithValue(node, node, effect, control); |
| 434 node->ReplaceInput(0, vtrue0); |
| 435 node->ReplaceInput(1, vtrue1); |
| 436 node->ReplaceInput(2, vtrue2); |
| 437 node->ReplaceInput(3, vtrue3); |
| 438 node->ReplaceInput(4, vfalse1); |
| 439 node->ReplaceInput(5, control); |
| 440 node->TrimInputCount(6); |
| 441 NodeProperties::ChangeOp( |
| 442 node, common()->Phi(MachineRepresentation::kTagged, 5)); |
| 443 return Changed(node); |
| 444 } |
| 445 } |
| 446 |
286 return NoChange(); | 447 return NoChange(); |
287 } | 448 } |
288 | 449 |
289 Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) { | 450 Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) { |
290 DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode()); | 451 DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode()); |
291 ContextAccess const& access = ContextAccessOf(node->op()); | 452 ContextAccess const& access = ContextAccessOf(node->op()); |
292 // Specialize JSLoadContext(NATIVE_CONTEXT_INDEX) to the known native | 453 // Specialize JSLoadContext(NATIVE_CONTEXT_INDEX) to the known native |
293 // context (if any), so we can constant-fold those fields, which is | 454 // context (if any), so we can constant-fold those fields, which is |
294 // safe, since the NATIVE_CONTEXT_INDEX slot is always immutable. | 455 // safe, since the NATIVE_CONTEXT_INDEX slot is always immutable. |
295 if (access.index() == Context::NATIVE_CONTEXT_INDEX) { | 456 if (access.index() == Context::NATIVE_CONTEXT_INDEX) { |
(...skipping 1933 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2229 // Install code dependencies on the prototype maps. | 2390 // Install code dependencies on the prototype maps. |
2230 for (Handle<Map> map : receiver_maps) { | 2391 for (Handle<Map> map : receiver_maps) { |
2231 dependencies()->AssumePrototypeMapsStable(map, initial_object_prototype); | 2392 dependencies()->AssumePrototypeMapsStable(map, initial_object_prototype); |
2232 } | 2393 } |
2233 | 2394 |
2234 // Install code dependency on the array protector cell. | 2395 // Install code dependency on the array protector cell. |
2235 dependencies()->AssumePropertyCell(factory()->array_protector()); | 2396 dependencies()->AssumePropertyCell(factory()->array_protector()); |
2236 return true; | 2397 return true; |
2237 } | 2398 } |
2238 | 2399 |
| 2400 JSNativeContextSpecialization::InferHasInPrototypeChainResult |
| 2401 JSNativeContextSpecialization::InferHasInPrototypeChain( |
| 2402 Node* receiver, Node* effect, Handle<JSReceiver> prototype) { |
| 2403 ZoneHandleSet<Map> receiver_maps; |
| 2404 NodeProperties::InferReceiverMapsResult result = |
| 2405 NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps); |
| 2406 if (result == NodeProperties::kNoReceiverMaps) return kMayBeInPrototypeChain; |
| 2407 |
| 2408 // Check if either all or none of the {receiver_maps} have the given |
| 2409 // {prototype} in their prototype chain. |
| 2410 bool all = true; |
| 2411 bool none = true; |
| 2412 for (size_t i = 0; i < receiver_maps.size(); ++i) { |
| 2413 Handle<Map> receiver_map = receiver_maps[i]; |
| 2414 if (receiver_map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) { |
| 2415 return kMayBeInPrototypeChain; |
| 2416 } |
| 2417 if (result == NodeProperties::kUnreliableReceiverMaps) { |
| 2418 // In case of an unreliable {result} we need to ensure that all |
| 2419 // {receiver_maps} are stable, because otherwise we cannot trust |
| 2420 // the {receiver_maps} information, since arbitrary side-effects |
| 2421 // may have happened. |
| 2422 if (!receiver_map->is_stable()) { |
| 2423 return kMayBeInPrototypeChain; |
| 2424 } |
| 2425 } |
| 2426 for (PrototypeIterator j(receiver_map);; j.Advance()) { |
| 2427 if (j.IsAtEnd()) { |
| 2428 all = false; |
| 2429 break; |
| 2430 } |
| 2431 Handle<JSReceiver> const current = |
| 2432 PrototypeIterator::GetCurrent<JSReceiver>(j); |
| 2433 if (current.is_identical_to(prototype)) { |
| 2434 none = false; |
| 2435 break; |
| 2436 } |
| 2437 if (!current->map()->is_stable() || |
| 2438 current->map()->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) { |
| 2439 return kMayBeInPrototypeChain; |
| 2440 } |
| 2441 } |
| 2442 } |
| 2443 DCHECK_IMPLIES(all, !none); |
| 2444 DCHECK_IMPLIES(none, !all); |
| 2445 |
| 2446 if (all) return kIsInPrototypeChain; |
| 2447 if (none) return kIsNotInPrototypeChain; |
| 2448 return kMayBeInPrototypeChain; |
| 2449 } |
| 2450 |
2239 bool JSNativeContextSpecialization::ExtractReceiverMaps( | 2451 bool JSNativeContextSpecialization::ExtractReceiverMaps( |
2240 Node* receiver, Node* effect, FeedbackNexus const& nexus, | 2452 Node* receiver, Node* effect, FeedbackNexus const& nexus, |
2241 MapHandleList* receiver_maps) { | 2453 MapHandleList* receiver_maps) { |
2242 DCHECK_EQ(0, receiver_maps->length()); | 2454 DCHECK_EQ(0, receiver_maps->length()); |
2243 // See if we can infer a concrete type for the {receiver}. | 2455 // See if we can infer a concrete type for the {receiver}. |
2244 if (InferReceiverMaps(receiver, effect, receiver_maps)) { | 2456 if (InferReceiverMaps(receiver, effect, receiver_maps)) { |
2245 // We can assume that the {receiver} still has the infered {receiver_maps}. | 2457 // We can assume that the {receiver} still has the infered {receiver_maps}. |
2246 return true; | 2458 return true; |
2247 } | 2459 } |
2248 // Try to extract some maps from the {nexus}. | 2460 // Try to extract some maps from the {nexus}. |
(...skipping 101 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 |