| 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_inliner.h" | 5 #include "vm/flow_graph_inliner.h" |
| 6 | 6 |
| 7 #include "vm/compiler.h" | 7 #include "vm/compiler.h" |
| 8 #include "vm/flags.h" | 8 #include "vm/flags.h" |
| 9 #include "vm/flow_graph.h" | 9 #include "vm/flow_graph.h" |
| 10 #include "vm/flow_graph_builder.h" | 10 #include "vm/flow_graph_builder.h" |
| (...skipping 16 matching lines...) Expand all Loading... |
| 27 if (FLAG_trace_inlining) statement; \ | 27 if (FLAG_trace_inlining) statement; \ |
| 28 } while (false) | 28 } while (false) |
| 29 | 29 |
| 30 | 30 |
| 31 class CallSiteInliner : public FlowGraphVisitor { | 31 class CallSiteInliner : public FlowGraphVisitor { |
| 32 public: | 32 public: |
| 33 explicit CallSiteInliner(FlowGraph* flow_graph) | 33 explicit CallSiteInliner(FlowGraph* flow_graph) |
| 34 : FlowGraphVisitor(flow_graph->postorder()), | 34 : FlowGraphVisitor(flow_graph->postorder()), |
| 35 caller_graph_(flow_graph), | 35 caller_graph_(flow_graph), |
| 36 next_ssa_temp_index_(flow_graph->max_virtual_register_number()), | 36 next_ssa_temp_index_(flow_graph->max_virtual_register_number()), |
| 37 inlined_(false) { } | 37 inlined_(false), |
| 38 static_calls_(), |
| 39 closure_calls_(), |
| 40 instance_calls_() { } |
| 38 | 41 |
| 42 void VisitClosureCall(ClosureCallInstr* call) { |
| 43 closure_calls_.Add(call); |
| 44 } |
| 45 |
| 46 void VisitPolymorphicInstanceCall(PolymorphicInstanceCallInstr* call) { |
| 47 instance_calls_.Add(call); |
| 48 } |
| 49 |
| 50 void VisitStaticCall(StaticCallInstr* call) { |
| 51 if (call->function().is_inlinable()) static_calls_.Add(call); |
| 52 } |
| 53 |
| 54 void FindCallSites() { |
| 55 VisitBlocks(); |
| 56 } |
| 57 |
| 58 void InlineCalls() { |
| 59 InlineStaticCalls(); |
| 60 InlineClosureCalls(); |
| 61 InlineInstanceCalls(); |
| 62 } |
| 63 |
| 64 bool inlined() const { return inlined_; } |
| 65 |
| 66 private: |
| 39 bool TryInlining(const Function& function, | 67 bool TryInlining(const Function& function, |
| 40 GrowableArray<Value*>* arguments, | 68 GrowableArray<Value*>* arguments, |
| 41 Definition* call) { | 69 Definition* call) { |
| 42 TRACE_INLINING(OS::Print(" => %s\n", function.ToCString())); | 70 TRACE_INLINING(OS::Print(" => %s\n", function.ToCString())); |
| 43 | 71 |
| 44 // Abort if the inlinable bit on the function is low. | 72 // Abort if the inlinable bit on the function is low. |
| 45 if (!function.is_inlinable()) { | 73 if (!function.is_inlinable()) { |
| 46 TRACE_INLINING(OS::Print(" Bailout: not inlinable\n")); | 74 TRACE_INLINING(OS::Print(" Bailout: not inlinable\n")); |
| 47 return false; | 75 return false; |
| 48 } | 76 } |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 84 if ((function.deoptimization_counter() < | 112 if ((function.deoptimization_counter() < |
| 85 FLAG_deoptimization_counter_threshold) && | 113 FLAG_deoptimization_counter_threshold) && |
| 86 function.HasCode()) { | 114 function.HasCode()) { |
| 87 const Code& unoptimized_code = | 115 const Code& unoptimized_code = |
| 88 Code::Handle(function.unoptimized_code()); | 116 Code::Handle(function.unoptimized_code()); |
| 89 isolate->set_ic_data_array(unoptimized_code.ExtractTypeFeedbackArray()); | 117 isolate->set_ic_data_array(unoptimized_code.ExtractTypeFeedbackArray()); |
| 90 } | 118 } |
| 91 | 119 |
| 92 // Build the callee graph. | 120 // Build the callee graph. |
| 93 FlowGraphBuilder builder(parsed_function); | 121 FlowGraphBuilder builder(parsed_function); |
| 122 builder.SetInitialBlockId(caller_graph_->max_block_id()); |
| 94 FlowGraph* callee_graph = | 123 FlowGraph* callee_graph = |
| 95 builder.BuildGraph(FlowGraphBuilder::kValueContext); | 124 builder.BuildGraph(FlowGraphBuilder::kValueContext); |
| 96 | 125 |
| 97 // Abort if the callee graph contains control flow. | |
| 98 if (callee_graph->preorder().length() != 2) { | |
| 99 function.set_is_inlinable(false); | |
| 100 isolate->set_long_jump_base(base); | |
| 101 isolate->set_ic_data_array(prev_ic_data.raw()); | |
| 102 TRACE_INLINING(OS::Print(" Bailout: control flow\n")); | |
| 103 return false; | |
| 104 } | |
| 105 | |
| 106 // Compute SSA on the callee graph, catching bailouts. | 126 // Compute SSA on the callee graph, catching bailouts. |
| 107 callee_graph->ComputeSSA(next_ssa_temp_index_); | 127 callee_graph->ComputeSSA(next_ssa_temp_index_); |
| 108 callee_graph->ComputeUseLists(); | 128 callee_graph->ComputeUseLists(); |
| 109 | 129 |
| 110 // TODO(zerny): Do more optimization passes on the callee graph. | 130 // TODO(zerny): Do more optimization passes on the callee graph. |
| 111 FlowGraphOptimizer optimizer(callee_graph); | 131 FlowGraphOptimizer optimizer(callee_graph); |
| 112 optimizer.ApplyICData(); | 132 optimizer.ApplyICData(); |
| 113 callee_graph->ComputeUseLists(); | 133 callee_graph->ComputeUseLists(); |
| 114 | 134 |
| 115 if (FLAG_trace_inlining && FLAG_print_flow_graph) { | 135 if (FLAG_trace_inlining && FLAG_print_flow_graph) { |
| 116 OS::Print("Callee graph for inlining %s\n", | 136 OS::Print("Callee graph for inlining %s\n", |
| 117 parsed_function.function().ToFullyQualifiedCString()); | 137 parsed_function.function().ToFullyQualifiedCString()); |
| 118 FlowGraphPrinter printer(*callee_graph); | 138 FlowGraphPrinter printer(*callee_graph); |
| 119 printer.PrintBlocks(); | 139 printer.PrintBlocks(); |
| 120 } | 140 } |
| 121 | 141 |
| 122 // TODO(zerny): If result is more than size threshold then abort. | 142 // TODO(zerny): If result is more than size threshold then abort. |
| 123 | 143 |
| 124 // TODO(zerny): If effort is less than threshold then inline recursively. | 144 // TODO(zerny): If effort is less than threshold then inline recursively. |
| 125 | 145 |
| 126 // Plug result in the caller graph. | 146 // Plug result in the caller graph. |
| 127 caller_graph_->InlineCall(call, callee_graph); | 147 caller_graph_->InlineCall(call, callee_graph); |
| 128 next_ssa_temp_index_ = caller_graph_->max_virtual_register_number(); | 148 next_ssa_temp_index_ = caller_graph_->max_virtual_register_number(); |
| 129 | 149 |
| 130 // Check that inlining maintains use lists. | |
| 131 DEBUG_ASSERT(caller_graph_->ValidateUseLists()); | |
| 132 | |
| 133 // Remove push arguments of the call. | 150 // Remove push arguments of the call. |
| 134 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { | 151 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { |
| 135 PushArgumentInstr* push = call->ArgumentAt(i); | 152 PushArgumentInstr* push = call->ArgumentAt(i); |
| 136 push->ReplaceUsesWith(push->value()->definition()); | 153 push->ReplaceUsesWith(push->value()->definition()); |
| 137 push->RemoveFromGraph(); | 154 push->RemoveFromGraph(); |
| 138 } | 155 } |
| 139 | 156 |
| 140 // Replace formal parameters with actuals. | 157 // Replace formal parameters with actuals. |
| 141 intptr_t arg_index = 0; | 158 intptr_t arg_index = 0; |
| 142 GrowableArray<Definition*>* defns = | 159 GrowableArray<Definition*>* defns = |
| 143 callee_graph->graph_entry()->initial_definitions(); | 160 callee_graph->graph_entry()->initial_definitions(); |
| 144 for (intptr_t i = 0; i < defns->length(); ++i) { | 161 for (intptr_t i = 0; i < defns->length(); ++i) { |
| 145 ParameterInstr* param = (*defns)[i]->AsParameter(); | 162 ParameterInstr* param = (*defns)[i]->AsParameter(); |
| 146 if (param != NULL) { | 163 if (param != NULL) { |
| 147 param->ReplaceUsesWith((*arguments)[arg_index++]->definition()); | 164 param->ReplaceUsesWith((*arguments)[arg_index++]->definition()); |
| 148 } | 165 } |
| 149 } | 166 } |
| 150 ASSERT(arg_index == arguments->length()); | 167 ASSERT(arg_index == arguments->length()); |
| 151 | 168 |
| 152 // Replace callee's null constant with caller's null constant. | 169 // Replace callee's null constant with caller's null constant. |
| 153 callee_graph->graph_entry()->constant_null()->ReplaceUsesWith( | 170 callee_graph->graph_entry()->constant_null()->ReplaceUsesWith( |
| 154 caller_graph_->graph_entry()->constant_null()); | 171 caller_graph_->graph_entry()->constant_null()); |
| 155 | 172 |
| 156 TRACE_INLINING(OS::Print(" Success\n")); | 173 TRACE_INLINING(OS::Print(" Success\n")); |
| 157 | 174 |
| 175 // Check that inlining maintains use lists. |
| 176 DEBUG_ASSERT(caller_graph_->ValidateUseLists()); |
| 177 |
| 158 // Build succeeded so we restore the bailout jump. | 178 // Build succeeded so we restore the bailout jump. |
| 159 inlined_ = true; | 179 inlined_ = true; |
| 160 isolate->set_long_jump_base(base); | 180 isolate->set_long_jump_base(base); |
| 161 isolate->set_deopt_id(prev_deopt_id); | 181 isolate->set_deopt_id(prev_deopt_id); |
| 162 isolate->set_ic_data_array(prev_ic_data.raw()); | 182 isolate->set_ic_data_array(prev_ic_data.raw()); |
| 163 return true; | 183 return true; |
| 164 } else { | 184 } else { |
| 165 Error& error = Error::Handle(); | 185 Error& error = Error::Handle(); |
| 166 error = isolate->object_store()->sticky_error(); | 186 error = isolate->object_store()->sticky_error(); |
| 167 isolate->object_store()->clear_sticky_error(); | 187 isolate->object_store()->clear_sticky_error(); |
| 168 isolate->set_long_jump_base(base); | 188 isolate->set_long_jump_base(base); |
| 169 isolate->set_deopt_id(prev_deopt_id); | 189 isolate->set_deopt_id(prev_deopt_id); |
| 170 isolate->set_ic_data_array(prev_ic_data.raw()); | 190 isolate->set_ic_data_array(prev_ic_data.raw()); |
| 171 TRACE_INLINING(OS::Print(" Bailout: %s\n", error.ToErrorCString())); | 191 TRACE_INLINING(OS::Print(" Bailout: %s\n", error.ToErrorCString())); |
| 172 return false; | 192 return false; |
| 173 } | 193 } |
| 174 } | 194 } |
| 175 | 195 |
| 176 void VisitClosureCall(ClosureCallInstr* call) { | 196 void InlineStaticCalls() { |
| 177 TRACE_INLINING(OS::Print(" ClosureCall\n")); | 197 TRACE_INLINING(OS::Print(" Static Calls (%d)\n", |
| 178 // Find the closure of the callee. | 198 static_calls_.length())); |
| 179 ASSERT(call->ArgumentCount() > 0); | 199 for (intptr_t i = 0; i < static_calls_.length(); ++i) { |
| 180 const CreateClosureInstr* closure = | 200 StaticCallInstr* call = static_calls_[i]; |
| 181 call->ArgumentAt(0)->value()->definition()->AsCreateClosure(); | 201 GrowableArray<Value*> arguments(call->ArgumentCount()); |
| 182 if (closure == NULL) { | 202 for (int i = 0; i < call->ArgumentCount(); ++i) { |
| 183 TRACE_INLINING(OS::Print(" Bailout: non-closure operator\n")); | 203 arguments.Add(call->ArgumentAt(i)->value()); |
| 184 return; | 204 } |
| 205 TryInlining(call->function(), &arguments, call); |
| 185 } | 206 } |
| 186 GrowableArray<Value*> arguments(call->ArgumentCount() - 1); | |
| 187 for (int i = 1; i < call->ArgumentCount(); ++i) { | |
| 188 arguments.Add(call->ArgumentAt(i)->value()); | |
| 189 } | |
| 190 TryInlining(closure->function(), &arguments, call); | |
| 191 } | 207 } |
| 192 | 208 |
| 193 void VisitPolymorphicInstanceCall(PolymorphicInstanceCallInstr* instr) { | 209 void InlineClosureCalls() { |
| 194 TRACE_INLINING(OS::Print(" PolymorphicInstanceCall\n")); | 210 TRACE_INLINING(OS::Print(" Closure Calls (%d)\n", |
| 195 const ICData& ic_data = instr->ic_data(); | 211 closure_calls_.length())); |
| 196 const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0)); | 212 for (intptr_t i = 0; i < closure_calls_.length(); ++i) { |
| 197 if (instr->with_checks()) { | 213 ClosureCallInstr* call = closure_calls_[i]; |
| 198 TRACE_INLINING(OS::Print(" Bailout: %"Pd" checks target '%s'\n", | 214 // Find the closure of the callee. |
| 199 ic_data.NumberOfChecks(), | 215 ASSERT(call->ArgumentCount() > 0); |
| 200 target.ToCString())); | 216 const CreateClosureInstr* closure = |
| 201 return; | 217 call->ArgumentAt(0)->value()->definition()->AsCreateClosure(); |
| 218 if (closure == NULL) { |
| 219 TRACE_INLINING(OS::Print(" Bailout: non-closure operator\n")); |
| 220 continue; |
| 221 } |
| 222 GrowableArray<Value*> arguments(call->ArgumentCount() - 1); |
| 223 for (int i = 1; i < call->ArgumentCount(); ++i) { |
| 224 arguments.Add(call->ArgumentAt(i)->value()); |
| 225 } |
| 226 TryInlining(closure->function(), &arguments, call); |
| 202 } | 227 } |
| 203 | |
| 204 GrowableArray<Value*> arguments(instr->ArgumentCount()); | |
| 205 for (int i = 0; i < instr->ArgumentCount(); ++i) { | |
| 206 arguments.Add(instr->ArgumentAt(i)->value()); | |
| 207 } | |
| 208 | |
| 209 TryInlining(target, &arguments, instr); | |
| 210 } | 228 } |
| 211 | 229 |
| 212 void VisitStaticCall(StaticCallInstr* call) { | 230 void InlineInstanceCalls() { |
| 213 TRACE_INLINING(OS::Print(" StaticCall\n")); | 231 TRACE_INLINING(OS::Print(" Polymorphic Instance Calls (%d)\n", |
| 214 GrowableArray<Value*> arguments(call->ArgumentCount()); | 232 instance_calls_.length())); |
| 215 for (int i = 0; i < call->ArgumentCount(); ++i) { | 233 for (intptr_t i = 0; i < instance_calls_.length(); ++i) { |
| 216 arguments.Add(call->ArgumentAt(i)->value()); | 234 PolymorphicInstanceCallInstr* instr = instance_calls_[i]; |
| 235 const ICData& ic_data = instr->ic_data(); |
| 236 const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0)); |
| 237 if (instr->with_checks()) { |
| 238 TRACE_INLINING(OS::Print(" Bailout: %"Pd" checks target '%s'\n", |
| 239 ic_data.NumberOfChecks(), |
| 240 target.ToCString())); |
| 241 continue; |
| 242 } |
| 243 GrowableArray<Value*> arguments(instr->ArgumentCount()); |
| 244 for (int i = 0; i < instr->ArgumentCount(); ++i) { |
| 245 arguments.Add(instr->ArgumentAt(i)->value()); |
| 246 } |
| 247 TryInlining(target, &arguments, instr); |
| 217 } | 248 } |
| 218 TryInlining(call->function(), &arguments, call); | |
| 219 } | 249 } |
| 220 | 250 |
| 221 bool inlined() const { return inlined_; } | |
| 222 | |
| 223 private: | |
| 224 FlowGraph* caller_graph_; | 251 FlowGraph* caller_graph_; |
| 225 intptr_t next_ssa_temp_index_; | 252 intptr_t next_ssa_temp_index_; |
| 226 bool inlined_; | 253 bool inlined_; |
| 254 |
| 255 GrowableArray<StaticCallInstr*> static_calls_; |
| 256 GrowableArray<ClosureCallInstr*> closure_calls_; |
| 257 GrowableArray<PolymorphicInstanceCallInstr*> instance_calls_; |
| 227 }; | 258 }; |
| 228 | 259 |
| 229 | 260 |
| 230 void FlowGraphInliner::Inline() { | 261 void FlowGraphInliner::Inline() { |
| 231 if ((FLAG_inlining_filter != NULL) && | 262 if ((FLAG_inlining_filter != NULL) && |
| 232 (strstr(flow_graph_-> | 263 (strstr(flow_graph_-> |
| 233 parsed_function().function().ToFullyQualifiedCString(), | 264 parsed_function().function().ToFullyQualifiedCString(), |
| 234 FLAG_inlining_filter) == NULL)) { | 265 FLAG_inlining_filter) == NULL)) { |
| 235 return; | 266 return; |
| 236 } | 267 } |
| 237 | 268 |
| 238 TRACE_INLINING(OS::Print( | 269 TRACE_INLINING(OS::Print( |
| 239 "Inlining calls in %s\n", | 270 "Inlining calls in %s\n", |
| 240 flow_graph_->parsed_function().function().ToCString())); | 271 flow_graph_->parsed_function().function().ToCString())); |
| 241 | 272 |
| 242 if (FLAG_trace_inlining && FLAG_print_flow_graph) { | 273 if (FLAG_trace_inlining && FLAG_print_flow_graph) { |
| 243 OS::Print("Before Inlining of %s\n", flow_graph_-> | 274 OS::Print("Before Inlining of %s\n", flow_graph_-> |
| 244 parsed_function().function().ToFullyQualifiedCString()); | 275 parsed_function().function().ToFullyQualifiedCString()); |
| 245 FlowGraphPrinter printer(*flow_graph_); | 276 FlowGraphPrinter printer(*flow_graph_); |
| 246 printer.PrintBlocks(); | 277 printer.PrintBlocks(); |
| 247 } | 278 } |
| 248 | 279 |
| 249 CallSiteInliner inliner(flow_graph_); | 280 CallSiteInliner inliner(flow_graph_); |
| 250 inliner.VisitBlocks(); | 281 inliner.FindCallSites(); |
| 282 inliner.InlineCalls(); |
| 251 | 283 |
| 252 if (inliner.inlined()) { | 284 if (inliner.inlined()) { |
| 253 if (FLAG_trace_inlining && FLAG_print_flow_graph) { | 285 if (FLAG_trace_inlining && FLAG_print_flow_graph) { |
| 254 OS::Print("After Inlining of %s\n", flow_graph_-> | 286 OS::Print("After Inlining of %s\n", flow_graph_-> |
| 255 parsed_function().function().ToFullyQualifiedCString()); | 287 parsed_function().function().ToFullyQualifiedCString()); |
| 256 FlowGraphPrinter printer(*flow_graph_); | 288 FlowGraphPrinter printer(*flow_graph_); |
| 257 printer.PrintBlocks(); | 289 printer.PrintBlocks(); |
| 258 } | 290 } |
| 259 } | 291 } |
| 260 } | 292 } |
| 261 | 293 |
| 262 } // namespace dart | 294 } // namespace dart |
| OLD | NEW |