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/compilation-dependencies.h" | 8 #include "src/compilation-dependencies.h" |
9 #include "src/compiler/access-builder.h" | 9 #include "src/compiler/access-builder.h" |
10 #include "src/compiler/js-graph.h" | 10 #include "src/compiler/js-graph.h" |
(...skipping 28 matching lines...) Expand all Loading... | |
39 | 39 |
40 | 40 |
41 Reduction JSNativeContextSpecialization::Reduce(Node* node) { | 41 Reduction JSNativeContextSpecialization::Reduce(Node* node) { |
42 switch (node->opcode()) { | 42 switch (node->opcode()) { |
43 case IrOpcode::kJSLoadGlobal: | 43 case IrOpcode::kJSLoadGlobal: |
44 return ReduceJSLoadGlobal(node); | 44 return ReduceJSLoadGlobal(node); |
45 case IrOpcode::kJSStoreGlobal: | 45 case IrOpcode::kJSStoreGlobal: |
46 return ReduceJSStoreGlobal(node); | 46 return ReduceJSStoreGlobal(node); |
47 case IrOpcode::kJSLoadNamed: | 47 case IrOpcode::kJSLoadNamed: |
48 return ReduceJSLoadNamed(node); | 48 return ReduceJSLoadNamed(node); |
49 case IrOpcode::kJSStoreNamed: | |
50 return ReduceJSStoreNamed(node); | |
49 default: | 51 default: |
50 break; | 52 break; |
51 } | 53 } |
52 return NoChange(); | 54 return NoChange(); |
53 } | 55 } |
54 | 56 |
55 | 57 |
56 Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) { | 58 Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) { |
57 DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode()); | 59 DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode()); |
58 Handle<Name> name = LoadGlobalParametersOf(node->op()).name(); | 60 Handle<Name> name = LoadGlobalParametersOf(node->op()).name(); |
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
236 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()), | 238 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()), |
237 jsgraph()->Constant(property_cell), value, effect, control); | 239 jsgraph()->Constant(property_cell), value, effect, control); |
238 return Replace(node, value, effect, control); | 240 return Replace(node, value, effect, control); |
239 } | 241 } |
240 | 242 |
241 | 243 |
242 // This class encapsulates all information required to access a certain | 244 // This class encapsulates all information required to access a certain |
243 // object property, either on the object itself or on the prototype chain. | 245 // object property, either on the object itself or on the prototype chain. |
244 class JSNativeContextSpecialization::PropertyAccessInfo final { | 246 class JSNativeContextSpecialization::PropertyAccessInfo final { |
245 public: | 247 public: |
246 enum Kind { kInvalid, kData, kDataConstant }; | 248 enum Kind { kInvalid, kDataConstant, kDataField }; |
247 | 249 |
248 static PropertyAccessInfo DataConstant(Type* receiver_type, | 250 static PropertyAccessInfo DataConstant(Type* receiver_type, |
249 Handle<Object> constant, | 251 Handle<Object> constant, |
250 MaybeHandle<JSObject> holder) { | 252 MaybeHandle<JSObject> holder) { |
251 return PropertyAccessInfo(holder, constant, receiver_type); | 253 return PropertyAccessInfo(holder, constant, receiver_type); |
252 } | 254 } |
253 static PropertyAccessInfo Data(Type* receiver_type, FieldIndex field_index, | 255 static PropertyAccessInfo DataField(Type* receiver_type, |
254 Representation field_representation, | 256 FieldIndex field_index, Type* field_type, |
255 Type* field_type, | 257 MaybeHandle<JSObject> holder) { |
256 MaybeHandle<JSObject> holder) { | 258 return PropertyAccessInfo(holder, field_index, field_type, receiver_type); |
257 return PropertyAccessInfo(holder, field_index, field_representation, | |
258 field_type, receiver_type); | |
259 } | 259 } |
260 | 260 |
261 PropertyAccessInfo() : kind_(kInvalid) {} | 261 PropertyAccessInfo() : kind_(kInvalid) {} |
262 PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Object> constant, | 262 PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Object> constant, |
263 Type* receiver_type) | 263 Type* receiver_type) |
264 : kind_(kDataConstant), | 264 : kind_(kDataConstant), |
265 receiver_type_(receiver_type), | 265 receiver_type_(receiver_type), |
266 constant_(constant), | 266 constant_(constant), |
267 holder_(holder) {} | 267 holder_(holder) {} |
268 PropertyAccessInfo(MaybeHandle<JSObject> holder, FieldIndex field_index, | 268 PropertyAccessInfo(MaybeHandle<JSObject> holder, FieldIndex field_index, |
269 Representation field_representation, Type* field_type, | 269 Type* field_type, Type* receiver_type) |
270 Type* receiver_type) | 270 : kind_(kDataField), |
271 : kind_(kData), | |
272 receiver_type_(receiver_type), | 271 receiver_type_(receiver_type), |
273 holder_(holder), | 272 holder_(holder), |
274 field_index_(field_index), | 273 field_index_(field_index), |
275 field_representation_(field_representation), | |
276 field_type_(field_type) {} | 274 field_type_(field_type) {} |
277 | 275 |
278 bool IsDataConstant() const { return kind() == kDataConstant; } | 276 bool IsDataConstant() const { return kind() == kDataConstant; } |
279 bool IsData() const { return kind() == kData; } | 277 bool IsDataField() const { return kind() == kDataField; } |
280 | 278 |
281 Kind kind() const { return kind_; } | 279 Kind kind() const { return kind_; } |
282 MaybeHandle<JSObject> holder() const { return holder_; } | 280 MaybeHandle<JSObject> holder() const { return holder_; } |
283 Handle<Object> constant() const { return constant_; } | 281 Handle<Object> constant() const { return constant_; } |
284 FieldIndex field_index() const { return field_index_; } | 282 FieldIndex field_index() const { return field_index_; } |
285 Representation field_representation() const { return field_representation_; } | |
286 Type* field_type() const { return field_type_; } | 283 Type* field_type() const { return field_type_; } |
287 Type* receiver_type() const { return receiver_type_; } | 284 Type* receiver_type() const { return receiver_type_; } |
288 | 285 |
289 private: | 286 private: |
290 Kind kind_; | 287 Kind kind_; |
291 Type* receiver_type_; | 288 Type* receiver_type_; |
292 Handle<Object> constant_; | 289 Handle<Object> constant_; |
293 MaybeHandle<JSObject> holder_; | 290 MaybeHandle<JSObject> holder_; |
294 FieldIndex field_index_; | 291 FieldIndex field_index_; |
295 Representation field_representation_; | |
296 Type* field_type_ = Type::Any(); | 292 Type* field_type_ = Type::Any(); |
297 }; | 293 }; |
298 | 294 |
299 | 295 |
300 namespace { | 296 namespace { |
301 | 297 |
302 bool CanInlinePropertyAccess(Handle<Map> map) { | 298 bool CanInlinePropertyAccess(Handle<Map> map) { |
303 // TODO(bmeurer): Do something about the number stuff. | 299 // TODO(bmeurer): Do something about the number stuff. |
304 if (map->instance_type() == HEAP_NUMBER_TYPE) return false; | 300 if (map->instance_type() == HEAP_NUMBER_TYPE) return false; |
305 if (map->instance_type() < FIRST_NONSTRING_TYPE) return true; | 301 if (map->instance_type() < FIRST_NONSTRING_TYPE) return true; |
306 return map->IsJSObjectMap() && !map->is_dictionary_map() && | 302 return map->IsJSObjectMap() && !map->is_dictionary_map() && |
307 !map->has_named_interceptor() && | 303 !map->has_named_interceptor() && |
308 // TODO(verwaest): Whitelist contexts to which we have access. | 304 // TODO(verwaest): Whitelist contexts to which we have access. |
309 !map->is_access_check_needed(); | 305 !map->is_access_check_needed(); |
310 } | 306 } |
311 | 307 |
312 } // namespace | 308 } // namespace |
313 | 309 |
314 | 310 |
315 bool JSNativeContextSpecialization::ComputePropertyAccessInfo( | 311 bool JSNativeContextSpecialization::ComputePropertyAccessInfo( |
316 Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) { | 312 Handle<Map> map, Handle<Name> name, PropertyAccessMode access_mode, |
313 PropertyAccessInfo* access_info) { | |
317 MaybeHandle<JSObject> holder; | 314 MaybeHandle<JSObject> holder; |
318 Type* receiver_type = Type::Class(map, graph()->zone()); | 315 Type* receiver_type = Type::Class(map, graph()->zone()); |
319 while (CanInlinePropertyAccess(map)) { | 316 while (CanInlinePropertyAccess(map)) { |
320 // Check for special JSObject field accessors. | 317 // Check for special JSObject field accessors. |
321 int offset; | 318 int offset; |
322 if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) { | 319 if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) { |
320 // Don't bother optimizing stores to special JSObject field accessors. | |
321 if (access_mode == kStore) { | |
322 break; | |
323 } | |
323 FieldIndex field_index = FieldIndex::ForInObjectOffset(offset); | 324 FieldIndex field_index = FieldIndex::ForInObjectOffset(offset); |
324 Representation field_representation = Representation::Tagged(); | |
325 Type* field_type = Type::Tagged(); | 325 Type* field_type = Type::Tagged(); |
326 if (map->IsStringMap()) { | 326 if (map->IsStringMap()) { |
327 DCHECK(Name::Equals(factory()->length_string(), name)); | 327 DCHECK(Name::Equals(factory()->length_string(), name)); |
328 // The String::length property is always a smi in the range | 328 // The String::length property is always a smi in the range |
329 // [0, String::kMaxLength]. | 329 // [0, String::kMaxLength]. |
330 field_representation = Representation::Smi(); | |
331 field_type = Type::Intersect( | 330 field_type = Type::Intersect( |
332 Type::Range(0.0, String::kMaxLength, graph()->zone()), | 331 Type::Range(0.0, String::kMaxLength, graph()->zone()), |
333 Type::TaggedSigned(), graph()->zone()); | 332 Type::TaggedSigned(), graph()->zone()); |
334 } else if (map->IsJSArrayMap()) { | 333 } else if (map->IsJSArrayMap()) { |
335 DCHECK(Name::Equals(factory()->length_string(), name)); | 334 DCHECK(Name::Equals(factory()->length_string(), name)); |
336 // The JSArray::length property is a smi in the range | 335 // The JSArray::length property is a smi in the range |
337 // [0, FixedDoubleArray::kMaxLength] in case of fast double | 336 // [0, FixedDoubleArray::kMaxLength] in case of fast double |
338 // elements, a smi in the range [0, FixedArray::kMaxLength] | 337 // elements, a smi in the range [0, FixedArray::kMaxLength] |
339 // in case of other fast elements, and [0, kMaxUInt32-1] in | 338 // in case of other fast elements, and [0, kMaxUInt32-1] in |
340 // case of other arrays. | 339 // case of other arrays. |
341 double field_type_upper = kMaxUInt32 - 1; | 340 double field_type_upper = kMaxUInt32 - 1; |
342 if (IsFastElementsKind(map->elements_kind())) { | 341 if (IsFastElementsKind(map->elements_kind())) { |
343 field_representation = Representation::Smi(); | |
344 field_type_upper = IsFastDoubleElementsKind(map->elements_kind()) | 342 field_type_upper = IsFastDoubleElementsKind(map->elements_kind()) |
345 ? FixedDoubleArray::kMaxLength | 343 ? FixedDoubleArray::kMaxLength |
346 : FixedArray::kMaxLength; | 344 : FixedArray::kMaxLength; |
347 } | 345 } |
348 field_type = | 346 field_type = |
349 Type::Intersect(Type::Range(0.0, field_type_upper, graph()->zone()), | 347 Type::Intersect(Type::Range(0.0, field_type_upper, graph()->zone()), |
350 Type::TaggedSigned(), graph()->zone()); | 348 Type::TaggedSigned(), graph()->zone()); |
351 } | 349 } |
352 *access_info = PropertyAccessInfo::Data( | 350 *access_info = PropertyAccessInfo::DataField(receiver_type, field_index, |
353 receiver_type, field_index, field_representation, field_type, holder); | 351 field_type, holder); |
354 return true; | 352 return true; |
355 } | 353 } |
356 | 354 |
357 // Lookup the named property on the {map}. | 355 // Lookup the named property on the {map}. |
358 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); | 356 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); |
359 int const number = descriptors->SearchWithCache(*name, *map); | 357 int const number = descriptors->SearchWithCache(*name, *map); |
360 if (number != DescriptorArray::kNotFound) { | 358 if (number != DescriptorArray::kNotFound) { |
361 PropertyDetails const details = descriptors->GetDetails(number); | 359 PropertyDetails const details = descriptors->GetDetails(number); |
362 if (details.type() == DATA_CONSTANT) { | 360 if (details.type() == DATA_CONSTANT) { |
363 *access_info = PropertyAccessInfo::DataConstant( | 361 *access_info = PropertyAccessInfo::DataConstant( |
364 receiver_type, handle(descriptors->GetValue(number), isolate()), | 362 receiver_type, handle(descriptors->GetValue(number), isolate()), |
365 holder); | 363 holder); |
366 return true; | 364 return true; |
367 } else if (details.type() == DATA) { | 365 } else if (details.type() == DATA) { |
366 // Don't bother optimizing stores to read-only properties. | |
367 if (access_mode == kStore) { | |
368 break; | |
369 } | |
368 int index = descriptors->GetFieldIndex(number); | 370 int index = descriptors->GetFieldIndex(number); |
369 Representation field_representation = details.representation(); | 371 Representation field_representation = details.representation(); |
370 FieldIndex field_index = FieldIndex::ForPropertyIndex( | 372 FieldIndex field_index = FieldIndex::ForPropertyIndex( |
371 *map, index, field_representation.IsDouble()); | 373 *map, index, field_representation.IsDouble()); |
372 Type* field_type = Type::Any(); | 374 Type* field_type = Type::Any(); |
373 if (field_representation.IsSmi()) { | 375 if (field_representation.IsSmi()) { |
374 field_type = Type::Intersect(Type::SignedSmall(), | 376 field_type = Type::Intersect(Type::SignedSmall(), |
375 Type::TaggedSigned(), graph()->zone()); | 377 Type::TaggedSigned(), graph()->zone()); |
376 } else if (field_representation.IsDouble()) { | 378 } else if (field_representation.IsDouble()) { |
379 if (access_mode == kStore) { | |
380 // TODO(bmeurer): Add support for storing to double fields. | |
381 break; | |
382 } | |
377 field_type = Type::Intersect(Type::Number(), Type::UntaggedFloat64(), | 383 field_type = Type::Intersect(Type::Number(), Type::UntaggedFloat64(), |
378 graph()->zone()); | 384 graph()->zone()); |
379 } else if (field_representation.IsHeapObject()) { | 385 } else if (field_representation.IsHeapObject()) { |
380 field_type = Type::TaggedPointer(); | 386 // Extract the field type from the property details (make sure its |
387 // representation is TaggedPointer to reflect the heap object case). | |
388 field_type = Type::Intersect( | |
389 Type::Convert<HeapType>( | |
390 handle(descriptors->GetFieldType(number), isolate()), | |
391 graph()->zone()), | |
392 Type::TaggedPointer(), graph()->zone()); | |
393 if (field_type->Is(Type::None())) { | |
394 if (access_mode == kStore) { | |
395 // Store is not safe if the field type was cleared. | |
396 break; | |
397 } | |
398 | |
399 // The field type was cleared by the GC, so we don't know anything | |
400 // about the contents now. | |
401 // TODO(bmeurer): It would be awesome to make this saner in the | |
402 // runtime/GC interaction. | |
403 field_type = Type::TaggedPointer(); | |
404 } else { | |
405 // Add proper code dependencies in case of stable field map(s). | |
406 if (field_type->NumClasses() > 0 && field_type->NowStable()) { | |
407 dependencies()->AssumeFieldType( | |
408 handle(map->FindFieldOwner(number), isolate())); | |
409 for (auto i = field_type->Classes(); !i.Done(); i.Advance()) { | |
410 dependencies()->AssumeMapStable(i.Current()); | |
411 } | |
412 } else { | |
413 field_type = Type::TaggedPointer(); | |
414 } | |
415 } | |
416 DCHECK(field_type->Is(Type::TaggedPointer())); | |
381 } | 417 } |
382 *access_info = | 418 *access_info = PropertyAccessInfo::DataField(receiver_type, field_index, |
383 PropertyAccessInfo::Data(receiver_type, field_index, | 419 field_type, holder); |
384 field_representation, field_type, holder); | |
385 return true; | 420 return true; |
386 } else { | 421 } else { |
387 // TODO(bmeurer): Add support for accessors. | 422 // TODO(bmeurer): Add support for accessors. |
388 break; | 423 break; |
389 } | 424 } |
390 } | 425 } |
391 | 426 |
392 // Don't search on the prototype chain for special indices in case of | 427 // Don't search on the prototype chain for special indices in case of |
393 // integer indexed exotic objects (see ES6 section 9.4.5). | 428 // integer indexed exotic objects (see ES6 section 9.4.5). |
394 if (map->IsJSTypedArrayMap() && name->IsString() && | 429 if (map->IsJSTypedArrayMap() && name->IsString() && |
(...skipping 14 matching lines...) Expand all Loading... | |
409 } | 444 } |
410 map = handle(map_prototype->map(), isolate()); | 445 map = handle(map_prototype->map(), isolate()); |
411 holder = map_prototype; | 446 holder = map_prototype; |
412 } | 447 } |
413 return false; | 448 return false; |
414 } | 449 } |
415 | 450 |
416 | 451 |
417 bool JSNativeContextSpecialization::ComputePropertyAccessInfos( | 452 bool JSNativeContextSpecialization::ComputePropertyAccessInfos( |
418 MapHandleList const& maps, Handle<Name> name, | 453 MapHandleList const& maps, Handle<Name> name, |
454 PropertyAccessMode access_mode, | |
419 ZoneVector<PropertyAccessInfo>* access_infos) { | 455 ZoneVector<PropertyAccessInfo>* access_infos) { |
420 for (Handle<Map> map : maps) { | 456 for (Handle<Map> map : maps) { |
421 PropertyAccessInfo access_info; | 457 PropertyAccessInfo access_info; |
422 if (!ComputePropertyAccessInfo(map, name, &access_info)) return false; | 458 if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) { |
459 return false; | |
460 } | |
423 access_infos->push_back(access_info); | 461 access_infos->push_back(access_info); |
424 } | 462 } |
425 return true; | 463 return true; |
426 } | 464 } |
427 | 465 |
428 | 466 |
429 Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { | 467 Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
430 DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); | 468 DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); |
431 LoadNamedParameters const p = LoadNamedParametersOf(node->op()); | 469 LoadNamedParameters const p = LoadNamedParametersOf(node->op()); |
432 Handle<Name> name = p.name(); | 470 Handle<Name> name = p.name(); |
433 Node* receiver = NodeProperties::GetValueInput(node, 0); | 471 Node* receiver = NodeProperties::GetValueInput(node, 0); |
434 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); | 472 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); |
435 Node* effect = NodeProperties::GetEffectInput(node); | 473 Node* effect = NodeProperties::GetEffectInput(node); |
436 Node* control = NodeProperties::GetControlInput(node); | 474 Node* control = NodeProperties::GetControlInput(node); |
437 | 475 |
438 // Not much we can do if deoptimization support is disabled. | 476 // Not much we can do if deoptimization support is disabled. |
439 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | 477 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); |
440 | 478 |
441 // Extract receiver maps from the LOAD_IC using the LoadICNexus. | 479 // Extract receiver maps from the LOAD_IC using the LoadICNexus. |
442 MapHandleList receiver_maps; | 480 MapHandleList receiver_maps; |
443 if (!p.feedback().IsValid()) return NoChange(); | 481 if (!p.feedback().IsValid()) return NoChange(); |
444 LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); | 482 LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
445 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); | 483 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); |
446 DCHECK_LT(0, receiver_maps.length()); | 484 DCHECK_LT(0, receiver_maps.length()); |
447 | 485 |
448 // Compute property access infos for the receiver maps. | 486 // Compute property access infos for the receiver maps. |
449 ZoneVector<PropertyAccessInfo> access_infos(zone()); | 487 ZoneVector<PropertyAccessInfo> access_infos(zone()); |
450 if (!ComputePropertyAccessInfos(receiver_maps, name, &access_infos)) { | 488 if (!ComputePropertyAccessInfos(receiver_maps, name, kLoad, &access_infos)) { |
451 return NoChange(); | 489 return NoChange(); |
452 } | 490 } |
453 DCHECK(!access_infos.empty()); | 491 DCHECK(!access_infos.empty()); |
454 | 492 |
455 // The final states for every polymorphic branch. We join them with | 493 // The final states for every polymorphic branch. We join them with |
456 // Merge+Phi+EffectPhi at the bottom. | 494 // Merge+Phi+EffectPhi at the bottom. |
457 ZoneVector<Node*> values(zone()); | 495 ZoneVector<Node*> values(zone()); |
458 ZoneVector<Node*> effects(zone()); | 496 ZoneVector<Node*> effects(zone()); |
459 ZoneVector<Node*> controls(zone()); | 497 ZoneVector<Node*> controls(zone()); |
460 | 498 |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
555 } | 593 } |
556 } | 594 } |
557 } | 595 } |
558 | 596 |
559 // Generate the actual property access. | 597 // Generate the actual property access. |
560 if (access_info.IsDataConstant()) { | 598 if (access_info.IsDataConstant()) { |
561 this_value = jsgraph()->Constant(access_info.constant()); | 599 this_value = jsgraph()->Constant(access_info.constant()); |
562 } else { | 600 } else { |
563 // TODO(bmeurer): This is sort of adhoc, and must be refactored into some | 601 // TODO(bmeurer): This is sort of adhoc, and must be refactored into some |
564 // common code once we also have support for stores. | 602 // common code once we also have support for stores. |
565 DCHECK(access_info.IsData()); | 603 DCHECK(access_info.IsDataField()); |
566 FieldIndex const field_index = access_info.field_index(); | 604 FieldIndex const field_index = access_info.field_index(); |
567 Representation const field_representation = | |
568 access_info.field_representation(); | |
569 Type* const field_type = access_info.field_type(); | 605 Type* const field_type = access_info.field_type(); |
570 if (!field_index.is_inobject()) { | 606 if (!field_index.is_inobject()) { |
571 this_value = this_effect = graph()->NewNode( | 607 this_value = this_effect = graph()->NewNode( |
572 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), | 608 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), |
573 this_value, this_effect, this_control); | 609 this_value, this_effect, this_control); |
574 } | 610 } |
575 FieldAccess field_access; | 611 FieldAccess field_access; |
576 field_access.base_is_tagged = kTaggedBase; | 612 field_access.base_is_tagged = kTaggedBase; |
577 field_access.offset = field_index.offset(); | 613 field_access.offset = field_index.offset(); |
578 field_access.name = name; | 614 field_access.name = name; |
579 field_access.type = field_type; | 615 field_access.type = field_type; |
580 field_access.machine_type = kMachAnyTagged; | 616 field_access.machine_type = kMachAnyTagged; |
581 if (field_representation.IsDouble()) { | 617 if (field_type->Is(Type::UntaggedFloat64())) { |
582 if (!field_index.is_inobject() || field_index.is_hidden_field() || | 618 if (!field_index.is_inobject() || field_index.is_hidden_field() || |
583 !FLAG_unbox_double_fields) { | 619 !FLAG_unbox_double_fields) { |
584 this_value = this_effect = | 620 this_value = this_effect = |
585 graph()->NewNode(simplified()->LoadField(field_access), | 621 graph()->NewNode(simplified()->LoadField(field_access), |
586 this_value, this_effect, this_control); | 622 this_value, this_effect, this_control); |
587 field_access.offset = HeapNumber::kValueOffset; | 623 field_access.offset = HeapNumber::kValueOffset; |
588 field_access.name = MaybeHandle<Name>(); | 624 field_access.name = MaybeHandle<Name>(); |
589 } | 625 } |
590 field_access.machine_type = kMachFloat64; | 626 field_access.machine_type = kMachFloat64; |
591 } | 627 } |
592 this_value = this_effect = | 628 this_value = this_effect = |
593 graph()->NewNode(simplified()->LoadField(field_access), this_value, | 629 graph()->NewNode(simplified()->LoadField(field_access), this_value, |
594 this_effect, this_control); | 630 this_effect, this_control); |
595 } | 631 } |
596 | 632 |
597 // Remember the final state for this property access. | 633 // Remember the final state for this property access. |
598 values.push_back(this_value); | 634 values.push_back(this_value); |
599 effects.push_back(this_effect); | 635 effects.push_back(this_effect); |
600 controls.push_back(this_control); | 636 controls.push_back(this_control); |
601 } | 637 } |
602 | 638 |
603 // Collect the fallthru control as final "exit" control. | 639 // Collect the fallthru control as final "exit" control. |
604 exit_controls.push_back(fallthrough_control); | 640 exit_controls.push_back(fallthrough_control); |
605 | 641 |
606 // TODO(bmeurer/mtrofin): Splintering cannot currently deal with deferred | |
607 // blocks that contain only a single non-deoptimize instruction (i.e. a | |
608 // jump). Generating a single Merge here, which joins all the deoptimizing | |
609 // controls would generate a lot of these basic blocks, however. So this | |
610 // is disabled for now until splintering is fixed. | |
611 #if 1 | |
612 // Generate the single "exit" point, where we get if either all map/instance | 642 // Generate the single "exit" point, where we get if either all map/instance |
613 // type checks failed, or one of the assumptions inside one of the cases | 643 // type checks failed, or one of the assumptions inside one of the cases |
614 // failes (i.e. failing prototype chain check). | 644 // failes (i.e. failing prototype chain check). |
615 // TODO(bmeurer): Consider falling back to IC here if deoptimization is | 645 // TODO(bmeurer): Consider falling back to IC here if deoptimization is |
616 // disabled. | 646 // disabled. |
617 int const exit_control_count = static_cast<int>(exit_controls.size()); | 647 int const exit_control_count = static_cast<int>(exit_controls.size()); |
618 Node* exit_control = | 648 Node* exit_control = |
619 (exit_control_count == 1) | 649 (exit_control_count == 1) |
620 ? exit_controls.front() | 650 ? exit_controls.front() |
621 : graph()->NewNode(common()->Merge(exit_control_count), | 651 : graph()->NewNode(common()->Merge(exit_control_count), |
622 exit_control_count, &exit_controls.front()); | 652 exit_control_count, &exit_controls.front()); |
623 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, | 653 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, |
624 exit_effect, exit_control); | 654 exit_effect, exit_control); |
625 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | 655 // TODO(bmeurer): This should be on the AdvancedReducer somehow. |
626 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | 656 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); |
627 #else | |
628 for (Node* const exit_control : exit_controls) { | |
629 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, | |
630 exit_effect, exit_control); | |
631 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | |
632 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | |
633 } | |
634 #endif | |
635 | 657 |
636 // Generate the final merge point for all (polymorphic) branches. | 658 // Generate the final merge point for all (polymorphic) branches. |
637 Node* value; | 659 Node* value; |
638 int const control_count = static_cast<int>(controls.size()); | 660 int const control_count = static_cast<int>(controls.size()); |
639 if (control_count == 1) { | 661 if (control_count == 1) { |
640 value = values.front(); | 662 value = values.front(); |
641 effect = effects.front(); | 663 effect = effects.front(); |
642 control = controls.front(); | 664 control = controls.front(); |
643 } else { | 665 } else { |
644 control = graph()->NewNode(common()->Merge(control_count), control_count, | 666 control = graph()->NewNode(common()->Merge(control_count), control_count, |
645 &controls.front()); | 667 &controls.front()); |
646 values.push_back(control); | 668 values.push_back(control); |
647 value = graph()->NewNode(common()->Phi(kMachAnyTagged, control_count), | 669 value = graph()->NewNode(common()->Phi(kMachAnyTagged, control_count), |
648 control_count + 1, &values.front()); | 670 control_count + 1, &values.front()); |
649 effects.push_back(control); | 671 effects.push_back(control); |
650 effect = graph()->NewNode(common()->EffectPhi(control_count), | 672 effect = graph()->NewNode(common()->EffectPhi(control_count), |
651 control_count + 1, &effects.front()); | 673 control_count + 1, &effects.front()); |
652 } | 674 } |
653 return Replace(node, value, effect, control); | 675 return Replace(node, value, effect, control); |
654 } | 676 } |
655 | 677 |
656 | 678 |
679 Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { | |
680 DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode()); | |
681 StoreNamedParameters const p = StoreNamedParametersOf(node->op()); | |
682 Handle<Name> name = p.name(); | |
683 Node* receiver = NodeProperties::GetValueInput(node, 0); | |
684 Node* value = NodeProperties::GetValueInput(node, 1); | |
685 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); | |
686 Node* effect = NodeProperties::GetEffectInput(node); | |
687 Node* control = NodeProperties::GetControlInput(node); | |
688 | |
689 // Not much we can do if deoptimization support is disabled. | |
690 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | |
691 | |
692 // Extract receiver maps from the STORE_IC using the StoreICNexus. | |
693 MapHandleList receiver_maps; | |
694 if (!p.feedback().IsValid()) return NoChange(); | |
695 StoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); | |
696 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); | |
697 DCHECK_LT(0, receiver_maps.length()); | |
698 | |
699 // Compute property access infos for the receiver maps. | |
700 ZoneVector<PropertyAccessInfo> access_infos(zone()); | |
701 if (!ComputePropertyAccessInfos(receiver_maps, name, kStore, &access_infos)) { | |
702 return NoChange(); | |
703 } | |
704 DCHECK(!access_infos.empty()); | |
705 | |
706 // The final states for every polymorphic branch. We join them with | |
707 // Merge+EffectPhi at the bottom. | |
708 ZoneVector<Node*> effects(zone()); | |
709 ZoneVector<Node*> controls(zone()); | |
710 | |
711 // The list of "exiting" controls, which currently go to a single deoptimize. | |
712 // TODO(bmeurer): Consider using an IC as fallback. | |
713 Node* const exit_effect = effect; | |
714 ZoneVector<Node*> exit_controls(zone()); | |
715 | |
716 // Ensure that {receiver} is a heap object. | |
717 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); | |
718 Node* branch = | |
719 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); | |
720 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | |
721 control = graph()->NewNode(common()->IfFalse(), branch); | |
722 | |
723 // Load the {receiver} map. The resulting effect is the dominating effect for | |
724 // all (polymorphic) branches. | |
725 Node* receiver_map = effect = | |
726 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | |
727 receiver, effect, control); | |
728 | |
729 // Generate code for the various different property access patterns. | |
730 Node* fallthrough_control = control; | |
731 for (PropertyAccessInfo const& access_info : access_infos) { | |
732 Node* this_receiver = receiver; | |
733 Node* this_effect = effect; | |
734 Node* this_control; | |
735 | |
736 // Perform map check on {receiver}. | |
737 Type* receiver_type = access_info.receiver_type(); | |
738 if (receiver_type->Is(Type::String())) { | |
739 // Emit an instance type check for strings. | |
740 Node* receiver_instance_type = this_effect = graph()->NewNode( | |
741 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), | |
742 receiver_map, this_effect, fallthrough_control); | |
743 Node* check = | |
744 graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type, | |
745 jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE)); | |
746 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
747 check, fallthrough_control); | |
748 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | |
749 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
750 } else { | |
751 // Emit a (sequence of) map checks for other properties. | |
752 ZoneVector<Node*> this_controls(zone()); | |
753 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); | |
754 i.Advance()) { | |
755 Handle<Map> map = i.Current(); | |
756 Node* check = | |
757 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), | |
758 receiver_map, jsgraph()->Constant(map)); | |
759 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
760 check, fallthrough_control); | |
761 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | |
762 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | |
763 } | |
764 int const this_control_count = static_cast<int>(this_controls.size()); | |
765 this_control = | |
766 (this_control_count == 1) | |
767 ? this_controls.front() | |
768 : graph()->NewNode(common()->Merge(this_control_count), | |
769 this_control_count, &this_controls.front()); | |
770 } | |
771 | |
772 // Determine actual holder and perform prototype chain checks. | |
773 Handle<JSObject> holder; | |
774 if (access_info.holder().ToHandle(&holder)) { | |
775 this_receiver = jsgraph()->Constant(holder); | |
776 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); | |
777 i.Advance()) { | |
778 Handle<Map> map = i.Current(); | |
779 PrototypeIterator j(map); | |
780 while (true) { | |
781 // Check that the {prototype} still has the same map. For stable | |
782 // maps, we can add a stability dependency on the prototype map; | |
783 // for everything else we need to perform a map check at runtime. | |
784 Handle<JSReceiver> prototype = | |
785 PrototypeIterator::GetCurrent<JSReceiver>(j); | |
786 if (prototype->map()->is_stable()) { | |
787 dependencies()->AssumeMapStable( | |
788 handle(prototype->map(), isolate())); | |
789 } else { | |
790 Node* prototype_map = this_effect = graph()->NewNode( | |
791 simplified()->LoadField(AccessBuilder::ForMap()), | |
792 jsgraph()->Constant(prototype), this_effect, this_control); | |
793 Node* check = graph()->NewNode( | |
794 simplified()->ReferenceEqual(Type::Internal()), prototype_map, | |
795 jsgraph()->Constant(handle(prototype->map(), isolate()))); | |
796 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
797 check, this_control); | |
798 exit_controls.push_back( | |
799 graph()->NewNode(common()->IfFalse(), branch)); | |
800 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
801 } | |
802 // Stop once we get to the holder. | |
803 if (prototype.is_identical_to(holder)) break; | |
804 j.Advance(); | |
805 } | |
806 } | |
807 } | |
808 | |
809 // Generate the actual property access. | |
810 if (access_info.IsDataConstant()) { | |
811 Node* check = | |
812 graph()->NewNode(simplified()->ReferenceEqual(Type::Tagged()), value, | |
813 jsgraph()->Constant(access_info.constant())); | |
814 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
815 check, this_control); | |
816 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | |
817 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
818 } else { | |
819 // TODO(bmeurer): This is sort of adhoc, and must be refactored into some | |
820 // common code once we also have support for stores. | |
Jarin
2015/10/22 09:27:30
Update comment.
Benedikt Meurer
2015/10/22 11:38:40
Done.
| |
821 DCHECK(access_info.IsDataField()); | |
822 FieldIndex const field_index = access_info.field_index(); | |
823 Type* const field_type = access_info.field_type(); | |
824 if (!field_index.is_inobject()) { | |
825 this_receiver = this_effect = graph()->NewNode( | |
826 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), | |
827 this_receiver, this_effect, this_control); | |
828 } | |
829 FieldAccess field_access; | |
830 field_access.base_is_tagged = kTaggedBase; | |
831 field_access.offset = field_index.offset(); | |
832 field_access.name = name; | |
833 field_access.type = field_type; | |
834 field_access.machine_type = kMachAnyTagged; | |
835 if (field_type->Is(Type::TaggedSigned())) { | |
836 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); | |
837 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
838 check, this_control); | |
839 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | |
840 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
841 } else if (field_type->Is(Type::TaggedPointer())) { | |
842 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); | |
843 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), | |
844 check, this_control); | |
845 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | |
846 this_control = graph()->NewNode(common()->IfFalse(), branch); | |
847 if (field_type->NumClasses() > 0) { | |
848 // Emit a (sequence of) map checks for the value. | |
849 ZoneVector<Node*> this_controls(zone()); | |
850 Node* value_map = this_effect = | |
851 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | |
852 value, this_effect, this_control); | |
853 for (auto i = field_type->Classes(); !i.Done(); i.Advance()) { | |
854 Handle<Map> field_map(i.Current()); | |
855 check = | |
Jarin
2015/10/22 09:27:30
How about not reusing the variable here and naming
| |
856 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), | |
857 value_map, jsgraph()->Constant(field_map)); | |
858 branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
859 check, this_control); | |
860 this_control = graph()->NewNode(common()->IfFalse(), branch); | |
861 this_controls.push_back( | |
862 graph()->NewNode(common()->IfTrue(), branch)); | |
863 } | |
864 exit_controls.push_back(this_control); | |
865 int const this_control_count = static_cast<int>(this_controls.size()); | |
866 this_control = (this_control_count == 1) | |
867 ? this_controls.front() | |
868 : graph()->NewNode( | |
869 common()->Merge(this_control_count), | |
870 this_control_count, &this_controls.front()); | |
871 } | |
872 } else { | |
873 DCHECK(field_type->Is(Type::Tagged())); | |
874 } | |
875 this_effect = | |
876 graph()->NewNode(simplified()->StoreField(field_access), | |
877 this_receiver, value, this_effect, this_control); | |
878 } | |
879 | |
880 // Remember the final state for this property access. | |
881 effects.push_back(this_effect); | |
882 controls.push_back(this_control); | |
883 } | |
884 | |
885 // Collect the fallthru control as final "exit" control. | |
886 exit_controls.push_back(fallthrough_control); | |
887 | |
888 // Generate the single "exit" point, where we get if either all map/instance | |
889 // type checks failed, or one of the assumptions inside one of the cases | |
890 // failes (i.e. failing prototype chain check). | |
891 // TODO(bmeurer): Consider falling back to IC here if deoptimization is | |
892 // disabled. | |
893 int const exit_control_count = static_cast<int>(exit_controls.size()); | |
894 Node* exit_control = | |
895 (exit_control_count == 1) | |
896 ? exit_controls.front() | |
897 : graph()->NewNode(common()->Merge(exit_control_count), | |
898 exit_control_count, &exit_controls.front()); | |
899 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, | |
900 exit_effect, exit_control); | |
901 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | |
902 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | |
903 | |
904 // Generate the final merge point for all (polymorphic) branches. | |
905 int const control_count = static_cast<int>(controls.size()); | |
906 if (control_count == 1) { | |
907 effect = effects.front(); | |
908 control = controls.front(); | |
909 } else { | |
910 control = graph()->NewNode(common()->Merge(control_count), control_count, | |
911 &controls.front()); | |
912 effects.push_back(control); | |
913 effect = graph()->NewNode(common()->EffectPhi(control_count), | |
914 control_count + 1, &effects.front()); | |
915 } | |
916 return Replace(node, value, effect, control); | |
917 } | |
918 | |
919 | |
657 Reduction JSNativeContextSpecialization::Replace(Node* node, | 920 Reduction JSNativeContextSpecialization::Replace(Node* node, |
658 Handle<Object> value) { | 921 Handle<Object> value) { |
659 return Replace(node, jsgraph()->Constant(value)); | 922 return Replace(node, jsgraph()->Constant(value)); |
660 } | 923 } |
661 | 924 |
662 | 925 |
663 bool JSNativeContextSpecialization::LookupInScriptContextTable( | 926 bool JSNativeContextSpecialization::LookupInScriptContextTable( |
664 Handle<Name> name, ScriptContextTableLookupResult* result) { | 927 Handle<Name> name, ScriptContextTableLookupResult* result) { |
665 if (!name->IsString()) return false; | 928 if (!name->IsString()) return false; |
666 Handle<ScriptContextTable> script_context_table( | 929 Handle<ScriptContextTable> script_context_table( |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
709 } | 972 } |
710 | 973 |
711 | 974 |
712 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { | 975 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { |
713 return jsgraph()->simplified(); | 976 return jsgraph()->simplified(); |
714 } | 977 } |
715 | 978 |
716 } // namespace compiler | 979 } // namespace compiler |
717 } // namespace internal | 980 } // namespace internal |
718 } // namespace v8 | 981 } // namespace v8 |
OLD | NEW |