Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(353)

Side by Side Diff: src/compiler/js-native-context-specialization.cc

Issue 1422573002: [turbofan] Initial support for monomorphic/polymorphic property stores. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/compiler/js-native-context-specialization.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « src/compiler/js-native-context-specialization.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698