aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/CodeGen/RegAllocBasic.cpp
blob: 8acfa5b87921019dd43f97f3ce204d201f10121e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// SPDX-License-Identifier: GPL-3.0-or-later

#include "RegAllocBasic.h"

#include "RegAllocBase.h"
#include "rcc/CodeGen/LiveIntervals.h"
#include "rcc/CodeGen/LiveRegMatrix.h"

#include <algorithm>
#include <memory>
#include <ranges>

namespace rcc {
namespace {

class BasicAllocatorEngine final : public RegAllocBase {
public:
  BasicAllocatorEngine(MachineFunction &Function, const TargetDescription &Target,
                       const TargetRegisterAllocationInfo &TargetInfo,
                       MachineFunctionAnalysisManager &Analyses, RegisterAllocationOptions Options)
      : RegAllocBase(Function, Target, TargetInfo, Analyses, Options) {}

private:
  [[nodiscard]] Expected<AllocationRoundResult>
  allocateRound(const LiveIntervalsAnalysis::Result &Intervals, VirtRegMap &Mapping) override {
    LiveRegMatrix Matrix(Function, Target, Intervals);
    AllocationRoundResult Result;
    std::vector<const LiveInterval *> Worklist = orderedAllocationIntervals(Intervals, Function);
    std::ranges::stable_sort(Worklist, [&](const LiveInterval *Left, const LiveInterval *Right) {
      const bool LeftSpillable = Mapping.isSpillable(Left->Reg);
      const bool RightSpillable = Mapping.isSpillable(Right->Reg);
      if (LeftSpillable != RightSpillable)
        return !LeftSpillable;
      if (Left->SpillWeight != Right->SpillWeight)
        return Left->SpillWeight > Right->SpillWeight;
      return false;
    });
    for (const LiveInterval *Interval : Worklist) {
      const Register VirtualReg = Interval->Reg;
      if (regalloc_detail::hasTerminatorDefinition(Function, Target, VirtualReg)) {
        auto Marked = Mapping.setSpillable(VirtualReg, false);
        if (!Marked)
          return std::unexpected(Marked.error());
      }
      auto Candidates = regalloc_detail::allocationOrder(Function, Target, TargetInfo, VirtualReg);
      if (!Candidates)
        return std::unexpected(Candidates.error());
      bool Assigned = false;
      for (const PhysicalRegister Candidate : *Candidates) {
        if (Matrix.checkInterference(VirtualReg, Candidate).Kind != RegisterInterferenceKind::Free)
          continue;
        auto MatrixAssignment = Matrix.assign(VirtualReg, Candidate);
        if (!MatrixAssignment)
          continue;
        auto MapAssignment = Mapping.assign(VirtualReg, Candidate);
        if (!MapAssignment)
          return std::unexpected(MapAssignment.error());
        Assigned = true;
        break;
      }
      if (!Assigned) {
        if (!Mapping.isSpillable(VirtualReg)) {
          return makeUnexpected(ErrorCode::RegisterAllocationFailed,
                                "BasicAllocator cannot assign a non-spillable virtual register");
        }
        Result.Spilled.push_back(VirtualReg);
      }
    }
    return Result;
  }
};

[[nodiscard]] const MachinePassContract &basicContract() {
  static const MachinePassContract Contract = [] {
    MachinePassContract Value{
        .Name = "BasicAllocator",
        .Requires = {MachineFunctionProperty::Selected, MachineFunctionProperty::NoPHIs,
                     MachineFunctionProperty::TiedOpsRewritten,
                     MachineFunctionProperty::TracksLiveness},
        .Sets = {},
        .Clears = {MachineFunctionProperty::IsSSA},
        .RequiredAnalyses = {MachineFunctionAnalysisManager::id<LiveIntervalsAnalysis>(),
                             MachineFunctionAnalysisManager::id<VirtRegMapAnalysis>()},
        .VerificationStage = std::nullopt,
    };
    Value.ThreadSafety = PassThreadSafety::ThreadSafe;
    return Value;
  }();
  return Contract;
}

} // namespace

const MachinePassContract &BasicAllocatorPass::contract() const noexcept {
  return basicContract();
}

Expected<MachinePassResult> BasicAllocatorPass::run(MachineFunction &Function,
                                                    const TargetDescription &Target,
                                                    MachineFunctionAnalysisManager &Analyses) {
  if (Info == nullptr)
    return makeUnexpected(ErrorCode::InvalidArgument, "BasicAllocator requires target behavior");
  const std::vector<PhysicalRegister> FunctionReserved =
      regalloc_detail::functionReservedRegisters(Function, Target, *Info);
  auto Reserved = regalloc_detail::reserveFunctionRegisters(Function, FunctionReserved);
  if (!Reserved)
    return std::unexpected(Reserved.error());
  BasicAllocatorEngine Allocator(Function, Target, *Info, Analyses, Options);
  auto Mapping = Analyses.getResult<VirtRegMapAnalysis>(Function, Target);
  if (!Mapping)
    return std::unexpected(Mapping.error());
  auto Allocated = Allocator.run(**Mapping);
  if (!Allocated)
    return std::unexpected(Allocated.error());
  Allocated->Changed |= *Reserved;
  MachinePassResult Result = regalloc_detail::passResult(*Allocated);
  Result.Preserved.preserve<VirtRegMapAnalysis>();
  return Result;
}

std::unique_ptr<MachineFunctionPass>
createBasicAllocatorPass(const TargetRegisterAllocationInfo &Info,
                         RegisterAllocationOptions Options) {
  return std::make_unique<BasicAllocatorPass>(Info, Options);
}

} // namespace rcc