Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/flow_graph_optimizer.h" | 5 #include "vm/flow_graph_optimizer.h" |
| 6 | 6 |
| 7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
| 8 #include "vm/cha.h" | 8 #include "vm/cha.h" |
| 9 #include "vm/flow_graph_builder.h" | 9 #include "vm/flow_graph_builder.h" |
| 10 #include "vm/flow_graph_compiler.h" | 10 #include "vm/flow_graph_compiler.h" |
| (...skipping 2863 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2874 (*kill_by_offs)[offset_in_words] = new BitVector(expr_id); | 2874 (*kill_by_offs)[offset_in_words] = new BitVector(expr_id); |
| 2875 } | 2875 } |
| 2876 (*kill_by_offs)[offset_in_words]->Add(defn->expr_id()); | 2876 (*kill_by_offs)[offset_in_words]->Add(defn->expr_id()); |
| 2877 } | 2877 } |
| 2878 | 2878 |
| 2879 | 2879 |
| 2880 return expr_id; | 2880 return expr_id; |
| 2881 } | 2881 } |
| 2882 | 2882 |
| 2883 | 2883 |
| 2884 static void ComputeAvailableLoads( | 2884 class LoadOptimizer : public ValueObject { |
| 2885 FlowGraph* graph, | 2885 public: |
| 2886 intptr_t max_expr_id, | 2886 explicit LoadOptimizer(FlowGraph* graph, |
|
Florian Schneider
2012/12/13 13:17:13
No explicit needed.
Vyacheslav Egorov (Google)
2012/12/13 14:02:59
Done.
| |
| 2887 const GrowableArray<BitVector*>& avail_in, | 2887 intptr_t max_expr_id, |
| 2888 const GrowableArray<BitVector*>& kill_by_offs) { | 2888 const GrowableArray<BitVector*>& kill_by_offset) |
| 2889 // Initialize gen-, kill-, out-sets. | 2889 : graph_(graph), |
| 2890 intptr_t num_blocks = graph->preorder().length(); | 2890 max_expr_id_(max_expr_id), |
| 2891 GrowableArray<BitVector*> avail_out(num_blocks); | 2891 kill_by_offset_(kill_by_offset), |
| 2892 GrowableArray<BitVector*> avail_gen(num_blocks); | 2892 in_(graph_->preorder().length()), |
| 2893 GrowableArray<BitVector*> avail_kill(num_blocks); | 2893 out_(graph_->preorder().length()), |
| 2894 for (intptr_t i = 0; i < num_blocks; i++) { | 2894 gen_(graph_->preorder().length()), |
| 2895 avail_out.Add(new BitVector(max_expr_id)); | 2895 kill_(graph_->preorder().length()), |
| 2896 avail_gen.Add(new BitVector(max_expr_id)); | 2896 exposed_values_(graph_->preorder().length()), |
| 2897 avail_kill.Add(new BitVector(max_expr_id)); | 2897 out_values_(graph_->preorder().length()), |
| 2898 } | 2898 phis_(5), |
| 2899 | 2899 worklist_(5), |
| 2900 for (BlockIterator block_it = graph->reverse_postorder_iterator(); | 2900 in_worklist_(NULL) { |
| 2901 !block_it.Done(); | 2901 const intptr_t num_blocks = graph_->preorder().length(); |
| 2902 block_it.Advance()) { | 2902 for (intptr_t i = 0; i < num_blocks; i++) { |
| 2903 BlockEntryInstr* block = block_it.Current(); | 2903 out_.Add(new BitVector(max_expr_id_)); |
| 2904 intptr_t preorder_number = block->preorder_number(); | 2904 gen_.Add(new BitVector(max_expr_id_)); |
| 2905 for (BackwardInstructionIterator instr_it(block); | 2905 kill_.Add(new BitVector(max_expr_id_)); |
| 2906 !instr_it.Done(); | 2906 in_.Add(new BitVector(max_expr_id_)); |
| 2907 instr_it.Advance()) { | 2907 |
| 2908 Instruction* instr = instr_it.Current(); | 2908 exposed_values_.Add(NULL); |
| 2909 | 2909 out_values_.Add(NULL); |
| 2910 intptr_t offset_in_words = 0; | 2910 } |
| 2911 if (IsInterferingStore(instr, &offset_in_words)) { | 2911 } |
| 2912 if ((offset_in_words < kill_by_offs.length()) && | 2912 |
| 2913 (kill_by_offs[offset_in_words] != NULL)) { | 2913 void Optimize() { |
| 2914 avail_kill[preorder_number]->AddAll(kill_by_offs[offset_in_words]); | 2914 ComputeInitialSets(); |
| 2915 } | 2915 ComputeOutValues(); |
| 2916 ASSERT(instr->IsDefinition() && | 2916 ForwardLoads(); |
| 2917 !IsLoadEliminationCandidate(instr->AsDefinition())); | 2917 EmitPhis(); |
| 2918 continue; | 2918 } |
| 2919 } else if (instr->HasSideEffect()) { | 2919 |
| 2920 avail_kill[preorder_number]->SetAll(); | 2920 private: |
| 2921 break; | 2921 // Compute sets of loads generated and killed by each block. |
| 2922 } | 2922 // Additionally compute upwards exposed and generated loads for each block. |
| 2923 Definition* defn = instr->AsDefinition(); | 2923 // Exposed loads are those that can be replaced if a corresponding |
| 2924 if ((defn == NULL) || !IsLoadEliminationCandidate(defn)) { | 2924 // reaching load will be found. |
| 2925 continue; | 2925 // Loads that are locally redundant will be replaced as we go through |
| 2926 } | 2926 // instructions. |
| 2927 | 2927 void ComputeInitialSets() { |
| 2928 const intptr_t expr_id = defn->expr_id(); | 2928 for (BlockIterator block_it = graph_->reverse_postorder_iterator(); |
| 2929 if (!avail_kill[preorder_number]->Contains(expr_id)) { | |
| 2930 avail_gen[preorder_number]->Add(expr_id); | |
| 2931 } | |
| 2932 } | |
| 2933 avail_out[preorder_number]->CopyFrom(avail_gen[preorder_number]); | |
| 2934 } | |
| 2935 | |
| 2936 BitVector* temp = new BitVector(avail_in[0]->length()); | |
| 2937 | |
| 2938 bool changed = true; | |
| 2939 while (changed) { | |
| 2940 changed = false; | |
| 2941 | |
| 2942 for (BlockIterator block_it = graph->reverse_postorder_iterator(); | |
| 2943 !block_it.Done(); | 2929 !block_it.Done(); |
| 2944 block_it.Advance()) { | 2930 block_it.Advance()) { |
| 2945 BlockEntryInstr* block = block_it.Current(); | 2931 BlockEntryInstr* block = block_it.Current(); |
| 2946 BitVector* block_in = avail_in[block->preorder_number()]; | 2932 const intptr_t preorder_number = block->preorder_number(); |
| 2947 BitVector* block_out = avail_out[block->preorder_number()]; | 2933 |
| 2948 BitVector* block_kill = avail_kill[block->preorder_number()]; | 2934 BitVector* kill = kill_[preorder_number]; |
| 2949 BitVector* block_gen = avail_gen[block->preorder_number()]; | 2935 BitVector* gen = gen_[preorder_number]; |
| 2950 | 2936 |
| 2951 if (FLAG_trace_optimization) { | 2937 ZoneGrowableArray<Definition*>* exposed_values = NULL; |
| 2952 OS::Print("B%"Pd"", block->block_id()); | 2938 ZoneGrowableArray<Definition*>* out_values = NULL; |
| 2953 block_in->Print(); | 2939 |
| 2954 block_out->Print(); | 2940 for (ForwardInstructionIterator instr_it(block); |
| 2955 OS::Print("\n"); | 2941 !instr_it.Done(); |
| 2956 } | 2942 instr_it.Advance()) { |
| 2957 | 2943 Instruction* instr = instr_it.Current(); |
| 2958 // Compute block_in as the intersection of all out(p) where p | 2944 |
| 2959 // is a predecessor of the current block. | 2945 intptr_t offset_in_words = 0; |
| 2960 if (block->IsGraphEntry()) { | 2946 if (IsInterferingStore(instr, &offset_in_words)) { |
| 2961 temp->Clear(); | 2947 // Interfering stores kill only loads from the same offset. |
| 2962 } else { | 2948 if ((offset_in_words < kill_by_offset_.length()) && |
| 2963 temp->SetAll(); | 2949 (kill_by_offset_[offset_in_words] != NULL)) { |
| 2964 ASSERT(block->PredecessorCount() > 0); | 2950 kill->AddAll(kill_by_offset_[offset_in_words]); |
| 2965 for (intptr_t i = 0; i < block->PredecessorCount(); i++) { | 2951 // There is no need to clear out_values when clearing GEN set |
| 2966 BlockEntryInstr* pred = block->PredecessorAt(i); | 2952 // because only those values that are in the GEN set |
| 2967 BitVector* pred_out = avail_out[pred->preorder_number()]; | 2953 // will ever be used. |
| 2968 temp->Intersect(*pred_out); | 2954 gen->RemoveAll(kill_by_offset_[offset_in_words]); |
| 2969 } | 2955 } |
| 2970 } | 2956 ASSERT(instr->IsDefinition() && |
| 2971 if (!temp->Equals(*block_in)) { | 2957 !IsLoadEliminationCandidate(instr->AsDefinition())); |
| 2972 block_in->CopyFrom(temp); | 2958 continue; |
| 2973 if (block_out->KillAndAdd(block_kill, block_gen)) changed = true; | 2959 } |
| 2974 } | 2960 |
| 2975 } | 2961 // Other instructions with side effects kill all loads. |
| 2976 } | 2962 if (instr->HasSideEffect()) { |
| 2977 } | 2963 kill->SetAll(); |
| 2978 | 2964 // There is no need to clear out_values when clearing GEN set |
| 2979 | 2965 // because only those values that are in the GEN set |
| 2980 static bool OptimizeLoads( | 2966 // will ever be used. |
| 2981 BlockEntryInstr* block, | 2967 gen->Clear(); |
| 2982 GrowableArray<Definition*>* definitions, | 2968 continue; |
| 2983 const GrowableArray<BitVector*>& avail_in, | 2969 } |
| 2984 const GrowableArray<BitVector*>& kill_by_offs) { | 2970 |
| 2985 // TODO(fschneider): Factor out code shared with the existing CSE pass. | 2971 Definition* defn = instr->AsDefinition(); |
| 2986 | 2972 if ((defn == NULL) || !IsLoadEliminationCandidate(defn)) { |
| 2987 // Delete loads that are killed (not available) at the entry. | 2973 continue; |
| 2988 intptr_t pre_num = block->preorder_number(); | 2974 } |
| 2989 ASSERT(avail_in[pre_num]->length() == definitions->length()); | 2975 |
| 2990 for (intptr_t i = 0; i < avail_in[pre_num]->length(); i++) { | 2976 const intptr_t expr_id = defn->expr_id(); |
| 2991 if (!avail_in[pre_num]->Contains(i)) { | 2977 if (gen->Contains(expr_id)) { |
| 2992 (*definitions)[i] = NULL; | 2978 // This is a locally redundant load. |
| 2993 } | 2979 ASSERT((out_values != NULL) && ((*out_values)[expr_id] != NULL)); |
| 2994 } | 2980 |
| 2995 | 2981 if (FLAG_trace_optimization) { |
| 2996 bool changed = false; | 2982 OS::Print("Replacing load v%"Pd" with v%"Pd"\n", |
| 2997 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { | 2983 defn->ssa_temp_index(), |
| 2998 Instruction* instr = it.Current(); | 2984 (*out_values)[expr_id]->ssa_temp_index()); |
| 2999 | 2985 } |
| 3000 intptr_t offset_in_words = 0; | 2986 |
| 3001 if (IsInterferingStore(instr, &offset_in_words)) { | 2987 defn->ReplaceUsesWith((*out_values)[expr_id]); |
| 3002 if ((offset_in_words < kill_by_offs.length()) && | 2988 instr_it.RemoveCurrentFromGraph(); |
| 3003 (kill_by_offs[offset_in_words] != NULL)) { | 2989 continue; |
| 3004 for (BitVector::Iterator it(kill_by_offs[offset_in_words]); | 2990 } else if (!kill->Contains(expr_id)) { |
| 3005 !it.Done(); | 2991 // This is an exposed load: it is the first representative of a |
| 3006 it.Advance()) { | 2992 // given expression id and it is not killed on the path from |
| 3007 (*definitions)[it.Current()] = NULL; | 2993 // the block entry. |
| 3008 } | 2994 if (exposed_values == NULL) { |
| 3009 } | 2995 exposed_values = new ZoneGrowableArray<Definition*>(5); |
|
Florian Schneider
2012/12/13 13:17:13
max_expr_id should be upper bound. Maybe sth like:
Vyacheslav Egorov (Google)
2012/12/13 14:02:59
Done.
| |
| 3010 ASSERT(instr->IsDefinition() && | 2996 } |
| 3011 !IsLoadEliminationCandidate(instr->AsDefinition())); | 2997 |
| 3012 continue; | 2998 exposed_values->Add(defn); |
| 3013 } else if (instr->HasSideEffect()) { | 2999 } |
| 3014 // Handle local side effects by clearing current definitions. | 3000 |
| 3015 for (intptr_t i = 0; i < definitions->length(); i++) { | 3001 gen->Add(expr_id); |
| 3016 (*definitions)[i] = NULL; | 3002 |
| 3017 } | 3003 if (out_values == NULL) out_values = CreateBlockOutValues(); |
| 3018 continue; | 3004 (*out_values)[expr_id] = defn; |
| 3019 } | 3005 } |
| 3020 Definition* defn = instr->AsDefinition(); | 3006 |
| 3021 if ((defn == NULL) || !IsLoadEliminationCandidate(defn)) { | 3007 out_[preorder_number]->CopyFrom(gen); |
| 3022 continue; | 3008 exposed_values_[preorder_number] = exposed_values; |
| 3023 } | 3009 out_values_[preorder_number] = out_values; |
| 3024 Definition* result = (*definitions)[defn->expr_id()]; | 3010 } |
| 3025 if (result == NULL) { | 3011 } |
| 3026 (*definitions)[defn->expr_id()] = defn; | 3012 |
| 3027 continue; | 3013 // Compute OUT sets and corresponding out_values mappings by propagating them |
| 3028 } | 3014 // iteratively until fix point is reached. |
| 3029 | 3015 // No replacement is done at this point and thus any out_value[expr_id] is |
| 3030 // Replace current with lookup result. | 3016 // changed at most once: from NULL to an actual value. |
| 3031 defn->ReplaceUsesWith(result); | 3017 // When merging incomming loads we might need to create a phi. |
|
Florian Schneider
2012/12/13 13:17:13
s/incomming/incoming/
Vyacheslav Egorov (Google)
2012/12/13 14:02:59
Done.
| |
| 3032 it.RemoveCurrentFromGraph(); | 3018 // These phies are not inserted at the graph immediately because some of them |
|
Florian Schneider
2012/12/13 13:17:13
s/phies/phis/
Vyacheslav Egorov (Google)
2012/12/13 14:02:59
Done.
| |
| 3033 changed = true; | 3019 // might become redundant after load forwarding is done. |
| 3034 if (FLAG_trace_optimization) { | 3020 void ComputeOutValues() { |
| 3035 OS::Print("Replacing load v%"Pd" with v%"Pd"\n", | 3021 BitVector* temp = new BitVector(max_expr_id_); |
| 3036 defn->ssa_temp_index(), | 3022 |
| 3037 result->ssa_temp_index()); | 3023 bool changed = true; |
| 3038 } | 3024 while (changed) { |
| 3039 } | 3025 changed = false; |
| 3040 | 3026 |
| 3041 // Process children in the dominator tree recursively. | 3027 for (BlockIterator block_it = graph_->reverse_postorder_iterator(); |
| 3042 intptr_t num_children = block->dominated_blocks().length(); | 3028 !block_it.Done(); |
| 3043 for (intptr_t i = 0; i < num_children; ++i) { | 3029 block_it.Advance()) { |
| 3044 BlockEntryInstr* child = block->dominated_blocks()[i]; | 3030 BlockEntryInstr* block = block_it.Current(); |
| 3045 if (i < num_children - 1) { | 3031 |
| 3046 GrowableArray<Definition*> child_defs(definitions->length()); | 3032 const intptr_t preorder_number = block->preorder_number(); |
| 3047 child_defs.AddArray(*definitions); | 3033 |
| 3048 changed = OptimizeLoads(child, &child_defs, avail_in, kill_by_offs) || | 3034 BitVector* block_in = in_[preorder_number]; |
| 3049 changed; | 3035 BitVector* block_out = out_[preorder_number]; |
| 3036 BitVector* block_kill = kill_[preorder_number]; | |
| 3037 BitVector* block_gen = gen_[preorder_number]; | |
| 3038 | |
| 3039 if (FLAG_trace_optimization) { | |
| 3040 OS::Print("B%"Pd"", block->block_id()); | |
| 3041 block_in->Print(); | |
| 3042 block_out->Print(); | |
| 3043 block_kill->Print(); | |
| 3044 block_gen->Print(); | |
| 3045 OS::Print("\n"); | |
| 3046 } | |
| 3047 | |
| 3048 ZoneGrowableArray<Definition*>* block_out_values = | |
| 3049 out_values_[preorder_number]; | |
| 3050 | |
| 3051 // Compute block_in as the intersection of all out(p) where p | |
| 3052 // is a predecessor of the current block. | |
| 3053 if (block->IsGraphEntry()) { | |
| 3054 temp->Clear(); | |
| 3055 } else { | |
| 3056 // TODO(vegorov): this can be optimized for the case of a single | |
| 3057 // predecessor. | |
| 3058 // TODO(vegorov): this can be reordered to reduce amount of operations | |
| 3059 // temp->CopyFrom(first_predecessor) | |
| 3060 temp->SetAll(); | |
| 3061 ASSERT(block->PredecessorCount() > 0); | |
| 3062 for (intptr_t i = 0; i < block->PredecessorCount(); i++) { | |
| 3063 BlockEntryInstr* pred = block->PredecessorAt(i); | |
| 3064 BitVector* pred_out = out_[pred->preorder_number()]; | |
| 3065 temp->Intersect(*pred_out); | |
| 3066 } | |
| 3067 } | |
| 3068 | |
| 3069 if (!temp->Equals(*block_in)) { | |
| 3070 // If IN set has changed propagate the change to OUT set. | |
| 3071 block_in->CopyFrom(temp); | |
| 3072 if (block_out->KillAndAdd(block_kill, block_in)) { | |
| 3073 // If OUT set has changed then we have new values available out of | |
| 3074 // the block. Compute these values creating phi where necessary. | |
| 3075 for (BitVector::Iterator it(block_out); | |
| 3076 !it.Done(); | |
| 3077 it.Advance()) { | |
| 3078 const intptr_t expr_id = it.Current(); | |
| 3079 | |
| 3080 if (block_out_values == NULL) { | |
| 3081 out_values_[preorder_number] = block_out_values = | |
| 3082 CreateBlockOutValues(); | |
| 3083 } | |
| 3084 | |
| 3085 if ((*block_out_values)[expr_id] == NULL) { | |
| 3086 ASSERT(block->PredecessorCount() > 0); | |
| 3087 (*block_out_values)[expr_id] = | |
| 3088 MergeIncomingValues(block, expr_id); | |
| 3089 } | |
| 3090 } | |
| 3091 changed = true; | |
| 3092 } | |
| 3093 } | |
| 3094 | |
| 3095 if (FLAG_trace_optimization) { | |
| 3096 OS::Print("after B%"Pd"", block->block_id()); | |
| 3097 block_in->Print(); | |
| 3098 block_out->Print(); | |
| 3099 block_kill->Print(); | |
| 3100 block_gen->Print(); | |
| 3101 OS::Print("\n"); | |
| 3102 } | |
| 3103 } | |
| 3104 } | |
| 3105 } | |
| 3106 | |
| 3107 // Compute incoming value for the given expression id. | |
| 3108 // Will create a phi if different values are incoming from multiple | |
| 3109 // predecessors. | |
| 3110 Definition* MergeIncomingValues(BlockEntryInstr* block, intptr_t expr_id) { | |
| 3111 // First check if the same value is coming in from all predecessors. | |
| 3112 Definition* incoming = NULL; | |
| 3113 for (intptr_t i = 0; i < block->PredecessorCount(); i++) { | |
| 3114 BlockEntryInstr* pred = block->PredecessorAt(i); | |
| 3115 ZoneGrowableArray<Definition*>* pred_out_values = | |
| 3116 out_values_[pred->preorder_number()]; | |
| 3117 if (incoming == NULL) { | |
| 3118 incoming = (*pred_out_values)[expr_id]; | |
| 3119 } else if (incoming != (*pred_out_values)[expr_id]) { | |
| 3120 incoming = NULL; | |
| 3121 break; | |
| 3122 } | |
| 3123 } | |
| 3124 | |
| 3125 if (incoming != NULL) { | |
| 3126 return incoming; | |
| 3127 } | |
| 3128 | |
| 3129 // Incoming values are different. Phi is required to merge. | |
| 3130 PhiInstr* phi = new PhiInstr( | |
| 3131 block->AsJoinEntry(), block->PredecessorCount()); | |
| 3132 | |
| 3133 for (intptr_t i = 0; i < block->PredecessorCount(); i++) { | |
| 3134 BlockEntryInstr* pred = block->PredecessorAt(i); | |
| 3135 ZoneGrowableArray<Definition*>* pred_out_values = | |
| 3136 out_values_[pred->preorder_number()]; | |
| 3137 | |
| 3138 // Sets of outgoing values are not linked into use lists so | |
| 3139 // they might contain values that were replace and removed | |
|
Florian Schneider
2012/12/13 13:17:13
s/replace/replaced/
Vyacheslav Egorov (Google)
2012/12/13 14:02:59
Done.
| |
| 3140 // from the graph by this iteration. | |
| 3141 // To prevent using them we additionally mark definitions themselves | |
| 3142 // as replaced and store a pointer to the replacement. | |
|
Florian Schneider
2012/12/13 13:17:13
Maybe add an assert that:
(*pred_out_values)[expr
Vyacheslav Egorov (Google)
2012/12/13 14:02:59
Done.
| |
| 3143 Value* input = new Value((*pred_out_values)[expr_id]->Replacement()); | |
| 3144 phi->SetInputAt(i, input); | |
| 3145 | |
| 3146 input->set_instruction(phi); | |
|
Florian Schneider
2012/12/13 13:17:13
Should we have a helper RegisterUse that does thes
Vyacheslav Egorov (Google)
2012/12/13 14:02:59
I will leave it for a separate CL.
| |
| 3147 input->set_use_index(i); | |
| 3148 input->AddToInputUseList(); | |
| 3149 } | |
| 3150 | |
| 3151 phi->set_ssa_temp_index(graph_->alloc_ssa_temp_index()); | |
| 3152 phis_.Add(phi); // Postpone phi insertion until after load forwarding. | |
| 3153 | |
| 3154 return phi; | |
| 3155 } | |
| 3156 | |
| 3157 Definition* IncomingValue(BlockEntryInstr* block, intptr_t expr_id) { | |
| 3158 // We might have already computed the value during OUT set computation. | |
| 3159 // Reuse it to prevent creation of identical phis. | |
| 3160 if (!kill_[block->preorder_number()]->Contains(expr_id) && | |
| 3161 !gen_[block->preorder_number()]->Contains(expr_id)) { | |
|
Florian Schneider
2012/12/13 13:17:13
I think this can be simplified: upward exposed loa
Vyacheslav Egorov (Google)
2012/12/13 14:02:59
Good observation. Killed the helper.
| |
| 3162 ASSERT(out_[block->preorder_number()]->Contains(expr_id)); | |
| 3163 ASSERT(out_values_[block->preorder_number()] != NULL); | |
| 3164 ASSERT((*out_values_[block->preorder_number()])[expr_id] != NULL); | |
| 3165 return (*out_values_[block->preorder_number()])[expr_id]; | |
| 3166 } | |
| 3167 | |
| 3168 return MergeIncomingValues(block, expr_id); | |
| 3169 } | |
| 3170 | |
| 3171 // Iterate over basic blocks and replace exposed loads with incoming | |
| 3172 // values. | |
| 3173 void ForwardLoads() { | |
| 3174 for (BlockIterator block_it = graph_->reverse_postorder_iterator(); | |
| 3175 !block_it.Done(); | |
| 3176 block_it.Advance()) { | |
| 3177 BlockEntryInstr* block = block_it.Current(); | |
| 3178 | |
| 3179 ZoneGrowableArray<Definition*>* loads = | |
| 3180 exposed_values_[block->preorder_number()]; | |
| 3181 if (loads == NULL) continue; // No exposed loads. | |
| 3182 | |
| 3183 BitVector* in = in_[block->preorder_number()]; | |
| 3184 | |
| 3185 for (intptr_t i = 0; i < loads->length(); i++) { | |
| 3186 Definition* load = (*loads)[i]; | |
| 3187 if (!in->Contains(load->expr_id())) continue; // No incoming value. | |
| 3188 | |
| 3189 Definition* replacement = IncomingValue(block, load->expr_id()); | |
| 3190 | |
| 3191 // Sets of outgoing values are not linked into use lists so | |
| 3192 // they might contain values that were replace and removed | |
| 3193 // from the graph by this iteration. | |
| 3194 // To prevent using them we additionally mark definitions themselves | |
| 3195 // as replaced and store a pointer to the replacement. | |
| 3196 replacement = replacement->Replacement(); | |
| 3197 | |
| 3198 if (load != replacement) { | |
| 3199 if (FLAG_trace_optimization) { | |
| 3200 OS::Print("Replacing load v%"Pd" with v%"Pd"\n", | |
| 3201 load->ssa_temp_index(), | |
| 3202 replacement->ssa_temp_index()); | |
| 3203 } | |
| 3204 | |
| 3205 load->ReplaceUsesWith(replacement); | |
| 3206 load->RemoveFromGraph(); | |
| 3207 load->SetReplacement(replacement); | |
| 3208 } | |
| 3209 } | |
| 3210 } | |
| 3211 } | |
| 3212 | |
| 3213 // Check if the given phi take the same value on all code paths. | |
| 3214 // Eliminate it as redundant if this is the case. | |
| 3215 // When analyzing phi operands assumes that only generated during | |
| 3216 // this load phase can be redundant. They can be distinguished because | |
| 3217 // they are not marked alive. | |
| 3218 // TODO(vegorov): move this into a separate phase over all phis. | |
| 3219 bool EliminateRedundantPhi(PhiInstr* phi) { | |
| 3220 Definition* value = NULL; // Possible value of this phi. | |
| 3221 | |
| 3222 worklist_.Clear(); | |
| 3223 if (in_worklist_ == NULL) { | |
| 3224 in_worklist_ = new BitVector(graph_->current_ssa_temp_index()); | |
| 3050 } else { | 3225 } else { |
| 3051 changed = OptimizeLoads(child, definitions, avail_in, kill_by_offs) || | 3226 in_worklist_->Clear(); |
| 3052 changed; | 3227 } |
| 3053 } | 3228 |
| 3054 } | 3229 worklist_.Add(phi); |
| 3055 return changed; | 3230 in_worklist_->Add(phi->ssa_temp_index()); |
| 3056 } | 3231 |
| 3232 for (intptr_t i = 0; i < worklist_.length(); i++) { | |
| 3233 PhiInstr* phi = worklist_[i]; | |
| 3234 | |
| 3235 for (intptr_t i = 0; i < phi->InputCount(); i++) { | |
| 3236 Definition* input = phi->InputAt(i)->definition(); | |
| 3237 if (input == phi) continue; | |
| 3238 | |
| 3239 PhiInstr* phi_input = input->AsPhi(); | |
| 3240 if ((phi_input != NULL) && !phi_input->is_alive()) { | |
| 3241 if (!in_worklist_->Contains(phi_input->ssa_temp_index())) { | |
| 3242 worklist_.Add(phi_input); | |
| 3243 in_worklist_->Add(phi_input->ssa_temp_index()); | |
| 3244 } | |
| 3245 continue; | |
| 3246 } | |
| 3247 | |
| 3248 if (value == NULL) { | |
| 3249 value = input; | |
| 3250 } else if (value != input) { | |
| 3251 return false; // This phi is not redundant. | |
| 3252 } | |
| 3253 } | |
| 3254 } | |
| 3255 | |
| 3256 // All phis in the worklist are redundant and have the same computed | |
| 3257 // value on all code paths. | |
| 3258 ASSERT(value != NULL); | |
| 3259 for (intptr_t i = 0; i < worklist_.length(); i++) { | |
| 3260 worklist_[i]->ReplaceUsesWith(value); | |
| 3261 } | |
| 3262 | |
| 3263 return true; | |
| 3264 } | |
| 3265 | |
| 3266 // Emit non-redundant phis created during ComputeOutValues and ForwardLoads. | |
| 3267 void EmitPhis() { | |
| 3268 for (intptr_t i = 0; i < phis_.length(); i++) { | |
| 3269 PhiInstr* phi = phis_[i]; | |
| 3270 if ((phi->input_use_list() != NULL) && !EliminateRedundantPhi(phi)) { | |
| 3271 phi->mark_alive(); | |
| 3272 phi->block()->InsertPhi(phi); | |
| 3273 } | |
| 3274 } | |
| 3275 } | |
| 3276 | |
| 3277 ZoneGrowableArray<Definition*>* CreateBlockOutValues() { | |
| 3278 ZoneGrowableArray<Definition*>* out = | |
| 3279 new ZoneGrowableArray<Definition*>(max_expr_id_); | |
| 3280 for (intptr_t i = 0; i < max_expr_id_; i++) { | |
| 3281 out->Add(NULL); | |
| 3282 } | |
| 3283 return out; | |
| 3284 } | |
| 3285 | |
| 3286 FlowGraph* graph_; | |
| 3287 const intptr_t max_expr_id_; | |
| 3288 | |
| 3289 // Mapping between field offsets in words and expression ids of loads from | |
| 3290 // that offset. | |
| 3291 const GrowableArray<BitVector*>& kill_by_offset_; | |
| 3292 | |
| 3293 // Per block sets of expression ids for loads that are: incoming (available | |
| 3294 // on the entry), outgoing (available on the exit), generated and killed. | |
| 3295 GrowableArray<BitVector*> in_; | |
| 3296 GrowableArray<BitVector*> out_; | |
| 3297 GrowableArray<BitVector*> gen_; | |
| 3298 GrowableArray<BitVector*> kill_; | |
| 3299 | |
| 3300 // Per block list of upwards exposed loads. | |
| 3301 GrowableArray<ZoneGrowableArray<Definition*>*> exposed_values_; | |
| 3302 | |
| 3303 // Per block mappings between expression ids and outgoing definitions that | |
| 3304 // represent those ids. | |
| 3305 GrowableArray<ZoneGrowableArray<Definition*>*> out_values_; | |
| 3306 | |
| 3307 // List of phis generated during ComputeOutValues and ForwardLoads. | |
| 3308 // Some of these phis might be redundant and thus a separate pass is | |
| 3309 // needed to emit only non-redundant ones. | |
| 3310 GrowableArray<PhiInstr*> phis_; | |
| 3311 | |
| 3312 // Auxiliary worklist used by redundant phi elimination. | |
| 3313 GrowableArray<PhiInstr*> worklist_; | |
| 3314 BitVector* in_worklist_; | |
| 3315 | |
| 3316 DISALLOW_COPY_AND_ASSIGN(LoadOptimizer); | |
| 3317 }; | |
| 3057 | 3318 |
| 3058 | 3319 |
| 3059 bool DominatorBasedCSE::Optimize(FlowGraph* graph) { | 3320 bool DominatorBasedCSE::Optimize(FlowGraph* graph) { |
| 3060 bool changed = false; | 3321 bool changed = false; |
| 3061 if (FLAG_load_cse) { | 3322 if (FLAG_load_cse) { |
| 3062 GrowableArray<BitVector*> kill_by_offs(10); | 3323 GrowableArray<BitVector*> kill_by_offs(10); |
| 3063 intptr_t max_expr_id = NumberLoadExpressions(graph, &kill_by_offs); | 3324 const intptr_t max_expr_id = NumberLoadExpressions(graph, &kill_by_offs); |
| 3064 if (max_expr_id > 0) { | 3325 if (max_expr_id > 0) { |
| 3065 intptr_t num_blocks = graph->preorder().length(); | 3326 LoadOptimizer load_optimizer(graph, max_expr_id, kill_by_offs); |
| 3066 GrowableArray<BitVector*> avail_in(num_blocks); | 3327 load_optimizer.Optimize(); |
| 3067 for (intptr_t i = 0; i < num_blocks; i++) { | 3328 } |
| 3068 avail_in.Add(new BitVector(max_expr_id)); | 3329 } |
| 3069 } | 3330 |
| 3070 | |
| 3071 ComputeAvailableLoads(graph, max_expr_id, avail_in, kill_by_offs); | |
| 3072 | |
| 3073 GrowableArray<Definition*> definitions(max_expr_id); | |
| 3074 for (intptr_t j = 0; j < max_expr_id ; j++) { | |
| 3075 definitions.Add(NULL); | |
| 3076 } | |
| 3077 changed = OptimizeLoads( | |
| 3078 graph->graph_entry(), &definitions, avail_in, kill_by_offs); | |
| 3079 } | |
| 3080 } | |
| 3081 | |
| 3082 DirectChainedHashMap<PointerKeyValueTrait<Instruction> > map; | 3331 DirectChainedHashMap<PointerKeyValueTrait<Instruction> > map; |
| 3083 changed = OptimizeRecursive(graph->graph_entry(), &map) || changed; | 3332 changed = OptimizeRecursive(graph->graph_entry(), &map) || changed; |
| 3084 | 3333 |
| 3085 return changed; | 3334 return changed; |
| 3086 } | 3335 } |
| 3087 | 3336 |
| 3088 | 3337 |
| 3089 bool DominatorBasedCSE::OptimizeRecursive( | 3338 bool DominatorBasedCSE::OptimizeRecursive( |
| 3090 BlockEntryInstr* block, | 3339 BlockEntryInstr* block, |
| 3091 DirectChainedHashMap<PointerKeyValueTrait<Instruction> >* map) { | 3340 DirectChainedHashMap<PointerKeyValueTrait<Instruction> >* map) { |
| (...skipping 830 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3922 | 4171 |
| 3923 if (FLAG_trace_constant_propagation) { | 4172 if (FLAG_trace_constant_propagation) { |
| 3924 OS::Print("\n==== After constant propagation ====\n"); | 4173 OS::Print("\n==== After constant propagation ====\n"); |
| 3925 FlowGraphPrinter printer(*graph_); | 4174 FlowGraphPrinter printer(*graph_); |
| 3926 printer.PrintBlocks(); | 4175 printer.PrintBlocks(); |
| 3927 } | 4176 } |
| 3928 } | 4177 } |
| 3929 | 4178 |
| 3930 | 4179 |
| 3931 } // namespace dart | 4180 } // namespace dart |
| OLD | NEW |