| 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 |