| OLD | NEW |
| 1 // Copyright 2013 the V8 project authors. All rights reserved. | 1 // Copyright 2013 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 "hydrogen-check-elimination.h" | 5 #include "hydrogen-check-elimination.h" |
| 6 | 6 |
| 7 #include "hydrogen-alias-analysis.h" | 7 #include "hydrogen-alias-analysis.h" |
| 8 #include "hydrogen-flow-engine.h" | 8 #include "hydrogen-flow-engine.h" |
| 9 | 9 |
| 10 #define GLOBAL 1 | 10 #define GLOBAL 1 |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 97 break; | 97 break; |
| 98 } | 98 } |
| 99 case HValue::kCompareMap: { | 99 case HValue::kCompareMap: { |
| 100 ReduceCompareMap(HCompareMap::cast(instr)); | 100 ReduceCompareMap(HCompareMap::cast(instr)); |
| 101 break; | 101 break; |
| 102 } | 102 } |
| 103 case HValue::kCompareObjectEqAndBranch: { | 103 case HValue::kCompareObjectEqAndBranch: { |
| 104 ReduceCompareObjectEqAndBranch(HCompareObjectEqAndBranch::cast(instr)); | 104 ReduceCompareObjectEqAndBranch(HCompareObjectEqAndBranch::cast(instr)); |
| 105 break; | 105 break; |
| 106 } | 106 } |
| 107 case HValue::kIsStringAndBranch: { | |
| 108 ReduceIsStringAndBranch(HIsStringAndBranch::cast(instr)); | |
| 109 break; | |
| 110 } | |
| 111 case HValue::kTransitionElementsKind: { | 107 case HValue::kTransitionElementsKind: { |
| 112 ReduceTransitionElementsKind( | 108 ReduceTransitionElementsKind( |
| 113 HTransitionElementsKind::cast(instr)); | 109 HTransitionElementsKind::cast(instr)); |
| 114 break; | 110 break; |
| 115 } | 111 } |
| 116 case HValue::kCheckHeapObject: { | 112 case HValue::kCheckHeapObject: { |
| 117 ReduceCheckHeapObject(HCheckHeapObject::cast(instr)); | 113 ReduceCheckHeapObject(HCheckHeapObject::cast(instr)); |
| 118 break; | 114 break; |
| 119 } | 115 } |
| 120 case HValue::kCheckInstanceType: { | |
| 121 ReduceCheckInstanceType(HCheckInstanceType::cast(instr)); | |
| 122 break; | |
| 123 } | |
| 124 default: { | 116 default: { |
| 125 // If the instruction changes maps uncontrollably, drop everything. | 117 // If the instruction changes maps uncontrollably, drop everything. |
| 126 if (instr->CheckChangesFlag(kOsrEntries)) { | 118 if (instr->CheckChangesFlag(kOsrEntries)) { |
| 127 Kill(); | 119 Kill(); |
| 128 break; | 120 break; |
| 129 } | 121 } |
| 130 if (instr->CheckChangesFlag(kElementsKind) || | 122 if (instr->CheckChangesFlag(kElementsKind) || |
| 131 instr->CheckChangesFlag(kMaps)) { | 123 instr->CheckChangesFlag(kMaps)) { |
| 132 KillUnstableEntries(); | 124 KillUnstableEntries(); |
| 133 } | 125 } |
| 134 } | 126 } |
| 135 // Improvements possible: | 127 // Improvements possible: |
| 136 // - eliminate redundant HCheckSmi instructions | 128 // - eliminate redundant HCheckSmi, HCheckInstanceType instructions |
| 137 // - track which values have been HCheckHeapObject'd | 129 // - track which values have been HCheckHeapObject'd |
| 138 } | 130 } |
| 139 | 131 |
| 140 return this; | 132 return this; |
| 141 } | 133 } |
| 142 | 134 |
| 143 // Support for global analysis with HFlowEngine: Merge given state with | 135 // Support for global analysis with HFlowEngine: Merge given state with |
| 144 // the other incoming state. | 136 // the other incoming state. |
| 145 static HCheckTable* Merge(HCheckTable* succ_state, HBasicBlock* succ_block, | 137 static HCheckTable* Merge(HCheckTable* succ_state, HBasicBlock* succ_block, |
| 146 HCheckTable* pred_state, HBasicBlock* pred_block, | 138 HCheckTable* pred_state, HBasicBlock* pred_block, |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 262 } else { | 254 } else { |
| 263 EnsureChecked(le, cmp->left(), cmp); | 255 EnsureChecked(le, cmp->left(), cmp); |
| 264 EnsureChecked(re, cmp->right(), cmp); | 256 EnsureChecked(re, cmp->right(), cmp); |
| 265 le->maps_ = re->maps_ = le->maps_->Intersect(re->maps_, zone); | 257 le->maps_ = re->maps_ = le->maps_->Intersect(re->maps_, zone); |
| 266 le->state_ = re->state_ = HCheckTableEntry::StateMerge( | 258 le->state_ = re->state_ = HCheckTableEntry::StateMerge( |
| 267 le->state_, re->state_); | 259 le->state_, re->state_); |
| 268 ASSERT_NE(HCheckTableEntry::UNCHECKED_STABLE, le->state_); | 260 ASSERT_NE(HCheckTableEntry::UNCHECKED_STABLE, le->state_); |
| 269 ASSERT_NE(HCheckTableEntry::UNCHECKED_STABLE, re->state_); | 261 ASSERT_NE(HCheckTableEntry::UNCHECKED_STABLE, re->state_); |
| 270 } | 262 } |
| 271 learned = true; | 263 learned = true; |
| 272 } else if (end->IsIsStringAndBranch()) { | |
| 273 HIsStringAndBranch* cmp = HIsStringAndBranch::cast(end); | |
| 274 HValue* object = cmp->value()->ActualValue(); | |
| 275 HCheckTableEntry* entry = copy->Find(object); | |
| 276 if (is_true_branch) { | |
| 277 // Learn on the true branch of if(IsString(x)). | |
| 278 if (entry == NULL) { | |
| 279 copy->Insert(object, NULL, string_maps(), | |
| 280 HCheckTableEntry::CHECKED); | |
| 281 } else { | |
| 282 EnsureChecked(entry, object, cmp); | |
| 283 entry->maps_ = entry->maps_->Intersect(string_maps(), zone); | |
| 284 ASSERT_NE(HCheckTableEntry::UNCHECKED_STABLE, entry->state_); | |
| 285 } | |
| 286 } else { | |
| 287 // Learn on the false branch of if(IsString(x)). | |
| 288 if (entry != NULL) { | |
| 289 EnsureChecked(entry, object, cmp); | |
| 290 entry->maps_ = entry->maps_->Subtract(string_maps(), zone); | |
| 291 ASSERT_NE(HCheckTableEntry::UNCHECKED_STABLE, entry->state_); | |
| 292 } | |
| 293 } | |
| 294 } | 264 } |
| 295 // Learning on false branches requires storing negative facts. | 265 // Learning on false branches requires storing negative facts. |
| 296 } | 266 } |
| 297 | 267 |
| 298 if (FLAG_trace_check_elimination) { | 268 if (FLAG_trace_check_elimination) { |
| 299 PrintF("B%d checkmaps-table %s from B%d:\n", | 269 PrintF("B%d checkmaps-table %s from B%d:\n", |
| 300 succ->block_id(), | 270 succ->block_id(), |
| 301 learned ? "learned" : "copied", | 271 learned ? "learned" : "copied", |
| 302 from_block->block_id()); | 272 from_block->block_id()); |
| 303 Print(copy); | 273 Print(copy); |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 438 } else { | 408 } else { |
| 439 // No entry; insert a new one. | 409 // No entry; insert a new one. |
| 440 HCheckTableEntry::State state = instr->maps_are_stable() | 410 HCheckTableEntry::State state = instr->maps_are_stable() |
| 441 ? HCheckTableEntry::CHECKED_STABLE | 411 ? HCheckTableEntry::CHECKED_STABLE |
| 442 : HCheckTableEntry::CHECKED; | 412 : HCheckTableEntry::CHECKED; |
| 443 HCheckMaps* check = instr->IsStabilityCheck() ? NULL : instr; | 413 HCheckMaps* check = instr->IsStabilityCheck() ? NULL : instr; |
| 444 Insert(object, check, instr->maps(), state); | 414 Insert(object, check, instr->maps(), state); |
| 445 } | 415 } |
| 446 } | 416 } |
| 447 | 417 |
| 448 void ReduceCheckInstanceType(HCheckInstanceType* instr) { | |
| 449 HValue* value = instr->value()->ActualValue(); | |
| 450 HCheckTableEntry* entry = Find(value); | |
| 451 if (entry == NULL) { | |
| 452 if (instr->check() == HCheckInstanceType::IS_STRING) { | |
| 453 Insert(value, NULL, string_maps(), HCheckTableEntry::CHECKED); | |
| 454 } | |
| 455 return; | |
| 456 } | |
| 457 UniqueSet<Map>* maps = new(zone()) UniqueSet<Map>( | |
| 458 entry->maps_->size(), zone()); | |
| 459 for (int i = 0; i < entry->maps_->size(); ++i) { | |
| 460 InstanceType type; | |
| 461 Unique<Map> map = entry->maps_->at(i); | |
| 462 { | |
| 463 // This is safe, because maps don't move and their instance type does | |
| 464 // not change. | |
| 465 AllowHandleDereference allow_deref; | |
| 466 type = map.handle()->instance_type(); | |
| 467 } | |
| 468 if (instr->is_interval_check()) { | |
| 469 InstanceType first_type, last_type; | |
| 470 instr->GetCheckInterval(&first_type, &last_type); | |
| 471 if (first_type <= type && type <= last_type) maps->Add(map, zone()); | |
| 472 } else { | |
| 473 uint8_t mask, tag; | |
| 474 instr->GetCheckMaskAndTag(&mask, &tag); | |
| 475 if ((type & mask) == tag) maps->Add(map, zone()); | |
| 476 } | |
| 477 } | |
| 478 if (maps->size() == entry->maps_->size()) { | |
| 479 TRACE(("Removing redundant CheckInstanceType #%d at B%d\n", | |
| 480 instr->id(), instr->block()->block_id())); | |
| 481 EnsureChecked(entry, value, instr); | |
| 482 instr->DeleteAndReplaceWith(value); | |
| 483 INC_STAT(removed_cit_); | |
| 484 } else if (maps->size() != 0) { | |
| 485 entry->maps_ = maps; | |
| 486 if (entry->state_ == HCheckTableEntry::UNCHECKED_STABLE) { | |
| 487 entry->state_ = HCheckTableEntry::CHECKED_STABLE; | |
| 488 } | |
| 489 } | |
| 490 } | |
| 491 | |
| 492 void ReduceLoadNamedField(HLoadNamedField* instr) { | 418 void ReduceLoadNamedField(HLoadNamedField* instr) { |
| 493 // Reduce a load of the map field when it is known to be a constant. | 419 // Reduce a load of the map field when it is known to be a constant. |
| 494 if (!instr->access().IsMap()) { | 420 if (!instr->access().IsMap()) { |
| 495 // Check if we introduce field maps here. | 421 // Check if we introduce field maps here. |
| 496 MapSet maps = instr->maps(); | 422 MapSet maps = instr->maps(); |
| 497 if (maps != NULL) { | 423 if (maps != NULL) { |
| 498 ASSERT_NE(0, maps->size()); | 424 ASSERT_NE(0, maps->size()); |
| 499 Insert(instr, NULL, maps, HCheckTableEntry::UNCHECKED_STABLE); | 425 Insert(instr, NULL, maps, HCheckTableEntry::UNCHECKED_STABLE); |
| 500 } | 426 } |
| 501 return; | 427 return; |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 595 | 521 |
| 596 TRACE(("Marking redundant CompareObjectEqAndBranch #%d at B%d as false\n", | 522 TRACE(("Marking redundant CompareObjectEqAndBranch #%d at B%d as false\n", |
| 597 instr->id(), instr->block()->block_id())); | 523 instr->id(), instr->block()->block_id())); |
| 598 int succ = 1; | 524 int succ = 1; |
| 599 instr->set_known_successor_index(succ); | 525 instr->set_known_successor_index(succ); |
| 600 | 526 |
| 601 int unreachable_succ = 1 - succ; | 527 int unreachable_succ = 1 - succ; |
| 602 instr->block()->MarkSuccEdgeUnreachable(unreachable_succ); | 528 instr->block()->MarkSuccEdgeUnreachable(unreachable_succ); |
| 603 } | 529 } |
| 604 | 530 |
| 605 void ReduceIsStringAndBranch(HIsStringAndBranch* instr) { | |
| 606 HValue* value = instr->value()->ActualValue(); | |
| 607 HCheckTableEntry* entry = Find(value); | |
| 608 if (entry == NULL) return; | |
| 609 EnsureChecked(entry, value, instr); | |
| 610 int succ; | |
| 611 if (entry->maps_->IsSubset(string_maps())) { | |
| 612 TRACE(("Marking redundant IsStringAndBranch #%d at B%d as true\n", | |
| 613 instr->id(), instr->block()->block_id())); | |
| 614 succ = 0; | |
| 615 } else { | |
| 616 MapSet intersection = entry->maps_->Intersect(string_maps(), zone()); | |
| 617 if (intersection->size() > 0) return; | |
| 618 TRACE(("Marking redundant IsStringAndBranch #%d at B%d as false\n", | |
| 619 instr->id(), instr->block()->block_id())); | |
| 620 succ = 1; | |
| 621 } | |
| 622 instr->set_known_successor_index(succ); | |
| 623 int unreachable_succ = 1 - succ; | |
| 624 instr->block()->MarkSuccEdgeUnreachable(unreachable_succ); | |
| 625 } | |
| 626 | |
| 627 void ReduceTransitionElementsKind(HTransitionElementsKind* instr) { | 531 void ReduceTransitionElementsKind(HTransitionElementsKind* instr) { |
| 628 HValue* object = instr->object()->ActualValue(); | 532 HValue* object = instr->object()->ActualValue(); |
| 629 HCheckTableEntry* entry = Find(object); | 533 HCheckTableEntry* entry = Find(object); |
| 630 // Can only learn more about an object that already has a known set of maps. | 534 // Can only learn more about an object that already has a known set of maps. |
| 631 if (entry == NULL) return; | 535 if (entry == NULL) return; |
| 632 EnsureChecked(entry, object, instr); | 536 EnsureChecked(entry, object, instr); |
| 633 if (entry->maps_->Contains(instr->original_map())) { | 537 if (entry->maps_->Contains(instr->original_map())) { |
| 634 // If the object has the original map, it will be transitioned. | 538 // If the object has the original map, it will be transitioned. |
| 635 UniqueSet<Map>* maps = entry->maps_->Copy(zone()); | 539 UniqueSet<Map>* maps = entry->maps_->Copy(zone()); |
| 636 maps->Remove(instr->original_map()); | 540 maps->Remove(instr->original_map()); |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 777 entry->object_ = object; | 681 entry->object_ = object; |
| 778 entry->check_ = check; | 682 entry->check_ = check; |
| 779 entry->maps_ = maps; | 683 entry->maps_ = maps; |
| 780 entry->state_ = state; | 684 entry->state_ = state; |
| 781 // If the table becomes full, wrap around and overwrite older entries. | 685 // If the table becomes full, wrap around and overwrite older entries. |
| 782 if (cursor_ == kMaxTrackedObjects) cursor_ = 0; | 686 if (cursor_ == kMaxTrackedObjects) cursor_ = 0; |
| 783 if (size_ < kMaxTrackedObjects) size_++; | 687 if (size_ < kMaxTrackedObjects) size_++; |
| 784 } | 688 } |
| 785 | 689 |
| 786 Zone* zone() const { return phase_->zone(); } | 690 Zone* zone() const { return phase_->zone(); } |
| 787 MapSet string_maps() const { return phase_->string_maps(); } | |
| 788 | 691 |
| 789 friend class HCheckMapsEffects; | 692 friend class HCheckMapsEffects; |
| 790 friend class HCheckEliminationPhase; | 693 friend class HCheckEliminationPhase; |
| 791 | 694 |
| 792 HCheckEliminationPhase* phase_; | 695 HCheckEliminationPhase* phase_; |
| 793 HCheckTableEntry entries_[kMaxTrackedObjects]; | 696 HCheckTableEntry entries_[kMaxTrackedObjects]; |
| 794 int16_t cursor_; // Must be <= kMaxTrackedObjects | 697 int16_t cursor_; // Must be <= kMaxTrackedObjects |
| 795 int16_t size_; // Must be <= kMaxTrackedObjects | 698 int16_t size_; // Must be <= kMaxTrackedObjects |
| 796 STATIC_ASSERT(kMaxTrackedObjects < (1 << 15)); | 699 STATIC_ASSERT(kMaxTrackedObjects < (1 << 15)); |
| 797 }; | 700 }; |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 884 // Are we eliminated yet? | 787 // Are we eliminated yet? |
| 885 void HCheckEliminationPhase::PrintStats() { | 788 void HCheckEliminationPhase::PrintStats() { |
| 886 #if DEBUG | 789 #if DEBUG |
| 887 #define PRINT_STAT(x) if (x##_ > 0) PrintF(" %-16s = %2d\n", #x, x##_) | 790 #define PRINT_STAT(x) if (x##_ > 0) PrintF(" %-16s = %2d\n", #x, x##_) |
| 888 #else | 791 #else |
| 889 #define PRINT_STAT(x) | 792 #define PRINT_STAT(x) |
| 890 #endif | 793 #endif |
| 891 PRINT_STAT(redundant); | 794 PRINT_STAT(redundant); |
| 892 PRINT_STAT(removed); | 795 PRINT_STAT(removed); |
| 893 PRINT_STAT(removed_cho); | 796 PRINT_STAT(removed_cho); |
| 894 PRINT_STAT(removed_cit); | |
| 895 PRINT_STAT(narrowed); | 797 PRINT_STAT(narrowed); |
| 896 PRINT_STAT(loads); | 798 PRINT_STAT(loads); |
| 897 PRINT_STAT(empty); | 799 PRINT_STAT(empty); |
| 898 PRINT_STAT(compares_true); | 800 PRINT_STAT(compares_true); |
| 899 PRINT_STAT(compares_false); | 801 PRINT_STAT(compares_false); |
| 900 PRINT_STAT(transitions); | 802 PRINT_STAT(transitions); |
| 901 } | 803 } |
| 902 | 804 |
| 903 } } // namespace v8::internal | 805 } } // namespace v8::internal |
| OLD | NEW |