1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
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. |
4 | |
5 | #ifndef RUNTIME_VM_COMPILER_BACKEND_FLOW_GRAPH_H_ |
6 | #define RUNTIME_VM_COMPILER_BACKEND_FLOW_GRAPH_H_ |
7 | |
8 | #if defined(DART_PRECOMPILED_RUNTIME) |
9 | #error "AOT runtime should not use compiler sources (including header files)" |
10 | #endif // defined(DART_PRECOMPILED_RUNTIME) |
11 | |
12 | #include "vm/bit_vector.h" |
13 | #include "vm/compiler/backend/il.h" |
14 | #include "vm/growable_array.h" |
15 | #include "vm/hash_map.h" |
16 | #include "vm/parser.h" |
17 | #include "vm/thread.h" |
18 | |
19 | namespace dart { |
20 | |
21 | class LoopHierarchy; |
22 | class VariableLivenessAnalysis; |
23 | |
24 | namespace compiler { |
25 | class GraphIntrinsifier; |
26 | } |
27 | |
28 | class BlockIterator : public ValueObject { |
29 | public: |
30 | explicit BlockIterator(const GrowableArray<BlockEntryInstr*>& block_order) |
31 | : block_order_(block_order), current_(0) {} |
32 | |
33 | BlockIterator(const BlockIterator& other) |
34 | : ValueObject(), |
35 | block_order_(other.block_order_), |
36 | current_(other.current_) {} |
37 | |
38 | void Advance() { |
39 | ASSERT(!Done()); |
40 | current_++; |
41 | } |
42 | |
43 | bool Done() const { return current_ >= block_order_.length(); } |
44 | |
45 | BlockEntryInstr* Current() const { return block_order_[current_]; } |
46 | |
47 | private: |
48 | const GrowableArray<BlockEntryInstr*>& block_order_; |
49 | intptr_t current_; |
50 | }; |
51 | |
52 | struct ConstantPoolTrait { |
53 | typedef ConstantInstr* Value; |
54 | typedef const Object& Key; |
55 | typedef ConstantInstr* Pair; |
56 | |
57 | static Key KeyOf(Pair kv) { return kv->value(); } |
58 | |
59 | static Value ValueOf(Pair kv) { return kv; } |
60 | |
61 | static inline intptr_t Hashcode(Key key) { |
62 | if (key.IsSmi()) { |
63 | return Smi::Cast(key).Value(); |
64 | } |
65 | if (key.IsDouble()) { |
66 | return static_cast<intptr_t>(bit_cast<int32_t, float>( |
67 | static_cast<float>(Double::Cast(key).value()))); |
68 | } |
69 | if (key.IsMint()) { |
70 | return static_cast<intptr_t>(Mint::Cast(key).value()); |
71 | } |
72 | if (key.IsString()) { |
73 | return String::Cast(key).Hash(); |
74 | } |
75 | return key.GetClassId(); |
76 | } |
77 | |
78 | static inline bool IsKeyEqual(Pair kv, Key key) { |
79 | return kv->value().raw() == key.raw(); |
80 | } |
81 | }; |
82 | |
83 | struct PrologueInfo { |
84 | // The first blockid used for prologue building. This information can be used |
85 | // by the inliner for budget calculations: The prologue code falls away when |
86 | // inlining, so we should not include it in the budget. |
87 | intptr_t min_block_id; |
88 | |
89 | // The last blockid used for prologue building. This information can be used |
90 | // by the inliner for budget calculations: The prologue code falls away when |
91 | // inlining, so we should not include it in the budget. |
92 | intptr_t max_block_id; |
93 | |
94 | PrologueInfo(intptr_t min, intptr_t max) |
95 | : min_block_id(min), max_block_id(max) {} |
96 | |
97 | bool Contains(intptr_t block_id) const { |
98 | return min_block_id <= block_id && block_id <= max_block_id; |
99 | } |
100 | }; |
101 | |
102 | // Class to encapsulate the construction and manipulation of the flow graph. |
103 | class FlowGraph : public ZoneAllocated { |
104 | public: |
105 | FlowGraph(const ParsedFunction& parsed_function, |
106 | GraphEntryInstr* graph_entry, |
107 | intptr_t max_block_id, |
108 | PrologueInfo prologue_info); |
109 | |
110 | // Function properties. |
111 | const ParsedFunction& parsed_function() const { return parsed_function_; } |
112 | const Function& function() const { return parsed_function_.function(); } |
113 | |
114 | // The number of directly accessable parameters (above the frame pointer). |
115 | // All other parameters can only be indirectly loaded via metadata found in |
116 | // the arguments descriptor. |
117 | intptr_t num_direct_parameters() const { return num_direct_parameters_; } |
118 | |
119 | // The number of words on the stack used by the direct parameters. |
120 | intptr_t direct_parameters_size() const { return direct_parameters_size_; } |
121 | |
122 | // The number of variables (or boxes) which code can load from / store to. |
123 | // The SSA renaming will insert phi's for them (and only them - i.e. there |
124 | // will be no phi insertion for [LocalVariable]s pointing to the expression |
125 | // stack!). |
126 | intptr_t variable_count() const { |
127 | return num_direct_parameters_ + parsed_function_.num_stack_locals(); |
128 | } |
129 | |
130 | // The number of variables during OSR, which may include stack slots |
131 | // that pass in initial contents for the expression stack. |
132 | intptr_t osr_variable_count() const { |
133 | ASSERT(IsCompiledForOsr()); |
134 | return variable_count() + graph_entry()->osr_entry()->stack_depth(); |
135 | } |
136 | |
137 | // This function returns the offset (in words) of the [index]th |
138 | // parameter, relative to the first parameter. |
139 | // If [last_slot] is true it gives the offset of the last slot of that |
140 | // location, otherwise it returns the first one. |
141 | static intptr_t ParameterOffsetAt(const Function& function, |
142 | intptr_t index, |
143 | bool last_slot = true); |
144 | |
145 | static Representation ParameterRepresentationAt(const Function& function, |
146 | intptr_t index); |
147 | |
148 | static Representation ReturnRepresentationOf(const Function& function); |
149 | |
150 | // The number of variables (or boxes) inside the functions frame - meaning |
151 | // below the frame pointer. This does not include the expression stack. |
152 | intptr_t num_stack_locals() const { |
153 | return parsed_function_.num_stack_locals(); |
154 | } |
155 | |
156 | bool IsIrregexpFunction() const { return function().IsIrregexpFunction(); } |
157 | |
158 | LocalVariable* CurrentContextVar() const { |
159 | return parsed_function().current_context_var(); |
160 | } |
161 | |
162 | intptr_t CurrentContextEnvIndex() const { |
163 | if (function().HasBytecode()) { |
164 | return -1; |
165 | } |
166 | |
167 | return EnvIndex(parsed_function().current_context_var()); |
168 | } |
169 | |
170 | intptr_t RawTypeArgumentEnvIndex() const { |
171 | return EnvIndex(parsed_function().RawTypeArgumentsVariable()); |
172 | } |
173 | |
174 | intptr_t ArgumentDescriptorEnvIndex() const { |
175 | return EnvIndex(parsed_function().arg_desc_var()); |
176 | } |
177 | |
178 | intptr_t EnvIndex(const LocalVariable* variable) const { |
179 | ASSERT(!variable->is_captured()); |
180 | return num_direct_parameters_ - variable->index().value(); |
181 | } |
182 | |
183 | static bool NeedsPairLocation(Representation representation) { |
184 | return representation == kUnboxedInt64 && |
185 | compiler::target::kIntSpillFactor == 2; |
186 | } |
187 | |
188 | // Flow graph orders. |
189 | const GrowableArray<BlockEntryInstr*>& preorder() const { return preorder_; } |
190 | const GrowableArray<BlockEntryInstr*>& postorder() const { |
191 | return postorder_; |
192 | } |
193 | const GrowableArray<BlockEntryInstr*>& reverse_postorder() const { |
194 | return reverse_postorder_; |
195 | } |
196 | static bool ShouldReorderBlocks(const Function& function, bool is_optimized); |
197 | GrowableArray<BlockEntryInstr*>* CodegenBlockOrder(bool is_optimized); |
198 | |
199 | // Iterators. |
200 | BlockIterator reverse_postorder_iterator() const { |
201 | return BlockIterator(reverse_postorder()); |
202 | } |
203 | BlockIterator postorder_iterator() const { |
204 | return BlockIterator(postorder()); |
205 | } |
206 | |
207 | void EnsureSSATempIndex(Definition* defn, Definition* replacement); |
208 | |
209 | void ReplaceCurrentInstruction(ForwardInstructionIterator* iterator, |
210 | Instruction* current, |
211 | Instruction* replacement); |
212 | |
213 | Instruction* CreateCheckClass(Definition* to_check, |
214 | const Cids& cids, |
215 | intptr_t deopt_id, |
216 | TokenPosition token_pos); |
217 | |
218 | Definition* CreateCheckBound(Definition* length, |
219 | Definition* index, |
220 | intptr_t deopt_id); |
221 | |
222 | void AddExactnessGuard(InstanceCallInstr* call, intptr_t receiver_cid); |
223 | |
224 | intptr_t current_ssa_temp_index() const { return current_ssa_temp_index_; } |
225 | void set_current_ssa_temp_index(intptr_t index) { |
226 | current_ssa_temp_index_ = index; |
227 | } |
228 | |
229 | intptr_t max_virtual_register_number() const { |
230 | return current_ssa_temp_index(); |
231 | } |
232 | |
233 | enum class ToCheck { kNoCheck, kCheckNull, kCheckCid }; |
234 | |
235 | // Uses CHA to determine if the called method can be overridden. |
236 | // Return value indicates that the call needs no check at all, |
237 | // just a null check, or a full class check. |
238 | ToCheck CheckForInstanceCall(InstanceCallInstr* call, |
239 | FunctionLayout::Kind kind) const; |
240 | |
241 | Thread* thread() const { return thread_; } |
242 | Zone* zone() const { return thread()->zone(); } |
243 | Isolate* isolate() const { return thread()->isolate(); } |
244 | |
245 | intptr_t max_block_id() const { return max_block_id_; } |
246 | void set_max_block_id(intptr_t id) { max_block_id_ = id; } |
247 | intptr_t allocate_block_id() { return ++max_block_id_; } |
248 | |
249 | GraphEntryInstr* graph_entry() const { return graph_entry_; } |
250 | |
251 | ConstantInstr* constant_null() const { return constant_null_; } |
252 | |
253 | ConstantInstr* constant_dead() const { return constant_dead_; } |
254 | |
255 | intptr_t alloc_ssa_temp_index() { return current_ssa_temp_index_++; } |
256 | |
257 | void AllocateSSAIndexes(Definition* def) { |
258 | ASSERT(def); |
259 | def->set_ssa_temp_index(alloc_ssa_temp_index()); |
260 | // Always allocate a second index. This index is unused except |
261 | // for Definitions with register pair outputs. |
262 | alloc_ssa_temp_index(); |
263 | } |
264 | |
265 | intptr_t InstructionCount() const; |
266 | |
267 | // Returns the definition for the object from the constant pool if |
268 | // one exists, otherwise returns nullptr. |
269 | ConstantInstr* GetExistingConstant(const Object& object) const; |
270 | |
271 | // Always returns a definition for the object from the constant pool, |
272 | // allocating one if it doesn't already exist. |
273 | ConstantInstr* GetConstant(const Object& object); |
274 | |
275 | void AddToGraphInitialDefinitions(Definition* defn); |
276 | void AddToInitialDefinitions(BlockEntryWithInitialDefs* entry, |
277 | Definition* defn); |
278 | |
279 | // Tries to create a constant definition with the given value which can be |
280 | // used to replace the given operation. Ensures that the representation of |
281 | // the replacement matches the representation of the original definition. |
282 | // If the given value can't be represented using matching representation |
283 | // then returns op itself. |
284 | Definition* TryCreateConstantReplacementFor(Definition* op, |
285 | const Object& value); |
286 | |
287 | // Returns true if the given constant value can be represented in the given |
288 | // representation. |
289 | static bool IsConstantRepresentable(const Object& value, |
290 | Representation target_rep, |
291 | bool tagged_value_must_be_smi); |
292 | |
293 | enum UseKind { kEffect, kValue }; |
294 | |
295 | void InsertBefore(Instruction* next, |
296 | Instruction* instr, |
297 | Environment* env, |
298 | UseKind use_kind); |
299 | void InsertAfter(Instruction* prev, |
300 | Instruction* instr, |
301 | Environment* env, |
302 | UseKind use_kind); |
303 | Instruction* AppendTo(Instruction* prev, |
304 | Instruction* instr, |
305 | Environment* env, |
306 | UseKind use_kind); |
307 | |
308 | // Operations on the flow graph. |
309 | void ComputeSSA(intptr_t next_virtual_register_number, |
310 | ZoneGrowableArray<Definition*>* inlining_parameters); |
311 | |
312 | // Verification method for debugging. |
313 | bool VerifyRedefinitions(); |
314 | |
315 | void DiscoverBlocks(); |
316 | |
317 | void MergeBlocks(); |
318 | |
319 | // Insert a redefinition of an original definition after prev and rename all |
320 | // dominated uses of the original. If an equivalent redefinition is already |
321 | // present, nothing is inserted. |
322 | // Returns the redefinition, if a redefinition was inserted, NULL otherwise. |
323 | RedefinitionInstr* EnsureRedefinition(Instruction* prev, |
324 | Definition* original, |
325 | CompileType compile_type); |
326 | |
327 | // Remove the redefinition instructions inserted to inhibit code motion. |
328 | void RemoveRedefinitions(bool keep_checks = false); |
329 | |
330 | // Insert PushArgument instructions and remove explicit def-use |
331 | // relations between calls and their arguments. |
332 | void InsertPushArguments(); |
333 | |
334 | // Copy deoptimization target from one instruction to another if we still |
335 | // have to keep deoptimization environment at gotos for LICM purposes. |
336 | void CopyDeoptTarget(Instruction* to, Instruction* from) { |
337 | if (is_licm_allowed()) { |
338 | to->InheritDeoptTarget(zone(), from); |
339 | } |
340 | } |
341 | |
342 | // Returns true if every Goto in the graph is expected to have a |
343 | // deoptimization environment and can be used as deoptimization target |
344 | // for hoisted instructions. |
345 | bool is_licm_allowed() const { return licm_allowed_; } |
346 | |
347 | // Stop preserving environments on Goto instructions. LICM is not allowed |
348 | // after this point. |
349 | void disallow_licm() { licm_allowed_ = false; } |
350 | |
351 | PrologueInfo prologue_info() const { return prologue_info_; } |
352 | |
353 | // Computes the loop hierarchy of the flow graph on demand. |
354 | const LoopHierarchy& GetLoopHierarchy() { |
355 | if (loop_hierarchy_ == nullptr) { |
356 | loop_hierarchy_ = ComputeLoops(); |
357 | } |
358 | return loop_hierarchy(); |
359 | } |
360 | |
361 | const LoopHierarchy& loop_hierarchy() const { return *loop_hierarchy_; } |
362 | |
363 | // Resets the loop hierarchy of the flow graph. Use this to |
364 | // force a recomputation of loop detection by the next call |
365 | // to GetLoopHierarchy() (note that this does not immediately |
366 | // reset the loop_info fields of block entries, although |
367 | // these will be overwritten by that next call). |
368 | void ResetLoopHierarchy() { |
369 | loop_hierarchy_ = nullptr; |
370 | loop_invariant_loads_ = nullptr; |
371 | } |
372 | |
373 | // Per loop header invariant loads sets. Each set contains load id for |
374 | // those loads that are not affected by anything in the loop and can be |
375 | // hoisted out. Sets are computed by LoadOptimizer. |
376 | ZoneGrowableArray<BitVector*>* loop_invariant_loads() const { |
377 | return loop_invariant_loads_; |
378 | } |
379 | void set_loop_invariant_loads( |
380 | ZoneGrowableArray<BitVector*>* loop_invariant_loads) { |
381 | loop_invariant_loads_ = loop_invariant_loads; |
382 | } |
383 | |
384 | bool IsCompiledForOsr() const { return graph_entry()->IsCompiledForOsr(); } |
385 | |
386 | BitVector* captured_parameters() const { return captured_parameters_; } |
387 | |
388 | intptr_t inlining_id() const { return inlining_id_; } |
389 | void set_inlining_id(intptr_t value) { inlining_id_ = value; } |
390 | |
391 | // Returns true if any instructions were canonicalized away. |
392 | bool Canonicalize(); |
393 | |
394 | // Attaches new ICData's to static/instance calls which don't already have |
395 | // them. |
396 | void PopulateWithICData(const Function& function); |
397 | |
398 | void SelectRepresentations(); |
399 | |
400 | void WidenSmiToInt32(); |
401 | |
402 | // Remove environments from the instructions which do not deoptimize. |
403 | void EliminateEnvironments(); |
404 | |
405 | bool IsReceiver(Definition* def) const; |
406 | |
407 | // Optimize (a << b) & c pattern: if c is a positive Smi or zero, then the |
408 | // shift can be a truncating Smi shift-left and result is always Smi. |
409 | // Merge instructions (only per basic-block). |
410 | void TryOptimizePatterns(); |
411 | |
412 | // Replaces uses that are dominated by dom of 'def' with 'other'. |
413 | // Note: uses that occur at instruction dom itself are not dominated by it. |
414 | static void RenameDominatedUses(Definition* def, |
415 | Instruction* dom, |
416 | Definition* other); |
417 | |
418 | // Renames uses of redefined values to make sure that uses of redefined |
419 | // values that are dominated by a redefinition are renamed. |
420 | void RenameUsesDominatedByRedefinitions(); |
421 | |
422 | bool should_print() const { return should_print_; } |
423 | |
424 | // |
425 | // High-level utilities. |
426 | // |
427 | |
428 | // Logical-AND (for use in short-circuit diamond). |
429 | struct LogicalAnd { |
430 | LogicalAnd(ComparisonInstr* x, ComparisonInstr* y) : oper1(x), oper2(y) {} |
431 | ComparisonInstr* oper1; |
432 | ComparisonInstr* oper2; |
433 | }; |
434 | |
435 | // Constructs a diamond control flow at the instruction, inheriting |
436 | // properties from inherit and using the given compare. Returns the |
437 | // join (and true/false blocks in out parameters). Updates dominance |
438 | // relation, but not the succ/pred ordering on block. |
439 | JoinEntryInstr* NewDiamond(Instruction* instruction, |
440 | Instruction* inherit, |
441 | ComparisonInstr* compare, |
442 | TargetEntryInstr** block_true, |
443 | TargetEntryInstr** block_false); |
444 | |
445 | // As above, but with a short-circuit on two comparisons. |
446 | JoinEntryInstr* NewDiamond(Instruction* instruction, |
447 | Instruction* inherit, |
448 | const LogicalAnd& condition, |
449 | TargetEntryInstr** block_true, |
450 | TargetEntryInstr** block_false); |
451 | |
452 | // Adds a 2-way phi. |
453 | PhiInstr* AddPhi(JoinEntryInstr* join, Definition* d1, Definition* d2); |
454 | |
455 | // SSA transformation methods and fields. |
456 | void ComputeDominators(GrowableArray<BitVector*>* dominance_frontier); |
457 | |
458 | void CreateCommonConstants(); |
459 | |
460 | private: |
461 | friend class FlowGraphCompiler; // TODO(ajcbik): restructure |
462 | friend class FlowGraphChecker; |
463 | friend class IfConverter; |
464 | friend class BranchSimplifier; |
465 | friend class ConstantPropagator; |
466 | friend class DeadCodeElimination; |
467 | friend class compiler::GraphIntrinsifier; |
468 | |
469 | void CompressPath(intptr_t start_index, |
470 | intptr_t current_index, |
471 | GrowableArray<intptr_t>* parent, |
472 | GrowableArray<intptr_t>* label); |
473 | |
474 | void AddSyntheticPhis(BlockEntryInstr* block); |
475 | |
476 | void Rename(GrowableArray<PhiInstr*>* live_phis, |
477 | VariableLivenessAnalysis* variable_liveness, |
478 | ZoneGrowableArray<Definition*>* inlining_parameters); |
479 | void RenameRecursive(BlockEntryInstr* block_entry, |
480 | GrowableArray<Definition*>* env, |
481 | GrowableArray<PhiInstr*>* live_phis, |
482 | VariableLivenessAnalysis* variable_liveness, |
483 | ZoneGrowableArray<Definition*>* inlining_parameters); |
484 | |
485 | void PopulateEnvironmentFromFunctionEntry( |
486 | FunctionEntryInstr* function_entry, |
487 | GrowableArray<Definition*>* env, |
488 | GrowableArray<PhiInstr*>* live_phis, |
489 | VariableLivenessAnalysis* variable_liveness, |
490 | ZoneGrowableArray<Definition*>* inlining_parameters); |
491 | |
492 | void PopulateEnvironmentFromOsrEntry(OsrEntryInstr* osr_entry, |
493 | GrowableArray<Definition*>* env); |
494 | |
495 | void PopulateEnvironmentFromCatchEntry(CatchBlockEntryInstr* catch_entry, |
496 | GrowableArray<Definition*>* env); |
497 | |
498 | void AttachEnvironment(Instruction* instr, GrowableArray<Definition*>* env); |
499 | |
500 | void InsertPhis(const GrowableArray<BlockEntryInstr*>& preorder, |
501 | const GrowableArray<BitVector*>& assigned_vars, |
502 | const GrowableArray<BitVector*>& dom_frontier, |
503 | GrowableArray<PhiInstr*>* live_phis); |
504 | |
505 | void RemoveDeadPhis(GrowableArray<PhiInstr*>* live_phis); |
506 | |
507 | void ReplacePredecessor(BlockEntryInstr* old_block, |
508 | BlockEntryInstr* new_block); |
509 | |
510 | // Finds the blocks in the natural loop for the back edge m->n. The |
511 | // algorithm is described in "Advanced Compiler Design & Implementation" |
512 | // (Muchnick) p192. Returns a BitVector indexed by block pre-order |
513 | // number where each bit indicates membership in the loop. |
514 | BitVector* FindLoopBlocks(BlockEntryInstr* m, BlockEntryInstr* n) const; |
515 | |
516 | // Finds the natural loops in the flow graph and attaches the loop |
517 | // information to each entry block. Returns the loop hierarchy. |
518 | LoopHierarchy* ComputeLoops() const; |
519 | |
520 | void InsertConversionsFor(Definition* def); |
521 | void ConvertUse(Value* use, Representation from); |
522 | void InsertConversion(Representation from, |
523 | Representation to, |
524 | Value* use, |
525 | bool is_environment_use); |
526 | |
527 | void ComputeIsReceiver(PhiInstr* phi) const; |
528 | void ComputeIsReceiverRecursive(PhiInstr* phi, |
529 | GrowableArray<PhiInstr*>* unmark) const; |
530 | |
531 | void OptimizeLeftShiftBitAndSmiOp( |
532 | ForwardInstructionIterator* current_iterator, |
533 | Definition* bit_and_instr, |
534 | Definition* left_instr, |
535 | Definition* right_instr); |
536 | |
537 | void TryMergeTruncDivMod(GrowableArray<BinarySmiOpInstr*>* merge_candidates); |
538 | |
539 | void (Definition* instr, |
540 | intptr_t ix, |
541 | Representation rep, |
542 | intptr_t cid); |
543 | |
544 | Thread* thread_; |
545 | |
546 | // DiscoverBlocks computes parent_ and assigned_vars_ which are then used |
547 | // if/when computing SSA. |
548 | GrowableArray<intptr_t> parent_; |
549 | GrowableArray<BitVector*> assigned_vars_; |
550 | |
551 | intptr_t current_ssa_temp_index_; |
552 | intptr_t max_block_id_; |
553 | |
554 | // Flow graph fields. |
555 | const ParsedFunction& parsed_function_; |
556 | intptr_t num_direct_parameters_; |
557 | intptr_t direct_parameters_size_; |
558 | GraphEntryInstr* graph_entry_; |
559 | GrowableArray<BlockEntryInstr*> preorder_; |
560 | GrowableArray<BlockEntryInstr*> postorder_; |
561 | GrowableArray<BlockEntryInstr*> reverse_postorder_; |
562 | GrowableArray<BlockEntryInstr*> optimized_block_order_; |
563 | ConstantInstr* constant_null_; |
564 | ConstantInstr* constant_dead_; |
565 | |
566 | bool licm_allowed_; |
567 | |
568 | const PrologueInfo prologue_info_; |
569 | |
570 | // Loop related fields. |
571 | LoopHierarchy* loop_hierarchy_; |
572 | ZoneGrowableArray<BitVector*>* loop_invariant_loads_; |
573 | |
574 | DirectChainedHashMap<ConstantPoolTrait> constant_instr_pool_; |
575 | BitVector* captured_parameters_; |
576 | |
577 | intptr_t inlining_id_; |
578 | bool should_print_; |
579 | }; |
580 | |
581 | class LivenessAnalysis : public ValueObject { |
582 | public: |
583 | LivenessAnalysis(intptr_t variable_count, |
584 | const GrowableArray<BlockEntryInstr*>& postorder); |
585 | |
586 | void Analyze(); |
587 | |
588 | virtual ~LivenessAnalysis() {} |
589 | |
590 | BitVector* GetLiveInSetAt(intptr_t postorder_number) const { |
591 | return live_in_[postorder_number]; |
592 | } |
593 | |
594 | BitVector* GetLiveOutSetAt(intptr_t postorder_number) const { |
595 | return live_out_[postorder_number]; |
596 | } |
597 | |
598 | BitVector* GetLiveInSet(BlockEntryInstr* block) const { |
599 | return GetLiveInSetAt(block->postorder_number()); |
600 | } |
601 | |
602 | BitVector* GetKillSet(BlockEntryInstr* block) const { |
603 | return kill_[block->postorder_number()]; |
604 | } |
605 | |
606 | BitVector* GetLiveOutSet(BlockEntryInstr* block) const { |
607 | return GetLiveOutSetAt(block->postorder_number()); |
608 | } |
609 | |
610 | // Print results of liveness analysis. |
611 | void Dump(); |
612 | |
613 | protected: |
614 | // Compute initial values for live-out, kill and live-in sets. |
615 | virtual void ComputeInitialSets() = 0; |
616 | |
617 | // Update live-out set for the given block: live-out should contain |
618 | // all values that are live-in for block's successors. |
619 | // Returns true if live-out set was changed. |
620 | bool UpdateLiveOut(const BlockEntryInstr& instr); |
621 | |
622 | // Update live-in set for the given block: live-in should contain |
623 | // all values that are live-out from the block and are not defined |
624 | // by this block. |
625 | // Returns true if live-in set was changed. |
626 | bool UpdateLiveIn(const BlockEntryInstr& instr); |
627 | |
628 | // Perform fix-point iteration updating live-out and live-in sets |
629 | // for blocks until they stop changing. |
630 | void ComputeLiveInAndLiveOutSets(); |
631 | |
632 | Zone* zone() const { return zone_; } |
633 | |
634 | Zone* zone_; |
635 | |
636 | const intptr_t variable_count_; |
637 | |
638 | const GrowableArray<BlockEntryInstr*>& postorder_; |
639 | |
640 | // Live-out sets for each block. They contain indices of variables |
641 | // that are live out from this block: that is values that were either |
642 | // defined in this block or live into it and that are used in some |
643 | // successor block. |
644 | GrowableArray<BitVector*> live_out_; |
645 | |
646 | // Kill sets for each block. They contain indices of variables that |
647 | // are defined by this block. |
648 | GrowableArray<BitVector*> kill_; |
649 | |
650 | // Live-in sets for each block. They contain indices of variables |
651 | // that are used by this block or its successors. |
652 | GrowableArray<BitVector*> live_in_; |
653 | }; |
654 | |
655 | class DefinitionWorklist : public ValueObject { |
656 | public: |
657 | DefinitionWorklist(FlowGraph* flow_graph, intptr_t initial_capacity) |
658 | : defs_(initial_capacity), |
659 | contains_vector_(new BitVector(flow_graph->zone(), |
660 | flow_graph->current_ssa_temp_index())) {} |
661 | |
662 | void Add(Definition* defn) { |
663 | if (!Contains(defn)) { |
664 | defs_.Add(defn); |
665 | contains_vector_->Add(defn->ssa_temp_index()); |
666 | } |
667 | } |
668 | |
669 | bool Contains(Definition* defn) const { |
670 | return (defn->ssa_temp_index() >= 0) && |
671 | contains_vector_->Contains(defn->ssa_temp_index()); |
672 | } |
673 | |
674 | bool IsEmpty() const { return defs_.is_empty(); } |
675 | |
676 | Definition* RemoveLast() { |
677 | Definition* defn = defs_.RemoveLast(); |
678 | contains_vector_->Remove(defn->ssa_temp_index()); |
679 | return defn; |
680 | } |
681 | |
682 | const GrowableArray<Definition*>& definitions() const { return defs_; } |
683 | BitVector* contains_vector() const { return contains_vector_; } |
684 | |
685 | void Clear() { |
686 | defs_.TruncateTo(0); |
687 | contains_vector_->Clear(); |
688 | } |
689 | |
690 | private: |
691 | GrowableArray<Definition*> defs_; |
692 | BitVector* contains_vector_; |
693 | }; |
694 | |
695 | } // namespace dart |
696 | |
697 | #endif // RUNTIME_VM_COMPILER_BACKEND_FLOW_GRAPH_H_ |
698 | |