| Index: src/compiler/greedy-allocator.cc
|
| diff --git a/src/compiler/greedy-allocator.cc b/src/compiler/greedy-allocator.cc
|
| index 4eb6f16da950b85579a01173c01e1eb5900dc39c..acd4333954a3b5a7b129708af7c13f0b53deafdc 100644
|
| --- a/src/compiler/greedy-allocator.cc
|
| +++ b/src/compiler/greedy-allocator.cc
|
| @@ -127,12 +127,18 @@ void AllocationScheduler::Schedule(LiveRange* range) {
|
| queue_.push(AllocationCandidate(range));
|
| }
|
|
|
| +
|
| +void AllocationScheduler::Schedule(LiveRangeGroup* group) {
|
| + queue_.push(AllocationCandidate(group));
|
| +}
|
| +
|
| GreedyAllocator::GreedyAllocator(RegisterAllocationData* data,
|
| RegisterKind kind, Zone* local_zone)
|
| : RegisterAllocator(data, kind),
|
| local_zone_(local_zone),
|
| allocations_(local_zone),
|
| - scheduler_(local_zone) {}
|
| + scheduler_(local_zone),
|
| + groups_(local_zone) {}
|
|
|
|
|
| void GreedyAllocator::AssignRangeToRegister(int reg_id, LiveRange* range) {
|
| @@ -169,11 +175,99 @@ void GreedyAllocator::PreallocateFixedRanges() {
|
| }
|
|
|
|
|
| +void GreedyAllocator::GroupLiveRanges() {
|
| + CoalescedLiveRanges groupper(local_zone());
|
| + for (TopLevelLiveRange* range : data()->live_ranges()) {
|
| + // Skip splinters, because we do not want to optimize for them, and moves
|
| + // due to assigning them to different registers occur in deferred blocks.
|
| + if (!CanProcessRange(range) || range->IsSplinter() || !range->is_phi()) {
|
| + continue;
|
| + }
|
| +
|
| + // A phi can't be a memory operand, so it couldn't have been split.
|
| + DCHECK(!range->spilled());
|
| +
|
| + // Maybe this phi range is itself an input to another phi which was already
|
| + // processed.
|
| + LiveRangeGroup* latest_grp = range->group() != nullptr
|
| + ? range->group()
|
| + : new (local_zone())
|
| + LiveRangeGroup(local_zone());
|
| +
|
| + // Populate the groupper.
|
| + if (range->group() == nullptr) {
|
| + groupper.clear();
|
| + groupper.AllocateRange(range);
|
| + } else {
|
| + for (LiveRange* member : range->group()->ranges()) {
|
| + groupper.AllocateRange(member);
|
| + }
|
| + }
|
| + for (int j : data()->GetPhiMapValueFor(range)->phi()->operands()) {
|
| + // skip output also in input, which may happen for loops.
|
| + if (j == range->vreg()) continue;
|
| +
|
| + TopLevelLiveRange* other_top = data()->live_ranges()[j];
|
| +
|
| + if (other_top->IsSplinter()) continue;
|
| + // If the other was a memory operand, it might have been split.
|
| + // So get the unsplit part.
|
| + LiveRange* other =
|
| + other_top->next() == nullptr ? other_top : other_top->next();
|
| +
|
| + if (other->spilled()) continue;
|
| +
|
| + LiveRangeGroup* other_group = other->group();
|
| + if (other_group != nullptr) {
|
| + bool can_merge = true;
|
| + for (LiveRange* member : other_group->ranges()) {
|
| + if (groupper.GetConflicts(member).Current() != nullptr) {
|
| + can_merge = false;
|
| + break;
|
| + }
|
| + }
|
| + // If each member doesn't conflict with the current group, then since
|
| + // the members don't conflict with eachother either, we can merge them.
|
| + if (can_merge) {
|
| + latest_grp->ranges().insert(latest_grp->ranges().end(),
|
| + other_group->ranges().begin(),
|
| + other_group->ranges().end());
|
| + for (LiveRange* member : other_group->ranges()) {
|
| + groupper.AllocateRange(member);
|
| + member->set_group(latest_grp);
|
| + }
|
| + // Clear the other range, so we avoid scheduling it.
|
| + other_group->ranges().clear();
|
| + }
|
| + } else if (groupper.GetConflicts(other).Current() == nullptr) {
|
| + groupper.AllocateRange(other);
|
| + latest_grp->ranges().push_back(other);
|
| + other->set_group(latest_grp);
|
| + }
|
| + }
|
| +
|
| + if (latest_grp->ranges().size() > 0 && range->group() == nullptr) {
|
| + latest_grp->ranges().push_back(range);
|
| + DCHECK(latest_grp->ranges().size() > 1);
|
| + groups().push_back(latest_grp);
|
| + range->set_group(latest_grp);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| void GreedyAllocator::ScheduleAllocationCandidates() {
|
| - for (auto range : data()->live_ranges()) {
|
| + for (LiveRangeGroup* group : groups()) {
|
| + if (group->ranges().size() > 0) {
|
| + // We shouldn't have added single-range groups.
|
| + DCHECK(group->ranges().size() != 1);
|
| + scheduler().Schedule(group);
|
| + }
|
| + }
|
| + for (LiveRange* range : data()->live_ranges()) {
|
| if (CanProcessRange(range)) {
|
| for (LiveRange* child = range; child != nullptr; child = child->next()) {
|
| - if (!child->spilled()) {
|
| + if (!child->spilled() && child->group() == nullptr) {
|
| scheduler().Schedule(child);
|
| }
|
| }
|
| @@ -184,8 +278,53 @@ void GreedyAllocator::ScheduleAllocationCandidates() {
|
|
|
| void GreedyAllocator::TryAllocateCandidate(
|
| const AllocationCandidate& candidate) {
|
| - // At this point, this is just a live range. TODO: groups.
|
| - TryAllocateLiveRange(candidate.live_range());
|
| + if (candidate.is_group()) {
|
| + TryAllocateGroup(candidate.group());
|
| + } else {
|
| + TryAllocateLiveRange(candidate.live_range());
|
| + }
|
| +}
|
| +
|
| +
|
| +void GreedyAllocator::TryAllocateGroup(LiveRangeGroup* group) {
|
| + float group_weight = 0.0;
|
| + for (LiveRange* member : group->ranges()) {
|
| + EnsureValidRangeWeight(member);
|
| + group_weight = Max(group_weight, member->weight());
|
| + }
|
| +
|
| + float eviction_weight = group_weight;
|
| + int eviction_reg = -1;
|
| + int free_reg = -1;
|
| + for (int reg = 0; reg < num_registers(); ++reg) {
|
| + float weight = GetMaximumConflictingWeight(reg, group, group_weight);
|
| + if (weight == LiveRange::kInvalidWeight) {
|
| + free_reg = reg;
|
| + break;
|
| + }
|
| + if (weight < eviction_weight) {
|
| + eviction_weight = weight;
|
| + eviction_reg = reg;
|
| + }
|
| + }
|
| + if (eviction_reg < 0 && free_reg < 0) {
|
| + for (LiveRange* member : group->ranges()) {
|
| + scheduler().Schedule(member);
|
| + }
|
| + return;
|
| + }
|
| + if (free_reg < 0) {
|
| + DCHECK(eviction_reg >= 0);
|
| + for (LiveRange* member : group->ranges()) {
|
| + EvictAndRescheduleConflicts(eviction_reg, member);
|
| + }
|
| + free_reg = eviction_reg;
|
| + }
|
| +
|
| + DCHECK(free_reg >= 0);
|
| + for (LiveRange* member : group->ranges()) {
|
| + AssignRangeToRegister(free_reg, member);
|
| + }
|
| }
|
|
|
|
|
| @@ -280,7 +419,7 @@ void GreedyAllocator::EvictAndRescheduleConflicts(unsigned reg_id,
|
| void GreedyAllocator::SplitAndSpillRangesDefinedByMemoryOperand() {
|
| size_t initial_range_count = data()->live_ranges().size();
|
| for (size_t i = 0; i < initial_range_count; ++i) {
|
| - auto range = data()->live_ranges()[i];
|
| + TopLevelLiveRange* range = data()->live_ranges()[i];
|
| if (!CanProcessRange(range)) continue;
|
| if (!range->HasSpillOperand()) continue;
|
|
|
| @@ -322,9 +461,9 @@ void GreedyAllocator::AllocateRegisters() {
|
| data()->debug_name());
|
|
|
| SplitAndSpillRangesDefinedByMemoryOperand();
|
| - PreallocateFixedRanges();
|
| + GroupLiveRanges();
|
| ScheduleAllocationCandidates();
|
| -
|
| + PreallocateFixedRanges();
|
| while (!scheduler().empty()) {
|
| AllocationCandidate candidate = scheduler().GetNext();
|
| TryAllocateCandidate(candidate);
|
| @@ -358,6 +497,24 @@ float GreedyAllocator::GetMaximumConflictingWeight(
|
| }
|
|
|
|
|
| +float GreedyAllocator::GetMaximumConflictingWeight(unsigned reg_id,
|
| + const LiveRangeGroup* group,
|
| + float group_weight) const {
|
| + float ret = LiveRange::kInvalidWeight;
|
| +
|
| + for (LiveRange* member : group->ranges()) {
|
| + float member_conflict_weight = GetMaximumConflictingWeight(reg_id, member);
|
| + if (member_conflict_weight == LiveRange::kMaxWeight) {
|
| + return LiveRange::kMaxWeight;
|
| + }
|
| + if (member_conflict_weight > group_weight) return LiveRange::kMaxWeight;
|
| + ret = Max(member_conflict_weight, ret);
|
| + }
|
| +
|
| + return ret;
|
| +}
|
| +
|
| +
|
| void GreedyAllocator::EnsureValidRangeWeight(LiveRange* range) {
|
| // The live range weight will be invalidated when ranges are created or split.
|
| // Otherwise, it is consistently updated when the range is allocated or
|
|
|