CIRCT 22.0.0git
Loading...
Searching...
No Matches
LinearScanRegisterAllocationPass.cpp
Go to the documentation of this file.
1//===- LinearScanRegisterAllocationPass.cpp - Register Allocation ---------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This pass allocates registers using a simple linear scan algorithm.
10//
11//===----------------------------------------------------------------------===//
12
17#include "mlir/IR/PatternMatch.h"
18#include "llvm/Support/Debug.h"
19
20namespace circt {
21namespace rtg {
22#define GEN_PASS_DEF_LINEARSCANREGISTERALLOCATIONPASS
23#include "circt/Dialect/RTG/Transforms/RTGPasses.h.inc"
24} // namespace rtg
25} // namespace circt
26
27using namespace mlir;
28using namespace circt;
29
30#define DEBUG_TYPE "rtg-linear-scan-register-allocation"
31
32namespace {
33
34/// Represents a register and its live range.
35struct RegisterLiveRange {
36 rtg::RegisterAttrInterface fixedReg;
37 rtg::VirtualRegisterOp regOp;
38 unsigned start;
39 unsigned end;
40};
41
42class LinearScanRegisterAllocationPass
43 : public circt::rtg::impl::LinearScanRegisterAllocationPassBase<
44 LinearScanRegisterAllocationPass> {
45public:
46 void runOnOperation() override;
47};
48
49} // end namespace
50
51static void expireOldInterval(SmallVector<RegisterLiveRange *> &active,
52 RegisterLiveRange *reg) {
53 // TODO: use a better datastructure for 'active'
54 llvm::sort(active, [](auto *a, auto *b) { return a->end < b->end; });
55
56 for (auto *iter = active.begin(); iter != active.end(); ++iter) {
57 auto *a = *iter;
58 if (a->end >= reg->start)
59 return;
60
61 active.erase(iter--);
62 }
63}
64
65void LinearScanRegisterAllocationPass::runOnOperation() {
66 LLVM_DEBUG(llvm::dbgs() << "=== Processing "
67 << OpWithFlags(getOperation(),
68 OpPrintingFlags().skipRegions())
69 << "\n\n");
70
71 if (getOperation()->getNumRegions() != 1 ||
72 getOperation()->getRegion(0).getBlocks().size() != 1) {
73 getOperation()->emitError("expected a single region with a single block");
74 return signalPassFailure();
75 }
76
77 DenseMap<Operation *, unsigned> opIndices;
78 unsigned maxIdx;
79 for (auto [i, op] :
80 llvm::enumerate(getOperation()->getRegion(0).getBlocks().front())) {
81 // TODO: ideally check that the IR is already fully elaborated
82 opIndices[&op] = i;
83 maxIdx = i;
84 }
85
86 // Collect all the register intervals we have to consider.
87 SmallVector<std::unique_ptr<RegisterLiveRange>> regRanges;
88 SmallVector<RegisterLiveRange *> active;
89 for (auto &op : getOperation()->getRegion(0).getBlocks().front()) {
90 if (!isa<rtg::FixedRegisterOp, rtg::VirtualRegisterOp>(&op))
91 continue;
92
93 RegisterLiveRange lr;
94 lr.start = maxIdx;
95 lr.end = 0;
96
97 if (auto regOp = dyn_cast<rtg::VirtualRegisterOp>(&op))
98 lr.regOp = regOp;
99
100 if (auto regOp = dyn_cast<rtg::FixedRegisterOp>(&op))
101 lr.fixedReg = regOp.getReg();
102
103 for (auto *user : op.getUsers()) {
104 if (!isa<rtg::InstructionOpInterface, rtg::ValidateOp>(user)) {
105 user->emitError("only operations implementing 'InstructionOpInterface' "
106 "and 'rtg.validate' are allowed to use registers");
107 return signalPassFailure();
108 }
109
110 // TODO: support labels and control-flow loops (jumps in general)
111 unsigned idx = opIndices.at(user);
112 lr.start = std::min(lr.start, idx);
113 lr.end = std::max(lr.end, idx);
114 }
115
116 regRanges.emplace_back(std::make_unique<RegisterLiveRange>(lr));
117
118 // Reserve fixed registers from the start. It will be made available again
119 // past the interval end. Not reserving it from the start can lead to the
120 // same register being chosen for a virtual register that overlaps with the
121 // fixed register interval.
122 // TODO: don't overapproximate that much
123 if (!lr.regOp)
124 active.push_back(regRanges.back().get());
125 }
126
127 // Sort such that we can process registers by increasing interval start.
128 llvm::sort(regRanges, [](const auto &a, const auto &b) {
129 return a->start < b->start || (a->start == b->start && !a->regOp);
130 });
131
132 for (auto &lr : regRanges) {
133 // Make registers out of live range available again.
134 expireOldInterval(active, lr.get());
135
136 // Handle already fixed registers.
137 if (!lr->regOp)
138 continue;
139
140 // Handle virtual registers.
141 auto configAttr =
142 cast<rtg::VirtualRegisterConfigAttr>(lr->regOp.getAllowedRegsAttr());
143 rtg::RegisterAttrInterface availableReg;
144 for (auto reg : configAttr.getAllowedRegs()) {
145 if (llvm::none_of(active, [&](auto *r) { return r->fixedReg == reg; })) {
146 availableReg = cast<rtg::RegisterAttrInterface>(reg);
147 break;
148 }
149 }
150
151 if (!availableReg) {
152 ++numRegistersSpilled;
153 lr->regOp->emitError(
154 "need to spill this register, but not supported yet");
155 return signalPassFailure();
156 }
157
158 lr->fixedReg = availableReg;
159 active.push_back(lr.get());
160 }
161
162 LLVM_DEBUG({
163 for (auto &regRange : regRanges) {
164 llvm::dbgs() << "Start: " << regRange->start << ", End: " << regRange->end
165 << ", Selected: " << regRange->fixedReg << "\n";
166 }
167 llvm::dbgs() << "\n";
168 });
169
170 for (auto &reg : regRanges) {
171 // No need to fix already fixed registers.
172 if (!reg->regOp)
173 continue;
174
175 IRRewriter rewriter(reg->regOp);
176 rewriter.replaceOpWithNewOp<rtg::FixedRegisterOp>(reg->regOp,
177 reg->fixedReg);
178 }
179}
static void expireOldInterval(SmallVector< RegisterLiveRange * > &active, RegisterLiveRange *reg)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition rtg.py:1
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition seq.py:21