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

Side by Side Diff: src/hydrogen.cc

Issue 10032029: Eliminate redundant array bound checks (checks already performed earlier in the DT). (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Fixed issue with lower bound changing in unusual ways. Created 8 years, 8 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 | Annotate | Revision Log
« no previous file with comments | « src/hydrogen.h ('k') | src/hydrogen-instructions.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « src/hydrogen.h ('k') | src/hydrogen-instructions.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698