Chromium Code Reviews| 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()) { | |
| 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 | |
|
fschneider
2012/04/18 09:42:18
Two lines space here.
Massi
2012/04/23 15:19:19
Done.
| |
| 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 public: | |
| 2638 BoundsCheckKey* Key() { return key_; } | |
| 2639 int32_t Offset() { return offset_; } | |
| 2640 HBasicBlock* BasicBlock() { return basic_block_; } | |
| 2641 HBoundsCheck* Check() { return check_; } | |
| 2642 BoundsCheckBbData* NextInBasicBlock() { return next_in_bb_; } | |
| 2643 BoundsCheckBbData* FatherInDominatorTree() { return father_in_dt_; } | |
| 2644 | |
| 2645 void CoverCheck(HBoundsCheck* new_check, | |
| 2646 int32_t new_offset) { | |
| 2647 ASSERT(new_check->index()->representation().IsInteger32()); | |
| 2648 HConstant* new_constant = | |
| 2649 new HConstant(Handle<Object>(Smi::FromInt(new_offset)), | |
| 2650 Representation::Integer32()); | |
| 2651 if (added_index_ == NULL) { | |
| 2652 new_constant->InsertBefore(Check()); | |
| 2653 added_index_ = | |
| 2654 new(BasicBlock()->zone()) HAdd(NULL, | |
| 2655 Key()->IndexBase(), | |
| 2656 new_constant); | |
| 2657 added_index_->AssumeRepresentation(new_check->index()->representation()); | |
| 2658 added_index_->InsertBefore(Check()); | |
| 2659 } else { | |
| 2660 new_constant->InsertBefore(added_index_); | |
| 2661 added_offset_->DeleteAndReplaceWith(new_constant); | |
| 2662 } | |
| 2663 added_offset_ = new_constant; | |
| 2664 Check()->SetOperandAt(0, added_index_); | |
| 2665 new_check->DeleteAndReplaceWith(NULL); | |
| 2666 offset_ = new_offset; | |
| 2667 } | |
| 2668 | |
| 2669 BoundsCheckBbData(BoundsCheckKey* key, | |
| 2670 int32_t offset, | |
| 2671 HBasicBlock* bb, | |
| 2672 HBoundsCheck* check, | |
| 2673 BoundsCheckBbData* next_in_bb, | |
| 2674 BoundsCheckBbData* father_in_dt) : | |
| 2675 key_(key), | |
| 2676 offset_(offset), | |
| 2677 basic_block_(bb), | |
| 2678 check_(check), | |
| 2679 added_offset_(NULL), | |
| 2680 added_index_(NULL), | |
| 2681 next_in_bb_(next_in_bb), | |
| 2682 father_in_dt_(father_in_dt) { } | |
| 2683 | |
| 2684 private: | |
| 2685 BoundsCheckKey* key_; | |
| 2686 int32_t offset_; | |
| 2687 HBasicBlock* basic_block_; | |
| 2688 HBoundsCheck* check_; | |
| 2689 HConstant* added_offset_; | |
| 2690 HAdd* added_index_; | |
| 2691 BoundsCheckBbData* next_in_bb_; | |
| 2692 BoundsCheckBbData* father_in_dt_; | |
| 2693 }; | |
| 2694 | |
|
fschneider
2012/04/18 09:42:18
Two lines space here.
Massi
2012/04/23 15:19:19
Done.
| |
| 2695 static bool BoundsCheckKeyMatch(void* key1, void* key2) { | |
| 2696 BoundsCheckKey* k1 = static_cast<BoundsCheckKey*>(key1); | |
| 2697 BoundsCheckKey* k2 = static_cast<BoundsCheckKey*>(key2); | |
| 2698 | |
| 2699 return k1->IndexBase() == k2->IndexBase() && k1->Length() == k2->Length(); | |
| 2700 } | |
| 2701 | |
|
fschneider
2012/04/18 09:42:18
Two lines space here.
Massi
2012/04/23 15:19:19
Done.
| |
| 2702 class BoundsCheckTable : private ZoneHashMap { | |
| 2703 public: | |
| 2704 BoundsCheckBbData** LookupOrInsert(BoundsCheckKey* key) { | |
| 2705 return reinterpret_cast<BoundsCheckBbData**>( | |
| 2706 &(Lookup(key, key->Hash(), true)->value)); | |
| 2707 } | |
| 2708 | |
| 2709 void Insert(BoundsCheckKey* key, BoundsCheckBbData* data) { | |
| 2710 Lookup(key, key->Hash(), true)->value = data; | |
| 2711 } | |
| 2712 | |
| 2713 void Delete(BoundsCheckKey* key) { | |
| 2714 Remove(key, key->Hash()); | |
| 2715 } | |
| 2716 | |
| 2717 BoundsCheckTable() : ZoneHashMap(BoundsCheckKeyMatch) { | |
| 2718 } | |
| 2719 }; | |
| 2720 | |
|
fschneider
2012/04/18 09:42:18
Two lines space here.
Massi
2012/04/23 15:19:19
Done.
| |
| 2721 // Eliminates checks in bb and recursively in the dominated blocks. | |
| 2722 // Also replace the results of check instructions with the original value, if | |
| 2723 // the result is used. This is safe now, since we don't do code motion after | |
| 2724 // this point. It enables better register allocation since the value produced | |
| 2725 // by check instructions is really a copy of the original value. | |
| 2726 void HGraph::EliminateRedundantBoundsChecks(HBasicBlock* bb, | |
| 2727 BoundsCheckTable* table) { | |
| 2728 BoundsCheckBbData* bb_data_list = NULL; | |
| 2729 | |
| 2730 for (HInstruction* i = bb->first(); i != NULL; i = i->next()) { | |
| 2731 if (!i->IsBoundsCheck()) continue; | |
| 2732 | |
| 2733 HBoundsCheck* check = HBoundsCheck::cast(i); | |
| 2734 check->ReplaceAllUsesWith(check->index()); | |
| 2735 isolate()->counters()->array_bounds_checks_seen()->Increment(); | |
| 2736 int32_t offset; | |
| 2737 // TODO(mmassi): allocate key only when we create a new table entry... | |
|
fschneider
2012/04/18 09:42:18
If we plan to implement the TODO, we usually creat
| |
| 2738 BoundsCheckKey* key = | |
| 2739 BoundsCheckKey::Create(bb->zone(), check, &offset); | |
| 2740 BoundsCheckBbData** data_p = table->LookupOrInsert(key); | |
| 2741 BoundsCheckBbData* data = *data_p; | |
| 2742 if (data == NULL) { | |
| 2743 bb_data_list = new(bb->zone()) BoundsCheckBbData(key, | |
| 2744 offset, | |
| 2745 bb, | |
| 2746 check, | |
| 2747 bb_data_list, | |
| 2748 NULL); | |
| 2749 *data_p = bb_data_list; | |
| 2750 } else if (offset <= data->Offset()) { | |
| 2751 check->DeleteAndReplaceWith(NULL); | |
| 2752 isolate()->counters()->array_bounds_checks_removed()->Increment(); | |
| 2753 } else if (data->BasicBlock() == bb) { | |
| 2754 data->CoverCheck(check, offset); | |
| 2755 isolate()->counters()->array_bounds_checks_removed()->Increment(); | |
| 2756 } else { | |
| 2757 bb_data_list = new(bb->zone()) BoundsCheckBbData(key, | |
| 2758 offset, | |
| 2759 bb, | |
| 2760 check, | |
| 2761 bb_data_list, | |
| 2762 data); | |
| 2763 table->Insert(key, bb_data_list); | |
| 2764 } | |
| 2765 } | |
| 2766 | |
| 2767 for (int i = 0; i < bb->dominated_blocks()->length(); ++i) { | |
| 2768 EliminateRedundantBoundsChecks(bb->dominated_blocks()->at(i), table); | |
| 2769 } | |
| 2770 | |
| 2771 for (BoundsCheckBbData* data = bb_data_list; | |
| 2772 data != NULL; | |
| 2773 data = data->NextInBasicBlock()) { | |
| 2774 if (data->FatherInDominatorTree()) { | |
| 2775 table->Insert(data->Key(), data->FatherInDominatorTree()); | |
| 2776 } else { | |
| 2777 table->Delete(data->Key()); | |
| 2778 } | |
| 2779 } | |
| 2780 } | |
| 2781 | |
|
fschneider
2012/04/18 09:42:18
Two lines space here.
Massi
2012/04/23 15:19:19
Done.
| |
| 2782 void HGraph::EliminateRedundantBoundsChecks() { | |
| 2783 HPhase phase("H_Eliminate bounds checks", this); | |
| 2784 AssertNoAllocation no_gc; | |
| 2785 BoundsCheckTable checks_table; | |
| 2786 EliminateRedundantBoundsChecks(entry_block(), &checks_table); | |
| 2787 } | |
| 2589 | 2788 |
| 2590 HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) { | 2789 HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) { |
| 2591 ASSERT(current_block() != NULL); | 2790 ASSERT(current_block() != NULL); |
| 2592 current_block()->AddInstruction(instr); | 2791 current_block()->AddInstruction(instr); |
| 2593 return instr; | 2792 return instr; |
| 2594 } | 2793 } |
| 2595 | 2794 |
| 2596 | 2795 |
| 2597 void HGraphBuilder::AddSimulate(int ast_id) { | 2796 void HGraphBuilder::AddSimulate(int ast_id) { |
| 2598 ASSERT(current_block() != NULL); | 2797 ASSERT(current_block() != NULL); |
| (...skipping 5632 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 8231 } | 8430 } |
| 8232 } | 8431 } |
| 8233 | 8432 |
| 8234 #ifdef DEBUG | 8433 #ifdef DEBUG |
| 8235 if (graph_ != NULL) graph_->Verify(false); // No full verify. | 8434 if (graph_ != NULL) graph_->Verify(false); // No full verify. |
| 8236 if (allocator_ != NULL) allocator_->Verify(); | 8435 if (allocator_ != NULL) allocator_->Verify(); |
| 8237 #endif | 8436 #endif |
| 8238 } | 8437 } |
| 8239 | 8438 |
| 8240 } } // namespace v8::internal | 8439 } } // namespace v8::internal |
| OLD | NEW |