| 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 |
| 11 // with the distribution. | 11 // with the distribution. |
| 12 // * Neither the name of Google Inc. nor the names of its | 12 // * Neither the name of Google Inc. nor the names of its |
| 13 // contributors may be used to endorse or promote products derived | 13 // contributors may be used to endorse or promote products derived |
| 14 // from this software without specific prior written permission. | 14 // from this software without specific prior written permission. |
| 15 // | 15 // |
| 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | 27 |
| 28 #include "hydrogen.h" | 28 #include "hydrogen.h" |
| 29 #include "hydrogen-gvn.h" |
| 29 | 30 |
| 30 #include <algorithm> | 31 #include <algorithm> |
| 31 | 32 |
| 32 #include "v8.h" | 33 #include "v8.h" |
| 33 #include "codegen.h" | 34 #include "codegen.h" |
| 34 #include "full-codegen.h" | 35 #include "full-codegen.h" |
| 35 #include "hashmap.h" | 36 #include "hashmap.h" |
| 36 #include "lithium-allocator.h" | 37 #include "lithium-allocator.h" |
| 37 #include "parser.h" | 38 #include "parser.h" |
| 38 #include "scopeinfo.h" | 39 #include "scopeinfo.h" |
| (...skipping 2711 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2750 TraceRange("Original range was [%d,%d]\n", | 2751 TraceRange("Original range was [%d,%d]\n", |
| 2751 original_range->lower(), | 2752 original_range->lower(), |
| 2752 original_range->upper()); | 2753 original_range->upper()); |
| 2753 } | 2754 } |
| 2754 TraceRange("New information was [%d,%d]\n", | 2755 TraceRange("New information was [%d,%d]\n", |
| 2755 range->lower(), | 2756 range->lower(), |
| 2756 range->upper()); | 2757 range->upper()); |
| 2757 } | 2758 } |
| 2758 | 2759 |
| 2759 | 2760 |
| 2760 void TraceGVN(const char* msg, ...) { | |
| 2761 va_list arguments; | |
| 2762 va_start(arguments, msg); | |
| 2763 OS::VPrint(msg, arguments); | |
| 2764 va_end(arguments); | |
| 2765 } | |
| 2766 | |
| 2767 // Wrap TraceGVN in macros to avoid the expense of evaluating its arguments when | |
| 2768 // --trace-gvn is off. | |
| 2769 #define TRACE_GVN_1(msg, a1) \ | |
| 2770 if (FLAG_trace_gvn) { \ | |
| 2771 TraceGVN(msg, a1); \ | |
| 2772 } | |
| 2773 | |
| 2774 #define TRACE_GVN_2(msg, a1, a2) \ | |
| 2775 if (FLAG_trace_gvn) { \ | |
| 2776 TraceGVN(msg, a1, a2); \ | |
| 2777 } | |
| 2778 | |
| 2779 #define TRACE_GVN_3(msg, a1, a2, a3) \ | |
| 2780 if (FLAG_trace_gvn) { \ | |
| 2781 TraceGVN(msg, a1, a2, a3); \ | |
| 2782 } | |
| 2783 | |
| 2784 #define TRACE_GVN_4(msg, a1, a2, a3, a4) \ | |
| 2785 if (FLAG_trace_gvn) { \ | |
| 2786 TraceGVN(msg, a1, a2, a3, a4); \ | |
| 2787 } | |
| 2788 | |
| 2789 #define TRACE_GVN_5(msg, a1, a2, a3, a4, a5) \ | |
| 2790 if (FLAG_trace_gvn) { \ | |
| 2791 TraceGVN(msg, a1, a2, a3, a4, a5); \ | |
| 2792 } | |
| 2793 | |
| 2794 | |
| 2795 HValueMap::HValueMap(Zone* zone, const HValueMap* other) | |
| 2796 : array_size_(other->array_size_), | |
| 2797 lists_size_(other->lists_size_), | |
| 2798 count_(other->count_), | |
| 2799 present_flags_(other->present_flags_), | |
| 2800 array_(zone->NewArray<HValueMapListElement>(other->array_size_)), | |
| 2801 lists_(zone->NewArray<HValueMapListElement>(other->lists_size_)), | |
| 2802 free_list_head_(other->free_list_head_) { | |
| 2803 OS::MemCopy( | |
| 2804 array_, other->array_, array_size_ * sizeof(HValueMapListElement)); | |
| 2805 OS::MemCopy( | |
| 2806 lists_, other->lists_, lists_size_ * sizeof(HValueMapListElement)); | |
| 2807 } | |
| 2808 | |
| 2809 | |
| 2810 void HValueMap::Kill(GVNFlagSet flags) { | |
| 2811 GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(flags); | |
| 2812 if (!present_flags_.ContainsAnyOf(depends_flags)) return; | |
| 2813 present_flags_.RemoveAll(); | |
| 2814 for (int i = 0; i < array_size_; ++i) { | |
| 2815 HValue* value = array_[i].value; | |
| 2816 if (value != NULL) { | |
| 2817 // Clear list of collisions first, so we know if it becomes empty. | |
| 2818 int kept = kNil; // List of kept elements. | |
| 2819 int next; | |
| 2820 for (int current = array_[i].next; current != kNil; current = next) { | |
| 2821 next = lists_[current].next; | |
| 2822 HValue* value = lists_[current].value; | |
| 2823 if (value->gvn_flags().ContainsAnyOf(depends_flags)) { | |
| 2824 // Drop it. | |
| 2825 count_--; | |
| 2826 lists_[current].next = free_list_head_; | |
| 2827 free_list_head_ = current; | |
| 2828 } else { | |
| 2829 // Keep it. | |
| 2830 lists_[current].next = kept; | |
| 2831 kept = current; | |
| 2832 present_flags_.Add(value->gvn_flags()); | |
| 2833 } | |
| 2834 } | |
| 2835 array_[i].next = kept; | |
| 2836 | |
| 2837 // Now possibly drop directly indexed element. | |
| 2838 value = array_[i].value; | |
| 2839 if (value->gvn_flags().ContainsAnyOf(depends_flags)) { // Drop it. | |
| 2840 count_--; | |
| 2841 int head = array_[i].next; | |
| 2842 if (head == kNil) { | |
| 2843 array_[i].value = NULL; | |
| 2844 } else { | |
| 2845 array_[i].value = lists_[head].value; | |
| 2846 array_[i].next = lists_[head].next; | |
| 2847 lists_[head].next = free_list_head_; | |
| 2848 free_list_head_ = head; | |
| 2849 } | |
| 2850 } else { | |
| 2851 present_flags_.Add(value->gvn_flags()); // Keep it. | |
| 2852 } | |
| 2853 } | |
| 2854 } | |
| 2855 } | |
| 2856 | |
| 2857 | |
| 2858 HValue* HValueMap::Lookup(HValue* value) const { | |
| 2859 uint32_t hash = static_cast<uint32_t>(value->Hashcode()); | |
| 2860 uint32_t pos = Bound(hash); | |
| 2861 if (array_[pos].value != NULL) { | |
| 2862 if (array_[pos].value->Equals(value)) return array_[pos].value; | |
| 2863 int next = array_[pos].next; | |
| 2864 while (next != kNil) { | |
| 2865 if (lists_[next].value->Equals(value)) return lists_[next].value; | |
| 2866 next = lists_[next].next; | |
| 2867 } | |
| 2868 } | |
| 2869 return NULL; | |
| 2870 } | |
| 2871 | |
| 2872 | |
| 2873 void HValueMap::Resize(int new_size, Zone* zone) { | |
| 2874 ASSERT(new_size > count_); | |
| 2875 // Hashing the values into the new array has no more collisions than in the | |
| 2876 // old hash map, so we can use the existing lists_ array, if we are careful. | |
| 2877 | |
| 2878 // Make sure we have at least one free element. | |
| 2879 if (free_list_head_ == kNil) { | |
| 2880 ResizeLists(lists_size_ << 1, zone); | |
| 2881 } | |
| 2882 | |
| 2883 HValueMapListElement* new_array = | |
| 2884 zone->NewArray<HValueMapListElement>(new_size); | |
| 2885 memset(new_array, 0, sizeof(HValueMapListElement) * new_size); | |
| 2886 | |
| 2887 HValueMapListElement* old_array = array_; | |
| 2888 int old_size = array_size_; | |
| 2889 | |
| 2890 int old_count = count_; | |
| 2891 count_ = 0; | |
| 2892 // Do not modify present_flags_. It is currently correct. | |
| 2893 array_size_ = new_size; | |
| 2894 array_ = new_array; | |
| 2895 | |
| 2896 if (old_array != NULL) { | |
| 2897 // Iterate over all the elements in lists, rehashing them. | |
| 2898 for (int i = 0; i < old_size; ++i) { | |
| 2899 if (old_array[i].value != NULL) { | |
| 2900 int current = old_array[i].next; | |
| 2901 while (current != kNil) { | |
| 2902 Insert(lists_[current].value, zone); | |
| 2903 int next = lists_[current].next; | |
| 2904 lists_[current].next = free_list_head_; | |
| 2905 free_list_head_ = current; | |
| 2906 current = next; | |
| 2907 } | |
| 2908 // Rehash the directly stored value. | |
| 2909 Insert(old_array[i].value, zone); | |
| 2910 } | |
| 2911 } | |
| 2912 } | |
| 2913 USE(old_count); | |
| 2914 ASSERT(count_ == old_count); | |
| 2915 } | |
| 2916 | |
| 2917 | |
| 2918 void HValueMap::ResizeLists(int new_size, Zone* zone) { | |
| 2919 ASSERT(new_size > lists_size_); | |
| 2920 | |
| 2921 HValueMapListElement* new_lists = | |
| 2922 zone->NewArray<HValueMapListElement>(new_size); | |
| 2923 memset(new_lists, 0, sizeof(HValueMapListElement) * new_size); | |
| 2924 | |
| 2925 HValueMapListElement* old_lists = lists_; | |
| 2926 int old_size = lists_size_; | |
| 2927 | |
| 2928 lists_size_ = new_size; | |
| 2929 lists_ = new_lists; | |
| 2930 | |
| 2931 if (old_lists != NULL) { | |
| 2932 OS::MemCopy(lists_, old_lists, old_size * sizeof(HValueMapListElement)); | |
| 2933 } | |
| 2934 for (int i = old_size; i < lists_size_; ++i) { | |
| 2935 lists_[i].next = free_list_head_; | |
| 2936 free_list_head_ = i; | |
| 2937 } | |
| 2938 } | |
| 2939 | |
| 2940 | |
| 2941 void HValueMap::Insert(HValue* value, Zone* zone) { | |
| 2942 ASSERT(value != NULL); | |
| 2943 // Resizing when half of the hashtable is filled up. | |
| 2944 if (count_ >= array_size_ >> 1) Resize(array_size_ << 1, zone); | |
| 2945 ASSERT(count_ < array_size_); | |
| 2946 count_++; | |
| 2947 uint32_t pos = Bound(static_cast<uint32_t>(value->Hashcode())); | |
| 2948 if (array_[pos].value == NULL) { | |
| 2949 array_[pos].value = value; | |
| 2950 array_[pos].next = kNil; | |
| 2951 } else { | |
| 2952 if (free_list_head_ == kNil) { | |
| 2953 ResizeLists(lists_size_ << 1, zone); | |
| 2954 } | |
| 2955 int new_element_pos = free_list_head_; | |
| 2956 ASSERT(new_element_pos != kNil); | |
| 2957 free_list_head_ = lists_[free_list_head_].next; | |
| 2958 lists_[new_element_pos].value = value; | |
| 2959 lists_[new_element_pos].next = array_[pos].next; | |
| 2960 ASSERT(array_[pos].next == kNil || lists_[array_[pos].next].value != NULL); | |
| 2961 array_[pos].next = new_element_pos; | |
| 2962 } | |
| 2963 } | |
| 2964 | |
| 2965 | |
| 2966 HSideEffectMap::HSideEffectMap() : count_(0) { | |
| 2967 memset(data_, 0, kNumberOfTrackedSideEffects * kPointerSize); | |
| 2968 } | |
| 2969 | |
| 2970 | |
| 2971 HSideEffectMap::HSideEffectMap(HSideEffectMap* other) : count_(other->count_) { | |
| 2972 *this = *other; // Calls operator=. | |
| 2973 } | |
| 2974 | |
| 2975 | |
| 2976 HSideEffectMap& HSideEffectMap::operator= (const HSideEffectMap& other) { | |
| 2977 if (this != &other) { | |
| 2978 OS::MemCopy(data_, other.data_, kNumberOfTrackedSideEffects * kPointerSize); | |
| 2979 } | |
| 2980 return *this; | |
| 2981 } | |
| 2982 | |
| 2983 void HSideEffectMap::Kill(GVNFlagSet flags) { | |
| 2984 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) { | |
| 2985 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i); | |
| 2986 if (flags.Contains(changes_flag)) { | |
| 2987 if (data_[i] != NULL) count_--; | |
| 2988 data_[i] = NULL; | |
| 2989 } | |
| 2990 } | |
| 2991 } | |
| 2992 | |
| 2993 | |
| 2994 void HSideEffectMap::Store(GVNFlagSet flags, HInstruction* instr) { | |
| 2995 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) { | |
| 2996 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i); | |
| 2997 if (flags.Contains(changes_flag)) { | |
| 2998 if (data_[i] == NULL) count_++; | |
| 2999 data_[i] = instr; | |
| 3000 } | |
| 3001 } | |
| 3002 } | |
| 3003 | |
| 3004 | |
| 3005 class HStackCheckEliminator BASE_EMBEDDED { | 2761 class HStackCheckEliminator BASE_EMBEDDED { |
| 3006 public: | 2762 public: |
| 3007 explicit HStackCheckEliminator(HGraph* graph) : graph_(graph) { } | 2763 explicit HStackCheckEliminator(HGraph* graph) : graph_(graph) { } |
| 3008 | 2764 |
| 3009 void Process(); | 2765 void Process(); |
| 3010 | 2766 |
| 3011 private: | 2767 private: |
| 3012 HGraph* graph_; | 2768 HGraph* graph_; |
| 3013 }; | 2769 }; |
| 3014 | 2770 |
| (...skipping 23 matching lines...) Expand all Loading... |
| 3038 if (dominator == block) break; | 2794 if (dominator == block) break; |
| 3039 | 2795 |
| 3040 // Move up the dominator tree. | 2796 // Move up the dominator tree. |
| 3041 dominator = dominator->dominator(); | 2797 dominator = dominator->dominator(); |
| 3042 } | 2798 } |
| 3043 } | 2799 } |
| 3044 } | 2800 } |
| 3045 } | 2801 } |
| 3046 | 2802 |
| 3047 | 2803 |
| 3048 // Simple sparse set with O(1) add, contains, and clear. | |
| 3049 class SparseSet { | |
| 3050 public: | |
| 3051 SparseSet(Zone* zone, int capacity) | |
| 3052 : capacity_(capacity), | |
| 3053 length_(0), | |
| 3054 dense_(zone->NewArray<int>(capacity)), | |
| 3055 sparse_(zone->NewArray<int>(capacity)) { | |
| 3056 #ifndef NVALGRIND | |
| 3057 // Initialize the sparse array to make valgrind happy. | |
| 3058 memset(sparse_, 0, sizeof(sparse_[0]) * capacity); | |
| 3059 #endif | |
| 3060 } | |
| 3061 | |
| 3062 bool Contains(int n) const { | |
| 3063 ASSERT(0 <= n && n < capacity_); | |
| 3064 int d = sparse_[n]; | |
| 3065 return 0 <= d && d < length_ && dense_[d] == n; | |
| 3066 } | |
| 3067 | |
| 3068 bool Add(int n) { | |
| 3069 if (Contains(n)) return false; | |
| 3070 dense_[length_] = n; | |
| 3071 sparse_[n] = length_; | |
| 3072 ++length_; | |
| 3073 return true; | |
| 3074 } | |
| 3075 | |
| 3076 void Clear() { length_ = 0; } | |
| 3077 | |
| 3078 private: | |
| 3079 int capacity_; | |
| 3080 int length_; | |
| 3081 int* dense_; | |
| 3082 int* sparse_; | |
| 3083 | |
| 3084 DISALLOW_COPY_AND_ASSIGN(SparseSet); | |
| 3085 }; | |
| 3086 | |
| 3087 | |
| 3088 class HGlobalValueNumberer BASE_EMBEDDED { | |
| 3089 public: | |
| 3090 explicit HGlobalValueNumberer(HGraph* graph, CompilationInfo* info) | |
| 3091 : graph_(graph), | |
| 3092 info_(info), | |
| 3093 removed_side_effects_(false), | |
| 3094 block_side_effects_(graph->blocks()->length(), graph->zone()), | |
| 3095 loop_side_effects_(graph->blocks()->length(), graph->zone()), | |
| 3096 visited_on_paths_(graph->zone(), graph->blocks()->length()) { | |
| 3097 #ifdef DEBUG | |
| 3098 ASSERT(info->isolate()->optimizing_compiler_thread()->IsOptimizerThread() || | |
| 3099 !info->isolate()->heap()->IsAllocationAllowed()); | |
| 3100 #endif | |
| 3101 block_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length(), | |
| 3102 graph_->zone()); | |
| 3103 loop_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length(), | |
| 3104 graph_->zone()); | |
| 3105 } | |
| 3106 | |
| 3107 // Returns true if values with side effects are removed. | |
| 3108 bool Analyze(); | |
| 3109 | |
| 3110 private: | |
| 3111 GVNFlagSet CollectSideEffectsOnPathsToDominatedBlock( | |
| 3112 HBasicBlock* dominator, | |
| 3113 HBasicBlock* dominated); | |
| 3114 void AnalyzeGraph(); | |
| 3115 void ComputeBlockSideEffects(); | |
| 3116 void LoopInvariantCodeMotion(); | |
| 3117 void ProcessLoopBlock(HBasicBlock* block, | |
| 3118 HBasicBlock* before_loop, | |
| 3119 GVNFlagSet loop_kills, | |
| 3120 GVNFlagSet* accumulated_first_time_depends, | |
| 3121 GVNFlagSet* accumulated_first_time_changes); | |
| 3122 bool AllowCodeMotion(); | |
| 3123 bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header); | |
| 3124 | |
| 3125 HGraph* graph() { return graph_; } | |
| 3126 CompilationInfo* info() { return info_; } | |
| 3127 Zone* zone() const { return graph_->zone(); } | |
| 3128 | |
| 3129 HGraph* graph_; | |
| 3130 CompilationInfo* info_; | |
| 3131 bool removed_side_effects_; | |
| 3132 | |
| 3133 // A map of block IDs to their side effects. | |
| 3134 ZoneList<GVNFlagSet> block_side_effects_; | |
| 3135 | |
| 3136 // A map of loop header block IDs to their loop's side effects. | |
| 3137 ZoneList<GVNFlagSet> loop_side_effects_; | |
| 3138 | |
| 3139 // Used when collecting side effects on paths from dominator to | |
| 3140 // dominated. | |
| 3141 SparseSet visited_on_paths_; | |
| 3142 }; | |
| 3143 | |
| 3144 | |
| 3145 bool HGlobalValueNumberer::Analyze() { | |
| 3146 removed_side_effects_ = false; | |
| 3147 ComputeBlockSideEffects(); | |
| 3148 if (FLAG_loop_invariant_code_motion) { | |
| 3149 LoopInvariantCodeMotion(); | |
| 3150 } | |
| 3151 AnalyzeGraph(); | |
| 3152 return removed_side_effects_; | |
| 3153 } | |
| 3154 | |
| 3155 | |
| 3156 void HGlobalValueNumberer::ComputeBlockSideEffects() { | |
| 3157 // The Analyze phase of GVN can be called multiple times. Clear loop side | |
| 3158 // effects before computing them to erase the contents from previous Analyze | |
| 3159 // passes. | |
| 3160 for (int i = 0; i < loop_side_effects_.length(); ++i) { | |
| 3161 loop_side_effects_[i].RemoveAll(); | |
| 3162 } | |
| 3163 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) { | |
| 3164 // Compute side effects for the block. | |
| 3165 HBasicBlock* block = graph_->blocks()->at(i); | |
| 3166 HInstruction* instr = block->first(); | |
| 3167 int id = block->block_id(); | |
| 3168 GVNFlagSet side_effects; | |
| 3169 while (instr != NULL) { | |
| 3170 side_effects.Add(instr->ChangesFlags()); | |
| 3171 if (instr->IsSoftDeoptimize()) { | |
| 3172 block_side_effects_[id].RemoveAll(); | |
| 3173 side_effects.RemoveAll(); | |
| 3174 break; | |
| 3175 } | |
| 3176 instr = instr->next(); | |
| 3177 } | |
| 3178 block_side_effects_[id].Add(side_effects); | |
| 3179 | |
| 3180 // Loop headers are part of their loop. | |
| 3181 if (block->IsLoopHeader()) { | |
| 3182 loop_side_effects_[id].Add(side_effects); | |
| 3183 } | |
| 3184 | |
| 3185 // Propagate loop side effects upwards. | |
| 3186 if (block->HasParentLoopHeader()) { | |
| 3187 int header_id = block->parent_loop_header()->block_id(); | |
| 3188 loop_side_effects_[header_id].Add(block->IsLoopHeader() | |
| 3189 ? loop_side_effects_[id] | |
| 3190 : side_effects); | |
| 3191 } | |
| 3192 } | |
| 3193 } | |
| 3194 | |
| 3195 | |
| 3196 SmartArrayPointer<char> GetGVNFlagsString(GVNFlagSet flags) { | |
| 3197 char underlying_buffer[kLastFlag * 128]; | |
| 3198 Vector<char> buffer(underlying_buffer, sizeof(underlying_buffer)); | |
| 3199 #if DEBUG | |
| 3200 int offset = 0; | |
| 3201 const char* separator = ""; | |
| 3202 const char* comma = ", "; | |
| 3203 buffer[0] = 0; | |
| 3204 uint32_t set_depends_on = 0; | |
| 3205 uint32_t set_changes = 0; | |
| 3206 for (int bit = 0; bit < kLastFlag; ++bit) { | |
| 3207 if ((flags.ToIntegral() & (1 << bit)) != 0) { | |
| 3208 if (bit % 2 == 0) { | |
| 3209 set_changes++; | |
| 3210 } else { | |
| 3211 set_depends_on++; | |
| 3212 } | |
| 3213 } | |
| 3214 } | |
| 3215 bool positive_changes = set_changes < (kLastFlag / 2); | |
| 3216 bool positive_depends_on = set_depends_on < (kLastFlag / 2); | |
| 3217 if (set_changes > 0) { | |
| 3218 if (positive_changes) { | |
| 3219 offset += OS::SNPrintF(buffer + offset, "changes ["); | |
| 3220 } else { | |
| 3221 offset += OS::SNPrintF(buffer + offset, "changes all except ["); | |
| 3222 } | |
| 3223 for (int bit = 0; bit < kLastFlag; ++bit) { | |
| 3224 if (((flags.ToIntegral() & (1 << bit)) != 0) == positive_changes) { | |
| 3225 switch (static_cast<GVNFlag>(bit)) { | |
| 3226 #define DECLARE_FLAG(type) \ | |
| 3227 case kChanges##type: \ | |
| 3228 offset += OS::SNPrintF(buffer + offset, separator); \ | |
| 3229 offset += OS::SNPrintF(buffer + offset, #type); \ | |
| 3230 separator = comma; \ | |
| 3231 break; | |
| 3232 GVN_TRACKED_FLAG_LIST(DECLARE_FLAG) | |
| 3233 GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG) | |
| 3234 #undef DECLARE_FLAG | |
| 3235 default: | |
| 3236 break; | |
| 3237 } | |
| 3238 } | |
| 3239 } | |
| 3240 offset += OS::SNPrintF(buffer + offset, "]"); | |
| 3241 } | |
| 3242 if (set_depends_on > 0) { | |
| 3243 separator = ""; | |
| 3244 if (set_changes > 0) { | |
| 3245 offset += OS::SNPrintF(buffer + offset, ", "); | |
| 3246 } | |
| 3247 if (positive_depends_on) { | |
| 3248 offset += OS::SNPrintF(buffer + offset, "depends on ["); | |
| 3249 } else { | |
| 3250 offset += OS::SNPrintF(buffer + offset, "depends on all except ["); | |
| 3251 } | |
| 3252 for (int bit = 0; bit < kLastFlag; ++bit) { | |
| 3253 if (((flags.ToIntegral() & (1 << bit)) != 0) == positive_depends_on) { | |
| 3254 switch (static_cast<GVNFlag>(bit)) { | |
| 3255 #define DECLARE_FLAG(type) \ | |
| 3256 case kDependsOn##type: \ | |
| 3257 offset += OS::SNPrintF(buffer + offset, separator); \ | |
| 3258 offset += OS::SNPrintF(buffer + offset, #type); \ | |
| 3259 separator = comma; \ | |
| 3260 break; | |
| 3261 GVN_TRACKED_FLAG_LIST(DECLARE_FLAG) | |
| 3262 GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG) | |
| 3263 #undef DECLARE_FLAG | |
| 3264 default: | |
| 3265 break; | |
| 3266 } | |
| 3267 } | |
| 3268 } | |
| 3269 offset += OS::SNPrintF(buffer + offset, "]"); | |
| 3270 } | |
| 3271 #else | |
| 3272 OS::SNPrintF(buffer, "0x%08X", flags.ToIntegral()); | |
| 3273 #endif | |
| 3274 size_t string_len = strlen(underlying_buffer) + 1; | |
| 3275 ASSERT(string_len <= sizeof(underlying_buffer)); | |
| 3276 char* result = new char[strlen(underlying_buffer) + 1]; | |
| 3277 OS::MemCopy(result, underlying_buffer, string_len); | |
| 3278 return SmartArrayPointer<char>(result); | |
| 3279 } | |
| 3280 | |
| 3281 | |
| 3282 void HGlobalValueNumberer::LoopInvariantCodeMotion() { | |
| 3283 TRACE_GVN_1("Using optimistic loop invariant code motion: %s\n", | |
| 3284 graph_->use_optimistic_licm() ? "yes" : "no"); | |
| 3285 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) { | |
| 3286 HBasicBlock* block = graph_->blocks()->at(i); | |
| 3287 if (block->IsLoopHeader()) { | |
| 3288 GVNFlagSet side_effects = loop_side_effects_[block->block_id()]; | |
| 3289 TRACE_GVN_2("Try loop invariant motion for block B%d %s\n", | |
| 3290 block->block_id(), | |
| 3291 *GetGVNFlagsString(side_effects)); | |
| 3292 | |
| 3293 GVNFlagSet accumulated_first_time_depends; | |
| 3294 GVNFlagSet accumulated_first_time_changes; | |
| 3295 HBasicBlock* last = block->loop_information()->GetLastBackEdge(); | |
| 3296 for (int j = block->block_id(); j <= last->block_id(); ++j) { | |
| 3297 ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects, | |
| 3298 &accumulated_first_time_depends, | |
| 3299 &accumulated_first_time_changes); | |
| 3300 } | |
| 3301 } | |
| 3302 } | |
| 3303 } | |
| 3304 | |
| 3305 | |
| 3306 void HGlobalValueNumberer::ProcessLoopBlock( | |
| 3307 HBasicBlock* block, | |
| 3308 HBasicBlock* loop_header, | |
| 3309 GVNFlagSet loop_kills, | |
| 3310 GVNFlagSet* first_time_depends, | |
| 3311 GVNFlagSet* first_time_changes) { | |
| 3312 HBasicBlock* pre_header = loop_header->predecessors()->at(0); | |
| 3313 GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills); | |
| 3314 TRACE_GVN_2("Loop invariant motion for B%d %s\n", | |
| 3315 block->block_id(), | |
| 3316 *GetGVNFlagsString(depends_flags)); | |
| 3317 HInstruction* instr = block->first(); | |
| 3318 while (instr != NULL) { | |
| 3319 HInstruction* next = instr->next(); | |
| 3320 bool hoisted = false; | |
| 3321 if (instr->CheckFlag(HValue::kUseGVN)) { | |
| 3322 TRACE_GVN_4("Checking instruction %d (%s) %s. Loop %s\n", | |
| 3323 instr->id(), | |
| 3324 instr->Mnemonic(), | |
| 3325 *GetGVNFlagsString(instr->gvn_flags()), | |
| 3326 *GetGVNFlagsString(loop_kills)); | |
| 3327 bool can_hoist = !instr->gvn_flags().ContainsAnyOf(depends_flags); | |
| 3328 if (can_hoist && !graph()->use_optimistic_licm()) { | |
| 3329 can_hoist = block->IsLoopSuccessorDominator(); | |
| 3330 } | |
| 3331 | |
| 3332 if (can_hoist) { | |
| 3333 bool inputs_loop_invariant = true; | |
| 3334 for (int i = 0; i < instr->OperandCount(); ++i) { | |
| 3335 if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) { | |
| 3336 inputs_loop_invariant = false; | |
| 3337 } | |
| 3338 } | |
| 3339 | |
| 3340 if (inputs_loop_invariant && ShouldMove(instr, loop_header)) { | |
| 3341 TRACE_GVN_1("Hoisting loop invariant instruction %d\n", instr->id()); | |
| 3342 // Move the instruction out of the loop. | |
| 3343 instr->Unlink(); | |
| 3344 instr->InsertBefore(pre_header->end()); | |
| 3345 if (instr->HasSideEffects()) removed_side_effects_ = true; | |
| 3346 hoisted = true; | |
| 3347 } | |
| 3348 } | |
| 3349 } | |
| 3350 if (!hoisted) { | |
| 3351 // If an instruction is not hoisted, we have to account for its side | |
| 3352 // effects when hoisting later HTransitionElementsKind instructions. | |
| 3353 GVNFlagSet previous_depends = *first_time_depends; | |
| 3354 GVNFlagSet previous_changes = *first_time_changes; | |
| 3355 first_time_depends->Add(instr->DependsOnFlags()); | |
| 3356 first_time_changes->Add(instr->ChangesFlags()); | |
| 3357 if (!(previous_depends == *first_time_depends)) { | |
| 3358 TRACE_GVN_1("Updated first-time accumulated %s\n", | |
| 3359 *GetGVNFlagsString(*first_time_depends)); | |
| 3360 } | |
| 3361 if (!(previous_changes == *first_time_changes)) { | |
| 3362 TRACE_GVN_1("Updated first-time accumulated %s\n", | |
| 3363 *GetGVNFlagsString(*first_time_changes)); | |
| 3364 } | |
| 3365 } | |
| 3366 instr = next; | |
| 3367 } | |
| 3368 } | |
| 3369 | |
| 3370 | |
| 3371 bool HGlobalValueNumberer::AllowCodeMotion() { | |
| 3372 return info()->IsStub() || info()->opt_count() + 1 < FLAG_max_opt_count; | |
| 3373 } | |
| 3374 | |
| 3375 | |
| 3376 bool HGlobalValueNumberer::ShouldMove(HInstruction* instr, | |
| 3377 HBasicBlock* loop_header) { | |
| 3378 // If we've disabled code motion or we're in a block that unconditionally | |
| 3379 // deoptimizes, don't move any instructions. | |
| 3380 return AllowCodeMotion() && !instr->block()->IsDeoptimizing(); | |
| 3381 } | |
| 3382 | |
| 3383 | |
| 3384 GVNFlagSet HGlobalValueNumberer::CollectSideEffectsOnPathsToDominatedBlock( | |
| 3385 HBasicBlock* dominator, HBasicBlock* dominated) { | |
| 3386 GVNFlagSet side_effects; | |
| 3387 for (int i = 0; i < dominated->predecessors()->length(); ++i) { | |
| 3388 HBasicBlock* block = dominated->predecessors()->at(i); | |
| 3389 if (dominator->block_id() < block->block_id() && | |
| 3390 block->block_id() < dominated->block_id() && | |
| 3391 visited_on_paths_.Add(block->block_id())) { | |
| 3392 side_effects.Add(block_side_effects_[block->block_id()]); | |
| 3393 if (block->IsLoopHeader()) { | |
| 3394 side_effects.Add(loop_side_effects_[block->block_id()]); | |
| 3395 } | |
| 3396 side_effects.Add(CollectSideEffectsOnPathsToDominatedBlock( | |
| 3397 dominator, block)); | |
| 3398 } | |
| 3399 } | |
| 3400 return side_effects; | |
| 3401 } | |
| 3402 | |
| 3403 | |
| 3404 // Each instance of this class is like a "stack frame" for the recursive | |
| 3405 // traversal of the dominator tree done during GVN (the stack is handled | |
| 3406 // as a double linked list). | |
| 3407 // We reuse frames when possible so the list length is limited by the depth | |
| 3408 // of the dominator tree but this forces us to initialize each frame calling | |
| 3409 // an explicit "Initialize" method instead of a using constructor. | |
| 3410 class GvnBasicBlockState: public ZoneObject { | |
| 3411 public: | |
| 3412 static GvnBasicBlockState* CreateEntry(Zone* zone, | |
| 3413 HBasicBlock* entry_block, | |
| 3414 HValueMap* entry_map) { | |
| 3415 return new(zone) | |
| 3416 GvnBasicBlockState(NULL, entry_block, entry_map, NULL, zone); | |
| 3417 } | |
| 3418 | |
| 3419 HBasicBlock* block() { return block_; } | |
| 3420 HValueMap* map() { return map_; } | |
| 3421 HSideEffectMap* dominators() { return &dominators_; } | |
| 3422 | |
| 3423 GvnBasicBlockState* next_in_dominator_tree_traversal( | |
| 3424 Zone* zone, | |
| 3425 HBasicBlock** dominator) { | |
| 3426 // This assignment needs to happen before calling next_dominated() because | |
| 3427 // that call can reuse "this" if we are at the last dominated block. | |
| 3428 *dominator = block(); | |
| 3429 GvnBasicBlockState* result = next_dominated(zone); | |
| 3430 if (result == NULL) { | |
| 3431 GvnBasicBlockState* dominator_state = pop(); | |
| 3432 if (dominator_state != NULL) { | |
| 3433 // This branch is guaranteed not to return NULL because pop() never | |
| 3434 // returns a state where "is_done() == true". | |
| 3435 *dominator = dominator_state->block(); | |
| 3436 result = dominator_state->next_dominated(zone); | |
| 3437 } else { | |
| 3438 // Unnecessary (we are returning NULL) but done for cleanness. | |
| 3439 *dominator = NULL; | |
| 3440 } | |
| 3441 } | |
| 3442 return result; | |
| 3443 } | |
| 3444 | |
| 3445 private: | |
| 3446 void Initialize(HBasicBlock* block, | |
| 3447 HValueMap* map, | |
| 3448 HSideEffectMap* dominators, | |
| 3449 bool copy_map, | |
| 3450 Zone* zone) { | |
| 3451 block_ = block; | |
| 3452 map_ = copy_map ? map->Copy(zone) : map; | |
| 3453 dominated_index_ = -1; | |
| 3454 length_ = block->dominated_blocks()->length(); | |
| 3455 if (dominators != NULL) { | |
| 3456 dominators_ = *dominators; | |
| 3457 } | |
| 3458 } | |
| 3459 bool is_done() { return dominated_index_ >= length_; } | |
| 3460 | |
| 3461 GvnBasicBlockState(GvnBasicBlockState* previous, | |
| 3462 HBasicBlock* block, | |
| 3463 HValueMap* map, | |
| 3464 HSideEffectMap* dominators, | |
| 3465 Zone* zone) | |
| 3466 : previous_(previous), next_(NULL) { | |
| 3467 Initialize(block, map, dominators, true, zone); | |
| 3468 } | |
| 3469 | |
| 3470 GvnBasicBlockState* next_dominated(Zone* zone) { | |
| 3471 dominated_index_++; | |
| 3472 if (dominated_index_ == length_ - 1) { | |
| 3473 // No need to copy the map for the last child in the dominator tree. | |
| 3474 Initialize(block_->dominated_blocks()->at(dominated_index_), | |
| 3475 map(), | |
| 3476 dominators(), | |
| 3477 false, | |
| 3478 zone); | |
| 3479 return this; | |
| 3480 } else if (dominated_index_ < length_) { | |
| 3481 return push(zone, | |
| 3482 block_->dominated_blocks()->at(dominated_index_), | |
| 3483 dominators()); | |
| 3484 } else { | |
| 3485 return NULL; | |
| 3486 } | |
| 3487 } | |
| 3488 | |
| 3489 GvnBasicBlockState* push(Zone* zone, | |
| 3490 HBasicBlock* block, | |
| 3491 HSideEffectMap* dominators) { | |
| 3492 if (next_ == NULL) { | |
| 3493 next_ = | |
| 3494 new(zone) GvnBasicBlockState(this, block, map(), dominators, zone); | |
| 3495 } else { | |
| 3496 next_->Initialize(block, map(), dominators, true, zone); | |
| 3497 } | |
| 3498 return next_; | |
| 3499 } | |
| 3500 GvnBasicBlockState* pop() { | |
| 3501 GvnBasicBlockState* result = previous_; | |
| 3502 while (result != NULL && result->is_done()) { | |
| 3503 TRACE_GVN_2("Backtracking from block B%d to block b%d\n", | |
| 3504 block()->block_id(), | |
| 3505 previous_->block()->block_id()) | |
| 3506 result = result->previous_; | |
| 3507 } | |
| 3508 return result; | |
| 3509 } | |
| 3510 | |
| 3511 GvnBasicBlockState* previous_; | |
| 3512 GvnBasicBlockState* next_; | |
| 3513 HBasicBlock* block_; | |
| 3514 HValueMap* map_; | |
| 3515 HSideEffectMap dominators_; | |
| 3516 int dominated_index_; | |
| 3517 int length_; | |
| 3518 }; | |
| 3519 | |
| 3520 // This is a recursive traversal of the dominator tree but it has been turned | |
| 3521 // into a loop to avoid stack overflows. | |
| 3522 // The logical "stack frames" of the recursion are kept in a list of | |
| 3523 // GvnBasicBlockState instances. | |
| 3524 void HGlobalValueNumberer::AnalyzeGraph() { | |
| 3525 HBasicBlock* entry_block = graph_->entry_block(); | |
| 3526 HValueMap* entry_map = new(zone()) HValueMap(zone()); | |
| 3527 GvnBasicBlockState* current = | |
| 3528 GvnBasicBlockState::CreateEntry(zone(), entry_block, entry_map); | |
| 3529 | |
| 3530 while (current != NULL) { | |
| 3531 HBasicBlock* block = current->block(); | |
| 3532 HValueMap* map = current->map(); | |
| 3533 HSideEffectMap* dominators = current->dominators(); | |
| 3534 | |
| 3535 TRACE_GVN_2("Analyzing block B%d%s\n", | |
| 3536 block->block_id(), | |
| 3537 block->IsLoopHeader() ? " (loop header)" : ""); | |
| 3538 | |
| 3539 // If this is a loop header kill everything killed by the loop. | |
| 3540 if (block->IsLoopHeader()) { | |
| 3541 map->Kill(loop_side_effects_[block->block_id()]); | |
| 3542 } | |
| 3543 | |
| 3544 // Go through all instructions of the current block. | |
| 3545 HInstruction* instr = block->first(); | |
| 3546 while (instr != NULL) { | |
| 3547 HInstruction* next = instr->next(); | |
| 3548 GVNFlagSet flags = instr->ChangesFlags(); | |
| 3549 if (!flags.IsEmpty()) { | |
| 3550 // Clear all instructions in the map that are affected by side effects. | |
| 3551 // Store instruction as the dominating one for tracked side effects. | |
| 3552 map->Kill(flags); | |
| 3553 dominators->Store(flags, instr); | |
| 3554 TRACE_GVN_2("Instruction %d %s\n", instr->id(), | |
| 3555 *GetGVNFlagsString(flags)); | |
| 3556 } | |
| 3557 if (instr->CheckFlag(HValue::kUseGVN)) { | |
| 3558 ASSERT(!instr->HasObservableSideEffects()); | |
| 3559 HValue* other = map->Lookup(instr); | |
| 3560 if (other != NULL) { | |
| 3561 ASSERT(instr->Equals(other) && other->Equals(instr)); | |
| 3562 TRACE_GVN_4("Replacing value %d (%s) with value %d (%s)\n", | |
| 3563 instr->id(), | |
| 3564 instr->Mnemonic(), | |
| 3565 other->id(), | |
| 3566 other->Mnemonic()); | |
| 3567 if (instr->HasSideEffects()) removed_side_effects_ = true; | |
| 3568 instr->DeleteAndReplaceWith(other); | |
| 3569 } else { | |
| 3570 map->Add(instr, zone()); | |
| 3571 } | |
| 3572 } | |
| 3573 if (instr->IsLinked() && | |
| 3574 instr->CheckFlag(HValue::kTrackSideEffectDominators)) { | |
| 3575 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) { | |
| 3576 HValue* other = dominators->at(i); | |
| 3577 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i); | |
| 3578 GVNFlag depends_on_flag = HValue::DependsOnFlagFromInt(i); | |
| 3579 if (instr->DependsOnFlags().Contains(depends_on_flag) && | |
| 3580 (other != NULL)) { | |
| 3581 TRACE_GVN_5("Side-effect #%d in %d (%s) is dominated by %d (%s)\n", | |
| 3582 i, | |
| 3583 instr->id(), | |
| 3584 instr->Mnemonic(), | |
| 3585 other->id(), | |
| 3586 other->Mnemonic()); | |
| 3587 instr->SetSideEffectDominator(changes_flag, other); | |
| 3588 } | |
| 3589 } | |
| 3590 } | |
| 3591 instr = next; | |
| 3592 } | |
| 3593 | |
| 3594 HBasicBlock* dominator_block; | |
| 3595 GvnBasicBlockState* next = | |
| 3596 current->next_in_dominator_tree_traversal(zone(), &dominator_block); | |
| 3597 | |
| 3598 if (next != NULL) { | |
| 3599 HBasicBlock* dominated = next->block(); | |
| 3600 HValueMap* successor_map = next->map(); | |
| 3601 HSideEffectMap* successor_dominators = next->dominators(); | |
| 3602 | |
| 3603 // Kill everything killed on any path between this block and the | |
| 3604 // dominated block. We don't have to traverse these paths if the | |
| 3605 // value map and the dominators list is already empty. If the range | |
| 3606 // of block ids (block_id, dominated_id) is empty there are no such | |
| 3607 // paths. | |
| 3608 if ((!successor_map->IsEmpty() || !successor_dominators->IsEmpty()) && | |
| 3609 dominator_block->block_id() + 1 < dominated->block_id()) { | |
| 3610 visited_on_paths_.Clear(); | |
| 3611 GVNFlagSet side_effects_on_all_paths = | |
| 3612 CollectSideEffectsOnPathsToDominatedBlock(dominator_block, | |
| 3613 dominated); | |
| 3614 successor_map->Kill(side_effects_on_all_paths); | |
| 3615 successor_dominators->Kill(side_effects_on_all_paths); | |
| 3616 } | |
| 3617 } | |
| 3618 current = next; | |
| 3619 } | |
| 3620 } | |
| 3621 | |
| 3622 | |
| 3623 void HInferRepresentation::AddToWorklist(HValue* current) { | 2804 void HInferRepresentation::AddToWorklist(HValue* current) { |
| 3624 if (current->representation().IsTagged()) return; | 2805 if (current->representation().IsTagged()) return; |
| 3625 if (!current->CheckFlag(HValue::kFlexibleRepresentation)) return; | 2806 if (!current->CheckFlag(HValue::kFlexibleRepresentation)) return; |
| 3626 if (in_worklist_.Contains(current->id())) return; | 2807 if (in_worklist_.Contains(current->id())) return; |
| 3627 worklist_.Add(current, zone()); | 2808 worklist_.Add(current, zone()); |
| 3628 in_worklist_.Add(current->id()); | 2809 in_worklist_.Add(current->id()); |
| 3629 } | 2810 } |
| 3630 | 2811 |
| 3631 | 2812 |
| 3632 void HInferRepresentation::Analyze() { | 2813 void HInferRepresentation::Analyze() { |
| (...skipping 8705 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 12338 } | 11519 } |
| 12339 } | 11520 } |
| 12340 | 11521 |
| 12341 #ifdef DEBUG | 11522 #ifdef DEBUG |
| 12342 if (graph_ != NULL) graph_->Verify(false); // No full verify. | 11523 if (graph_ != NULL) graph_->Verify(false); // No full verify. |
| 12343 if (allocator_ != NULL) allocator_->Verify(); | 11524 if (allocator_ != NULL) allocator_->Verify(); |
| 12344 #endif | 11525 #endif |
| 12345 } | 11526 } |
| 12346 | 11527 |
| 12347 } } // namespace v8::internal | 11528 } } // namespace v8::internal |
| OLD | NEW |