CIRCT 20.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 auto testOp = getOperation();
66
67 LLVM_DEBUG(llvm::dbgs() << "=== Processing test @" << testOp.getSymName()
68 << "\n\n");
69
70 DenseMap<Operation *, unsigned> opIndices;
71 unsigned maxIdx;
72 for (auto [i, op] : llvm::enumerate(*testOp.getBody())) {
73 // TODO: ideally check that the IR is already fully elaborated
74 opIndices[&op] = i;
75 maxIdx = i;
76 }
77
78 // Collect all the register intervals we have to consider.
79 SmallVector<std::unique_ptr<RegisterLiveRange>> regRanges;
80 SmallVector<RegisterLiveRange *> active;
81 for (auto &op : *testOp.getBody()) {
82 if (!isa<rtg::FixedRegisterOp, rtg::VirtualRegisterOp>(&op))
83 continue;
84
85 RegisterLiveRange lr;
86 lr.start = maxIdx;
87 lr.end = 0;
88
89 if (auto regOp = dyn_cast<rtg::VirtualRegisterOp>(&op))
90 lr.regOp = regOp;
91
92 if (auto regOp = dyn_cast<rtg::FixedRegisterOp>(&op))
93 lr.fixedReg = regOp.getReg();
94
95 for (auto *user : op.getUsers()) {
96 if (!isa<rtg::InstructionOpInterface>(user)) {
97 user->emitError("only operations implementing 'InstructionOpInterface "
98 "are allowed to use registers");
99 return signalPassFailure();
100 }
101
102 // TODO: support labels and control-flow loops (jumps in general)
103 unsigned idx = opIndices.at(user);
104 lr.start = std::min(lr.start, idx);
105 lr.end = std::max(lr.end, idx);
106 }
107
108 regRanges.emplace_back(std::make_unique<RegisterLiveRange>(lr));
109
110 // Reserve fixed registers from the start. It will be made available again
111 // past the interval end. Not reserving it from the start can lead to the
112 // same register being chosen for a virtual register that overlaps with the
113 // fixed register interval.
114 // TODO: don't overapproximate that much
115 if (!lr.regOp)
116 active.push_back(regRanges.back().get());
117 }
118
119 // Sort such that we can process registers by increasing interval start.
120 llvm::sort(regRanges, [](const auto &a, const auto &b) {
121 return a->start < b->start || (a->start == b->start && !a->regOp);
122 });
123
124 for (auto &lr : regRanges) {
125 // Make registers out of live range available again.
126 expireOldInterval(active, lr.get());
127
128 // Handle already fixed registers.
129 if (!lr->regOp)
130 continue;
131
132 // Handle virtual registers.
133 rtg::RegisterAttrInterface availableReg;
134 for (auto reg : lr->regOp.getAllowedRegs()) {
135 if (llvm::none_of(active, [&](auto *r) { return r->fixedReg == reg; })) {
136 availableReg = cast<rtg::RegisterAttrInterface>(reg);
137 break;
138 }
139 }
140
141 if (!availableReg) {
142 ++numRegistersSpilled;
143 lr->regOp->emitError(
144 "need to spill this register, but not supported yet");
145 return signalPassFailure();
146 }
147
148 lr->fixedReg = availableReg;
149 active.push_back(lr.get());
150 }
151
152 LLVM_DEBUG({
153 for (auto &regRange : regRanges) {
154 llvm::dbgs() << "Start: " << regRange->start << ", End: " << regRange->end
155 << ", Selected: " << regRange->fixedReg << "\n";
156 }
157 llvm::dbgs() << "\n";
158 });
159
160 for (auto &reg : regRanges) {
161 // No need to fix already fixed registers.
162 if (!reg->regOp)
163 continue;
164
165 IRRewriter rewriter(reg->regOp);
166 rewriter.replaceOpWithNewOp<rtg::FixedRegisterOp>(reg->regOp,
167 reg->fixedReg);
168 }
169}
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