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