OLD | NEW |
---|---|
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 2543 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2554 if (FLAG_use_range) { | 2554 if (FLAG_use_range) { |
2555 HRangeAnalysis rangeAnalysis(graph()); | 2555 HRangeAnalysis rangeAnalysis(graph()); |
2556 rangeAnalysis.Analyze(); | 2556 rangeAnalysis.Analyze(); |
2557 } | 2557 } |
2558 graph()->ComputeMinusZeroChecks(); | 2558 graph()->ComputeMinusZeroChecks(); |
2559 | 2559 |
2560 // Eliminate redundant stack checks on backwards branches. | 2560 // Eliminate redundant stack checks on backwards branches. |
2561 HStackCheckEliminator sce(graph()); | 2561 HStackCheckEliminator sce(graph()); |
2562 sce.Process(); | 2562 sce.Process(); |
2563 | 2563 |
2564 // Replace the results of check instructions with the original value, if the | 2564 graph()->EliminateRedundantBoundsChecks(); |
2565 // result is used. This is safe now, since we don't do code motion after this | |
2566 // point. It enables better register allocation since the value produced by | |
2567 // check instructions is really a copy of the original value. | |
2568 graph()->ReplaceCheckedValues(); | |
2569 | 2565 |
2570 return graph(); | 2566 return graph(); |
2571 } | 2567 } |
2572 | 2568 |
2573 | 2569 // We try to "factor up" HBoundsCheck instructions towards the root of the |
2574 void HGraph::ReplaceCheckedValues() { | 2570 // dominator tree. |
2575 HPhase phase("H_Replace checked values", this); | 2571 // For now we handle checks where the index is like "exp + int32value". |
2576 for (int i = 0; i < blocks()->length(); ++i) { | 2572 // If in the dominator tree we check "exp + v1" and later (dominated) |
2577 HInstruction* instr = blocks()->at(i)->first(); | 2573 // "exp + v2", if v2 <= v1 we can safely remove the second check, and if |
2578 while (instr != NULL) { | 2574 // v2 > v1 we can use v2 in the 1st check and again remove the second. |
2579 if (instr->IsBoundsCheck()) { | 2575 // To do so we keep a dictionary of all checks where the key if the pair |
2580 // Replace all uses of the checked value with the original input. | 2576 // "exp, length". |
2581 ASSERT(instr->UseCount() > 0); | 2577 // The class BoundsCheckKey represents this key. |
2582 instr->ReplaceAllUsesWith(HBoundsCheck::cast(instr)->index()); | 2578 class BoundsCheckKey : public ZoneObject { |
2579 public: | |
2580 HValue* IndexBase() const {return index_base_;} | |
2581 HValue* Length() const {return length_;} | |
2582 | |
2583 uint32_t Hash() { | |
2584 return static_cast<uint32_t>(index_base_->Hashcode() ^ length_->Hashcode()); | |
2585 } | |
2586 | |
2587 static BoundsCheckKey* Create(Zone* zone, | |
2588 HBoundsCheck* check, | |
2589 int32_t* offset) { | |
2590 HValue* index_base = NULL; | |
2591 HConstant* constant = NULL; | |
2592 if (check->index()->IsAdd()) { | |
Sven Panne
2012/04/13 11:38:06
If we extract a function like 'MatchOffsetExpresss
Massi
2012/04/17 10:56:58
I'll do it in a separate CL.
| |
2593 HAdd* index = HAdd::cast(check->index()); | |
2594 if (index->left()->IsConstant()) { | |
2595 constant = HConstant::cast(index->left()); | |
2596 index_base = index->right(); | |
2597 } else if (index->right()->IsConstant()) { | |
2598 constant = HConstant::cast(index->right()); | |
2599 index_base = index->left(); | |
2583 } | 2600 } |
2584 instr = instr->next(); | 2601 } |
2585 } | 2602 |
2586 } | 2603 if (constant != NULL && constant->HasInteger32Value()) { |
2587 } | 2604 *offset = constant->Integer32Value(); |
2588 | 2605 } else { |
2606 *offset = 0; | |
2607 index_base = check->index(); | |
2608 } | |
2609 return new(zone) BoundsCheckKey(index_base, check->length()); | |
2610 } | |
2611 | |
2612 private: | |
2613 BoundsCheckKey(HValue* index_base, HValue* length) { | |
2614 index_base_ = index_base; | |
2615 length_ = length; | |
2616 } | |
2617 | |
2618 HValue* index_base_; | |
2619 HValue* length_; | |
2620 }; | |
2621 | |
2622 // Data about each HBoundsCheck that can be eliminated or moved. | |
2623 // It is the "value" in the dictionary indexed by "base-index, length" | |
2624 // (the key is BoundsCheckKey). | |
2625 // We scan the code with a dominator tree traversal. | |
2626 // Traversing the dominator tree we keep a stack (implemented as a singly | |
2627 // linked list) of "data" for each basic block that contains a relevant check | |
2628 // with the same key (the dictionary holds the head of the list). | |
2629 // We also keep all the "data" created for a given basic block in a list, and | |
2630 // use it to "clean up" the dictionary when backtracking in the dominator tree | |
2631 // traversal. | |
2632 // Doing this each dictionary entry always directly points to the check that | |
2633 // is dominating the code being examined now. | |
2634 // We also track the current "offset" of the index expression and use it to | |
2635 // decide if any check is already "covered" (so it can be removed) or not. | |
2636 class BoundsCheckBbData: public ZoneObject { | |
2637 // class BoundsCheckBbData { | |
Sven Panne
2012/04/13 11:38:06
Remove that line.
Massi
2012/04/17 10:56:58
Done.
| |
2638 public: | |
2639 BoundsCheckKey* Key() {return key_;} | |
fschneider
2012/04/13 12:38:00
style-nit: add spaces after/before {}.
Massi
2012/04/17 10:56:58
Done.
| |
2640 int32_t Offset() {return offset_;} | |
2641 HBasicBlock* BasicBlock() {return basic_block_;} | |
2642 HBoundsCheck* Check() {return check_;} | |
2643 BoundsCheckBbData* NextInBasicBlock() {return next_in_basic_block_;} | |
2644 BoundsCheckBbData* FatherInDominatorTree() {return father_in_dominator_tree_;} | |
2645 | |
2646 static void RemoveCheck(HBoundsCheck* check) { | |
Sven Panne
2012/04/13 11:38:06
I think this method should be generalized and move
Massi
2012/04/17 10:56:58
Done.
| |
2647 HValue* index = check->index(); | |
2648 HValue* length = check->length(); | |
2649 check->DeleteAndReplaceWith(NULL); | |
2650 if (index->HasNoUses()) { | |
2651 index->DeleteAndReplaceWith(NULL); | |
2652 } | |
2653 if (length->HasNoUses()) { | |
2654 length->DeleteAndReplaceWith(NULL); | |
2655 } | |
2656 } | |
2657 | |
2658 void CoverCheck(HBoundsCheck* new_check, int32_t new_offset) { | |
2659 if (check_simulate_ != NULL) { | |
Sven Panne
2012/04/13 11:38:06
Use a guard clause instead, see http://martinfowle
Massi
2012/04/17 10:56:58
Done.
| |
2660 ASSERT(new_check->index()->IsAdd()); | |
2661 HAdd* new_check_index = HAdd::cast(new_check->index()); | |
2662 if (added_index_ == NULL) { | |
2663 HSimulate* added_simulate = | |
2664 new(BasicBlock()->zone()) HSimulate(check_simulate_->ast_id(), | |
2665 check_simulate_->pop_count()); | |
2666 added_simulate->InsertBefore(Check()); | |
2667 added_index_ = | |
2668 new(BasicBlock()->zone()) HAdd(new_check_index->context(), | |
2669 new_check_index->left(), | |
2670 new_check_index->right()); | |
2671 added_index_->InsertBefore(added_simulate); | |
fschneider
2012/04/13 12:38:00
I think this is a little problematic and can be si
Sven Panne
2012/04/13 12:49:33
Good point, but I suggest using 'foo->HasObservabl
Massi
2012/04/17 10:56:58
I ended up asserting that the index representation
| |
2672 } else { | |
2673 added_index_->SetOperandAt(1, new_check_index->left()); | |
2674 added_index_->SetOperandAt(2, new_check_index->right()); | |
2675 } | |
2676 Check()->SetOperandAt(0, added_index_); | |
2677 RemoveCheck(new_check); | |
2678 offset_ = new_offset; | |
2679 } | |
2680 } | |
2681 | |
2682 BoundsCheckBbData(BoundsCheckKey* key, int32_t offset, HBasicBlock* bb, | |
fschneider
2012/04/13 12:38:00
style-nit: One parameter pre line in definitions a
Massi
2012/04/17 10:56:58
Done.
| |
2683 HBoundsCheck* check, BoundsCheckBbData* next_in_bb, | |
2684 BoundsCheckBbData* father_in_dt) { | |
2685 key_ = key; | |
2686 offset_ = offset; | |
2687 basic_block_ = bb; | |
2688 check_ = check; | |
2689 if (check->next()->IsSimulate()) { | |
Sven Panne
2012/04/13 11:38:06
Using a ternary operator here and using initialize
Massi
2012/04/17 10:56:58
Done.
| |
2690 check_simulate_ = HSimulate::cast(check->next()); | |
2691 } else { | |
2692 check_simulate_ = NULL; | |
2693 } | |
2694 added_index_ = NULL; | |
2695 next_in_basic_block_ = next_in_bb; | |
2696 father_in_dominator_tree_ = father_in_dt; | |
2697 } | |
2698 | |
2699 private: | |
2700 BoundsCheckKey* key_; | |
2701 int32_t offset_; | |
2702 HBasicBlock* basic_block_; | |
2703 HBoundsCheck* check_; | |
2704 HSimulate* check_simulate_; | |
2705 HAdd* added_index_; | |
2706 BoundsCheckBbData* next_in_basic_block_; | |
2707 BoundsCheckBbData* father_in_dominator_tree_; | |
2708 }; | |
2709 | |
2710 static bool BoundsCheckKeyMatch(void* key1, void* key2) { | |
2711 BoundsCheckKey* k1 = static_cast<BoundsCheckKey*>(key1); | |
2712 BoundsCheckKey* k2 = static_cast<BoundsCheckKey*>(key2); | |
2713 | |
2714 return k1->IndexBase() == k2->IndexBase() && k1->Length() == k2->Length(); | |
2715 } | |
2716 | |
2717 class BoundsCheckTable : private ZoneHashMap { | |
2718 public: | |
2719 BoundsCheckBbData** LookupOrInsert(BoundsCheckKey* key) { | |
2720 intptr_t hash = key->Hash(); | |
Sven Panne
2012/04/13 11:38:06
Wrong type, but this is nicer when inlined, anyway
Massi
2012/04/17 10:56:58
Done.
| |
2721 ZoneHashMap::Entry* entry = Lookup(key, hash, true); | |
2722 return reinterpret_cast<BoundsCheckBbData**>(&(entry->value)); | |
2723 } | |
2724 | |
2725 void Insert(BoundsCheckKey* key, BoundsCheckBbData* data) { | |
2726 intptr_t hash = key->Hash(); | |
Sven Panne
2012/04/13 11:38:06
Inline again.
Massi
2012/04/17 10:56:58
Done.
| |
2727 ZoneHashMap::Entry* entry = Lookup(key, hash, true); | |
Sven Panne
2012/04/13 11:38:06
No need to name this explicitly.
Massi
2012/04/17 10:56:58
Done.
| |
2728 entry->value = data; | |
2729 } | |
2730 | |
2731 void Delete(BoundsCheckKey* key) { | |
2732 Remove(key, key->Hash()); | |
2733 } | |
2734 | |
2735 BoundsCheckTable() : ZoneHashMap(BoundsCheckKeyMatch) { | |
2736 } | |
2737 }; | |
2738 | |
2739 // Eliminates checks in bb and recursively in the dominated blocks. | |
2740 // Also replace the results of check instructions with the original value, if | |
2741 // the result is used. This is safe now, since we don't do code motion after | |
2742 // this point. It enables better register allocation since the value produced | |
2743 // by check instructions is really a copy of the original value. | |
2744 void HGraph::EliminateRedundantBoundsChecks(HBasicBlock* bb, | |
2745 BoundsCheckTable* table) { | |
2746 BoundsCheckBbData* bb_data_list = NULL; | |
2747 | |
2748 for (HInstruction* i = bb->first(); i != NULL; i = i->next()) { | |
2749 if (i->IsBoundsCheck()) { | |
Sven Panne
2012/04/13 11:38:06
'if (!i->IsBoundsCheck()) continue;' reduces nesti
Massi
2012/04/17 10:56:58
Done.
| |
2750 HBoundsCheck* check = HBoundsCheck::cast(i); | |
2751 check->ReplaceAllUsesWith(check->index()); | |
2752 isolate()->counters()->array_bounds_checks_seen()->Increment(); | |
2753 int32_t offset; | |
2754 // TODO(mmassi): allocate key only when we create a new table entry... | |
2755 BoundsCheckKey* key = | |
2756 BoundsCheckKey::Create(bb->zone(), check, &offset); | |
2757 | |
2758 BoundsCheckBbData** data_p = table->LookupOrInsert(key); | |
2759 BoundsCheckBbData* data = *data_p; | |
2760 if (data == NULL) { | |
2761 bb_data_list = new(bb->zone()) BoundsCheckBbData(key, | |
2762 offset, | |
2763 bb, | |
2764 check, | |
2765 bb_data_list, | |
2766 NULL); | |
2767 *data_p = bb_data_list; | |
2768 } else if (offset <= data->Offset()) { | |
2769 BoundsCheckBbData::RemoveCheck(check); | |
Sven Panne
2012/04/13 11:38:06
This will probably be something like 'check->Remov
Massi
2012/04/17 10:56:58
Done.
| |
2770 isolate()->counters()->array_bounds_checks_removed()->Increment(); | |
2771 } else if (data->BasicBlock() == bb) { | |
2772 data->CoverCheck(check, offset); | |
2773 isolate()->counters()->array_bounds_checks_removed()->Increment(); | |
Sven Panne
2012/04/13 11:38:06
I don't think that the increment here is correct,
Massi
2012/04/17 10:56:58
Done.
| |
2774 } else { | |
2775 bb_data_list = new(bb->zone()) BoundsCheckBbData(key, | |
2776 offset, | |
2777 bb, | |
2778 check, | |
2779 bb_data_list, | |
2780 data); | |
2781 table->Insert(key, bb_data_list); | |
2782 } | |
2783 } | |
2784 } | |
2785 | |
2786 for (int i = 0; i < bb->dominated_blocks()->length(); ++i) { | |
2787 EliminateRedundantBoundsChecks(bb->dominated_blocks()->at(i), table); | |
2788 } | |
2789 | |
2790 for (BoundsCheckBbData* data = bb_data_list; | |
2791 data != NULL; | |
2792 data = data->NextInBasicBlock()) { | |
2793 if (data->FatherInDominatorTree()) { | |
2794 table->Insert(data->Key(), data->FatherInDominatorTree()); | |
2795 } else { | |
2796 table->Delete(data->Key()); | |
2797 } | |
2798 } | |
2799 } | |
2800 | |
2801 void HGraph::EliminateRedundantBoundsChecks() { | |
2802 HPhase phase("H_Eliminate redundant bounds checks", this); | |
2803 AssertNoAllocation no_gc; | |
2804 BoundsCheckTable checks_table; | |
2805 EliminateRedundantBoundsChecks(entry_block(), &checks_table); | |
2806 } | |
2589 | 2807 |
2590 HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) { | 2808 HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) { |
2591 ASSERT(current_block() != NULL); | 2809 ASSERT(current_block() != NULL); |
2592 current_block()->AddInstruction(instr); | 2810 current_block()->AddInstruction(instr); |
2593 return instr; | 2811 return instr; |
2594 } | 2812 } |
2595 | 2813 |
2596 | 2814 |
2597 void HGraphBuilder::AddSimulate(int ast_id) { | 2815 void HGraphBuilder::AddSimulate(int ast_id) { |
2598 ASSERT(current_block() != NULL); | 2816 ASSERT(current_block() != NULL); |
(...skipping 5632 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
8231 } | 8449 } |
8232 } | 8450 } |
8233 | 8451 |
8234 #ifdef DEBUG | 8452 #ifdef DEBUG |
8235 if (graph_ != NULL) graph_->Verify(false); // No full verify. | 8453 if (graph_ != NULL) graph_->Verify(false); // No full verify. |
8236 if (allocator_ != NULL) allocator_->Verify(); | 8454 if (allocator_ != NULL) allocator_->Verify(); |
8237 #endif | 8455 #endif |
8238 } | 8456 } |
8239 | 8457 |
8240 } } // namespace v8::internal | 8458 } } // namespace v8::internal |
OLD | NEW |