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 |
2574 void HGraph::ReplaceCheckedValues() { | 2570 // We try to "factor up" HBoundsCheck instructions towards the root of the |
2575 HPhase phase("H_Replace checked values", this); | 2571 // dominator tree. |
2576 for (int i = 0; i < blocks()->length(); ++i) { | 2572 // For now we handle checks where the index is like "exp + int32value". |
2577 HInstruction* instr = blocks()->at(i)->first(); | 2573 // If in the dominator tree we check "exp + v1" and later (dominated) |
2578 while (instr != NULL) { | 2574 // "exp + v2", if v2 <= v1 we can safely remove the second check, and if |
2579 if (instr->IsBoundsCheck()) { | 2575 // v2 > v1 we can use v2 in the 1st check and again remove the second. |
2580 // Replace all uses of the checked value with the original input. | 2576 // To do so we keep a dictionary of all checks where the key if the pair |
2581 ASSERT(instr->UseCount() > 0); | 2577 // "exp, length". |
2582 instr->ReplaceAllUsesWith(HBoundsCheck::cast(instr)->index()); | 2578 // The class BoundsCheckKey represents this key. |
2579 class BoundsCheckKey : public ZoneObject { | |
2580 public: | |
2581 HValue* IndexBase() const { return index_base_; } | |
2582 HValue* Length() const { return length_; } | |
2583 | |
2584 uint32_t Hash() { | |
2585 return static_cast<uint32_t>(index_base_->Hashcode() ^ length_->Hashcode()); | |
2586 } | |
2587 | |
2588 static BoundsCheckKey* Create(Zone* zone, | |
2589 HBoundsCheck* check, | |
2590 int32_t* offset) { | |
2591 HValue* index_base = NULL; | |
2592 HConstant* constant = NULL; | |
2593 bool is_sub = false; | |
2594 | |
2595 if (check->index()->IsAdd()) { | |
2596 HAdd* index = HAdd::cast(check->index()); | |
2597 if (index->left()->IsConstant()) { | |
2598 constant = HConstant::cast(index->left()); | |
2599 index_base = index->right(); | |
2600 } else if (index->right()->IsConstant()) { | |
2601 constant = HConstant::cast(index->right()); | |
2602 index_base = index->left(); | |
2583 } | 2603 } |
2584 instr = instr->next(); | 2604 } else if (check->index()->IsSub()) { |
2585 } | 2605 HSub* index = HSub::cast(check->index()); |
2586 } | 2606 is_sub = true; |
2607 if (index->left()->IsConstant()) { | |
2608 constant = HConstant::cast(index->left()); | |
2609 index_base = index->right(); | |
2610 } else if (index->right()->IsConstant()) { | |
2611 constant = HConstant::cast(index->right()); | |
2612 index_base = index->left(); | |
2613 } | |
2614 } | |
2615 | |
2616 if (constant != NULL && constant->HasInteger32Value()) { | |
2617 *offset = is_sub ? - constant->Integer32Value() | |
fschneider
2012/04/25 09:40:48
I'd break this expression like this:
*offset = is
Massi
2012/04/25 12:19:19
Done.
| |
2618 : constant->Integer32Value(); | |
2619 } else { | |
2620 *offset = 0; | |
2621 index_base = check->index(); | |
2622 } | |
2623 | |
2624 return new(zone) BoundsCheckKey(index_base, check->length()); | |
2625 } | |
2626 | |
2627 private: | |
2628 BoundsCheckKey(HValue* index_base, HValue* length) { | |
fschneider
2012/04/25 09:40:48
This is may be rewritten shorter using an initiali
Massi
2012/04/25 12:19:19
Done but with the colon be on the 1st line: is it
Massi
2012/04/25 12:29:16
Fixed this and another one...
| |
2629 index_base_ = index_base; | |
2630 length_ = length; | |
2631 } | |
2632 | |
2633 HValue* index_base_; | |
2634 HValue* length_; | |
2635 }; | |
2636 | |
2637 | |
2638 // Data about each HBoundsCheck that can be eliminated or moved. | |
2639 // It is the "value" in the dictionary indexed by "base-index, length" | |
2640 // (the key is BoundsCheckKey). | |
2641 // We scan the code with a dominator tree traversal. | |
2642 // Traversing the dominator tree we keep a stack (implemented as a singly | |
2643 // linked list) of "data" for each basic block that contains a relevant check | |
2644 // with the same key (the dictionary holds the head of the list). | |
2645 // We also keep all the "data" created for a given basic block in a list, and | |
2646 // use it to "clean up" the dictionary when backtracking in the dominator tree | |
2647 // traversal. | |
2648 // Doing this each dictionary entry always directly points to the check that | |
2649 // is dominating the code being examined now. | |
2650 // We also track the current "offset" of the index expression and use it to | |
2651 // decide if any check is already "covered" (so it can be removed) or not. | |
2652 class BoundsCheckBbData: public ZoneObject { | |
2653 public: | |
2654 BoundsCheckKey* Key() const { return key_; } | |
2655 int32_t LowerOffset() const { return lower_offset_; } | |
2656 int32_t UpperOffset() const { return upper_offset_; } | |
2657 HBasicBlock* BasicBlock() const { return basic_block_; } | |
2658 HBoundsCheck* Check() const { return check_; } | |
2659 BoundsCheckBbData* NextInBasicBlock() const { return next_in_bb_; } | |
2660 BoundsCheckBbData* FatherInDominatorTree() const { return father_in_dt_; } | |
2661 | |
2662 bool OffsetIsCovered(int32_t offset) const { | |
2663 return offset >= LowerOffset() && offset <= UpperOffset(); | |
2664 } | |
2665 | |
2666 // This method removes new_check and modifies the current check so that it | |
2667 // also "covers" what new_check covered. | |
2668 // The obvious precondition is that new_check follows Check() in the | |
2669 // same basic block, and that new_offset is not covered (otherwise we | |
2670 // could simply remove new_check). | |
2671 // As a consequence LowerOffset() or UpperOffset() change (the covered | |
2672 // range grows). | |
2673 // | |
2674 // In the general case the check covering the current range should be like | |
2675 // these two checks: | |
2676 // 0 <= Key()->IndexBase() + LowerOffset() | |
2677 // Key()->IndexBase() + UpperOffset() < Key()->Length() | |
2678 // | |
2679 // We can transform the second check like this: | |
2680 // Key()->IndexBase() + LowerOffset() < | |
2681 // Key()->Length() + (LowerOffset() - UpperOffset()) | |
2682 // so we can handle both checks with a single unsigned comparison. | |
2683 // | |
2684 // The bulk of this method changes Check()->index() and Check()->length() | |
2685 // replacing them with new HAdd instructions to perform the transformation | |
2686 // described above. | |
2687 void CoverCheck(HBoundsCheck* new_check, | |
2688 int32_t new_offset) { | |
2689 ASSERT(new_check->index()->representation().IsInteger32()); | |
2690 | |
2691 if (new_offset > upper_offset_) { | |
2692 upper_offset_ = new_offset; | |
2693 } else if (new_offset < lower_offset_) { | |
2694 lower_offset_ = new_offset; | |
2695 } else { | |
2696 ASSERT(false); | |
2697 } | |
2698 | |
2699 BuildOffsetAdd(&added_index_, | |
2700 &added_index_offset_, | |
2701 Key()->IndexBase(), | |
2702 new_check->index()->representation(), | |
2703 lower_offset_); | |
2704 Check()->SetOperandAt(0, added_index_); | |
2705 BuildOffsetAdd(&added_length_, | |
2706 &added_length_offset_, | |
2707 Key()->Length(), | |
2708 new_check->length()->representation(), | |
2709 lower_offset_ - upper_offset_); | |
2710 Check()->SetOperandAt(1, added_length_); | |
2711 | |
2712 new_check->DeleteAndReplaceWith(NULL); | |
2713 } | |
2714 | |
2715 void RemoveZeroOperations() { | |
2716 RemoveZeroAdd(&added_index_, &added_index_offset_); | |
2717 RemoveZeroAdd(&added_length_, &added_length_offset_); | |
2718 } | |
2719 | |
2720 BoundsCheckBbData(BoundsCheckKey* key, | |
2721 int32_t lower_offset, | |
2722 int32_t upper_offset, | |
2723 HBasicBlock* bb, | |
2724 HBoundsCheck* check, | |
2725 BoundsCheckBbData* next_in_bb, | |
2726 BoundsCheckBbData* father_in_dt) : | |
2727 key_(key), | |
2728 lower_offset_(lower_offset), | |
2729 upper_offset_(upper_offset), | |
2730 basic_block_(bb), | |
2731 check_(check), | |
2732 added_index_offset_(NULL), | |
2733 added_index_(NULL), | |
2734 added_length_offset_(NULL), | |
2735 added_length_(NULL), | |
2736 next_in_bb_(next_in_bb), | |
2737 father_in_dt_(father_in_dt) { } | |
2738 | |
2739 private: | |
2740 BoundsCheckKey* key_; | |
2741 int32_t lower_offset_; | |
2742 int32_t upper_offset_; | |
2743 HBasicBlock* basic_block_; | |
2744 HBoundsCheck* check_; | |
2745 HConstant* added_index_offset_; | |
2746 HAdd* added_index_; | |
2747 HConstant* added_length_offset_; | |
2748 HAdd* added_length_; | |
2749 BoundsCheckBbData* next_in_bb_; | |
2750 BoundsCheckBbData* father_in_dt_; | |
2751 | |
2752 void BuildOffsetAdd(HAdd** add, | |
2753 HConstant** constant, | |
2754 HValue* original_value, | |
2755 Representation representation, | |
2756 int32_t new_offset) { | |
2757 HConstant* new_constant = new(BasicBlock()->zone()) | |
2758 HConstant(Handle<Object>(Smi::FromInt(new_offset)), | |
2759 Representation::Integer32()); | |
2760 if (*add == NULL) { | |
2761 new_constant->InsertBefore(Check()); | |
2762 *add = new(BasicBlock()->zone()) HAdd(NULL, | |
2763 original_value, | |
fschneider
2012/04/25 09:40:48
Don't the arguments fit on one line?
Massi
2012/04/25 12:19:19
No: 81 chars wide :-(
| |
2764 new_constant); | |
2765 (*add)->AssumeRepresentation(representation); | |
2766 (*add)->InsertBefore(Check()); | |
2767 } else { | |
2768 new_constant->InsertBefore(*add); | |
2769 (*constant)->DeleteAndReplaceWith(new_constant); | |
2770 } | |
2771 *constant = new_constant; | |
2772 } | |
2773 | |
2774 void RemoveZeroAdd(HAdd** add, | |
2775 HConstant** constant) { | |
fschneider
2012/04/25 09:40:48
Fits on the previous line?
Massi
2012/04/25 12:19:19
Done.
| |
2776 if (*add != NULL && (*constant)->Integer32Value() == 0) { | |
2777 (*add)->DeleteAndReplaceWith((*add)->left()); | |
2778 (*constant)->Unlink(); | |
fschneider
2012/04/25 09:40:48
We use normally the helper DeleteAndReplaceWith(NU
Massi
2012/04/25 12:19:19
Done.
| |
2779 } | |
2780 } | |
2781 }; | |
2782 | |
2783 | |
2784 static bool BoundsCheckKeyMatch(void* key1, void* key2) { | |
2785 BoundsCheckKey* k1 = static_cast<BoundsCheckKey*>(key1); | |
2786 BoundsCheckKey* k2 = static_cast<BoundsCheckKey*>(key2); | |
2787 return k1->IndexBase() == k2->IndexBase() && k1->Length() == k2->Length(); | |
2587 } | 2788 } |
2588 | 2789 |
2589 | 2790 |
2791 class BoundsCheckTable : private ZoneHashMap { | |
2792 public: | |
2793 BoundsCheckBbData** LookupOrInsert(BoundsCheckKey* key) { | |
2794 return reinterpret_cast<BoundsCheckBbData**>( | |
2795 &(Lookup(key, key->Hash(), true)->value)); | |
2796 } | |
2797 | |
2798 void Insert(BoundsCheckKey* key, BoundsCheckBbData* data) { | |
2799 Lookup(key, key->Hash(), true)->value = data; | |
2800 } | |
2801 | |
2802 void Delete(BoundsCheckKey* key) { | |
2803 Remove(key, key->Hash()); | |
2804 } | |
2805 | |
2806 BoundsCheckTable() : ZoneHashMap(BoundsCheckKeyMatch) { | |
2807 } | |
fschneider
2012/04/25 09:40:48
Fits on the previous line.
Massi
2012/04/25 12:19:19
Done.
| |
2808 }; | |
2809 | |
2810 | |
2811 // Eliminates checks in bb and recursively in the dominated blocks. | |
2812 // Also replace the results of check instructions with the original value, if | |
2813 // the result is used. This is safe now, since we don't do code motion after | |
2814 // this point. It enables better register allocation since the value produced | |
2815 // by check instructions is really a copy of the original value. | |
2816 void HGraph::EliminateRedundantBoundsChecks(HBasicBlock* bb, | |
2817 BoundsCheckTable* table) { | |
2818 BoundsCheckBbData* bb_data_list = NULL; | |
2819 | |
2820 for (HInstruction* i = bb->first(); i != NULL; i = i->next()) { | |
2821 if (!i->IsBoundsCheck()) continue; | |
2822 | |
2823 HBoundsCheck* check = HBoundsCheck::cast(i); | |
2824 check->ReplaceAllUsesWith(check->index()); | |
2825 | |
2826 isolate()->counters()->array_bounds_checks_seen()->Increment(); | |
2827 if (!FLAG_array_bounds_checks_elimination) continue; | |
2828 | |
2829 int32_t offset; | |
2830 BoundsCheckKey* key = | |
2831 BoundsCheckKey::Create(bb->zone(), check, &offset); | |
2832 BoundsCheckBbData** data_p = table->LookupOrInsert(key); | |
2833 BoundsCheckBbData* data = *data_p; | |
2834 if (data == NULL) { | |
2835 bb_data_list = new(bb->zone()) BoundsCheckBbData(key, | |
fschneider
2012/04/25 09:40:48
Since HGraph already has a zone() accessor, you ca
Massi
2012/04/25 12:19:19
Done.
| |
2836 offset, | |
2837 offset, | |
2838 bb, | |
2839 check, | |
2840 bb_data_list, | |
2841 NULL); | |
2842 *data_p = bb_data_list; | |
2843 } else if (data->OffsetIsCovered(offset)) { | |
2844 check->DeleteAndReplaceWith(NULL); | |
2845 isolate()->counters()->array_bounds_checks_removed()->Increment(); | |
2846 } else if (data->BasicBlock() == bb) { | |
2847 data->CoverCheck(check, offset); | |
2848 isolate()->counters()->array_bounds_checks_removed()->Increment(); | |
2849 } else { | |
2850 int32_t new_lower_offset = offset < data->LowerOffset() ? offset | |
fschneider
2012/04/25 09:40:48
Maybe the expressions here better readable as:
in
Massi
2012/04/25 12:19:19
Done.
| |
2851 : data->LowerOffset(); | |
2852 int32_t new_upper_offset = offset > data->UpperOffset() ? offset | |
2853 : data->UpperOffset(); | |
2854 bb_data_list = new(bb->zone()) BoundsCheckBbData(key, | |
2855 new_lower_offset, | |
2856 new_upper_offset, | |
2857 bb, | |
2858 check, | |
2859 bb_data_list, | |
2860 data); | |
2861 table->Insert(key, bb_data_list); | |
2862 } | |
2863 } | |
2864 | |
2865 for (int i = 0; i < bb->dominated_blocks()->length(); ++i) { | |
2866 EliminateRedundantBoundsChecks(bb->dominated_blocks()->at(i), table); | |
2867 } | |
2868 | |
2869 for (BoundsCheckBbData* data = bb_data_list; | |
2870 data != NULL; | |
2871 data = data->NextInBasicBlock()) { | |
2872 data->RemoveZeroOperations(); | |
2873 if (data->FatherInDominatorTree()) { | |
2874 table->Insert(data->Key(), data->FatherInDominatorTree()); | |
2875 } else { | |
2876 table->Delete(data->Key()); | |
2877 } | |
2878 } | |
2879 } | |
2880 | |
2881 | |
2882 void HGraph::EliminateRedundantBoundsChecks() { | |
2883 HPhase phase("H_Eliminate bounds checks", this); | |
2884 AssertNoAllocation no_gc; | |
fschneider
2012/04/25 09:40:48
Why would you want to assert no allocation here? I
Massi
2012/04/25 12:19:19
It is necessary: I was hitting asserts without it.
fschneider
2012/04/26 09:35:49
I don't believe it is necessary. If anything it wi
| |
2885 BoundsCheckTable checks_table; | |
2886 EliminateRedundantBoundsChecks(entry_block(), &checks_table); | |
2887 } | |
2888 | |
2889 | |
2590 HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) { | 2890 HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) { |
2591 ASSERT(current_block() != NULL); | 2891 ASSERT(current_block() != NULL); |
2592 current_block()->AddInstruction(instr); | 2892 current_block()->AddInstruction(instr); |
2593 return instr; | 2893 return instr; |
2594 } | 2894 } |
2595 | 2895 |
2596 | 2896 |
2597 void HGraphBuilder::AddSimulate(int ast_id) { | 2897 void HGraphBuilder::AddSimulate(int ast_id) { |
2598 ASSERT(current_block() != NULL); | 2898 ASSERT(current_block() != NULL); |
2599 current_block()->AddSimulate(ast_id); | 2899 current_block()->AddSimulate(ast_id); |
(...skipping 5631 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
8231 } | 8531 } |
8232 } | 8532 } |
8233 | 8533 |
8234 #ifdef DEBUG | 8534 #ifdef DEBUG |
8235 if (graph_ != NULL) graph_->Verify(false); // No full verify. | 8535 if (graph_ != NULL) graph_->Verify(false); // No full verify. |
8236 if (allocator_ != NULL) allocator_->Verify(); | 8536 if (allocator_ != NULL) allocator_->Verify(); |
8237 #endif | 8537 #endif |
8238 } | 8538 } |
8239 | 8539 |
8240 } } // namespace v8::internal | 8540 } } // namespace v8::internal |
OLD | NEW |