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