CIRCT 23.0.0git
Loading...
Searching...
No Matches
LinearScanRegisterAllocationPass.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
18#include "llvm/ADT/SmallString.h"
19#include "llvm/Support/Debug.h"
20
21#include <variant>
22
23namespace circt {
24namespace rtg {
25#define GEN_PASS_DEF_LINEARSCANREGISTERALLOCATIONPASS
26#include "circt/Dialect/RTG/Transforms/RTGPasses.h.inc"
27} // namespace rtg
28} // namespace circt
29
30using namespace mlir;
31using namespace circt;
32
33#define DEBUG_TYPE "rtg-linear-scan-register-allocation"
34
35//===----------------------------------------------------------------------===//
36// Class declarations
37//===----------------------------------------------------------------------===//
38
39namespace {
40
41/// Base class for all live ranges. Should not be used to construct a live range
42/// directly.
43struct LiveRange {
44 LiveRange(Location loc, unsigned start, unsigned end)
45 : loc(loc), start(start), end(end) {}
46
47 /// Location of the defining operation.
48 Location loc;
49 /// Operation index of the first user.
50 unsigned start;
51 /// Operation index of the last user.
52 unsigned end;
53};
54
55/// Represents a fixed register and its live range.
56struct FixedLiveRange : public LiveRange {
57 FixedLiveRange(Location loc, rtg::RegisterAttrInterface fixedReg,
58 unsigned start, unsigned end)
59 : LiveRange(loc, start, end), fixedReg(fixedReg) {}
60
61 /// The concrete register as attribute.
62 rtg::RegisterAttrInterface fixedReg;
63};
64
65/// Represents a dependent register and its live range.
66struct DependentLiveRange : public LiveRange {
67 DependentLiveRange(Value value, unsigned start, unsigned end)
68 : LiveRange(value.getLoc(), start, end), value(value) {}
69
70 /// The value that is dependent on the virtual register. It is of
71 /// `rtg::RegisterTypeInterface` type.
72 Value value;
73};
74
75/// Represents a virtual register and its live range.
76struct VirtualLiveRange : public LiveRange {
77 VirtualLiveRange(rtg::VirtualRegisterOp virtualReg, unsigned start,
78 unsigned end)
79 : LiveRange(virtualReg.getLoc(), start, end), virtualReg(virtualReg) {}
80
81 /// The virtual register operation defining this live range.
82 rtg::VirtualRegisterOp virtualReg;
83 /// Dependent registers that must be allocated together with this range.
84 SmallVector<DependentLiveRange> dependentRegs;
85};
86
87struct RegisterAllocationResult {
88 enum class Kind {
89 /// The register is available for allocation.
90 Available,
91 /// The register is currently in use by an active live range.
92 InUse,
93 /// The register violates one or more constraints (any may be in use).
94 ConstraintViolation,
95 /// A fatal error occurred that prevents allocation from continuing.
96 FatalError,
97 };
98
99private:
100 explicit RegisterAllocationResult(Kind kind) : kind(kind) {}
101
102 Kind kind;
103 std::variant<rtg::ConstraintOp, LiveRange> value;
104
105public:
106 Kind getKind() const { return kind; }
107
108 LiveRange getUser() const {
109 assert(kind == Kind::InUse);
110 return std::get<LiveRange>(value);
111 }
112
113 rtg::ConstraintOp getConstraint() const {
114 assert(kind == Kind::ConstraintViolation);
115 return std::get<rtg::ConstraintOp>(value);
116 }
117
118 bool isAvailable() const { return kind == Kind::Available; }
119
120 static RegisterAllocationResult available() {
121 return RegisterAllocationResult(Kind::Available);
122 }
123
124 static RegisterAllocationResult inUseBy(LiveRange liveRange) {
125 auto res = RegisterAllocationResult(Kind::InUse);
126 res.value = liveRange;
127 return res;
128 }
129
130 static RegisterAllocationResult
131 constraintViolation(rtg::ConstraintOp constraintOp) {
132 auto res = RegisterAllocationResult(Kind::ConstraintViolation);
133 res.value = constraintOp;
134 return res;
135 }
136
137 static RegisterAllocationResult fatalError() {
138 return RegisterAllocationResult(Kind::FatalError);
139 }
140};
141
142/// Caches information about register live ranges.
143struct LiveRangeCache {
144 LogicalResult populate(Region &region);
145 void clear();
146
147 /// Maps each operation to its index in the segment for ordering.
148 DenseMap<Operation *, unsigned> opIndices;
149 /// Reverse mapping from index to operation for diagnostic purposes.
150 DenseMap<unsigned, Operation *> indexToOp;
151 /// Storage for all register live ranges.
152 SmallVector<VirtualLiveRange> virtualRanges;
153 /// All fixed live ranges (before any allocation) sorted by increasing start
154 /// index.
155 SmallVector<FixedLiveRange> reservedRanges;
156};
157
158class LinearScanRegisterAllocationPass
159 : public circt::rtg::impl::LinearScanRegisterAllocationPassBase<
160 LinearScanRegisterAllocationPass> {
161public:
162 void runOnOperation() override;
163};
164
165} // end namespace
166
167//===----------------------------------------------------------------------===//
168// Helper functions
169//===----------------------------------------------------------------------===//
170
171/// Folds an operation and adds its results to the worklist.
172/// Returns Available on success, or an error status on failure.
173static LogicalResult
174foldAndEnqueueResults(Operation *user, Value current, Attribute currentAttr,
175 SmallVectorImpl<std::pair<Value, Attribute>> &worklist,
176 DenseMap<Value, Attribute> &visited) {
177 // Check that all additional operands are provided by constant-like operations
178 SmallVector<Attribute> operandAttrs;
179 for (auto operand : user->getOperands()) {
180 if (operand == current) {
181 operandAttrs.push_back(currentAttr);
182 continue;
183 }
184 auto *defOp = operand.getDefiningOp();
185 if (!defOp || !defOp->hasTrait<OpTrait::ConstantLike>())
186 return failure();
187
188 SmallVector<OpFoldResult> operandFoldResults;
189 if (failed(operand.getDefiningOp()->fold(operandFoldResults))) {
190 return operand.getDefiningOp()->emitError(
191 "folding constant like operation failed");
192 }
193
194 operandAttrs.push_back(cast<Attribute>(operandFoldResults[0]));
195 }
196
197 // Fold the operation to get the result attribute
198 SmallVector<OpFoldResult> foldResults;
199 if (failed(user->fold(operandAttrs, foldResults))) {
200 LLVM_DEBUG(llvm::dbgs()
201 << " Failed to fold operation: " << *user << "\n");
202 return user->emitError("operation could not be folded");
203 }
204
205 for (auto [result, foldResult] : llvm::zip(user->getResults(), foldResults)) {
206 Attribute resultAttr = dyn_cast<Attribute>(foldResult);
207 if (!resultAttr) {
208 return user->emitError("fold result is not an attribute");
209 }
210
211 if (visited.insert({result, resultAttr}).second)
212 worklist.push_back({result, resultAttr});
213 }
214
215 return success();
216}
217
218// Follow all users of a value transitively until reaching an operation that
219// implements RegisterAllocationOpInterface. Store the operand values at those
220// interface operations in the output vector.
221static RegisterAllocationResult collectTransitiveRegisterAllocationOperands(
222 Value value, rtg::RegisterAttrInterface reg,
223 SetVector<std::pair<Value, rtg::RegisterAttrInterface>> &operands) {
224 LLVM_DEBUG(llvm::dbgs()
225 << "Collecting transitive register allocation operands for value: "
226 << value << " with register: " << reg << "\n");
227
228 SmallVector<std::pair<Value, Attribute>> worklist;
229 DenseMap<Value, Attribute> visited;
230 worklist.push_back({value, reg});
231 visited.insert({value, reg});
232
233 while (!worklist.empty()) {
234 auto [current, currentAttr] = worklist.pop_back_val();
235 LLVM_DEBUG(llvm::dbgs() << " Processing current value: " << current
236 << " with attribute: " << currentAttr << "\n");
237
238 for (Operation *user : current.getUsers()) {
239 if (auto regAllocOp =
240 dyn_cast<rtg::RegisterAllocationOpInterface>(user)) {
241 // Found a RegisterAllocationOpInterface - store the operand and don't
242 // follow results further
243 LLVM_DEBUG(llvm::dbgs() << " Found RegisterAllocationOpInterface: "
244 << *user << "\n");
245 operands.insert(
246 {current, cast_or_null<rtg::RegisterAttrInterface>(currentAttr)});
247 continue;
248 }
249
250 if (auto constraintOp = dyn_cast<rtg::ConstraintOp>(user)) {
251 if (!reg)
252 continue;
253
254 auto condAttr = dyn_cast<IntegerAttr>(currentAttr);
255 if (!condAttr || condAttr.getValue().isZero()) {
256 LLVM_DEBUG(llvm::dbgs() << " Constraint could not be satisfied\n");
257 return RegisterAllocationResult::constraintViolation(constraintOp);
258 }
259 continue;
260 }
261
262 LLVM_DEBUG(llvm::dbgs()
263 << " Following non-RegisterAllocationOpInterface: " << *user
264 << "\n");
265
266 if (reg) {
267 if (failed(foldAndEnqueueResults(user, current, currentAttr, worklist,
268 visited)))
269 return RegisterAllocationResult::fatalError();
270 } else {
271 for (auto result : user->getResults()) {
272 if (visited.insert({result, Attribute()}).second)
273 worklist.push_back({result, Attribute()});
274 }
275 }
276 }
277 }
278
279 LLVM_DEBUG(llvm::dbgs() << "Collected " << operands.size()
280 << " transitive register allocation operands\n");
281 return RegisterAllocationResult::available();
282}
283
284static void expireOldInterval(SmallVector<FixedLiveRange> &active,
285 const VirtualLiveRange &liveRange) {
286 LLVM_DEBUG(llvm::dbgs() << "Expiring old intervals for register starting at "
287 << liveRange.start << "\n");
288 LLVM_DEBUG(llvm::dbgs() << " Active intervals before expiration: "
289 << active.size() << "\n");
290
291 // TODO: use a better datastructure for 'active'
292 llvm::sort(active, [](const FixedLiveRange &a, const FixedLiveRange &b) {
293 return a.end < b.end;
294 });
295
296 for (auto *iter = active.begin(); iter != active.end(); ++iter) {
297 const auto &activeRange = *iter;
298 if (activeRange.end >= liveRange.start) {
299 LLVM_DEBUG(llvm::dbgs() << " Keeping active interval ending at "
300 << activeRange.end << "\n");
301 return;
302 }
303
304 LLVM_DEBUG(llvm::dbgs()
305 << " Expiring interval ending at " << activeRange.end << "\n");
306 active.erase(iter--);
307 }
308
309 LLVM_DEBUG(llvm::dbgs() << " Active intervals after expiration: "
310 << active.size() << "\n");
311}
312
313/// Updates the live range based on users of a value. Returns true if any
314/// RegisterAllocationOpInterface users were found.
315static bool updateLiveRangeFromUsers(const LiveRangeCache &cache, Value value,
316 LiveRange &liveRange) {
317 bool hasUser = false;
318 for (auto *user : value.getUsers()) {
319 if (!isa<rtg::RegisterAllocationOpInterface>(user))
320 continue;
321
322 // TODO: support labels and control-flow loops (jumps in general)
323 unsigned idx = cache.opIndices.at(user);
324 LLVM_DEBUG(llvm::dbgs()
325 << " User at index " << idx << ": " << *user << "\n");
326 liveRange.start = std::min(liveRange.start, idx);
327 liveRange.end = std::max(liveRange.end, idx);
328 hasUser = true;
329 }
330 return hasUser;
331}
332
333/// Creates a live range for a virtual register operation. Returns nullptr
334/// if the virtual register has no users that implement
335/// RegisterAllocationOpInterface.
336static void createRange(LiveRangeCache &cache, rtg::VirtualRegisterOp op) {
337 LLVM_DEBUG(llvm::dbgs() << "Processing virtual register: " << op << "\n");
338
339 auto &liveRange =
340 cache.virtualRanges.emplace_back(op, cache.opIndices.size(), 0);
341
342 SetVector<std::pair<Value, rtg::RegisterAttrInterface>> registers;
343 auto res = collectTransitiveRegisterAllocationOperands(op.getResult(), {},
344 registers);
345 if (!res.isAvailable())
346 return;
347
348 bool hasUser = updateLiveRangeFromUsers(cache, op.getResult(), liveRange);
349
350 for (auto [reg, regAttr] : registers) {
351 auto &depRange =
352 liveRange.dependentRegs.emplace_back(reg, cache.opIndices.size(), 0);
353 updateLiveRangeFromUsers(cache, reg, depRange);
354 // TODO: instead of updating the virtual register range we should
355 // cache/compute that differently
356 hasUser |= updateLiveRangeFromUsers(cache, reg, liveRange);
357 }
358
359 if (!hasUser) {
360 LLVM_DEBUG(llvm::dbgs()
361 << " No RegisterAllocationOpInterface users, removing range\n");
362 cache.virtualRanges.pop_back();
363 }
364
365 LLVM_DEBUG(llvm::dbgs() << " Live range: [" << liveRange.start << ", "
366 << liveRange.end << "]\n");
367}
368
369/// Computes live ranges for all virtual registers and identifies reserved
370/// (fixed) registers.
371static LogicalResult computeLiveRanges(LiveRangeCache &cache, Region &region) {
372 LLVM_DEBUG(llvm::dbgs() << "\nCollecting register live ranges...\n");
373
374 for (auto op : region.getOps<rtg::ConstantOp>()) {
375 auto reg = dyn_cast<rtg::RegisterAttrInterface>(op.getValue());
376 if (!reg)
377 continue;
378
379 unsigned maxIdx = 0, minIdx = cache.opIndices.size();
380 for (auto *user : op->getUsers()) {
381 minIdx = std::min(minIdx, cache.opIndices.at(user));
382 maxIdx = std::max(maxIdx, cache.opIndices.at(user));
383 }
384
385 cache.reservedRanges.emplace_back(op.getLoc(), reg, minIdx, maxIdx);
386
387 LLVM_DEBUG(llvm::dbgs()
388 << " Added fixed register to active list: " << reg << "\n");
389 }
390
391 llvm::sort(cache.reservedRanges,
392 [](const FixedLiveRange &a, const FixedLiveRange &b) {
393 return a.start < b.start;
394 });
395
396 for (auto op : region.getOps<rtg::VirtualRegisterOp>())
397 createRange(cache, op);
398
399 // Sort such that we can process registers by increasing interval start.
400 LLVM_DEBUG(llvm::dbgs() << "\nSorting " << cache.virtualRanges.size()
401 << " register ranges by start time\n");
402 llvm::sort(cache.virtualRanges, [](const VirtualLiveRange &a,
403 const VirtualLiveRange &b) {
404 return a.start < b.start || (a.start == b.start && a.end > b.end);
405 });
406
407 return success();
408}
409
410/// Checks if a specific register is available for allocation to a virtual
411/// register. Verifies the register is not reserved, not in use by active
412/// ranges, and satisfies all constraints.
413static RegisterAllocationResult isRegisterAvailable(
414 const LiveRangeCache &cache, ArrayRef<FixedLiveRange> active,
415 rtg::VirtualRegisterOp virtualReg, rtg::RegisterAttrInterface reg,
416 DenseMap<Value, rtg::RegisterAttrInterface> &dependentRegValues) {
417 LLVM_DEBUG(llvm::dbgs() << "Trying register: " << reg << "\n");
418
419 SetVector<std::pair<Value, rtg::RegisterAttrInterface>> registers;
420 auto res = collectTransitiveRegisterAllocationOperands(virtualReg.getResult(),
421 reg, registers);
422 if (!res.isAvailable())
423 return res;
424
425 LLVM_DEBUG(
426 llvm::dbgs() << "Checking live range overlap with active registers\n");
427
428 for (auto activeRange : active) {
429 if (activeRange.fixedReg == reg)
430 return RegisterAllocationResult::inUseBy(activeRange);
431
432 for (auto [candidateReg, candidateRegAttr] : registers) {
433 if (candidateRegAttr == activeRange.fixedReg)
434 return RegisterAllocationResult::inUseBy(activeRange);
435 }
436 }
437
438 for (auto [regVal, regAttr] : registers)
439 dependentRegValues[regVal] = regAttr;
440
441 LLVM_DEBUG(llvm::dbgs() << "Register: " << reg << " is available\n");
442
443 return RegisterAllocationResult::available();
444}
445
446namespace {
447/// Helper struct to accumulate diagnostic messages for register allocation
448/// failures. Uses composition with a temporary stream to avoid issues with
449/// non-copyable base classes.
450struct DiagnosticNote {
451 DiagnosticNote(Location loc) : loc(loc) {}
452
453 DiagnosticNote &operator<<(StringRef str) {
454 message += str.str();
455 return *this;
456 }
457
458 Location loc;
459 SmallString<128> message;
460};
461} // namespace
462
463/// Finds an available register for the given virtual register from its
464/// allowed register set. Returns an empty RegisterAttrInterface on failure.
465static rtg::RegisterAttrInterface findAvailableRegister(
466 const LiveRangeCache &cache, ArrayRef<FixedLiveRange> active,
467 VirtualLiveRange &liveRange,
468 DenseMap<Value, rtg::RegisterAttrInterface> &dependentRegValues) {
469 auto virtualReg = liveRange.virtualReg;
470 auto configAttr =
471 cast<rtg::VirtualRegisterConfigAttr>(virtualReg.getAllowedRegsAttr());
472 LLVM_DEBUG(llvm::dbgs() << " Allowed registers: "
473 << configAttr.getAllowedRegs().size() << "\n");
474
475 // Track diagnostic notes to emit only if we fail to find a register.
476 SmallVector<DiagnosticNote> diagnosticNotes;
477
478 // Try all registers allowed by the virtual register configuration in
479 // decreasing order of preference.
480 for (auto reg : configAttr.getAllowedRegs()) {
481 dependentRegValues.clear();
482 auto res =
483 isRegisterAvailable(cache, active, virtualReg, reg, dependentRegValues);
484 switch (res.getKind()) {
485 case RegisterAllocationResult::Kind::Available:
486 // If we found a valid register, use it without trying any other.
487 return reg;
488 case RegisterAllocationResult::Kind::InUse:
489 diagnosticNotes.emplace_back(res.getUser().loc)
490 << "cannot choose '" << reg.getRegisterAssembly()
491 << "' because of overlapping live-range with this register";
492 continue;
493 case RegisterAllocationResult::Kind::ConstraintViolation:
494 diagnosticNotes.emplace_back(res.getConstraint().getLoc())
495 << "constraint would be violated when choosing '"
496 << reg.getRegisterAssembly() << "'";
497 continue;
498 case RegisterAllocationResult::Kind::FatalError:
499 // Fatal errors are already reported when the error happened.
500 return {};
501 }
502 }
503
504 // No register was found, emit all accumulated diagnostics.
505 auto diag = virtualReg.emitError(
506 "no register available for allocation within constraints");
507 if (auto *startOp = cache.indexToOp.lookup(liveRange.start))
508 diag.attachNote(startOp->getLoc()) << "live range starts here";
509 if (auto *endOp = cache.indexToOp.lookup(liveRange.end))
510 diag.attachNote(endOp->getLoc()) << "live range ends here";
511 for (const auto &note : diagnosticNotes)
512 diag.attachNote(note.loc) << note.message;
513
514 return {};
515}
516
517/// Replaces all virtual register operations with their allocated fixed
518/// registers and removes unused operations.
520 const LiveRangeCache &cache,
521 const DenseMap<rtg::VirtualRegisterOp, rtg::RegisterAttrInterface>
522 &assignedRegisters) {
523 LLVM_DEBUG(llvm::dbgs()
524 << "\nReplacing virtual registers with fixed registers...\n");
525
526 circt::UnusedOpPruner operationPruner;
527 for (auto &liveRange : cache.virtualRanges) {
528 auto virtualReg = liveRange.virtualReg;
529 auto fixedReg = assignedRegisters.at(virtualReg);
530
531 LLVM_DEBUG(llvm::dbgs() << "Replacing virtual register " << virtualReg
532 << " with fixed register " << fixedReg << "\n");
533
534 OpBuilder builder(virtualReg);
535 auto newFixedReg =
536 rtg::ConstantOp::create(builder, virtualReg.getLoc(), fixedReg);
537 virtualReg.getResult().replaceAllUsesWith(newFixedReg);
538 operationPruner.eraseNow(virtualReg);
539 }
540
541 // Erase any operations that became unused after erasing the virtual register
542 // operations
543 operationPruner.eraseNow();
544
545 LLVM_DEBUG(llvm::dbgs() << "Register allocation completed successfully!\n");
546}
547
548/// Performs the main register allocation using linear scan algorithm.
549/// Processes live ranges in order, expires old intervals, and assigns
550/// registers to virtual registers.
551static LogicalResult allocateVirtualRegistersInCache(LiveRangeCache &cache) {
552 SmallVector<FixedLiveRange> active;
553 DenseMap<rtg::VirtualRegisterOp, rtg::RegisterAttrInterface>
554 assignedRegisters;
555
556 // TODO: would be better to only add them to active once their range actually
557 // starts
558 for (auto &fixedRange : cache.reservedRanges)
559 active.push_back(fixedRange);
560
561 for (auto &virtualRange : cache.virtualRanges) {
562 LLVM_DEBUG(llvm::dbgs() << "Processing register range ["
563 << virtualRange.start << ", " << virtualRange.end
564 << "] for " << virtualRange.virtualReg << "\n");
565
566 // Make registers out of live range available again.
567 expireOldInterval(active, virtualRange);
568
569 DenseMap<Value, rtg::RegisterAttrInterface> dependentRegValues;
570 auto availableReg =
571 findAvailableRegister(cache, active, virtualRange, dependentRegValues);
572 if (!availableReg)
573 return failure();
574
575 assignedRegisters[virtualRange.virtualReg] = availableReg;
576
577 LLVM_DEBUG(llvm::dbgs() << " Assigned register " << availableReg
578 << " to virtual register\n");
579
580 active.emplace_back(virtualRange.loc, availableReg, virtualRange.start,
581 virtualRange.end);
582
583 for (auto &dependentRange : virtualRange.dependentRegs)
584 active.emplace_back(dependentRange.loc,
585 dependentRegValues.at(dependentRange.value),
586 dependentRange.start, dependentRange.end);
587 }
588
589 LLVM_DEBUG({
590 for (auto &virtualRange : cache.virtualRanges) {
591 llvm::dbgs() << "Start: " << virtualRange.start
592 << ", End: " << virtualRange.end << ", Selected: "
593 << assignedRegisters.at(virtualRange.virtualReg) << "\n";
594 }
595 llvm::dbgs() << "\n";
596 });
597
598 materializeAllocation(cache, assignedRegisters);
599 return success();
600}
601
602//===----------------------------------------------------------------------===//
603// Class implementations
604//===----------------------------------------------------------------------===//
605
606void LinearScanRegisterAllocationPass::runOnOperation() {
607 LLVM_DEBUG(llvm::dbgs() << "=== Processing "
608 << OpWithFlags(getOperation(),
609 OpPrintingFlags().skipRegions())
610 << "\n\n");
611
612 if (getOperation()->getNumRegions() != 1 ||
613 getOperation()->getRegion(0).getBlocks().size() != 1) {
614 getOperation()->emitError("expected a single region with a single block");
615 return signalPassFailure();
616 }
617
618 LiveRangeCache cache;
619 for (auto segOp : getOperation()->getRegion(0).getOps<rtg::SegmentOp>()) {
620 // Perform register allocation for all text segments in isolation. There
621 // should usually only be one such segment.
622 if (segOp.getKind() == rtg::SegmentKind::Text) {
623 if (failed(cache.populate(segOp.getRegion())))
624 return signalPassFailure();
625
626 if (failed(allocateVirtualRegistersInCache(cache)))
627 return signalPassFailure();
628
629 cache.clear();
630 }
631 }
632}
633
634LogicalResult LiveRangeCache::populate(Region &region) {
635 for (auto [i, op] : llvm::enumerate(region.front())) {
636 // TODO: ideally check that the IR is already fully elaborated
637 opIndices[&op] = i;
638 indexToOp[i] = &op;
639 LLVM_DEBUG(llvm::dbgs() << " Op " << i << ": " << op << "\n");
640 }
641
642 LLVM_DEBUG(llvm::dbgs() << "Total operations in text segment: "
643 << opIndices.size() << "\n");
644
645 return computeLiveRanges(*this, region);
646}
647
648void LiveRangeCache::clear() {
649 opIndices.clear();
650 indexToOp.clear();
651 virtualRanges.clear();
652 reservedRanges.clear();
653}
assert(baseType &&"element must be base type")
static rtg::RegisterAttrInterface findAvailableRegister(const LiveRangeCache &cache, ArrayRef< FixedLiveRange > active, VirtualLiveRange &liveRange, DenseMap< Value, rtg::RegisterAttrInterface > &dependentRegValues)
Finds an available register for the given virtual register from its allowed register set.
static void createRange(LiveRangeCache &cache, rtg::VirtualRegisterOp op)
Creates a live range for a virtual register operation.
static RegisterAllocationResult collectTransitiveRegisterAllocationOperands(Value value, rtg::RegisterAttrInterface reg, SetVector< std::pair< Value, rtg::RegisterAttrInterface > > &operands)
static LogicalResult foldAndEnqueueResults(Operation *user, Value current, Attribute currentAttr, SmallVectorImpl< std::pair< Value, Attribute > > &worklist, DenseMap< Value, Attribute > &visited)
Folds an operation and adds its results to the worklist.
static void expireOldInterval(SmallVector< FixedLiveRange > &active, const VirtualLiveRange &liveRange)
static LogicalResult computeLiveRanges(LiveRangeCache &cache, Region &region)
Computes live ranges for all virtual registers and identifies reserved (fixed) registers.
static LogicalResult allocateVirtualRegistersInCache(LiveRangeCache &cache)
Performs the main register allocation using linear scan algorithm.
static void materializeAllocation(const LiveRangeCache &cache, const DenseMap< rtg::VirtualRegisterOp, rtg::RegisterAttrInterface > &assignedRegisters)
Replaces all virtual register operations with their allocated fixed registers and removes unused oper...
static RegisterAllocationResult isRegisterAvailable(const LiveRangeCache &cache, ArrayRef< FixedLiveRange > active, rtg::VirtualRegisterOp virtualReg, rtg::RegisterAttrInterface reg, DenseMap< Value, rtg::RegisterAttrInterface > &dependentRegValues)
Checks if a specific register is available for allocation to a virtual register.
static bool updateLiveRangeFromUsers(const LiveRangeCache &cache, Value value, LiveRange &liveRange)
Updates the live range based on users of a value.
OS & operator<<(OS &os, const InnerSymTarget &target)
Printing InnerSymTarget's.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition rtg.py:1
Utility that tracks operations that have potentially become unused and allows them to be cleaned up a...
void eraseNow(Operation *op)
Erase an operation immediately, and remove it from the set of ops to be removed later.