// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_GREEDY_ALLOCATOR_H_ #define V8_GREEDY_ALLOCATOR_H_ #include "src/compiler/coalesced-live-ranges.h" #include "src/compiler/register-allocator.h" #include "src/zone-containers.h" namespace v8 { namespace internal { namespace compiler { // The object of allocation scheduling. At minimum, this is a LiveRange, but // we may extend this to groups of LiveRanges. It has to be comparable. class AllocationCandidate { public: explicit AllocationCandidate(LiveRange* range) : is_group_(false), size_(range->GetSize()) { candidate_.range_ = range; } explicit AllocationCandidate(LiveRangeGroup* ranges) : is_group_(true), size_(CalculateGroupSize(ranges)) { candidate_.group_ = ranges; } // Strict ordering operators bool operator<(const AllocationCandidate& other) const { return size() < other.size(); } bool operator>(const AllocationCandidate& other) const { return size() > other.size(); } bool is_group() const { return is_group_; } LiveRange* live_range() const { return candidate_.range_; } LiveRangeGroup* group() const { return candidate_.group_; } private: unsigned CalculateGroupSize(LiveRangeGroup* group) { unsigned ret = 0; for (LiveRange* range : group->ranges()) { ret += range->GetSize(); } return ret; } unsigned size() const { return size_; } bool is_group_; unsigned size_; union { LiveRange* range_; LiveRangeGroup* group_; } candidate_; }; // Schedule processing (allocating) of AllocationCandidates. class AllocationScheduler final : ZoneObject { public: explicit AllocationScheduler(Zone* zone) : queue_(zone) {} void Schedule(LiveRange* range); void Schedule(LiveRangeGroup* group); AllocationCandidate GetNext(); bool empty() const { return queue_.empty(); } private: typedef ZonePriorityQueue ScheduleQueue; ScheduleQueue queue_; DISALLOW_COPY_AND_ASSIGN(AllocationScheduler); }; // A variant of the LLVM Greedy Register Allocator. See // http://blog.llvm.org/2011/09/greedy-register-allocation-in-llvm-30.html class GreedyAllocator final : public RegisterAllocator { public: explicit GreedyAllocator(RegisterAllocationData* data, RegisterKind kind, Zone* local_zone); void AllocateRegisters(); private: static const float kAllocatedRangeMultiplier; static void UpdateWeightAtAllocation(LiveRange* range) { DCHECK_NE(range->weight(), LiveRange::kInvalidWeight); range->set_weight(range->weight() * kAllocatedRangeMultiplier); } static void UpdateWeightAtEviction(LiveRange* range) { DCHECK_NE(range->weight(), LiveRange::kInvalidWeight); range->set_weight(range->weight() / kAllocatedRangeMultiplier); } AllocationScheduler& scheduler() { return scheduler_; } CoalescedLiveRanges* current_allocations(unsigned i) { return allocations_[i]; } CoalescedLiveRanges* current_allocations(unsigned i) const { return allocations_[i]; } Zone* local_zone() const { return local_zone_; } ZoneVector& groups() { return groups_; } const ZoneVector& groups() const { return groups_; } // Insert fixed ranges. void PreallocateFixedRanges(); void GroupLiveRanges(); // Schedule unassigned live ranges for allocation. void ScheduleAllocationCandidates(); void AllocateRegisterToRange(unsigned reg_id, LiveRange* range) { UpdateWeightAtAllocation(range); current_allocations(reg_id)->AllocateRange(range); } // Evict and reschedule conflicts of a given range, at a given register. void EvictAndRescheduleConflicts(unsigned reg_id, const LiveRange* range); void TryAllocateCandidate(const AllocationCandidate& candidate); void TryAllocateLiveRange(LiveRange* range); void TryAllocateGroup(LiveRangeGroup* group); // Calculate the weight of a candidate for allocation. void EnsureValidRangeWeight(LiveRange* range); // Calculate the new weight of a range that is about to be allocated. float GetAllocatedRangeWeight(float candidate_weight); // Returns kInvalidWeight if there are no conflicts, or the largest weight of // a range conflicting with the given range, at the given register. float GetMaximumConflictingWeight(unsigned reg_id, const LiveRange* range, float competing_weight) const; // Returns kInvalidWeight if there are no conflicts, or the largest weight of // a range conflicting with the given range, at the given register. float GetMaximumConflictingWeight(unsigned reg_id, const LiveRangeGroup* group, float group_weight) const; // This is the extension point for splitting heuristics. void SplitOrSpillBlockedRange(LiveRange* range); // Find a good position where to fill, after a range was spilled after a call. LifetimePosition FindSplitPositionAfterCall(const LiveRange* range, int call_index); // Split a range around all calls it passes over. Returns true if any changes // were made, or false if no calls were found. bool TrySplitAroundCalls(LiveRange* range); // Find a split position at the outmost loop. LifetimePosition FindSplitPositionBeforeLoops(LiveRange* range); // Finds the first call instruction in the path of this range. Splits before // and requeues that segment (if any), spills the section over the call, and // returns the section after the call. The return is: // - same range, if no call was found // - nullptr, if the range finished at the call and there's no "after the // call" portion. // - the portion after the call. LiveRange* GetRemainderAfterSplittingAroundFirstCall(LiveRange* range); // While we attempt to merge spill ranges later on in the allocation pipeline, // we want to ensure group elements get merged. Waiting until later may hinder // merge-ability, since the pipeline merger (being naive) may create conflicts // between spill ranges of group members. void TryReuseSpillRangesForGroups(); LifetimePosition GetLastResortSplitPosition(const LiveRange* range); bool IsProgressPossible(const LiveRange* range); // Necessary heuristic: spill when all else failed. void SpillRangeAsLastResort(LiveRange* range); void AssignRangeToRegister(int reg_id, LiveRange* range); Zone* local_zone_; ZoneVector allocations_; AllocationScheduler scheduler_; ZoneVector groups_; DISALLOW_COPY_AND_ASSIGN(GreedyAllocator); }; } // namespace compiler } // namespace internal } // namespace v8 #endif // V8_GREEDY_ALLOCATOR_H_