OLD | NEW |
| (Empty) |
1 // Copyright 2012 the V8 project authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #ifndef V8_LITHIUM_ALLOCATOR_H_ | |
6 #define V8_LITHIUM_ALLOCATOR_H_ | |
7 | |
8 #include "src/allocation.h" | |
9 #include "src/lithium.h" | |
10 #include "src/zone.h" | |
11 | |
12 namespace v8 { | |
13 namespace internal { | |
14 | |
15 // Forward declarations. | |
16 class HBasicBlock; | |
17 class HGraph; | |
18 class HPhi; | |
19 class HTracer; | |
20 class HValue; | |
21 class BitVector; | |
22 class StringStream; | |
23 | |
24 class LPlatformChunk; | |
25 class LOperand; | |
26 class LUnallocated; | |
27 class LGap; | |
28 class LParallelMove; | |
29 class LPointerMap; | |
30 | |
31 | |
32 // This class represents a single point of a LOperand's lifetime. | |
33 // For each lithium instruction there are exactly two lifetime positions: | |
34 // the beginning and the end of the instruction. Lifetime positions for | |
35 // different lithium instructions are disjoint. | |
36 class LifetimePosition { | |
37 public: | |
38 // Return the lifetime position that corresponds to the beginning of | |
39 // the instruction with the given index. | |
40 static LifetimePosition FromInstructionIndex(int index) { | |
41 return LifetimePosition(index * kStep); | |
42 } | |
43 | |
44 // Returns a numeric representation of this lifetime position. | |
45 int Value() const { | |
46 return value_; | |
47 } | |
48 | |
49 // Returns the index of the instruction to which this lifetime position | |
50 // corresponds. | |
51 int InstructionIndex() const { | |
52 DCHECK(IsValid()); | |
53 return value_ / kStep; | |
54 } | |
55 | |
56 // Returns true if this lifetime position corresponds to the instruction | |
57 // start. | |
58 bool IsInstructionStart() const { | |
59 return (value_ & (kStep - 1)) == 0; | |
60 } | |
61 | |
62 // Returns the lifetime position for the start of the instruction which | |
63 // corresponds to this lifetime position. | |
64 LifetimePosition InstructionStart() const { | |
65 DCHECK(IsValid()); | |
66 return LifetimePosition(value_ & ~(kStep - 1)); | |
67 } | |
68 | |
69 // Returns the lifetime position for the end of the instruction which | |
70 // corresponds to this lifetime position. | |
71 LifetimePosition InstructionEnd() const { | |
72 DCHECK(IsValid()); | |
73 return LifetimePosition(InstructionStart().Value() + kStep/2); | |
74 } | |
75 | |
76 // Returns the lifetime position for the beginning of the next instruction. | |
77 LifetimePosition NextInstruction() const { | |
78 DCHECK(IsValid()); | |
79 return LifetimePosition(InstructionStart().Value() + kStep); | |
80 } | |
81 | |
82 // Returns the lifetime position for the beginning of the previous | |
83 // instruction. | |
84 LifetimePosition PrevInstruction() const { | |
85 DCHECK(IsValid()); | |
86 DCHECK(value_ > 1); | |
87 return LifetimePosition(InstructionStart().Value() - kStep); | |
88 } | |
89 | |
90 // Constructs the lifetime position which does not correspond to any | |
91 // instruction. | |
92 LifetimePosition() : value_(-1) {} | |
93 | |
94 // Returns true if this lifetime positions corrensponds to some | |
95 // instruction. | |
96 bool IsValid() const { return value_ != -1; } | |
97 | |
98 static inline LifetimePosition Invalid() { return LifetimePosition(); } | |
99 | |
100 static inline LifetimePosition MaxPosition() { | |
101 // We have to use this kind of getter instead of static member due to | |
102 // crash bug in GDB. | |
103 return LifetimePosition(kMaxInt); | |
104 } | |
105 | |
106 private: | |
107 static const int kStep = 2; | |
108 | |
109 // Code relies on kStep being a power of two. | |
110 STATIC_ASSERT(IS_POWER_OF_TWO(kStep)); | |
111 | |
112 explicit LifetimePosition(int value) : value_(value) { } | |
113 | |
114 int value_; | |
115 }; | |
116 | |
117 | |
118 // Representation of the non-empty interval [start,end[. | |
119 class UseInterval: public ZoneObject { | |
120 public: | |
121 UseInterval(LifetimePosition start, LifetimePosition end) | |
122 : start_(start), end_(end), next_(NULL) { | |
123 DCHECK(start.Value() < end.Value()); | |
124 } | |
125 | |
126 LifetimePosition start() const { return start_; } | |
127 LifetimePosition end() const { return end_; } | |
128 UseInterval* next() const { return next_; } | |
129 | |
130 // Split this interval at the given position without effecting the | |
131 // live range that owns it. The interval must contain the position. | |
132 void SplitAt(LifetimePosition pos, Zone* zone); | |
133 | |
134 // If this interval intersects with other return smallest position | |
135 // that belongs to both of them. | |
136 LifetimePosition Intersect(const UseInterval* other) const { | |
137 if (other->start().Value() < start_.Value()) return other->Intersect(this); | |
138 if (other->start().Value() < end_.Value()) return other->start(); | |
139 return LifetimePosition::Invalid(); | |
140 } | |
141 | |
142 bool Contains(LifetimePosition point) const { | |
143 return start_.Value() <= point.Value() && point.Value() < end_.Value(); | |
144 } | |
145 | |
146 private: | |
147 void set_start(LifetimePosition start) { start_ = start; } | |
148 void set_next(UseInterval* next) { next_ = next; } | |
149 | |
150 LifetimePosition start_; | |
151 LifetimePosition end_; | |
152 UseInterval* next_; | |
153 | |
154 friend class LiveRange; // Assigns to start_. | |
155 }; | |
156 | |
157 // Representation of a use position. | |
158 class UsePosition: public ZoneObject { | |
159 public: | |
160 UsePosition(LifetimePosition pos, LOperand* operand, LOperand* hint); | |
161 | |
162 LOperand* operand() const { return operand_; } | |
163 bool HasOperand() const { return operand_ != NULL; } | |
164 | |
165 LOperand* hint() const { return hint_; } | |
166 bool HasHint() const; | |
167 bool RequiresRegister() const; | |
168 bool RegisterIsBeneficial() const; | |
169 | |
170 LifetimePosition pos() const { return pos_; } | |
171 UsePosition* next() const { return next_; } | |
172 | |
173 private: | |
174 void set_next(UsePosition* next) { next_ = next; } | |
175 | |
176 LOperand* const operand_; | |
177 LOperand* const hint_; | |
178 LifetimePosition const pos_; | |
179 UsePosition* next_; | |
180 bool requires_reg_; | |
181 bool register_beneficial_; | |
182 | |
183 friend class LiveRange; | |
184 }; | |
185 | |
186 // Representation of SSA values' live ranges as a collection of (continuous) | |
187 // intervals over the instruction ordering. | |
188 class LiveRange: public ZoneObject { | |
189 public: | |
190 static const int kInvalidAssignment = 0x7fffffff; | |
191 | |
192 LiveRange(int id, Zone* zone); | |
193 | |
194 UseInterval* first_interval() const { return first_interval_; } | |
195 UsePosition* first_pos() const { return first_pos_; } | |
196 LiveRange* parent() const { return parent_; } | |
197 LiveRange* TopLevel() { return (parent_ == NULL) ? this : parent_; } | |
198 LiveRange* next() const { return next_; } | |
199 bool IsChild() const { return parent() != NULL; } | |
200 int id() const { return id_; } | |
201 bool IsFixed() const { return id_ < 0; } | |
202 bool IsEmpty() const { return first_interval() == NULL; } | |
203 LOperand* CreateAssignedOperand(Zone* zone); | |
204 int assigned_register() const { return assigned_register_; } | |
205 int spill_start_index() const { return spill_start_index_; } | |
206 void set_assigned_register(int reg, Zone* zone); | |
207 void MakeSpilled(Zone* zone); | |
208 | |
209 // Returns use position in this live range that follows both start | |
210 // and last processed use position. | |
211 // Modifies internal state of live range! | |
212 UsePosition* NextUsePosition(LifetimePosition start); | |
213 | |
214 // Returns use position for which register is required in this live | |
215 // range and which follows both start and last processed use position | |
216 // Modifies internal state of live range! | |
217 UsePosition* NextRegisterPosition(LifetimePosition start); | |
218 | |
219 // Returns use position for which register is beneficial in this live | |
220 // range and which follows both start and last processed use position | |
221 // Modifies internal state of live range! | |
222 UsePosition* NextUsePositionRegisterIsBeneficial(LifetimePosition start); | |
223 | |
224 // Returns use position for which register is beneficial in this live | |
225 // range and which precedes start. | |
226 UsePosition* PreviousUsePositionRegisterIsBeneficial(LifetimePosition start); | |
227 | |
228 // Can this live range be spilled at this position. | |
229 bool CanBeSpilled(LifetimePosition pos); | |
230 | |
231 // Split this live range at the given position which must follow the start of | |
232 // the range. | |
233 // All uses following the given position will be moved from this | |
234 // live range to the result live range. | |
235 void SplitAt(LifetimePosition position, LiveRange* result, Zone* zone); | |
236 | |
237 RegisterKind Kind() const { return kind_; } | |
238 bool HasRegisterAssigned() const { | |
239 return assigned_register_ != kInvalidAssignment; | |
240 } | |
241 bool IsSpilled() const { return spilled_; } | |
242 | |
243 LOperand* current_hint_operand() const { | |
244 DCHECK(current_hint_operand_ == FirstHint()); | |
245 return current_hint_operand_; | |
246 } | |
247 LOperand* FirstHint() const { | |
248 UsePosition* pos = first_pos_; | |
249 while (pos != NULL && !pos->HasHint()) pos = pos->next(); | |
250 if (pos != NULL) return pos->hint(); | |
251 return NULL; | |
252 } | |
253 | |
254 LifetimePosition Start() const { | |
255 DCHECK(!IsEmpty()); | |
256 return first_interval()->start(); | |
257 } | |
258 | |
259 LifetimePosition End() const { | |
260 DCHECK(!IsEmpty()); | |
261 return last_interval_->end(); | |
262 } | |
263 | |
264 bool HasAllocatedSpillOperand() const; | |
265 LOperand* GetSpillOperand() const { return spill_operand_; } | |
266 void SetSpillOperand(LOperand* operand); | |
267 | |
268 void SetSpillStartIndex(int start) { | |
269 spill_start_index_ = Min(start, spill_start_index_); | |
270 } | |
271 | |
272 bool ShouldBeAllocatedBefore(const LiveRange* other) const; | |
273 bool CanCover(LifetimePosition position) const; | |
274 bool Covers(LifetimePosition position); | |
275 LifetimePosition FirstIntersection(LiveRange* other); | |
276 | |
277 // Add a new interval or a new use position to this live range. | |
278 void EnsureInterval(LifetimePosition start, | |
279 LifetimePosition end, | |
280 Zone* zone); | |
281 void AddUseInterval(LifetimePosition start, | |
282 LifetimePosition end, | |
283 Zone* zone); | |
284 void AddUsePosition(LifetimePosition pos, | |
285 LOperand* operand, | |
286 LOperand* hint, | |
287 Zone* zone); | |
288 | |
289 // Shorten the most recently added interval by setting a new start. | |
290 void ShortenTo(LifetimePosition start); | |
291 | |
292 #ifdef DEBUG | |
293 // True if target overlaps an existing interval. | |
294 bool HasOverlap(UseInterval* target) const; | |
295 void Verify() const; | |
296 #endif | |
297 | |
298 private: | |
299 void ConvertOperands(Zone* zone); | |
300 UseInterval* FirstSearchIntervalForPosition(LifetimePosition position) const; | |
301 void AdvanceLastProcessedMarker(UseInterval* to_start_of, | |
302 LifetimePosition but_not_past) const; | |
303 | |
304 int id_; | |
305 bool spilled_; | |
306 RegisterKind kind_; | |
307 int assigned_register_; | |
308 UseInterval* last_interval_; | |
309 UseInterval* first_interval_; | |
310 UsePosition* first_pos_; | |
311 LiveRange* parent_; | |
312 LiveRange* next_; | |
313 // This is used as a cache, it doesn't affect correctness. | |
314 mutable UseInterval* current_interval_; | |
315 UsePosition* last_processed_use_; | |
316 // This is used as a cache, it's invalid outside of BuildLiveRanges. | |
317 LOperand* current_hint_operand_; | |
318 LOperand* spill_operand_; | |
319 int spill_start_index_; | |
320 | |
321 friend class LAllocator; // Assigns to kind_. | |
322 }; | |
323 | |
324 | |
325 class LAllocator BASE_EMBEDDED { | |
326 public: | |
327 LAllocator(int first_virtual_register, HGraph* graph); | |
328 | |
329 static void TraceAlloc(const char* msg, ...); | |
330 | |
331 // Checks whether the value of a given virtual register is tagged. | |
332 bool HasTaggedValue(int virtual_register) const; | |
333 | |
334 // Returns the register kind required by the given virtual register. | |
335 RegisterKind RequiredRegisterKind(int virtual_register) const; | |
336 | |
337 bool Allocate(LChunk* chunk); | |
338 | |
339 const ZoneList<LiveRange*>* live_ranges() const { return &live_ranges_; } | |
340 const Vector<LiveRange*>* fixed_live_ranges() const { | |
341 return &fixed_live_ranges_; | |
342 } | |
343 const Vector<LiveRange*>* fixed_double_live_ranges() const { | |
344 return &fixed_double_live_ranges_; | |
345 } | |
346 | |
347 LPlatformChunk* chunk() const { return chunk_; } | |
348 HGraph* graph() const { return graph_; } | |
349 Isolate* isolate() const { return graph_->isolate(); } | |
350 Zone* zone() { return &zone_; } | |
351 | |
352 int GetVirtualRegister() { | |
353 if (next_virtual_register_ >= LUnallocated::kMaxVirtualRegisters) { | |
354 allocation_ok_ = false; | |
355 // Maintain the invariant that we return something below the maximum. | |
356 return 0; | |
357 } | |
358 return next_virtual_register_++; | |
359 } | |
360 | |
361 bool AllocationOk() { return allocation_ok_; } | |
362 | |
363 void MarkAsOsrEntry() { | |
364 // There can be only one. | |
365 DCHECK(!has_osr_entry_); | |
366 // Simply set a flag to find and process instruction later. | |
367 has_osr_entry_ = true; | |
368 } | |
369 | |
370 #ifdef DEBUG | |
371 void Verify() const; | |
372 #endif | |
373 | |
374 BitVector* assigned_registers() { | |
375 return assigned_registers_; | |
376 } | |
377 BitVector* assigned_double_registers() { | |
378 return assigned_double_registers_; | |
379 } | |
380 | |
381 private: | |
382 void MeetRegisterConstraints(); | |
383 void ResolvePhis(); | |
384 void BuildLiveRanges(); | |
385 void AllocateGeneralRegisters(); | |
386 void AllocateDoubleRegisters(); | |
387 void ConnectRanges(); | |
388 void ResolveControlFlow(); | |
389 void PopulatePointerMaps(); | |
390 void AllocateRegisters(); | |
391 bool CanEagerlyResolveControlFlow(HBasicBlock* block) const; | |
392 inline bool SafePointsAreInOrder() const; | |
393 | |
394 // Liveness analysis support. | |
395 void InitializeLivenessAnalysis(); | |
396 BitVector* ComputeLiveOut(HBasicBlock* block); | |
397 void AddInitialIntervals(HBasicBlock* block, BitVector* live_out); | |
398 void ProcessInstructions(HBasicBlock* block, BitVector* live); | |
399 void MeetRegisterConstraints(HBasicBlock* block); | |
400 void MeetConstraintsBetween(LInstruction* first, | |
401 LInstruction* second, | |
402 int gap_index); | |
403 void ResolvePhis(HBasicBlock* block); | |
404 | |
405 // Helper methods for building intervals. | |
406 LOperand* AllocateFixed(LUnallocated* operand, int pos, bool is_tagged); | |
407 LiveRange* LiveRangeFor(LOperand* operand); | |
408 void Define(LifetimePosition position, LOperand* operand, LOperand* hint); | |
409 void Use(LifetimePosition block_start, | |
410 LifetimePosition position, | |
411 LOperand* operand, | |
412 LOperand* hint); | |
413 void AddConstraintsGapMove(int index, LOperand* from, LOperand* to); | |
414 | |
415 // Helper methods for updating the life range lists. | |
416 void AddToActive(LiveRange* range); | |
417 void AddToInactive(LiveRange* range); | |
418 void AddToUnhandledSorted(LiveRange* range); | |
419 void AddToUnhandledUnsorted(LiveRange* range); | |
420 void SortUnhandled(); | |
421 bool UnhandledIsSorted(); | |
422 void ActiveToHandled(LiveRange* range); | |
423 void ActiveToInactive(LiveRange* range); | |
424 void InactiveToHandled(LiveRange* range); | |
425 void InactiveToActive(LiveRange* range); | |
426 void FreeSpillSlot(LiveRange* range); | |
427 LOperand* TryReuseSpillSlot(LiveRange* range); | |
428 | |
429 // Helper methods for allocating registers. | |
430 bool TryAllocateFreeReg(LiveRange* range); | |
431 void AllocateBlockedReg(LiveRange* range); | |
432 | |
433 // Live range splitting helpers. | |
434 | |
435 // Split the given range at the given position. | |
436 // If range starts at or after the given position then the | |
437 // original range is returned. | |
438 // Otherwise returns the live range that starts at pos and contains | |
439 // all uses from the original range that follow pos. Uses at pos will | |
440 // still be owned by the original range after splitting. | |
441 LiveRange* SplitRangeAt(LiveRange* range, LifetimePosition pos); | |
442 | |
443 // Split the given range in a position from the interval [start, end]. | |
444 LiveRange* SplitBetween(LiveRange* range, | |
445 LifetimePosition start, | |
446 LifetimePosition end); | |
447 | |
448 // Find a lifetime position in the interval [start, end] which | |
449 // is optimal for splitting: it is either header of the outermost | |
450 // loop covered by this interval or the latest possible position. | |
451 LifetimePosition FindOptimalSplitPos(LifetimePosition start, | |
452 LifetimePosition end); | |
453 | |
454 // Spill the given life range after position pos. | |
455 void SpillAfter(LiveRange* range, LifetimePosition pos); | |
456 | |
457 // Spill the given life range after position [start] and up to position [end]. | |
458 void SpillBetween(LiveRange* range, | |
459 LifetimePosition start, | |
460 LifetimePosition end); | |
461 | |
462 // Spill the given life range after position [start] and up to position [end]. | |
463 // Range is guaranteed to be spilled at least until position [until]. | |
464 void SpillBetweenUntil(LiveRange* range, | |
465 LifetimePosition start, | |
466 LifetimePosition until, | |
467 LifetimePosition end); | |
468 | |
469 void SplitAndSpillIntersecting(LiveRange* range); | |
470 | |
471 // If we are trying to spill a range inside the loop try to | |
472 // hoist spill position out to the point just before the loop. | |
473 LifetimePosition FindOptimalSpillingPos(LiveRange* range, | |
474 LifetimePosition pos); | |
475 | |
476 void Spill(LiveRange* range); | |
477 bool IsBlockBoundary(LifetimePosition pos); | |
478 | |
479 // Helper methods for resolving control flow. | |
480 void ResolveControlFlow(LiveRange* range, | |
481 HBasicBlock* block, | |
482 HBasicBlock* pred); | |
483 | |
484 inline void SetLiveRangeAssignedRegister(LiveRange* range, int reg); | |
485 | |
486 // Return parallel move that should be used to connect ranges split at the | |
487 // given position. | |
488 LParallelMove* GetConnectingParallelMove(LifetimePosition pos); | |
489 | |
490 // Return the block which contains give lifetime position. | |
491 HBasicBlock* GetBlock(LifetimePosition pos); | |
492 | |
493 // Helper methods for the fixed registers. | |
494 int RegisterCount() const; | |
495 static int FixedLiveRangeID(int index) { return -index - 1; } | |
496 static int FixedDoubleLiveRangeID(int index); | |
497 LiveRange* FixedLiveRangeFor(int index); | |
498 LiveRange* FixedDoubleLiveRangeFor(int index); | |
499 LiveRange* LiveRangeFor(int index); | |
500 HPhi* LookupPhi(LOperand* operand) const; | |
501 LGap* GetLastGap(HBasicBlock* block); | |
502 | |
503 const char* RegisterName(int allocation_index); | |
504 | |
505 inline bool IsGapAt(int index); | |
506 | |
507 inline LInstruction* InstructionAt(int index); | |
508 | |
509 inline LGap* GapAt(int index); | |
510 | |
511 Zone zone_; | |
512 | |
513 LPlatformChunk* chunk_; | |
514 | |
515 // During liveness analysis keep a mapping from block id to live_in sets | |
516 // for blocks already analyzed. | |
517 ZoneList<BitVector*> live_in_sets_; | |
518 | |
519 // Liveness analysis results. | |
520 ZoneList<LiveRange*> live_ranges_; | |
521 | |
522 // Lists of live ranges | |
523 EmbeddedVector<LiveRange*, Register::kNumRegisters> fixed_live_ranges_; | |
524 EmbeddedVector<LiveRange*, DoubleRegister::kMaxNumRegisters> | |
525 fixed_double_live_ranges_; | |
526 ZoneList<LiveRange*> unhandled_live_ranges_; | |
527 ZoneList<LiveRange*> active_live_ranges_; | |
528 ZoneList<LiveRange*> inactive_live_ranges_; | |
529 ZoneList<LiveRange*> reusable_slots_; | |
530 | |
531 // Next virtual register number to be assigned to temporaries. | |
532 int next_virtual_register_; | |
533 int first_artificial_register_; | |
534 GrowableBitVector double_artificial_registers_; | |
535 | |
536 RegisterKind mode_; | |
537 int num_registers_; | |
538 const int* allocatable_register_codes_; | |
539 | |
540 BitVector* assigned_registers_; | |
541 BitVector* assigned_double_registers_; | |
542 | |
543 HGraph* graph_; | |
544 | |
545 bool has_osr_entry_; | |
546 | |
547 // Indicates success or failure during register allocation. | |
548 bool allocation_ok_; | |
549 | |
550 #ifdef DEBUG | |
551 LifetimePosition allocation_finger_; | |
552 #endif | |
553 | |
554 DISALLOW_COPY_AND_ASSIGN(LAllocator); | |
555 }; | |
556 | |
557 | |
558 class LAllocatorPhase : public CompilationPhase { | |
559 public: | |
560 LAllocatorPhase(const char* name, LAllocator* allocator); | |
561 ~LAllocatorPhase(); | |
562 | |
563 private: | |
564 LAllocator* allocator_; | |
565 size_t allocator_zone_start_allocation_size_; | |
566 | |
567 DISALLOW_COPY_AND_ASSIGN(LAllocatorPhase); | |
568 }; | |
569 | |
570 | |
571 } // namespace internal | |
572 } // namespace v8 | |
573 | |
574 #endif // V8_LITHIUM_ALLOCATOR_H_ | |
OLD | NEW |