1 // Copyright 2014 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_REGISTER_ALLOCATOR_H_
6 #define V8_REGISTER_ALLOCATOR_H_
7
8 #include "src/base/compiler-specific.h"
9 #include "src/compiler/instruction.h"
10 #include "src/globals.h"
11 #include "src/ostreams.h"
12 #include "src/register-configuration.h"
13 #include "src/zone/zone-containers.h"
14
15 namespace v8 {
16 namespace internal {
17 namespace compiler {
18
19 enum RegisterKind { GENERAL_REGISTERS, FP_REGISTERS };
20
21 // This class represents a single point of a InstructionOperand's lifetime. For
22 // each instruction there are four lifetime positions:
23 //
24 // [[START, END], [START, END]]
25 //
26 // Where the first half position corresponds to
27 //
28 // [GapPosition::START, GapPosition::END]
29 //
30 // and the second half position corresponds to
31 //
32 // [Lifetime::USED_AT_START, Lifetime::USED_AT_END]
33 //
34 class LifetimePosition final {
35 public:
36 // Return the lifetime position that corresponds to the beginning of
37 // the gap with the given index.
GapFromInstructionIndex(int index)38 static LifetimePosition GapFromInstructionIndex(int index) {
39 return LifetimePosition(index * kStep);
40 }
41 // Return the lifetime position that corresponds to the beginning of
42 // the instruction with the given index.
InstructionFromInstructionIndex(int index)43 static LifetimePosition InstructionFromInstructionIndex(int index) {
44 return LifetimePosition(index * kStep + kHalfStep);
45 }
46
ExistsGapPositionBetween(LifetimePosition pos1,LifetimePosition pos2)47 static bool ExistsGapPositionBetween(LifetimePosition pos1,
48 LifetimePosition pos2) {
49 if (pos1 > pos2) std::swap(pos1, pos2);
50 LifetimePosition next(pos1.value_ + 1);
51 if (next.IsGapPosition()) return next < pos2;
52 return next.NextFullStart() < pos2;
53 }
54
55 // Returns a numeric representation of this lifetime position.
value()56 int value() const { return value_; }
57
58 // Returns the index of the instruction to which this lifetime position
59 // corresponds.
ToInstructionIndex()60 int ToInstructionIndex() const {
61 DCHECK(IsValid());
62 return value_ / kStep;
63 }
64
65 // Returns true if this lifetime position corresponds to a START value
IsStart()66 bool IsStart() const { return (value_ & (kHalfStep - 1)) == 0; }
67 // Returns true if this lifetime position corresponds to an END value
IsEnd()68 bool IsEnd() const { return (value_ & (kHalfStep - 1)) == 1; }
69 // Returns true if this lifetime position corresponds to a gap START value
IsFullStart()70 bool IsFullStart() const { return (value_ & (kStep - 1)) == 0; }
71
IsGapPosition()72 bool IsGapPosition() const { return (value_ & 0x2) == 0; }
IsInstructionPosition()73 bool IsInstructionPosition() const { return !IsGapPosition(); }
74
75 // Returns the lifetime position for the current START.
Start()76 LifetimePosition Start() const {
77 DCHECK(IsValid());
78 return LifetimePosition(value_ & ~(kHalfStep - 1));
79 }
80
81 // Returns the lifetime position for the current gap START.
FullStart()82 LifetimePosition FullStart() const {
83 DCHECK(IsValid());
84 return LifetimePosition(value_ & ~(kStep - 1));
85 }
86
87 // Returns the lifetime position for the current END.
End()88 LifetimePosition End() const {
89 DCHECK(IsValid());
90 return LifetimePosition(Start().value_ + kHalfStep / 2);
91 }
92
93 // Returns the lifetime position for the beginning of the next START.
NextStart()94 LifetimePosition NextStart() const {
95 DCHECK(IsValid());
96 return LifetimePosition(Start().value_ + kHalfStep);
97 }
98
99 // Returns the lifetime position for the beginning of the next gap START.
NextFullStart()100 LifetimePosition NextFullStart() const {
101 DCHECK(IsValid());
102 return LifetimePosition(FullStart().value_ + kStep);
103 }
104
105 // Returns the lifetime position for the beginning of the previous START.
PrevStart()106 LifetimePosition PrevStart() const {
107 DCHECK(IsValid());
108 DCHECK(value_ >= kHalfStep);
109 return LifetimePosition(Start().value_ - kHalfStep);
110 }
111
112 // Constructs the lifetime position which does not correspond to any
113 // instruction.
LifetimePosition()114 LifetimePosition() : value_(-1) {}
115
116 // Returns true if this lifetime positions corrensponds to some
117 // instruction.
IsValid()118 bool IsValid() const { return value_ != -1; }
119
120 bool operator<(const LifetimePosition& that) const {
121 return this->value_ < that.value_;
122 }
123
124 bool operator<=(const LifetimePosition& that) const {
125 return this->value_ <= that.value_;
126 }
127
128 bool operator==(const LifetimePosition& that) const {
129 return this->value_ == that.value_;
130 }
131
132 bool operator!=(const LifetimePosition& that) const {
133 return this->value_ != that.value_;
134 }
135
136 bool operator>(const LifetimePosition& that) const {
137 return this->value_ > that.value_;
138 }
139
140 bool operator>=(const LifetimePosition& that) const {
141 return this->value_ >= that.value_;
142 }
143
144 void Print() const;
145
Invalid()146 static inline LifetimePosition Invalid() { return LifetimePosition(); }
147
MaxPosition()148 static inline LifetimePosition MaxPosition() {
149 // We have to use this kind of getter instead of static member due to
150 // crash bug in GDB.
151 return LifetimePosition(kMaxInt);
152 }
153
FromInt(int value)154 static inline LifetimePosition FromInt(int value) {
155 return LifetimePosition(value);
156 }
157
158 private:
159 static const int kHalfStep = 2;
160 static const int kStep = 2 * kHalfStep;
161
162 // Code relies on kStep and kHalfStep being a power of two.
163 STATIC_ASSERT(IS_POWER_OF_TWO(kHalfStep));
164
LifetimePosition(int value)165 explicit LifetimePosition(int value) : value_(value) {}
166
167 int value_;
168 };
169
170
171 std::ostream& operator<<(std::ostream& os, const LifetimePosition pos);
172
173
174 // Representation of the non-empty interval [start,end[.
175 class UseInterval final : public ZoneObject {
176 public:
UseInterval(LifetimePosition start,LifetimePosition end)177 UseInterval(LifetimePosition start, LifetimePosition end)
178 : start_(start), end_(end), next_(nullptr) {
179 DCHECK(start < end);
180 }
181
start()182 LifetimePosition start() const { return start_; }
set_start(LifetimePosition start)183 void set_start(LifetimePosition start) { start_ = start; }
end()184 LifetimePosition end() const { return end_; }
set_end(LifetimePosition end)185 void set_end(LifetimePosition end) { end_ = end; }
next()186 UseInterval* next() const { return next_; }
set_next(UseInterval * next)187 void set_next(UseInterval* next) { next_ = next; }
188
189 // Split this interval at the given position without effecting the
190 // live range that owns it. The interval must contain the position.
191 UseInterval* SplitAt(LifetimePosition pos, Zone* zone);
192
193 // If this interval intersects with other return smallest position
194 // that belongs to both of them.
Intersect(const UseInterval * other)195 LifetimePosition Intersect(const UseInterval* other) const {
196 if (other->start() < start_) return other->Intersect(this);
197 if (other->start() < end_) return other->start();
198 return LifetimePosition::Invalid();
199 }
200
Contains(LifetimePosition point)201 bool Contains(LifetimePosition point) const {
202 return start_ <= point && point < end_;
203 }
204
205 // Returns the index of the first gap covered by this interval.
FirstGapIndex()206 int FirstGapIndex() const {
207 int ret = start_.ToInstructionIndex();
208 if (start_.IsInstructionPosition()) {
209 ++ret;
210 }
211 return ret;
212 }
213
214 // Returns the index of the last gap covered by this interval.
LastGapIndex()215 int LastGapIndex() const {
216 int ret = end_.ToInstructionIndex();
217 if (end_.IsGapPosition() && end_.IsStart()) {
218 --ret;
219 }
220 return ret;
221 }
222
223 private:
224 LifetimePosition start_;
225 LifetimePosition end_;
226 UseInterval* next_;
227
228 DISALLOW_COPY_AND_ASSIGN(UseInterval);
229 };
230
231
232 enum class UsePositionType : uint8_t { kAny, kRequiresRegister, kRequiresSlot };
233
234
235 enum class UsePositionHintType : uint8_t {
236 kNone,
237 kOperand,
238 kUsePos,
239 kPhi,
240 kUnresolved
241 };
242
243
244 static const int32_t kUnassignedRegister =
245 RegisterConfiguration::kMaxGeneralRegisters;
246
247 static_assert(kUnassignedRegister <= RegisterConfiguration::kMaxFPRegisters,
248 "kUnassignedRegister too small");
249
250 // Representation of a use position.
251 class V8_EXPORT_PRIVATE UsePosition final
NON_EXPORTED_BASE(ZoneObject)252 : public NON_EXPORTED_BASE(ZoneObject) {
253 public:
254 UsePosition(LifetimePosition pos, InstructionOperand* operand, void* hint,
255 UsePositionHintType hint_type);
256
257 InstructionOperand* operand() const { return operand_; }
258 bool HasOperand() const { return operand_ != nullptr; }
259
260 bool RegisterIsBeneficial() const {
261 return RegisterBeneficialField::decode(flags_);
262 }
263 UsePositionType type() const { return TypeField::decode(flags_); }
264 void set_type(UsePositionType type, bool register_beneficial);
265
266 LifetimePosition pos() const { return pos_; }
267
268 UsePosition* next() const { return next_; }
269 void set_next(UsePosition* next) { next_ = next; }
270
271 // For hinting only.
272 void set_assigned_register(int register_code) {
273 flags_ = AssignedRegisterField::update(flags_, register_code);
274 }
275
276 UsePositionHintType hint_type() const {
277 return HintTypeField::decode(flags_);
278 }
279 bool HasHint() const;
280 bool HintRegister(int* register_code) const;
281 void SetHint(UsePosition* use_pos);
282 void ResolveHint(UsePosition* use_pos);
283 bool IsResolved() const {
284 return hint_type() != UsePositionHintType::kUnresolved;
285 }
286 static UsePositionHintType HintTypeForOperand(const InstructionOperand& op);
287
288 private:
289 typedef BitField<UsePositionType, 0, 2> TypeField;
290 typedef BitField<UsePositionHintType, 2, 3> HintTypeField;
291 typedef BitField<bool, 5, 1> RegisterBeneficialField;
292 typedef BitField<int32_t, 6, 6> AssignedRegisterField;
293
294 InstructionOperand* const operand_;
295 void* hint_;
296 UsePosition* next_;
297 LifetimePosition const pos_;
298 uint32_t flags_;
299
300 DISALLOW_COPY_AND_ASSIGN(UsePosition);
301 };
302
303
304 class SpillRange;
305 class RegisterAllocationData;
306 class TopLevelLiveRange;
307 class LiveRangeGroup;
308
309 // Representation of SSA values' live ranges as a collection of (continuous)
310 // intervals over the instruction ordering.
NON_EXPORTED_BASE(ZoneObject)311 class V8_EXPORT_PRIVATE LiveRange : public NON_EXPORTED_BASE(ZoneObject) {
312 public:
313 UseInterval* first_interval() const { return first_interval_; }
314 UsePosition* first_pos() const { return first_pos_; }
315 TopLevelLiveRange* TopLevel() { return top_level_; }
316 const TopLevelLiveRange* TopLevel() const { return top_level_; }
317
318 bool IsTopLevel() const;
319
320 LiveRange* next() const { return next_; }
321
322 int relative_id() const { return relative_id_; }
323
324 bool IsEmpty() const { return first_interval() == nullptr; }
325
326 InstructionOperand GetAssignedOperand() const;
327
328 MachineRepresentation representation() const {
329 return RepresentationField::decode(bits_);
330 }
331
332 int assigned_register() const { return AssignedRegisterField::decode(bits_); }
333 bool HasRegisterAssigned() const {
334 return assigned_register() != kUnassignedRegister;
335 }
336 void set_assigned_register(int reg);
337 void UnsetAssignedRegister();
338
339 bool spilled() const { return SpilledField::decode(bits_); }
340 void Spill();
341
342 RegisterKind kind() const;
343
344 // Returns use position in this live range that follows both start
345 // and last processed use position.
346 UsePosition* NextUsePosition(LifetimePosition start) const;
347
348 // Returns use position for which register is required in this live
349 // range and which follows both start and last processed use position
350 UsePosition* NextRegisterPosition(LifetimePosition start) const;
351
352 // Returns the first use position requiring stack slot, or nullptr.
353 UsePosition* NextSlotPosition(LifetimePosition start) const;
354
355 // Returns use position for which register is beneficial in this live
356 // range and which follows both start and last processed use position
357 UsePosition* NextUsePositionRegisterIsBeneficial(
358 LifetimePosition start) const;
359
360 // Returns lifetime position for which register is beneficial in this live
361 // range and which follows both start and last processed use position.
362 LifetimePosition NextLifetimePositionRegisterIsBeneficial(
363 const LifetimePosition& start) const;
364
365 // Returns use position for which register is beneficial in this live
366 // range and which precedes start.
367 UsePosition* PreviousUsePositionRegisterIsBeneficial(
368 LifetimePosition start) const;
369
370 // Can this live range be spilled at this position.
371 bool CanBeSpilled(LifetimePosition pos) const;
372
373 // Splitting primitive used by both splitting and splintering members.
374 // Performs the split, but does not link the resulting ranges.
375 // The given position must follow the start of the range.
376 // All uses following the given position will be moved from this
377 // live range to the result live range.
378 // The current range will terminate at position, while result will start from
379 // position.
380 enum HintConnectionOption : bool {
381 DoNotConnectHints = false,
382 ConnectHints = true
383 };
384 UsePosition* DetachAt(LifetimePosition position, LiveRange* result,
385 Zone* zone, HintConnectionOption connect_hints);
386
387 // Detaches at position, and then links the resulting ranges. Returns the
388 // child, which starts at position.
389 LiveRange* SplitAt(LifetimePosition position, Zone* zone);
390
391 // Returns nullptr when no register is hinted, otherwise sets register_index.
392 UsePosition* FirstHintPosition(int* register_index) const;
393 UsePosition* FirstHintPosition() const {
394 int register_index;
395 return FirstHintPosition(®ister_index);
396 }
397
398 UsePosition* current_hint_position() const {
399 DCHECK(current_hint_position_ == FirstHintPosition());
400 return current_hint_position_;
401 }
402
403 LifetimePosition Start() const {
404 DCHECK(!IsEmpty());
405 return first_interval()->start();
406 }
407
408 LifetimePosition End() const {
409 DCHECK(!IsEmpty());
410 return last_interval_->end();
411 }
412
413 bool ShouldBeAllocatedBefore(const LiveRange* other) const;
414 bool CanCover(LifetimePosition position) const;
415 bool Covers(LifetimePosition position) const;
416 LifetimePosition FirstIntersection(LiveRange* other) const;
417
418 void VerifyChildStructure() const {
419 VerifyIntervals();
420 VerifyPositions();
421 }
422
423 void ConvertUsesToOperand(const InstructionOperand& op,
424 const InstructionOperand& spill_op);
425 void SetUseHints(int register_index);
426 void UnsetUseHints() { SetUseHints(kUnassignedRegister); }
427
428 void Print(const RegisterConfiguration* config, bool with_children) const;
429 void Print(bool with_children) const;
430
431 private:
432 friend class TopLevelLiveRange;
433 explicit LiveRange(int relative_id, MachineRepresentation rep,
434 TopLevelLiveRange* top_level);
435
436 void UpdateParentForAllChildren(TopLevelLiveRange* new_top_level);
437
438 void set_spilled(bool value) { bits_ = SpilledField::update(bits_, value); }
439
440 UseInterval* FirstSearchIntervalForPosition(LifetimePosition position) const;
441 void AdvanceLastProcessedMarker(UseInterval* to_start_of,
442 LifetimePosition but_not_past) const;
443
444 void VerifyPositions() const;
445 void VerifyIntervals() const;
446
447 typedef BitField<bool, 0, 1> SpilledField;
448 typedef BitField<int32_t, 6, 6> AssignedRegisterField;
449 typedef BitField<MachineRepresentation, 12, 8> RepresentationField;
450
451 // Unique among children and splinters of the same virtual register.
452 int relative_id_;
453 uint32_t bits_;
454 UseInterval* last_interval_;
455 UseInterval* first_interval_;
456 UsePosition* first_pos_;
457 TopLevelLiveRange* top_level_;
458 LiveRange* next_;
459 // This is used as a cache, it doesn't affect correctness.
460 mutable UseInterval* current_interval_;
461 // This is used as a cache, it doesn't affect correctness.
462 mutable UsePosition* last_processed_use_;
463 // This is used as a cache, it's invalid outside of BuildLiveRanges.
464 mutable UsePosition* current_hint_position_;
465 // Cache the last position splintering stopped at.
466 mutable UsePosition* splitting_pointer_;
467
468 DISALLOW_COPY_AND_ASSIGN(LiveRange);
469 };
470
471
472 class LiveRangeGroup final : public ZoneObject {
473 public:
LiveRangeGroup(Zone * zone)474 explicit LiveRangeGroup(Zone* zone) : ranges_(zone) {}
ranges()475 ZoneVector<LiveRange*>& ranges() { return ranges_; }
ranges()476 const ZoneVector<LiveRange*>& ranges() const { return ranges_; }
477
assigned_register()478 int assigned_register() const { return assigned_register_; }
set_assigned_register(int reg)479 void set_assigned_register(int reg) { assigned_register_ = reg; }
480
481 private:
482 ZoneVector<LiveRange*> ranges_;
483 int assigned_register_;
484 DISALLOW_COPY_AND_ASSIGN(LiveRangeGroup);
485 };
486
487 class V8_EXPORT_PRIVATE TopLevelLiveRange final : public LiveRange {
488 public:
489 explicit TopLevelLiveRange(int vreg, MachineRepresentation rep);
spill_start_index()490 int spill_start_index() const { return spill_start_index_; }
491
IsFixed()492 bool IsFixed() const { return vreg_ < 0; }
493
is_phi()494 bool is_phi() const { return IsPhiField::decode(bits_); }
set_is_phi(bool value)495 void set_is_phi(bool value) { bits_ = IsPhiField::update(bits_, value); }
496
is_non_loop_phi()497 bool is_non_loop_phi() const { return IsNonLoopPhiField::decode(bits_); }
set_is_non_loop_phi(bool value)498 void set_is_non_loop_phi(bool value) {
499 bits_ = IsNonLoopPhiField::update(bits_, value);
500 }
501
has_slot_use()502 bool has_slot_use() const { return HasSlotUseField::decode(bits_); }
set_has_slot_use(bool value)503 void set_has_slot_use(bool value) {
504 bits_ = HasSlotUseField::update(bits_, value);
505 }
506
507 // Add a new interval or a new use position to this live range.
508 void EnsureInterval(LifetimePosition start, LifetimePosition end, Zone* zone);
509 void AddUseInterval(LifetimePosition start, LifetimePosition end, Zone* zone);
510 void AddUsePosition(UsePosition* pos);
511
512 // Shorten the most recently added interval by setting a new start.
513 void ShortenTo(LifetimePosition start);
514
515 // Detaches between start and end, and attributes the resulting range to
516 // result.
517 // The current range is pointed to as "splintered_from". No parent/child
518 // relationship is established between this and result.
519 void Splinter(LifetimePosition start, LifetimePosition end, Zone* zone);
520
521 // Assuming other was splintered from this range, embeds other and its
522 // children as part of the children sequence of this range.
523 void Merge(TopLevelLiveRange* other, Zone* zone);
524
525 // Spill range management.
526 void SetSpillRange(SpillRange* spill_range);
527 enum class SpillType { kNoSpillType, kSpillOperand, kSpillRange };
set_spill_type(SpillType value)528 void set_spill_type(SpillType value) {
529 bits_ = SpillTypeField::update(bits_, value);
530 }
spill_type()531 SpillType spill_type() const { return SpillTypeField::decode(bits_); }
GetSpillOperand()532 InstructionOperand* GetSpillOperand() const {
533 DCHECK(spill_type() == SpillType::kSpillOperand);
534 return spill_operand_;
535 }
536
GetAllocatedSpillRange()537 SpillRange* GetAllocatedSpillRange() const {
538 DCHECK(spill_type() != SpillType::kSpillOperand);
539 return spill_range_;
540 }
541
GetSpillRange()542 SpillRange* GetSpillRange() const {
543 DCHECK(spill_type() == SpillType::kSpillRange);
544 return spill_range_;
545 }
HasNoSpillType()546 bool HasNoSpillType() const {
547 return spill_type() == SpillType::kNoSpillType;
548 }
HasSpillOperand()549 bool HasSpillOperand() const {
550 return spill_type() == SpillType::kSpillOperand;
551 }
HasSpillRange()552 bool HasSpillRange() const { return spill_type() == SpillType::kSpillRange; }
553
554 AllocatedOperand GetSpillRangeOperand() const;
555
556 void RecordSpillLocation(Zone* zone, int gap_index,
557 InstructionOperand* operand);
558 void SetSpillOperand(InstructionOperand* operand);
SetSpillStartIndex(int start)559 void SetSpillStartIndex(int start) {
560 spill_start_index_ = Min(start, spill_start_index_);
561 }
562
563 void CommitSpillMoves(InstructionSequence* sequence,
564 const InstructionOperand& operand,
565 bool might_be_duplicated);
566
567 // If all the children of this range are spilled in deferred blocks, and if
568 // for any non-spilled child with a use position requiring a slot, that range
569 // is contained in a deferred block, mark the range as
570 // IsSpilledOnlyInDeferredBlocks, so that we avoid spilling at definition,
571 // and instead let the LiveRangeConnector perform the spills within the
572 // deferred blocks. If so, we insert here spills for non-spilled ranges
573 // with slot use positions.
TreatAsSpilledInDeferredBlock(Zone * zone,int total_block_count)574 void TreatAsSpilledInDeferredBlock(Zone* zone, int total_block_count) {
575 spill_start_index_ = -1;
576 spilled_in_deferred_blocks_ = true;
577 spill_move_insertion_locations_ = nullptr;
578 list_of_blocks_requiring_spill_operands_ =
579 new (zone) BitVector(total_block_count, zone);
580 }
581
582 void CommitSpillInDeferredBlocks(RegisterAllocationData* data,
583 const InstructionOperand& spill_operand,
584 BitVector* necessary_spill_points);
585
splintered_from()586 TopLevelLiveRange* splintered_from() const { return splintered_from_; }
IsSplinter()587 bool IsSplinter() const { return splintered_from_ != nullptr; }
MayRequireSpillRange()588 bool MayRequireSpillRange() const {
589 DCHECK(!IsSplinter());
590 return !HasSpillOperand() && spill_range_ == nullptr;
591 }
592 void UpdateSpillRangePostMerge(TopLevelLiveRange* merged);
vreg()593 int vreg() const { return vreg_; }
594
595 #if DEBUG
596 int debug_virt_reg() const;
597 #endif
598
599 void Verify() const;
600 void VerifyChildrenInOrder() const;
601
GetNextChildId()602 int GetNextChildId() {
603 return IsSplinter() ? splintered_from()->GetNextChildId()
604 : ++last_child_id_;
605 }
606
GetChildCount()607 int GetChildCount() const { return last_child_id_ + 1; }
608
IsSpilledOnlyInDeferredBlocks()609 bool IsSpilledOnlyInDeferredBlocks() const {
610 return spilled_in_deferred_blocks_;
611 }
612
613 struct SpillMoveInsertionList;
614
GetSpillMoveInsertionLocations()615 SpillMoveInsertionList* GetSpillMoveInsertionLocations() const {
616 DCHECK(!IsSpilledOnlyInDeferredBlocks());
617 return spill_move_insertion_locations_;
618 }
splinter()619 TopLevelLiveRange* splinter() const { return splinter_; }
SetSplinter(TopLevelLiveRange * splinter)620 void SetSplinter(TopLevelLiveRange* splinter) {
621 DCHECK_NULL(splinter_);
622 DCHECK_NOT_NULL(splinter);
623
624 splinter_ = splinter;
625 splinter->relative_id_ = GetNextChildId();
626 splinter->set_spill_type(spill_type());
627 splinter->SetSplinteredFrom(this);
628 }
629
MarkHasPreassignedSlot()630 void MarkHasPreassignedSlot() { has_preassigned_slot_ = true; }
has_preassigned_slot()631 bool has_preassigned_slot() const { return has_preassigned_slot_; }
632
AddBlockRequiringSpillOperand(RpoNumber block_id)633 void AddBlockRequiringSpillOperand(RpoNumber block_id) {
634 DCHECK(IsSpilledOnlyInDeferredBlocks());
635 GetListOfBlocksRequiringSpillOperands()->Add(block_id.ToInt());
636 }
637
GetListOfBlocksRequiringSpillOperands()638 BitVector* GetListOfBlocksRequiringSpillOperands() const {
639 DCHECK(IsSpilledOnlyInDeferredBlocks());
640 return list_of_blocks_requiring_spill_operands_;
641 }
642
643 private:
644 void SetSplinteredFrom(TopLevelLiveRange* splinter_parent);
645
646 typedef BitField<bool, 1, 1> HasSlotUseField;
647 typedef BitField<bool, 2, 1> IsPhiField;
648 typedef BitField<bool, 3, 1> IsNonLoopPhiField;
649 typedef BitField<SpillType, 4, 2> SpillTypeField;
650
651 int vreg_;
652 int last_child_id_;
653 TopLevelLiveRange* splintered_from_;
654 union {
655 // Correct value determined by spill_type()
656 InstructionOperand* spill_operand_;
657 SpillRange* spill_range_;
658 };
659
660 union {
661 SpillMoveInsertionList* spill_move_insertion_locations_;
662 BitVector* list_of_blocks_requiring_spill_operands_;
663 };
664
665 // TODO(mtrofin): generalize spilling after definition, currently specialized
666 // just for spill in a single deferred block.
667 bool spilled_in_deferred_blocks_;
668 int spill_start_index_;
669 UsePosition* last_pos_;
670 TopLevelLiveRange* splinter_;
671 bool has_preassigned_slot_;
672
673 DISALLOW_COPY_AND_ASSIGN(TopLevelLiveRange);
674 };
675
676
677 struct PrintableLiveRange {
678 const RegisterConfiguration* register_configuration_;
679 const LiveRange* range_;
680 };
681
682
683 std::ostream& operator<<(std::ostream& os,
684 const PrintableLiveRange& printable_range);
685
686
687 class SpillRange final : public ZoneObject {
688 public:
689 static const int kUnassignedSlot = -1;
690 SpillRange(TopLevelLiveRange* range, Zone* zone);
691
interval()692 UseInterval* interval() const { return use_interval_; }
693
IsEmpty()694 bool IsEmpty() const { return live_ranges_.empty(); }
695 bool TryMerge(SpillRange* other);
HasSlot()696 bool HasSlot() const { return assigned_slot_ != kUnassignedSlot; }
697
set_assigned_slot(int index)698 void set_assigned_slot(int index) {
699 DCHECK_EQ(kUnassignedSlot, assigned_slot_);
700 assigned_slot_ = index;
701 }
assigned_slot()702 int assigned_slot() {
703 DCHECK_NE(kUnassignedSlot, assigned_slot_);
704 return assigned_slot_;
705 }
live_ranges()706 const ZoneVector<TopLevelLiveRange*>& live_ranges() const {
707 return live_ranges_;
708 }
live_ranges()709 ZoneVector<TopLevelLiveRange*>& live_ranges() { return live_ranges_; }
710 // Spill slots can be 4, 8, or 16 bytes wide.
byte_width()711 int byte_width() const { return byte_width_; }
712 void Print() const;
713
714 private:
End()715 LifetimePosition End() const { return end_position_; }
716 bool IsIntersectingWith(SpillRange* other) const;
717 // Merge intervals, making sure the use intervals are sorted
718 void MergeDisjointIntervals(UseInterval* other);
719
720 ZoneVector<TopLevelLiveRange*> live_ranges_;
721 UseInterval* use_interval_;
722 LifetimePosition end_position_;
723 int assigned_slot_;
724 int byte_width_;
725
726 DISALLOW_COPY_AND_ASSIGN(SpillRange);
727 };
728
729
730 class RegisterAllocationData final : public ZoneObject {
731 public:
732 class PhiMapValue : public ZoneObject {
733 public:
734 PhiMapValue(PhiInstruction* phi, const InstructionBlock* block, Zone* zone);
735
phi()736 const PhiInstruction* phi() const { return phi_; }
block()737 const InstructionBlock* block() const { return block_; }
738
739 // For hinting.
assigned_register()740 int assigned_register() const { return assigned_register_; }
set_assigned_register(int register_code)741 void set_assigned_register(int register_code) {
742 DCHECK_EQ(assigned_register_, kUnassignedRegister);
743 assigned_register_ = register_code;
744 }
UnsetAssignedRegister()745 void UnsetAssignedRegister() { assigned_register_ = kUnassignedRegister; }
746
747 void AddOperand(InstructionOperand* operand);
748 void CommitAssignment(const InstructionOperand& operand);
749
750 private:
751 PhiInstruction* const phi_;
752 const InstructionBlock* const block_;
753 ZoneVector<InstructionOperand*> incoming_operands_;
754 int assigned_register_;
755 };
756 typedef ZoneMap<int, PhiMapValue*> PhiMap;
757
758 struct DelayedReference {
759 ReferenceMap* map;
760 InstructionOperand* operand;
761 };
762 typedef ZoneVector<DelayedReference> DelayedReferences;
763 typedef ZoneVector<std::pair<TopLevelLiveRange*, int>>
764 RangesWithPreassignedSlots;
765
766 RegisterAllocationData(const RegisterConfiguration* config,
767 Zone* allocation_zone, Frame* frame,
768 InstructionSequence* code,
769 const char* debug_name = nullptr);
770
live_ranges()771 const ZoneVector<TopLevelLiveRange*>& live_ranges() const {
772 return live_ranges_;
773 }
live_ranges()774 ZoneVector<TopLevelLiveRange*>& live_ranges() { return live_ranges_; }
fixed_live_ranges()775 const ZoneVector<TopLevelLiveRange*>& fixed_live_ranges() const {
776 return fixed_live_ranges_;
777 }
fixed_live_ranges()778 ZoneVector<TopLevelLiveRange*>& fixed_live_ranges() {
779 return fixed_live_ranges_;
780 }
fixed_float_live_ranges()781 ZoneVector<TopLevelLiveRange*>& fixed_float_live_ranges() {
782 return fixed_float_live_ranges_;
783 }
fixed_float_live_ranges()784 const ZoneVector<TopLevelLiveRange*>& fixed_float_live_ranges() const {
785 return fixed_float_live_ranges_;
786 }
fixed_double_live_ranges()787 ZoneVector<TopLevelLiveRange*>& fixed_double_live_ranges() {
788 return fixed_double_live_ranges_;
789 }
fixed_double_live_ranges()790 const ZoneVector<TopLevelLiveRange*>& fixed_double_live_ranges() const {
791 return fixed_double_live_ranges_;
792 }
fixed_simd128_live_ranges()793 ZoneVector<TopLevelLiveRange*>& fixed_simd128_live_ranges() {
794 return fixed_simd128_live_ranges_;
795 }
fixed_simd128_live_ranges()796 const ZoneVector<TopLevelLiveRange*>& fixed_simd128_live_ranges() const {
797 return fixed_simd128_live_ranges_;
798 }
live_in_sets()799 ZoneVector<BitVector*>& live_in_sets() { return live_in_sets_; }
live_out_sets()800 ZoneVector<BitVector*>& live_out_sets() { return live_out_sets_; }
spill_ranges()801 ZoneVector<SpillRange*>& spill_ranges() { return spill_ranges_; }
delayed_references()802 DelayedReferences& delayed_references() { return delayed_references_; }
code()803 InstructionSequence* code() const { return code_; }
804 // This zone is for data structures only needed during register allocation
805 // phases.
allocation_zone()806 Zone* allocation_zone() const { return allocation_zone_; }
807 // This zone is for InstructionOperands and moves that live beyond register
808 // allocation.
code_zone()809 Zone* code_zone() const { return code()->zone(); }
frame()810 Frame* frame() const { return frame_; }
debug_name()811 const char* debug_name() const { return debug_name_; }
config()812 const RegisterConfiguration* config() const { return config_; }
813
814 MachineRepresentation RepresentationFor(int virtual_register);
815
816 TopLevelLiveRange* GetOrCreateLiveRangeFor(int index);
817 // Creates a new live range.
818 TopLevelLiveRange* NewLiveRange(int index, MachineRepresentation rep);
819 TopLevelLiveRange* NextLiveRange(MachineRepresentation rep);
820
821 SpillRange* AssignSpillRangeToLiveRange(TopLevelLiveRange* range);
822 SpillRange* CreateSpillRangeForLiveRange(TopLevelLiveRange* range);
823
824 MoveOperands* AddGapMove(int index, Instruction::GapPosition position,
825 const InstructionOperand& from,
826 const InstructionOperand& to);
827
IsReference(TopLevelLiveRange * top_range)828 bool IsReference(TopLevelLiveRange* top_range) const {
829 return code()->IsReference(top_range->vreg());
830 }
831
832 bool ExistsUseWithoutDefinition();
833 bool RangesDefinedInDeferredStayInDeferred();
834
835 void MarkAllocated(MachineRepresentation rep, int index);
836
837 PhiMapValue* InitializePhiMap(const InstructionBlock* block,
838 PhiInstruction* phi);
839 PhiMapValue* GetPhiMapValueFor(TopLevelLiveRange* top_range);
840 PhiMapValue* GetPhiMapValueFor(int virtual_register);
841 bool IsBlockBoundary(LifetimePosition pos) const;
842
preassigned_slot_ranges()843 RangesWithPreassignedSlots& preassigned_slot_ranges() {
844 return preassigned_slot_ranges_;
845 }
846
847 private:
848 int GetNextLiveRangeId();
849
850 Zone* const allocation_zone_;
851 Frame* const frame_;
852 InstructionSequence* const code_;
853 const char* const debug_name_;
854 const RegisterConfiguration* const config_;
855 PhiMap phi_map_;
856 ZoneVector<BitVector*> live_in_sets_;
857 ZoneVector<BitVector*> live_out_sets_;
858 ZoneVector<TopLevelLiveRange*> live_ranges_;
859 ZoneVector<TopLevelLiveRange*> fixed_live_ranges_;
860 ZoneVector<TopLevelLiveRange*> fixed_float_live_ranges_;
861 ZoneVector<TopLevelLiveRange*> fixed_double_live_ranges_;
862 ZoneVector<TopLevelLiveRange*> fixed_simd128_live_ranges_;
863 ZoneVector<SpillRange*> spill_ranges_;
864 DelayedReferences delayed_references_;
865 BitVector* assigned_registers_;
866 BitVector* assigned_double_registers_;
867 int virtual_register_count_;
868 RangesWithPreassignedSlots preassigned_slot_ranges_;
869
870 DISALLOW_COPY_AND_ASSIGN(RegisterAllocationData);
871 };
872
873
874 class ConstraintBuilder final : public ZoneObject {
875 public:
876 explicit ConstraintBuilder(RegisterAllocationData* data);
877
878 // Phase 1 : insert moves to account for fixed register operands.
879 void MeetRegisterConstraints();
880
881 // Phase 2: deconstruct SSA by inserting moves in successors and the headers
882 // of blocks containing phis.
883 void ResolvePhis();
884
885 private:
data()886 RegisterAllocationData* data() const { return data_; }
code()887 InstructionSequence* code() const { return data()->code(); }
allocation_zone()888 Zone* allocation_zone() const { return data()->allocation_zone(); }
889
890 InstructionOperand* AllocateFixed(UnallocatedOperand* operand, int pos,
891 bool is_tagged);
892 void MeetRegisterConstraints(const InstructionBlock* block);
893 void MeetConstraintsBefore(int index);
894 void MeetConstraintsAfter(int index);
895 void MeetRegisterConstraintsForLastInstructionInBlock(
896 const InstructionBlock* block);
897 void ResolvePhis(const InstructionBlock* block);
898
899 RegisterAllocationData* const data_;
900
901 DISALLOW_COPY_AND_ASSIGN(ConstraintBuilder);
902 };
903
904
905 class LiveRangeBuilder final : public ZoneObject {
906 public:
907 explicit LiveRangeBuilder(RegisterAllocationData* data, Zone* local_zone);
908
909 // Phase 3: compute liveness of all virtual register.
910 void BuildLiveRanges();
911 static BitVector* ComputeLiveOut(const InstructionBlock* block,
912 RegisterAllocationData* data);
913
914 private:
data()915 RegisterAllocationData* data() const { return data_; }
code()916 InstructionSequence* code() const { return data()->code(); }
allocation_zone()917 Zone* allocation_zone() const { return data()->allocation_zone(); }
code_zone()918 Zone* code_zone() const { return code()->zone(); }
config()919 const RegisterConfiguration* config() const { return data()->config(); }
live_in_sets()920 ZoneVector<BitVector*>& live_in_sets() const {
921 return data()->live_in_sets();
922 }
923
924 // Verification.
925 void Verify() const;
926 bool IntervalStartsAtBlockBoundary(const UseInterval* interval) const;
927 bool IntervalPredecessorsCoveredByRange(const UseInterval* interval,
928 const TopLevelLiveRange* range) const;
929 bool NextIntervalStartsInDifferentBlocks(const UseInterval* interval) const;
930
931 // Liveness analysis support.
932 void AddInitialIntervals(const InstructionBlock* block, BitVector* live_out);
933 void ProcessInstructions(const InstructionBlock* block, BitVector* live);
934 void ProcessPhis(const InstructionBlock* block, BitVector* live);
935 void ProcessLoopHeader(const InstructionBlock* block, BitVector* live);
936
FixedLiveRangeID(int index)937 static int FixedLiveRangeID(int index) { return -index - 1; }
938 int FixedFPLiveRangeID(int index, MachineRepresentation rep);
939 TopLevelLiveRange* FixedLiveRangeFor(int index);
940 TopLevelLiveRange* FixedFPLiveRangeFor(int index, MachineRepresentation rep);
941
942 void MapPhiHint(InstructionOperand* operand, UsePosition* use_pos);
943 void ResolvePhiHint(InstructionOperand* operand, UsePosition* use_pos);
944
945 UsePosition* NewUsePosition(LifetimePosition pos, InstructionOperand* operand,
946 void* hint, UsePositionHintType hint_type);
NewUsePosition(LifetimePosition pos)947 UsePosition* NewUsePosition(LifetimePosition pos) {
948 return NewUsePosition(pos, nullptr, nullptr, UsePositionHintType::kNone);
949 }
950 TopLevelLiveRange* LiveRangeFor(InstructionOperand* operand);
951 // Helper methods for building intervals.
952 UsePosition* Define(LifetimePosition position, InstructionOperand* operand,
953 void* hint, UsePositionHintType hint_type);
Define(LifetimePosition position,InstructionOperand * operand)954 void Define(LifetimePosition position, InstructionOperand* operand) {
955 Define(position, operand, nullptr, UsePositionHintType::kNone);
956 }
957 UsePosition* Use(LifetimePosition block_start, LifetimePosition position,
958 InstructionOperand* operand, void* hint,
959 UsePositionHintType hint_type);
Use(LifetimePosition block_start,LifetimePosition position,InstructionOperand * operand)960 void Use(LifetimePosition block_start, LifetimePosition position,
961 InstructionOperand* operand) {
962 Use(block_start, position, operand, nullptr, UsePositionHintType::kNone);
963 }
964
965 RegisterAllocationData* const data_;
966 ZoneMap<InstructionOperand*, UsePosition*> phi_hints_;
967
968 DISALLOW_COPY_AND_ASSIGN(LiveRangeBuilder);
969 };
970
971
972 class RegisterAllocator : public ZoneObject {
973 public:
974 RegisterAllocator(RegisterAllocationData* data, RegisterKind kind);
975
976 protected:
data()977 RegisterAllocationData* data() const { return data_; }
code()978 InstructionSequence* code() const { return data()->code(); }
mode()979 RegisterKind mode() const { return mode_; }
num_registers()980 int num_registers() const { return num_registers_; }
num_allocatable_registers()981 int num_allocatable_registers() const { return num_allocatable_registers_; }
allocatable_register_codes()982 const int* allocatable_register_codes() const {
983 return allocatable_register_codes_;
984 }
985 // Returns true iff. we must check float register aliasing.
check_fp_aliasing()986 bool check_fp_aliasing() const { return check_fp_aliasing_; }
987
988 // TODO(mtrofin): explain why splitting in gap START is always OK.
989 LifetimePosition GetSplitPositionForInstruction(const LiveRange* range,
990 int instruction_index);
991
allocation_zone()992 Zone* allocation_zone() const { return data()->allocation_zone(); }
993
994 // Find the optimal split for ranges defined by a memory operand, e.g.
995 // constants or function parameters passed on the stack.
996 void SplitAndSpillRangesDefinedByMemoryOperand();
997
998 // Split the given range at the given position.
999 // If range starts at or after the given position then the
1000 // original range is returned.
1001 // Otherwise returns the live range that starts at pos and contains
1002 // all uses from the original range that follow pos. Uses at pos will
1003 // still be owned by the original range after splitting.
1004 LiveRange* SplitRangeAt(LiveRange* range, LifetimePosition pos);
1005
CanProcessRange(LiveRange * range)1006 bool CanProcessRange(LiveRange* range) const {
1007 return range != nullptr && !range->IsEmpty() && range->kind() == mode();
1008 }
1009
1010
1011 // Split the given range in a position from the interval [start, end].
1012 LiveRange* SplitBetween(LiveRange* range, LifetimePosition start,
1013 LifetimePosition end);
1014
1015 // Find a lifetime position in the interval [start, end] which
1016 // is optimal for splitting: it is either header of the outermost
1017 // loop covered by this interval or the latest possible position.
1018 LifetimePosition FindOptimalSplitPos(LifetimePosition start,
1019 LifetimePosition end);
1020
1021 void Spill(LiveRange* range);
1022
1023 // If we are trying to spill a range inside the loop try to
1024 // hoist spill position out to the point just before the loop.
1025 LifetimePosition FindOptimalSpillingPos(LiveRange* range,
1026 LifetimePosition pos);
1027
1028 const ZoneVector<TopLevelLiveRange*>& GetFixedRegisters() const;
1029 const char* RegisterName(int allocation_index) const;
1030
1031 private:
1032 RegisterAllocationData* const data_;
1033 const RegisterKind mode_;
1034 const int num_registers_;
1035 int num_allocatable_registers_;
1036 const int* allocatable_register_codes_;
1037 bool check_fp_aliasing_;
1038
1039 private:
1040 bool no_combining_;
1041
1042 DISALLOW_COPY_AND_ASSIGN(RegisterAllocator);
1043 };
1044
1045
1046 class LinearScanAllocator final : public RegisterAllocator {
1047 public:
1048 LinearScanAllocator(RegisterAllocationData* data, RegisterKind kind,
1049 Zone* local_zone);
1050
1051 // Phase 4: compute register assignments.
1052 void AllocateRegisters();
1053
1054 private:
unhandled_live_ranges()1055 ZoneVector<LiveRange*>& unhandled_live_ranges() {
1056 return unhandled_live_ranges_;
1057 }
active_live_ranges()1058 ZoneVector<LiveRange*>& active_live_ranges() { return active_live_ranges_; }
inactive_live_ranges()1059 ZoneVector<LiveRange*>& inactive_live_ranges() {
1060 return inactive_live_ranges_;
1061 }
1062
1063 void SetLiveRangeAssignedRegister(LiveRange* range, int reg);
1064
1065 // Helper methods for updating the life range lists.
1066 void AddToActive(LiveRange* range);
1067 void AddToInactive(LiveRange* range);
1068 void AddToUnhandledSorted(LiveRange* range);
1069 void AddToUnhandledUnsorted(LiveRange* range);
1070 void SortUnhandled();
1071 bool UnhandledIsSorted();
1072 void ActiveToHandled(LiveRange* range);
1073 void ActiveToInactive(LiveRange* range);
1074 void InactiveToHandled(LiveRange* range);
1075 void InactiveToActive(LiveRange* range);
1076
1077 // Helper methods for allocating registers.
1078 bool TryReuseSpillForPhi(TopLevelLiveRange* range);
1079 bool TryAllocateFreeReg(LiveRange* range,
1080 const Vector<LifetimePosition>& free_until_pos);
1081 bool TryAllocatePreferredReg(LiveRange* range,
1082 const Vector<LifetimePosition>& free_until_pos);
1083 void GetFPRegisterSet(MachineRepresentation rep, int* num_regs,
1084 int* num_codes, const int** codes) const;
1085 void FindFreeRegistersForRange(LiveRange* range,
1086 Vector<LifetimePosition> free_until_pos);
1087 void ProcessCurrentRange(LiveRange* current);
1088 void AllocateBlockedReg(LiveRange* range);
1089 bool TrySplitAndSpillSplinter(LiveRange* range);
1090
1091 // Spill the given life range after position pos.
1092 void SpillAfter(LiveRange* range, LifetimePosition pos);
1093
1094 // Spill the given life range after position [start] and up to position [end].
1095 void SpillBetween(LiveRange* range, LifetimePosition start,
1096 LifetimePosition end);
1097
1098 // Spill the given life range after position [start] and up to position [end].
1099 // Range is guaranteed to be spilled at least until position [until].
1100 void SpillBetweenUntil(LiveRange* range, LifetimePosition start,
1101 LifetimePosition until, LifetimePosition end);
1102
1103 void SplitAndSpillIntersecting(LiveRange* range);
1104
1105 ZoneVector<LiveRange*> unhandled_live_ranges_;
1106 ZoneVector<LiveRange*> active_live_ranges_;
1107 ZoneVector<LiveRange*> inactive_live_ranges_;
1108
1109 #ifdef DEBUG
1110 LifetimePosition allocation_finger_;
1111 #endif
1112
1113 DISALLOW_COPY_AND_ASSIGN(LinearScanAllocator);
1114 };
1115
1116
1117 class SpillSlotLocator final : public ZoneObject {
1118 public:
1119 explicit SpillSlotLocator(RegisterAllocationData* data);
1120
1121 void LocateSpillSlots();
1122
1123 private:
data()1124 RegisterAllocationData* data() const { return data_; }
1125
1126 RegisterAllocationData* const data_;
1127
1128 DISALLOW_COPY_AND_ASSIGN(SpillSlotLocator);
1129 };
1130
1131
1132 class OperandAssigner final : public ZoneObject {
1133 public:
1134 explicit OperandAssigner(RegisterAllocationData* data);
1135
1136 // Phase 5: assign spill splots.
1137 void AssignSpillSlots();
1138
1139 // Phase 6: commit assignment.
1140 void CommitAssignment();
1141
1142 private:
data()1143 RegisterAllocationData* data() const { return data_; }
1144
1145 RegisterAllocationData* const data_;
1146
1147 DISALLOW_COPY_AND_ASSIGN(OperandAssigner);
1148 };
1149
1150
1151 class ReferenceMapPopulator final : public ZoneObject {
1152 public:
1153 explicit ReferenceMapPopulator(RegisterAllocationData* data);
1154
1155 // Phase 7: compute values for pointer maps.
1156 void PopulateReferenceMaps();
1157
1158 private:
data()1159 RegisterAllocationData* data() const { return data_; }
1160
1161 bool SafePointsAreInOrder() const;
1162
1163 RegisterAllocationData* const data_;
1164
1165 DISALLOW_COPY_AND_ASSIGN(ReferenceMapPopulator);
1166 };
1167
1168
1169 class LiveRangeBoundArray;
1170 // Insert moves of the form
1171 //
1172 // Operand(child_(k+1)) = Operand(child_k)
1173 //
1174 // where child_k and child_(k+1) are consecutive children of a range (so
1175 // child_k->next() == child_(k+1)), and Operand(...) refers to the
1176 // assigned operand, be it a register or a slot.
1177 class LiveRangeConnector final : public ZoneObject {
1178 public:
1179 explicit LiveRangeConnector(RegisterAllocationData* data);
1180
1181 // Phase 8: reconnect split ranges with moves, when the control flow
1182 // between the ranges is trivial (no branches).
1183 void ConnectRanges(Zone* local_zone);
1184
1185 // Phase 9: insert moves to connect ranges across basic blocks, when the
1186 // control flow between them cannot be trivially resolved, such as joining
1187 // branches.
1188 void ResolveControlFlow(Zone* local_zone);
1189
1190 private:
data()1191 RegisterAllocationData* data() const { return data_; }
code()1192 InstructionSequence* code() const { return data()->code(); }
code_zone()1193 Zone* code_zone() const { return code()->zone(); }
1194
1195 bool CanEagerlyResolveControlFlow(const InstructionBlock* block) const;
1196
1197 int ResolveControlFlow(const InstructionBlock* block,
1198 const InstructionOperand& cur_op,
1199 const InstructionBlock* pred,
1200 const InstructionOperand& pred_op);
1201
1202 void CommitSpillsInDeferredBlocks(TopLevelLiveRange* range,
1203 LiveRangeBoundArray* array,
1204 Zone* temp_zone);
1205
1206 RegisterAllocationData* const data_;
1207
1208 DISALLOW_COPY_AND_ASSIGN(LiveRangeConnector);
1209 };
1210
1211 } // namespace compiler
1212 } // namespace internal
1213 } // namespace v8
1214
1215 #endif // V8_REGISTER_ALLOCATOR_H_
1216