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 #ifndef VM_FLOW_GRAPH_OPTIMIZER_H_ | 5 #ifndef VM_CONSTANT_PROPAGATOR_H_ |
6 #define VM_FLOW_GRAPH_OPTIMIZER_H_ | 6 #define VM_CONSTANT_PROPAGATOR_H_ |
7 | 7 |
8 #include "vm/intermediate_language.h" | 8 #include "vm/intermediate_language.h" |
9 #include "vm/flow_graph.h" | 9 #include "vm/flow_graph.h" |
10 | 10 |
11 namespace dart { | 11 namespace dart { |
12 | 12 |
13 class CSEInstructionMap; | |
14 template <typename T> class GrowableArray; | |
15 class ParsedFunction; | |
16 | |
17 class FlowGraphOptimizer : public FlowGraphVisitor { | |
18 public: | |
19 explicit FlowGraphOptimizer(FlowGraph* flow_graph) | |
20 : FlowGraphVisitor(flow_graph->reverse_postorder()), | |
21 flow_graph_(flow_graph) { } | |
22 virtual ~FlowGraphOptimizer() {} | |
23 | |
24 FlowGraph* flow_graph() const { return flow_graph_; } | |
25 | |
26 // Use ICData to optimize, replace or eliminate instructions. | |
27 void ApplyICData(); | |
28 | |
29 // Use propagated class ids to optimize, replace or eliminate instructions. | |
30 void ApplyClassIds(); | |
31 | |
32 // Optimize (a << b) & c pattern: if c is a positive Smi or zero, then the | |
33 // shift can be a truncating Smi shift-left and result is always Smi. | |
34 // Merge instructions (only per basic-block). | |
35 void TryOptimizePatterns(); | |
36 | |
37 // Returns true if any instructions were canonicalized away. | |
38 bool Canonicalize(); | |
39 | |
40 void EliminateDeadPhis(); | |
41 | |
42 void SelectRepresentations(); | |
43 | |
44 void WidenSmiToInt32(); | |
45 | |
46 void InferIntRanges(); | |
47 | |
48 void SelectIntegerInstructions(); | |
49 | |
50 void AnalyzeTryCatch(); | |
51 | |
52 bool TryInlineRecognizedMethod(intptr_t receiver_cid, | |
53 const Function& target, | |
54 Instruction* call, | |
55 Definition* receiver, | |
56 intptr_t token_pos, | |
57 const ICData& ic_data, | |
58 TargetEntryInstr** entry, | |
59 Definition** last); | |
60 | |
61 // Remove environments from the instructions which do not deoptimize. | |
62 void EliminateEnvironments(); | |
63 | |
64 virtual void VisitStaticCall(StaticCallInstr* instr); | |
65 virtual void VisitInstanceCall(InstanceCallInstr* instr); | |
66 virtual void VisitStoreInstanceField(StoreInstanceFieldInstr* instr); | |
67 virtual void VisitAllocateContext(AllocateContextInstr* instr); | |
68 virtual void VisitLoadCodeUnits(LoadCodeUnitsInstr* instr); | |
69 | |
70 void InsertBefore(Instruction* next, | |
71 Instruction* instr, | |
72 Environment* env, | |
73 FlowGraph::UseKind use_kind) { | |
74 flow_graph_->InsertBefore(next, instr, env, use_kind); | |
75 } | |
76 | |
77 private: | |
78 // Attempt to build ICData for call using propagated class-ids. | |
79 bool TryCreateICData(InstanceCallInstr* call); | |
80 const ICData& TrySpecializeICData(const ICData& ic_data, intptr_t cid); | |
81 | |
82 void SpecializePolymorphicInstanceCall(PolymorphicInstanceCallInstr* call); | |
83 | |
84 bool TryReplaceWithStoreIndexed(InstanceCallInstr* call); | |
85 bool InlineSetIndexed(MethodRecognizer::Kind kind, | |
86 const Function& target, | |
87 Instruction* call, | |
88 Definition* receiver, | |
89 intptr_t token_pos, | |
90 const ICData* ic_data, | |
91 const ICData& value_check, | |
92 TargetEntryInstr** entry, | |
93 Definition** last); | |
94 bool TryReplaceWithLoadIndexed(InstanceCallInstr* call); | |
95 bool InlineGetIndexed(MethodRecognizer::Kind kind, | |
96 Instruction* call, | |
97 Definition* receiver, | |
98 const ICData& ic_data, | |
99 TargetEntryInstr** entry, | |
100 Definition** last); | |
101 intptr_t PrepareInlineIndexedOp(Instruction* call, | |
102 intptr_t array_cid, | |
103 Definition** array, | |
104 Definition* index, | |
105 Instruction** cursor); | |
106 | |
107 | |
108 bool TryReplaceWithBinaryOp(InstanceCallInstr* call, Token::Kind op_kind); | |
109 bool TryReplaceWithUnaryOp(InstanceCallInstr* call, Token::Kind op_kind); | |
110 | |
111 bool TryReplaceWithEqualityOp(InstanceCallInstr* call, Token::Kind op_kind); | |
112 bool TryReplaceWithRelationalOp(InstanceCallInstr* call, Token::Kind op_kind); | |
113 | |
114 bool TryInlineInstanceGetter(InstanceCallInstr* call); | |
115 bool TryInlineInstanceSetter(InstanceCallInstr* call, | |
116 const ICData& unary_ic_data); | |
117 | |
118 bool TryInlineInstanceMethod(InstanceCallInstr* call); | |
119 bool TryInlineFloat32x4Constructor(StaticCallInstr* call, | |
120 MethodRecognizer::Kind recognized_kind); | |
121 bool TryInlineFloat64x2Constructor(StaticCallInstr* call, | |
122 MethodRecognizer::Kind recognized_kind); | |
123 bool TryInlineInt32x4Constructor(StaticCallInstr* call, | |
124 MethodRecognizer::Kind recognized_kind); | |
125 bool TryInlineFloat32x4Method(InstanceCallInstr* call, | |
126 MethodRecognizer::Kind recognized_kind); | |
127 bool TryInlineFloat64x2Method(InstanceCallInstr* call, | |
128 MethodRecognizer::Kind recognized_kind); | |
129 bool TryInlineInt32x4Method(InstanceCallInstr* call, | |
130 MethodRecognizer::Kind recognized_kind); | |
131 void ReplaceWithInstanceOf(InstanceCallInstr* instr); | |
132 bool TypeCheckAsClassEquality(const AbstractType& type); | |
133 void ReplaceWithTypeCast(InstanceCallInstr* instr); | |
134 | |
135 bool TryReplaceInstanceCallWithInline(InstanceCallInstr* call); | |
136 | |
137 Definition* PrepareInlineStringIndexOp(Instruction* call, | |
138 intptr_t cid, | |
139 Definition* str, | |
140 Definition* index, | |
141 Instruction* cursor); | |
142 | |
143 bool InlineStringCodeUnitAt(Instruction* call, | |
144 intptr_t cid, | |
145 TargetEntryInstr** entry, | |
146 Definition** last); | |
147 | |
148 bool InlineStringBaseCharAt(Instruction* call, | |
149 intptr_t cid, | |
150 TargetEntryInstr** entry, | |
151 Definition** last); | |
152 | |
153 bool InlineDoubleOp(Token::Kind op_kind, | |
154 Instruction* call, | |
155 TargetEntryInstr** entry, | |
156 Definition** last); | |
157 | |
158 bool InlineByteArrayViewLoad(Instruction* call, | |
159 Definition* receiver, | |
160 intptr_t array_cid, | |
161 intptr_t view_cid, | |
162 const ICData& ic_data, | |
163 TargetEntryInstr** entry, | |
164 Definition** last); | |
165 | |
166 bool InlineByteArrayViewStore(const Function& target, | |
167 Instruction* call, | |
168 Definition* receiver, | |
169 intptr_t array_cid, | |
170 intptr_t view_cid, | |
171 const ICData& ic_data, | |
172 TargetEntryInstr** entry, | |
173 Definition** last); | |
174 | |
175 intptr_t PrepareInlineByteArrayViewOp(Instruction* call, | |
176 intptr_t array_cid, | |
177 intptr_t view_cid, | |
178 Definition** array, | |
179 Definition* index, | |
180 Instruction** cursor); | |
181 | |
182 bool BuildByteArrayViewLoad(InstanceCallInstr* call, | |
183 intptr_t view_cid); | |
184 bool BuildByteArrayViewStore(InstanceCallInstr* call, | |
185 intptr_t view_cid); | |
186 | |
187 // Insert a check of 'to_check' determined by 'unary_checks'. If the | |
188 // check fails it will deoptimize to 'deopt_id' using the deoptimization | |
189 // environment 'deopt_environment'. The check is inserted immediately | |
190 // before 'insert_before'. | |
191 void AddCheckClass(Definition* to_check, | |
192 const ICData& unary_checks, | |
193 intptr_t deopt_id, | |
194 Environment* deopt_environment, | |
195 Instruction* insert_before); | |
196 Instruction* GetCheckClass(Definition* to_check, | |
197 const ICData& unary_checks, | |
198 intptr_t deopt_id, | |
199 intptr_t token_pos); | |
200 | |
201 // Insert a Smi check if needed. | |
202 void AddCheckSmi(Definition* to_check, | |
203 intptr_t deopt_id, | |
204 Environment* deopt_environment, | |
205 Instruction* insert_before); | |
206 | |
207 // Add a class check for a call's first argument immediately before the | |
208 // call, using the call's IC data to determine the check, and the call's | |
209 // deopt ID and deoptimization environment if the check fails. | |
210 void AddReceiverCheck(InstanceCallInstr* call); | |
211 | |
212 void ReplaceCall(Definition* call, Definition* replacement); | |
213 | |
214 void InsertConversionsFor(Definition* def); | |
215 | |
216 void ConvertUse(Value* use, Representation from); | |
217 void ConvertEnvironmentUse(Value* use, Representation from); | |
218 | |
219 void InsertConversion(Representation from, | |
220 Representation to, | |
221 Value* use, | |
222 bool is_environment_use); | |
223 | |
224 bool InstanceCallNeedsClassCheck(InstanceCallInstr* call, | |
225 RawFunction::Kind kind) const; | |
226 | |
227 bool InlineFloat32x4Getter(InstanceCallInstr* call, | |
228 MethodRecognizer::Kind getter); | |
229 bool InlineFloat64x2Getter(InstanceCallInstr* call, | |
230 MethodRecognizer::Kind getter); | |
231 bool InlineInt32x4Getter(InstanceCallInstr* call, | |
232 MethodRecognizer::Kind getter); | |
233 bool InlineFloat32x4BinaryOp(InstanceCallInstr* call, | |
234 Token::Kind op_kind); | |
235 bool InlineInt32x4BinaryOp(InstanceCallInstr* call, | |
236 Token::Kind op_kind); | |
237 bool InlineFloat64x2BinaryOp(InstanceCallInstr* call, | |
238 Token::Kind op_kind); | |
239 void InlineImplicitInstanceGetter(InstanceCallInstr* call); | |
240 | |
241 RawBool* InstanceOfAsBool(const ICData& ic_data, | |
242 const AbstractType& type, | |
243 ZoneGrowableArray<intptr_t>* results) const; | |
244 | |
245 void ReplaceWithMathCFunction(InstanceCallInstr* call, | |
246 MethodRecognizer::Kind recognized_kind); | |
247 | |
248 void OptimizeLeftShiftBitAndSmiOp(Definition* bit_and_instr, | |
249 Definition* left_instr, | |
250 Definition* right_instr); | |
251 void TryMergeTruncDivMod(GrowableArray<BinarySmiOpInstr*>* merge_candidates); | |
252 void TryMergeMathUnary(GrowableArray<MathUnaryInstr*>* merge_candidates); | |
253 | |
254 void AppendLoadIndexedForMerged(Definition* instr, intptr_t ix, intptr_t cid); | |
255 void AppendExtractNthOutputForMerged(Definition* instr, intptr_t ix, | |
256 Representation rep, intptr_t cid); | |
257 bool TryStringLengthOneEquality(InstanceCallInstr* call, Token::Kind op_kind); | |
258 | |
259 Isolate* isolate() const { return flow_graph_->isolate(); } | |
260 | |
261 FlowGraph* flow_graph_; | |
262 | |
263 DISALLOW_COPY_AND_ASSIGN(FlowGraphOptimizer); | |
264 }; | |
265 | |
266 | |
267 // Loop invariant code motion. | |
268 class LICM : public ValueObject { | |
269 public: | |
270 explicit LICM(FlowGraph* flow_graph); | |
271 | |
272 void Optimize(); | |
273 | |
274 void OptimisticallySpecializeSmiPhis(); | |
275 | |
276 private: | |
277 FlowGraph* flow_graph() const { return flow_graph_; } | |
278 | |
279 void Hoist(ForwardInstructionIterator* it, | |
280 BlockEntryInstr* pre_header, | |
281 Instruction* current); | |
282 | |
283 void TrySpecializeSmiPhi(PhiInstr* phi, | |
284 BlockEntryInstr* header, | |
285 BlockEntryInstr* pre_header); | |
286 | |
287 FlowGraph* const flow_graph_; | |
288 }; | |
289 | |
290 | |
291 // A simple common subexpression elimination based | |
292 // on the dominator tree. | |
293 class DominatorBasedCSE : public AllStatic { | |
294 public: | |
295 // Return true, if the optimization changed the flow graph. | |
296 // False, if nothing changed. | |
297 static bool Optimize(FlowGraph* graph); | |
298 | |
299 private: | |
300 static bool OptimizeRecursive( | |
301 FlowGraph* graph, | |
302 BlockEntryInstr* entry, | |
303 CSEInstructionMap* map); | |
304 }; | |
305 | |
306 | |
307 class DeadStoreElimination : public AllStatic { | |
308 public: | |
309 static void Optimize(FlowGraph* graph); | |
310 }; | |
311 | |
312 | |
313 class DeadCodeElimination : public AllStatic { | |
314 public: | |
315 static void EliminateDeadPhis(FlowGraph* graph); | |
316 }; | |
317 | |
318 | |
319 // Sparse conditional constant propagation and unreachable code elimination. | 13 // Sparse conditional constant propagation and unreachable code elimination. |
320 // Assumes that use lists are computed and preserves them. | 14 // Assumes that use lists are computed and preserves them. |
321 class ConstantPropagator : public FlowGraphVisitor { | 15 class ConstantPropagator : public FlowGraphVisitor { |
322 public: | 16 public: |
323 ConstantPropagator(FlowGraph* graph, | 17 ConstantPropagator(FlowGraph* graph, |
324 const GrowableArray<BlockEntryInstr*>& ignored); | 18 const GrowableArray<BlockEntryInstr*>& ignored); |
325 | 19 |
326 static void Optimize(FlowGraph* graph); | 20 static void Optimize(FlowGraph* graph); |
327 | 21 |
328 // (1) Visit branches to optimize away unreachable blocks discovered by range | 22 // (1) Visit branches to optimize away unreachable blocks discovered by range |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
386 BitVector* reachable_; | 80 BitVector* reachable_; |
387 | 81 |
388 BitVector* marked_phis_; | 82 BitVector* marked_phis_; |
389 | 83 |
390 // Worklists of blocks and definitions. | 84 // Worklists of blocks and definitions. |
391 GrowableArray<BlockEntryInstr*> block_worklist_; | 85 GrowableArray<BlockEntryInstr*> block_worklist_; |
392 DefinitionWorklist definition_worklist_; | 86 DefinitionWorklist definition_worklist_; |
393 }; | 87 }; |
394 | 88 |
395 | 89 |
396 // Rewrite branches to eliminate materialization of boolean values after | |
397 // inlining, and to expose other optimizations (e.g., constant folding of | |
398 // branches, unreachable code elimination). | |
399 class BranchSimplifier : public AllStatic { | |
400 public: | |
401 static void Simplify(FlowGraph* flow_graph); | |
402 | |
403 // Replace a target entry instruction with a join entry instruction. Does | |
404 // not update the original target's predecessors to point to the new block | |
405 // and does not replace the target in already computed block order lists. | |
406 static JoinEntryInstr* ToJoinEntry(Isolate* isolate, | |
407 TargetEntryInstr* target); | |
408 | |
409 private: | |
410 // Match an instance of the pattern to rewrite. See the implementation | |
411 // for the patterns that are handled by this pass. | |
412 static bool Match(JoinEntryInstr* block); | |
413 | |
414 // Duplicate a branch while replacing its comparison's left and right | |
415 // inputs. | |
416 static BranchInstr* CloneBranch(Isolate* isolate, | |
417 BranchInstr* branch, | |
418 Value* new_left, | |
419 Value* new_right); | |
420 }; | |
421 | |
422 | |
423 // Rewrite diamond control flow patterns that materialize values to use more | |
424 // efficient branchless code patterns if such are supported on the current | |
425 // platform. | |
426 class IfConverter : public AllStatic { | |
427 public: | |
428 static void Simplify(FlowGraph* flow_graph); | |
429 }; | |
430 | |
431 | |
432 class AllocationSinking : public ZoneAllocated { | |
433 public: | |
434 explicit AllocationSinking(FlowGraph* flow_graph) | |
435 : flow_graph_(flow_graph), | |
436 candidates_(5), | |
437 materializations_(5) { } | |
438 | |
439 const GrowableArray<Definition*>& candidates() const { | |
440 return candidates_; | |
441 } | |
442 | |
443 // Find the materialization insterted for the given allocation | |
444 // at the given exit. | |
445 MaterializeObjectInstr* MaterializationFor(Definition* alloc, | |
446 Instruction* exit); | |
447 | |
448 void Optimize(); | |
449 | |
450 void DetachMaterializations(); | |
451 | |
452 private: | |
453 // Helper class to collect deoptimization exits that might need to | |
454 // rematerialize an object: that is either instructions that reference | |
455 // this object explicitly in their deoptimization environment or | |
456 // reference some other allocation sinking candidate that points to | |
457 // this object. | |
458 class ExitsCollector : public ValueObject { | |
459 public: | |
460 ExitsCollector() : exits_(10), worklist_(3) { } | |
461 | |
462 const GrowableArray<Instruction*>& exits() const { return exits_; } | |
463 | |
464 void CollectTransitively(Definition* alloc); | |
465 | |
466 private: | |
467 // Collect immediate uses of this object in the environments. | |
468 // If this object is stored into other allocation sinking candidates | |
469 // put them onto worklist so that CollectTransitively will process them. | |
470 void Collect(Definition* alloc); | |
471 | |
472 GrowableArray<Instruction*> exits_; | |
473 GrowableArray<Definition*> worklist_; | |
474 }; | |
475 | |
476 void CollectCandidates(); | |
477 | |
478 void NormalizeMaterializations(); | |
479 | |
480 void RemoveUnusedMaterializations(); | |
481 | |
482 void DiscoverFailedCandidates(); | |
483 | |
484 void InsertMaterializations(Definition* alloc); | |
485 | |
486 void CreateMaterializationAt( | |
487 Instruction* exit, | |
488 Definition* alloc, | |
489 const ZoneGrowableArray<const Object*>& fields); | |
490 | |
491 void EliminateAllocation(Definition* alloc); | |
492 | |
493 Isolate* isolate() const { return flow_graph_->isolate(); } | |
494 | |
495 FlowGraph* flow_graph_; | |
496 | |
497 GrowableArray<Definition*> candidates_; | |
498 GrowableArray<MaterializeObjectInstr*> materializations_; | |
499 | |
500 ExitsCollector exits_collector_; | |
501 }; | |
502 | |
503 | |
504 // Optimize spill stores inside try-blocks by identifying values that always | |
505 // contain a single known constant at catch block entry. | |
506 class TryCatchAnalyzer : public AllStatic { | |
507 public: | |
508 static void Optimize(FlowGraph* flow_graph); | |
509 }; | |
510 | |
511 } // namespace dart | 90 } // namespace dart |
512 | 91 |
513 #endif // VM_FLOW_GRAPH_OPTIMIZER_H_ | 92 #endif // VM_CONSTANT_PROPAGATOR_H_ |
OLD | NEW |