rucc_regalloc/assign.rs
1//! Which register each value lives in, and which values live on the stack instead.
2//!
3//! Design: `spec/10-backend.md` section 10.4.
4//!
5//! This is the `-O0` allocator's decision and nothing else. It is linear scan over the line
6//! [`crate::order`] lays the function out in: the values are taken in the order they are written,
7//! each is given a register that nothing else live at the same time is in, and when there is no
8//! such register one of the values in flight goes to the stack instead. There is no splitting and
9//! no coalescing, so a value gets one place for the whole of its range and keeps it. That produces
10//! mediocre code quickly, which is what `-O0` is for, and the allocator that produces good code
11//! slowly is a separate one, in M4.
12//!
13//! Which value is sent to the stack is the one whose range ends last, counting the value being
14//! placed among the candidates. A value wanted for a long time is the cheapest to spill per
15//! instruction it frees a register over, and it is the only heuristic here. What is picked is
16//! really a register and not a value, since two values that are never both wanted share one, and
17//! then every value in that register which is in this one's way goes.
18//!
19//! # Where the line is not the function
20//!
21//! The line is the order the blocks arrived in, and `crate::layout` puts them in a different one
22//! afterwards, so being between two blocks on the line says nothing about being between them in
23//! the code. A value live in one loop and live again in a later one is written down with
24//! everything in between inside the interval around it, and it is not live in any of it.
25//!
26//! Which is why what decides anything here is the area from `crate::live`, and the interval is
27//! only the sweep's bookkeeping: it says which values to compare and the areas say which of them
28//! actually collide. Three loops one after another in a function put a dozen values in flight at
29//! the same instant of the line and never at the same instant of the program, and asking the
30//! interval would spill the one this loop is walking for the sake of eleven values in the other
31//! two. tamnd/rucc#982.
32//!
33//! The same holds for a register an instruction insists on. A call destroys seven registers on
34//! x86-64, and a function whose blocks happen to arrive with a call written between the blocks of
35//! a loop would otherwise lose all seven for every value in that loop, for a call the loop never
36//! reaches, so that question is asked of the area and not of the interval either.
37//!
38//! Allowed is not the same as free, though, so the registers are offered in two passes. First the
39//! ones nothing insists on anywhere the range reaches, then the ones something insists on somewhere
40//! the value never goes. The second kind costs: the instruction that insists has to be handed the
41//! register in the end, and what hands it over is a move. A function that gives a value back has an
42//! operand fixed to `rax` at the end of it, and putting the busiest value in the function in `rax`
43//! because no path reaches the return with it live buys one register and pays a move at every
44//! return. Ordering the two passes is what keeps the register and drops the moves.
45//!
46//! The hint below is asked the first question rather than the second for the same reason. A value
47//! taking the register its own operand asked for saves a move, and taking one somebody else's
48//! operand asked for somewhere it never goes costs one, so a hint is worth following when the
49//! register is clear and not worth following when it is merely allowed.
50//!
51//! # What it does with a register an instruction insists on
52//!
53//! Two things. It stays out of that register for everybody else, and it tries that register first
54//! for the value the operand names. A division wants its dividend in `rax`, so `rax` is
55//! unavailable to every other value that is live where the division reads, and it is the first
56//! register offered to the dividend itself. When the dividend gets it there is no move on the way
57//! in, and when it does not the rewrite writes one and nothing else changes.
58//!
59//! That second half is the hint, and without it the register an instruction insists on is the one
60//! register the value in it can never have, since the value's own operand is what makes the
61//! register look busy. The effect is largest on returns, because a function that gives a value
62//! back has an operand fixed to `rax` at the end of it and most functions give a value back.
63//!
64//! What makes the hint safe is asking about the register at each of the instruction's two points
65//! rather than across the whole of it. An instruction reads at the first and writes at the second,
66//! so a register it insists on is one value's at the first, another value's at the second, and
67//! nobody else's at either. A division reads its dividend from `rax` and writes its quotient to
68//! `rax`, and those are different values that can both live there. A value passed to a call in
69//! `rdi` and wanted again afterwards cannot, because nothing writes `rdi` at the second point and
70//! a register the call does not write is a register the call is assumed to destroy.
71//!
72//! An operand that has to be in memory is the other way round. The value it names goes on the
73//! stack whatever else is true of it, because that is the only place the instruction could read it
74//! from.
75//!
76//! # What it does with a two address instruction
77//!
78//! An `add` on x86-64 writes one of the registers it reads, which the operand says as a reuse of
79//! another operand. The rewrite can always make that true by copying the source into the
80//! destination first, but only if the destination is a register the instruction does not otherwise
81//! read, so a value written by a reuse is treated here as live from where the instruction reads
82//! rather than from where it writes. Then the copy is always safe.
83//!
84//! The copy is also usually unnecessary, and the one place this looks past the interval it is
85//! placing is to see that: if the value being reused is read here for the last time and the value
86//! being written starts here, the second may have the first's register, and the instruction is
87//! already two address without anything being moved anywhere. That is the whole of the coalescing
88//! this allocator does, and it is worth the dozen lines, because otherwise every piece of
89//! arithmetic in the output carries a move in front of it.
90//!
91//! Both halves of that are needed. The second is the one a loop breaks: an instruction at the
92//! bottom of a loop can write a value the top of the loop reads on the next turn, and such a value
93//! is live on the way into the instruction that writes it as well as after. It is then wanted at
94//! the same time as the value it reuses, whatever is true of the reuse, and giving it the same
95//! register makes an addition read the answer to the last one instead of its own operand.
96//!
97//! # What it does not do
98//!
99//! It does not touch the function. What comes out is a table saying where each value went, and the
100//! pass that rewrites the operands and writes the moves reads it. Keeping the decision and the
101//! rewrite apart is what lets the decision be checked by looking at it, and it is the shape
102//! `spec/10-backend.md` section 10.4 asks for: an allocator is a function from a program to an
103//! assignment and the moves that make it true.
104
105use std::cmp::Reverse;
106
107use rucc_mir::{Constraint, Flags, Func, Inst, Operand, Reg, Role};
108use rucc_target::{PhysReg, RegClass};
109
110use crate::live::{Area, Live, Range};
111use crate::order::{Order, Point};
112
113/// Where a value lives.
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
115pub enum Place {
116 /// In a register, for the whole of its range.
117 Reg(PhysReg),
118 /// In a slot of the frame, which is what a value the allocator ran out of registers for gets,
119 /// and what a value an instruction can only read from memory gets.
120 Slot(u32),
121}
122
123/// What the allocator is allowed to use.
124///
125/// The order is the calling convention's, because which register to hand out first follows from
126/// which ones a call destroys, and `rucc-target` is where a convention says so. The scratch
127/// registers are held back out of the order and are what a spilled value is read into at each
128/// instruction that wants it, so a class needs as many of them as one of its instructions has
129/// register operands. Nothing here uses them, since a spilled value is only read once the rewrite
130/// is writing the instruction that reads it, but they are held back here because this is what
131/// decides what everything else may have.
132#[derive(Debug, Clone, Default)]
133pub struct Env {
134 classes: Vec<Class>,
135}
136
137/// What one class of registers offers.
138#[derive(Debug, Clone, Default)]
139struct Class {
140 order: Vec<PhysReg>,
141 scratch: Vec<PhysReg>,
142}
143
144impl Env {
145 /// An environment offering nothing, which is what a target that has said nothing offers.
146 #[must_use]
147 pub fn new() -> Self {
148 Self::default()
149 }
150
151 /// The same environment, with that class described.
152 #[must_use]
153 pub fn with(mut self, class: RegClass, order: &[PhysReg], scratch: &[PhysReg]) -> Self {
154 let index = usize::from(class.number());
155 if self.classes.len() <= index {
156 self.classes.resize(index + 1, Class::default());
157 }
158 self.classes[index] = Class { order: order.to_vec(), scratch: scratch.to_vec() };
159 self
160 }
161
162 /// The registers it may hand out in a class, in the order it prefers them.
163 #[must_use]
164 pub fn order(&self, class: RegClass) -> &[PhysReg] {
165 self.classes.get(usize::from(class.number())).map_or(&[], |class| &class.order)
166 }
167
168 /// The registers it may hand out, by class number, empty for a class it says nothing about.
169 pub(crate) fn offered(&self) -> impl Iterator<Item = &[PhysReg]> + '_ {
170 self.classes.iter().map(|class| class.order.as_slice())
171 }
172
173 /// The registers held back in a class for reading a spilled value into.
174 #[must_use]
175 pub fn scratch(&self, class: RegClass) -> &[PhysReg] {
176 self.classes.get(usize::from(class.number())).map_or(&[], |class| &class.scratch)
177 }
178}
179
180/// Where every value in a function went.
181#[derive(Debug, Clone)]
182pub struct Assignment {
183 places: Vec<Option<Place>>,
184 slots: Vec<RegClass>,
185 commuted: Vec<Inst>,
186 saves: Vec<Save>,
187 /// How many of the slots are where a value in a register waits out an instruction that
188 /// destroys it, which are not values that went to the stack.
189 waiting: usize,
190}
191
192/// A value kept in a register an instruction destroys, put away in front of that instruction and
193/// brought back behind it.
194///
195/// The register is the value's [`Place`] and the slot is somewhere it waits for the length of the
196/// one instruction. That is what a value read in every turn of a loop and wanted on the far side of
197/// a call the loop only makes now and then gets instead of the stack: a store and a load where the
198/// call is, rather than a load at every read.
199#[derive(Debug, Clone, Copy, PartialEq, Eq)]
200pub struct Save {
201 /// The value.
202 pub reg: Reg,
203 /// The instruction it is put away around.
204 pub inst: Inst,
205 /// The slot it waits in.
206 pub slot: u32,
207}
208
209impl Assignment {
210 /// Records that the sources of `inst` are to be swapped, for an answer written over the second.
211 pub(crate) fn commute(&mut self, inst: Inst) {
212 self.commuted.push(inst);
213 }
214
215 /// An assignment that says nothing yet about a function with that many values.
216 ///
217 /// This and [`Assignment::put`] and [`Assignment::take_slot`] are how an allocator says what
218 /// it decided. There will be a second one in M4 and it will not reach its answer this way, so
219 /// what an assignment is has to be separable from how this file arrives at one, and the
220 /// checker in [`crate::check`] reads an assignment without caring which allocator wrote it.
221 #[must_use]
222 pub fn empty(vregs: usize) -> Self {
223 Self {
224 places: vec![None; vregs],
225 slots: Vec::new(),
226 commuted: Vec::new(),
227 saves: Vec::new(),
228 waiting: 0,
229 }
230 }
231
232 /// The two address instructions whose answer went into the register of their second source.
233 ///
234 /// Each has to have its two sources swapped before anything reads the assignment against the
235 /// function, which [`crate::run`] does. After that the answer reuses what is then the first
236 /// source, as every two address instruction does. tamnd/rucc#1895.
237 #[must_use]
238 pub fn commuted(&self) -> &[Inst] {
239 &self.commuted
240 }
241
242 /// Records where a value went.
243 ///
244 /// # Panics
245 ///
246 /// Panics on a physical register, which is somewhere already, and on a virtual one the
247 /// function never handed out.
248 pub fn put(&mut self, reg: Reg, place: Place) {
249 self.places[index(reg)] = Some(place);
250 }
251
252 /// Takes a slot of the frame, of that class, and gives back which one it is.
253 ///
254 /// # Panics
255 ///
256 /// Panics past four billion slots, which is a frame no machine has room for.
257 pub fn take_slot(&mut self, class: RegClass) -> u32 {
258 let slot = u32::try_from(self.slots.len()).expect("too many spilled values");
259 self.slots.push(class);
260 slot
261 }
262
263 /// Where a value lives, or `None` for a virtual register this function never mentions and for
264 /// a physical one, which is already where it is.
265 #[must_use]
266 pub fn place(&self, reg: Reg) -> Option<Place> {
267 self.places.get(usize::try_from(reg.number()?).ok()?).copied().flatten()
268 }
269
270 /// The class of each slot of the frame, which is what says how wide it has to be.
271 #[must_use]
272 pub fn slots(&self) -> &[RegClass] {
273 &self.slots
274 }
275
276 /// Every value that went somewhere, and where it went.
277 ///
278 /// The assignment read the other way round, which is what a caller wants when the question is
279 /// about the places rather than about the values. The stack slot allocator asks it that way,
280 /// since what it needs is which value is in each slot and the assignment is stored by value.
281 pub fn placed(&self) -> impl Iterator<Item = (Reg, Place)> + '_ {
282 self.places.iter().enumerate().filter_map(|(number, place)| {
283 let number = u32::try_from(number).ok()?;
284 Some((Reg::virtual_reg(number), (*place)?))
285 })
286 }
287
288 /// How many values went to the stack.
289 #[must_use]
290 pub fn spilled(&self) -> usize {
291 self.slots.len() - self.waiting
292 }
293
294 /// Every instruction a value in a register is put away around, in the order they were found.
295 #[must_use]
296 pub fn saves(&self) -> &[Save] {
297 &self.saves
298 }
299
300 /// Records that a value in a register is put away around each of those instructions, all in
301 /// the one slot.
302 pub(crate) fn save(&mut self, reg: Reg, class: RegClass, insts: &[Inst]) {
303 if insts.is_empty() {
304 return;
305 }
306 let slot = self.take_slot(class);
307 self.waiting += 1;
308 self.saves.extend(insts.iter().map(|&inst| Save { reg, inst, slot }));
309 }
310
311 /// Puts a value on the stack, in a slot of its own.
312 pub(crate) fn spill(&mut self, reg: Reg, class: RegClass) {
313 let slot = self.take_slot(class);
314 self.put(reg, Place::Slot(slot));
315 }
316}
317
318/// One value waiting for a place.
319#[derive(Debug, Clone, Copy)]
320struct Interval<'a> {
321 reg: Reg,
322 class: RegClass,
323 /// The interval around the area, which is what the sweep below reads and what says which value
324 /// is wanted for longest when one of them has to go.
325 range: Range,
326 /// Everywhere the value is really live, which is what says whether two of them fit in one
327 /// register.
328 area: Area<'a>,
329}
330
331/// One value that has a register, for as long as it still wants it.
332#[derive(Debug, Clone, Copy)]
333struct Held<'a> {
334 reg: Reg,
335 class: RegClass,
336 range: Range,
337 area: Area<'a>,
338 at: PhysReg,
339 /// How many values were given a register before this one, which is the order the values in
340 /// flight are looked at in when one register has to be taken back.
341 since: usize,
342}
343
344/// The values that have a register, kept by the register each is in.
345///
346/// Nearly every question asked of them is about one register: whether it is free for an interval,
347/// or whether a value is still in it. They used to be one list walked from the start for every
348/// register tried, and on a function with a thousand values in flight that walk was most of the
349/// time the allocator took. Keeping them by register means asking about one reads only the values
350/// that are in it. Registers are numbered within their class, so two classes can share a list and
351/// the class is still checked.
352#[derive(Default)]
353struct Active<'a> {
354 by: Vec<Vec<Held<'a>>>,
355 /// For each register, a point no value in it ends before. Values are let go of at the start of
356 /// every interval, and most of those times nothing in most registers has ended, so a register
357 /// whose values all end at or after the point is not walked at all.
358 soonest: Vec<Point>,
359 /// How many values have been given a register so far.
360 count: usize,
361 /// The pieces of the values in each register, by class and then by register number, which is
362 /// what [`available`] asks about.
363 pieces: Vec<Vec<Pieces>>,
364 /// The list [`spill_one`] weighs the registers in, kept so a spill does not build a new one.
365 costs: Vec<(usize, PhysReg, usize, Point)>,
366}
367
368impl<'a> Active<'a> {
369 /// The values in one register, in the order they were given it.
370 fn at(&self, at: PhysReg) -> &[Held<'a>] {
371 self.by.get(usize::from(at.number())).map_or(&[], Vec::as_slice)
372 }
373
374 fn push(&mut self, reg: Reg, class: RegClass, range: Range, area: Area<'a>, at: PhysReg) {
375 let slot = usize::from(at.number());
376 if self.by.len() <= slot {
377 self.by.resize_with(slot + 1, Vec::new);
378 self.soonest.resize(slot + 1, Point::MAX);
379 }
380 self.by[slot].push(Held { reg, class, range, area, at, since: self.count });
381 self.soonest[slot] = self.soonest[slot].min(range.end);
382 self.count += 1;
383 let held = &self.by[slot];
384 let pieces = pieces_mut(&mut self.pieces, class, slot);
385 if pieces.kept {
386 pieces.drop_before(range.start);
387 for piece in area.pieces() {
388 pieces.insert(piece, reg);
389 }
390 } else if held.len() > FEW {
391 // Enough values to be worth a list, which starts with what is already there.
392 pieces.kept = true;
393 for held in held.iter().filter(|held| held.class == class) {
394 for piece in held.area.pieces() {
395 pieces.insert(piece, held.reg);
396 }
397 }
398 }
399 }
400
401 /// Whether a value of the class in `at` other than `except` is live anywhere the area is.
402 fn taken(&self, class: RegClass, at: PhysReg, area: Area<'_>, except: Option<Reg>) -> bool {
403 let held = self.at(at);
404 if held.len() > FEW {
405 if let Some(answer) = self.listed(class, at, area, except) {
406 return answer;
407 }
408 }
409 held.iter()
410 .any(|held| held.class == class && Some(held.reg) != except && held.area.overlaps(area))
411 }
412
413 /// What the list of `at`'s pieces says, or nothing when it is not kept or not in order. Out of
414 /// line so that the walk above, which is all most registers ever need, stays small enough to
415 /// be put inline where it is asked.
416 #[inline(never)]
417 fn listed(
418 &self,
419 class: RegClass,
420 at: PhysReg,
421 area: Area<'_>,
422 except: Option<Reg>,
423 ) -> Option<bool> {
424 let by = self.pieces.get(usize::from(class.number()))?;
425 let pieces = by.get(usize::from(at.number()))?;
426 (pieces.kept && !pieces.broken).then(|| pieces.touch(area, except))
427 }
428
429 /// The values of the class in register number `slot` that touch the area, from its list of
430 /// pieces, or nothing when the list is not kept or not in order.
431 #[inline(never)]
432 fn owners(&self, class: RegClass, slot: usize, area: Area<'_>) -> Option<Vec<Reg>> {
433 let pieces = self.pieces.get(usize::from(class.number()))?.get(slot)?;
434 if pieces.kept { pieces.owners(area) } else { None }
435 }
436
437 /// Takes the values of the class in `at` whose areas `goes` says to, and hands each to `gone`.
438 fn evict(
439 &mut self,
440 class: RegClass,
441 at: PhysReg,
442 goes: impl Fn(&Held<'a>) -> bool,
443 mut gone: impl FnMut(&Held<'a>),
444 ) {
445 let slot = usize::from(at.number());
446 let mut taken = Vec::new();
447 self.by[slot].retain(|held| {
448 let out = held.class == class && goes(held);
449 if out {
450 gone(held);
451 taken.push((held.reg, held.area));
452 }
453 !out
454 });
455 let pieces = pieces_mut(&mut self.pieces, class, slot);
456 if pieces.kept {
457 for (reg, area) in taken {
458 for piece in area.pieces() {
459 pieces.remove(piece, reg);
460 }
461 }
462 }
463 }
464
465 /// Lets go of every value whose interval ends before a point.
466 ///
467 /// Taking a value out of a register anywhere else leaves that register's soonest end where it
468 /// was, which is still a point nothing in it ends before, so only this and [`Active::push`]
469 /// have to keep it.
470 fn expire(&mut self, point: Point) {
471 for (held, soonest) in self.by.iter_mut().zip(&mut self.soonest) {
472 if *soonest >= point {
473 continue;
474 }
475 held.retain(|held| held.range.end >= point);
476 *soonest = held.iter().map(|held| held.range.end).min().unwrap_or(Point::MAX);
477 }
478 }
479}
480
481/// How many values a register can hold before its pieces are kept in a list. A register with no
482/// more than this in it, which is most of them without optimization, is quicker to ask about by
483/// walking its values, and keeping a list for every one of those cost more than it saved.
484pub(crate) const FEW: usize = 4;
485
486/// The list for one class and register number, made when it is first asked for.
487fn pieces_mut(pieces: &mut Vec<Vec<Pieces>>, class: RegClass, slot: usize) -> &mut Pieces {
488 let class = usize::from(class.number());
489 if pieces.len() <= class {
490 pieces.resize_with(class + 1, Vec::new);
491 }
492 let by = &mut pieces[class];
493 if by.len() <= slot {
494 by.resize_with(slot + 1, Pieces::default);
495 }
496 &mut by[slot]
497}
498
499/// The pieces of every value of one class in one register, sorted by where they start.
500///
501/// Asking whether a register is free for a value used to compare the value with every other value
502/// in the register, a walk over both lists of pieces for each one, and with a few dozen values
503/// in each register that was a large part of an optimized build of a large file. Two values in
504/// one register are never live at once, bar the one point a value written over the one it reuses
505/// shares with it, so in start order the pieces end in order too. Then the only piece that can
506/// touch one of the value's is the last one that starts before that piece ends, and asking is a
507/// search for each piece of the value rather than a walk over everything in the register.
508///
509/// Whether the ends really are in order is checked as each piece goes in, and a register where
510/// they are not is answered by the walk over its values instead, so the answer is the same either
511/// way.
512#[derive(Default)]
513pub(crate) struct Pieces {
514 /// Start, end and whose, sorted by start and then by end.
515 list: Vec<(Point, Point, Reg)>,
516 /// Whether a piece went in that ends before one in front of it.
517 broken: bool,
518 /// Whether the list is kept at all, which it is from the first time the register holds more
519 /// than [`FEW`] values.
520 pub(crate) kept: bool,
521}
522
523impl Pieces {
524 #[inline(never)]
525 pub(crate) fn insert(&mut self, piece: Range, reg: Reg) {
526 let key = (piece.start, piece.end);
527 let at = self.list.partition_point(|&(start, end, _)| (start, end) <= key);
528 let after = at == 0 || self.list[at - 1].1 <= piece.end;
529 let before = self.list.get(at).is_none_or(|next| piece.end <= next.1);
530 if !(after && before) {
531 self.broken = true;
532 }
533 self.list.insert(at, (piece.start, piece.end, reg));
534 }
535
536 pub(crate) fn remove(&mut self, piece: Range, reg: Reg) {
537 let from = self.list.partition_point(|&(start, _, _)| start < piece.start);
538 let found = self.list[from..]
539 .iter()
540 .take_while(|&&(start, _, _)| start == piece.start)
541 .position(|&(_, end, owner)| end == piece.end && owner == reg);
542 if let Some(offset) = found {
543 self.list.remove(from + offset);
544 }
545 }
546
547 /// Lets go of the pieces that end before a point, which no value starting there can touch.
548 /// They are the ones at the front while the ends are in order.
549 fn drop_before(&mut self, point: Point) {
550 if !self.broken {
551 let gone = self.list.partition_point(|&(_, end, _)| end < point);
552 self.list.drain(..gone);
553 }
554 }
555
556 /// Whether a piece of a value other than `except` touches the area.
557 fn touch(&self, area: Area<'_>, except: Option<Reg>) -> bool {
558 area.pieces().any(|piece| {
559 let below = self.list.partition_point(|&(start, _, _)| start <= piece.end);
560 self.list[..below]
561 .iter()
562 .rev()
563 .find(|&&(_, _, owner)| Some(owner) != except)
564 .is_some_and(|&(_, end, _)| end >= piece.start)
565 })
566 }
567
568 /// Every value with a piece that touches the area, each once and in order, or `None` for a
569 /// list whose ends are out of order. With the ends in order the pieces that touch one of the
570 /// area's are the last few that start before it ends, back to the first that ends before it
571 /// starts.
572 pub(crate) fn owners(&self, area: Area<'_>) -> Option<Vec<Reg>> {
573 if self.broken {
574 return None;
575 }
576 let mut owners = Vec::new();
577 for piece in area.pieces() {
578 let below = self.list.partition_point(|&(start, _, _)| start <= piece.end);
579 let touching =
580 self.list[..below].iter().rev().take_while(|&&(_, end, _)| end >= piece.start);
581 owners.extend(touching.map(|&(_, _, owner)| owner));
582 }
583 owners.sort_unstable();
584 owners.dedup();
585 Some(owners)
586 }
587}
588
589/// A register an instruction insists on, and where it insists on it.
590#[derive(Debug, Clone, Copy)]
591struct Blocked {
592 class: RegClass,
593 at: PhysReg,
594 /// One of the instruction's two points. Every register an instruction insists on has an entry
595 /// at each of them, because a register held at one of the two is a register nothing else may
596 /// be in across the instruction.
597 point: Point,
598 /// The one value that may be in it there, which is the value of an operand the instruction
599 /// reads at that point or writes at it. `None` means nothing may: an operand naming a physical
600 /// register outright claims it against everything, and a point no operand covers is a point
601 /// the instruction has the register to itself at.
602 by: Option<Reg>,
603 /// The byte the instruction writes the register from, when it leaves the bottom of it alone,
604 /// which is what a call does to a register AArch64 keeps the low half of. A value that fits
605 /// below it is not in the way. See [`Constraint::Above`].
606 above: Option<u8>,
607}
608
609impl Blocked {
610 /// Whether this is in the way of a value of that width, which it is unless it writes only
611 /// above everything the value takes.
612 fn reaches(&self, width: Option<u8>) -> bool {
613 match (self.above, width) {
614 (Some(above), Some(width)) => width > above,
615 _ => true,
616 }
617 }
618}
619
620/// A value written into the register another operand of the same instruction was read from.
621#[derive(Debug, Clone, Copy)]
622pub(crate) struct Reuse {
623 /// The value being read, which is the one whose register would do.
624 pub(crate) source: Reg,
625 /// The other value the instruction reads, when the instruction reads the two either way round
626 /// and so could write its answer over this one instead.
627 pub(crate) second: Option<Reg>,
628 /// Where the instruction reads it.
629 pub(crate) at: Point,
630 /// The instruction, which is swapped round if the answer takes the second value's register.
631 pub(crate) inst: Inst,
632}
633
634/// Decides where every value in a function lives.
635///
636/// # Panics
637///
638/// Panics if a class has no registers to hand out and something in the function is in that class,
639/// since that is a target description that does not describe the target the function is for.
640#[must_use]
641pub fn assign(func: &Func, order: &Order, live: &Live, env: &Env) -> Assignment {
642 let blocked = blocked(func, order);
643 let forced = forced(func);
644 let reuses = reuses(func, order);
645 let hints = hints(func);
646 let passed = passed(func);
647
648 let mut intervals = Vec::with_capacity(func.vregs());
649 for (number, reuse) in reuses.iter().enumerate() {
650 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
651 let (Some(mut area), Some(class)) = (live.area(reg), func.class_of(reg)) else {
652 continue;
653 };
654 if let Some(reuse) = reuse {
655 area = area.with(reuse.at);
656 }
657 intervals.push(Interval { reg, class, range: area.hull(), area });
658 }
659 intervals.sort_by_key(|interval| (interval.range.start, interval.reg));
660
661 let mut assignment = Assignment::empty(func.vregs());
662 let mut active = Active::default();
663 for interval in intervals {
664 active.expire(interval.range.start);
665 if forced.contains(&interval.reg) {
666 assignment.spill(interval.reg, interval.class);
667 continue;
668 }
669 // A class with no order is one the target says nothing allocates from, which on x86-64 is
670 // the x87 stack. A value of such a class is a mistake at the point it was made rather than
671 // a value with nowhere to go: what the target means is that the value lives in memory and
672 // that whatever operates on it takes an address. See `ClassInfo::allocatable`.
673 assert!(
674 !env.order(interval.class).is_empty(),
675 "a value in class {}, which the target hands out no registers from",
676 interval.class.number()
677 );
678 let reuse = reuses[index(interval.reg)];
679 let coalesced = |source| coalesce(&assignment, &active, &blocked, live, interval, source);
680 let first = reuse.and_then(|reuse| coalesced(reuse.source));
681 let second = reuse.and_then(|reuse| reuse.second).and_then(coalesced);
682 // An instruction that reads its sources either way round can write over the second one
683 // instead, which is what it needs when the first is read again later and the second is
684 // not. When both would do, the first is kept unless only the second is where something
685 // wants the answer, which saves the move in front of that reader.
686 //
687 // A block the answer is passed to wants it where that block's parameter already is. That
688 // has to count as much as an instruction asking for a register. A sum a loop carries is
689 // passed back to the parameter it was read from, and taking the register of the other
690 // source because the sum is also printed at the end moves the copy onto the back edge,
691 // where it runs every turn instead of once.
692 let hinted_at = |at: Option<PhysReg>| {
693 at.is_some_and(|at| {
694 hints[index(interval.reg)].contains(&at)
695 || passed[index(interval.reg)]
696 .iter()
697 .any(|¶m| assignment.place(param) == Some(Place::Reg(at)))
698 })
699 };
700 let commute =
701 second.is_some() && (first.is_none() || hinted_at(second) && !hinted_at(first));
702 let two_address = if commute { second } else { first };
703 if let (true, Some(reuse)) = (commute, reuse) {
704 assignment.commuted.push(reuse.inst);
705 }
706 // The reuse comes first, because a two address instruction that has to copy its left
707 // operand in pays for the copy whatever the hint says, and taking the hint here would buy
708 // one move at the cost of another.
709 let hinted = hints[index(interval.reg)].iter().copied().find(|&at| {
710 env.order(interval.class).contains(&at)
711 && available(&active, &blocked, interval, at, None, Want::Clear)
712 });
713 // A register nobody else wants anywhere near this value first, and one somebody wants
714 // somewhere the value never goes only when there is no other. Both are correct and the
715 // second is the worse buy, since the instruction that wants it has to be handed it and
716 // whatever this value is doing there has to move out of the way first.
717 let scan = |want| {
718 env.order(interval.class)
719 .iter()
720 .copied()
721 .find(|&at| available(&active, &blocked, interval, at, None, want))
722 };
723 let chosen =
724 two_address.or(hinted).or_else(|| scan(Want::Clear)).or_else(|| scan(Want::Allowed));
725 match chosen {
726 Some(at) => {
727 assignment.places[index(interval.reg)] = Some(Place::Reg(at));
728 active.push(interval.reg, interval.class, interval.range, interval.area, at);
729 }
730 None => spill_one(&mut assignment, &mut active, &blocked, interval),
731 }
732 }
733 assignment
734}
735
736/// How much a register suits an interval.
737#[derive(Debug, Clone, Copy, PartialEq, Eq)]
738pub(crate) enum Want {
739 /// No other value is handed it anywhere the range reaches, and nothing writes it where the
740 /// value is live, so taking it costs nobody anything.
741 ///
742 /// A register an instruction only destroys, which is what a call does to seven of them, is
743 /// clear for a value that is dead there. There is no value of the instruction's own to move
744 /// in, so a loop counter that is passed to a call on the way out may stay in a register the
745 /// call destroys. Counting the clobber over the whole range sent such a value to a callee
746 /// saved register, which is a push and a pop for nothing. tamnd/rucc#2202.
747 Clear,
748 /// Something insists on it somewhere the range reaches and nowhere the value is live, so taking
749 /// it is allowed and may still cost: the instruction that insists wants the register for a
750 /// value of its own, and that value now has to be moved into it.
751 Allowed,
752}
753
754/// Every register every instruction in the function insists on, arranged to be asked about.
755///
756/// Built once and never changed afterwards, and there is only one question ever asked of it: of the
757/// constraints naming one register of one class, is there one at a point some interval covers. So
758/// the entries are ordered by the register they name and then by the point, and the question is a
759/// binary search for the start of the interval followed by a walk that stops at its end.
760///
761/// It used to be a flat list walked from one end for every candidate register of every interval,
762/// which is quadratic in the size of a function and is most of the compile on a large one. See
763/// tamnd/rucc#1003 for the profile that found it.
764///
765/// The search is over the points alone and only among the one register's entries. A search over
766/// the whole list compares three fields of an entry several times its size at every step, and on a
767/// large function that is most of what asking costs, since every candidate register of every
768/// interval asks.
769pub(crate) struct Blocks {
770 /// The constraints, sorted by class, then by register, then by point.
771 all: Vec<Blocked>,
772 /// The point of each constraint, in the same order, which is what the search reads.
773 points: Vec<Point>,
774 /// Where each register's constraints start and end in the list, by class times `stride` plus
775 /// the register's number.
776 spans: Vec<(usize, usize)>,
777 /// One more than the highest register number anything insists on.
778 stride: usize,
779 /// How many bytes of its register each virtual register's value takes, by number, which is
780 /// what [`Blocked::reaches`] asks.
781 widths: Vec<Option<u8>>,
782}
783
784impl Blocks {
785 /// Whether an instruction insists on `at` where a value over `area` would be in its way: at any
786 /// point the value's range reaches when the register is wanted clear and the instruction wants
787 /// it for a value of its own, and otherwise only at a point the value is live at. The value's
788 /// own operands never count.
789 pub(crate) fn insists(
790 &self,
791 reg: Reg,
792 class: RegClass,
793 area: Area<'_>,
794 range: Range,
795 at: PhysReg,
796 want: Want,
797 ) -> bool {
798 self.over(class, at, range).any(|one| {
799 one.by != Some(reg)
800 && one.reaches(self.width(reg))
801 && ((want == Want::Clear && one.by.is_some()) || area.covers(one.point))
802 })
803 }
804
805 /// The points where an instruction destroys `at` while a value over `area` is in it, when those
806 /// are all that is in the way, or `None` when anything else is.
807 ///
808 /// What counts is a write at the point an instruction writes, by nobody's value, of the whole
809 /// of the register, with the value live on the way into the instruction as well as out of it,
810 /// at a point `saveable` says the value can be put away around. Everything else in the way is
811 /// in the way for good: a register taken where the instruction reads is taken while the value
812 /// would still have to be in it, and one handed to another value is that value's.
813 pub(crate) fn destroyed(
814 &self,
815 reg: Reg,
816 class: RegClass,
817 area: Area<'_>,
818 range: Range,
819 at: PhysReg,
820 saveable: impl Fn(Point) -> bool,
821 ) -> Option<Vec<Point>> {
822 let mut points = Vec::new();
823 for one in self.over(class, at, range) {
824 if one.by == Some(reg) || !one.reaches(self.width(reg)) || !area.covers(one.point) {
825 continue;
826 }
827 let through = one.point > 0 && area.covers(one.point - 1);
828 if one.by.is_some() || one.above.is_some() || !through || !saveable(one.point) {
829 return None;
830 }
831 if points.last() != Some(&one.point) {
832 points.push(one.point);
833 }
834 }
835 Some(points)
836 }
837
838 /// How many bytes of its register a value takes, or `None` for all of it.
839 fn width(&self, reg: Reg) -> Option<u8> {
840 let number = usize::try_from(reg.number()?).ok()?;
841 self.widths.get(number).copied().flatten()
842 }
843
844 /// Every register an instruction takes for itself where no value may be in it, with the class
845 /// and the point, sorted by class, then by register, then by point.
846 ///
847 /// Not one it writes only the top of, since a value narrow enough may still be in that.
848 pub(crate) fn taken(&self) -> impl Iterator<Item = (RegClass, PhysReg, Point)> + '_ {
849 self.all
850 .iter()
851 .filter(|one| one.by.is_none() && one.above.is_none())
852 .map(|one| (one.class, one.at, one.point))
853 }
854
855 /// The constraints on one register of one class at the points an interval covers.
856 ///
857 /// Both ends of the walk come from the ordering rather than from a test, so what comes back is
858 /// exactly what the old `covers` call used to keep and in the same order.
859 #[inline]
860 fn over(
861 &self,
862 class: RegClass,
863 at: PhysReg,
864 range: Range,
865 ) -> impl Iterator<Item = &Blocked> + '_ {
866 let (low, high) = if usize::from(at.number()) < self.stride {
867 let key = usize::from(class.number()) * self.stride + usize::from(at.number());
868 self.spans.get(key).copied().unwrap_or((0, 0))
869 } else {
870 (0, 0)
871 };
872 let first = low + self.points[low..high].partition_point(|&point| point < range.start);
873 self.all[first..high].iter().take_while(move |one| one.point <= range.end)
874 }
875}
876
877/// Whether a register is one this interval could have.
878///
879/// The exception is the value a reuse is coalescing with, which holds the register right up to the
880/// point the new value takes it over and is the one thing that may overlap.
881///
882/// The sweep only keeps a value in `active` while the interval around it reaches this one, so the
883/// areas still have to be compared: two values whose intervals cross can have holes that let them
884/// share a register anyway, which on a function with several loops in it is most of them.
885fn available(
886 active: &Active<'_>,
887 blocked: &Blocks,
888 interval: Interval<'_>,
889 at: PhysReg,
890 except: Option<Reg>,
891 want: Want,
892) -> bool {
893 let taken = active.taken(interval.class, at, interval.area, except);
894 let width = blocked.width(interval.reg);
895 let insisted = blocked.over(interval.class, at, interval.range).any(|one| {
896 one.by != Some(interval.reg)
897 && one.reaches(width)
898 && ((want == Want::Clear && one.by.is_some()) || interval.area.covers(one.point))
899 });
900 !taken && !insisted
901}
902
903/// The register the value being reused is in, when the value being written is never live at the
904/// same time as it and the register is otherwise free.
905fn coalesce(
906 assignment: &Assignment,
907 active: &Active<'_>,
908 blocked: &Blocks,
909 live: &Live,
910 interval: Interval<'_>,
911 source: Reg,
912) -> Option<PhysReg> {
913 let Some(Place::Reg(at)) = assignment.place(source) else { return None };
914 active.at(at).iter().find(|held| held.reg == source)?;
915 // The two have to be apart everywhere, asked of the areas liveness worked out and without the
916 // point the reuse adds, since that point is the one they are allowed to share.
917 //
918 // That covers both ways it can go wrong. A value read again later needs its register after
919 // this instruction would have overwritten it. And a value being written that is live where the
920 // instruction reads already is what a loop carrying its own result round looks like: the
921 // instruction writes it at the bottom and the top of the loop reads what the last turn wrote.
922 // Either way the two are wanted at once, and no register holds both.
923 //
924 // It used to be asked of the end of the interval around the value being read, and that is not
925 // the same question. A block laid out after this instruction where the value is still live,
926 // such as the default arm of a `switch` that joins back in above it, stretches the interval
927 // past this point when nothing past it reads the value at all. The sum a loop carries round
928 // then went into a new register and was copied back at the bottom of every turn.
929 // tamnd/rucc#1965.
930 let free = available(active, blocked, interval, at, Some(source), Want::Allowed);
931 (apart(live, source, interval.reg) && free).then_some(at)
932}
933
934/// Whether two values are never live at the same time, going by what liveness worked out.
935pub(crate) fn apart(live: &Live, first: Reg, second: Reg) -> bool {
936 match (live.area(first), live.area(second)) {
937 (Some(first), Some(second)) => !first.overlaps(second),
938 _ => false,
939 }
940}
941
942/// Sends values to the stack to free a register: the ones wanted for longest, since a register
943/// held that long pays for itself over the most instructions.
944///
945/// What is chosen is a register rather than a value, because two values whose areas miss each
946/// other share one and taking it means every value in it this one is really on top of has to go.
947/// A register holding two of those costs twice as much to take as one holding a single value, so
948/// the cheap ones are looked at first and the reach only settles ties.
949fn spill_one<'a>(
950 assignment: &mut Assignment,
951 active: &mut Active<'a>,
952 blocked: &Blocks,
953 interval: Interval<'a>,
954) {
955 // What each register would cost: how many values would go, and the furthest any of them
956 // reaches. The list is one entry per register of the class, so walking it for each value in
957 // flight is the same shape as everything else here. The first number is when the earliest of
958 // them was given the register, and sorting by it puts the registers in the order the values
959 // were given them, which is what settles a tie.
960 let mut costs = std::mem::take(&mut active.costs);
961 costs.clear();
962 for (slot, values) in active.by.iter().enumerate() {
963 // A register with a list of its pieces says which values touch the area, which is quicker
964 // than asking each value in it when it holds many.
965 let owners = if values.len() > FEW {
966 active.owners(interval.class, slot, interval.area)
967 } else {
968 None
969 };
970 for held in values {
971 if held.class != interval.class {
972 continue;
973 }
974 let touches = match &owners {
975 Some(owners) => owners.binary_search(&held.reg).is_ok(),
976 None => held.area.overlaps(interval.area),
977 };
978 if !touches {
979 continue;
980 }
981 match costs.iter_mut().find(|(_, at, _, _)| *at == held.at) {
982 Some((first, _, count, reach)) => {
983 *first = (*first).min(held.since);
984 *count += 1;
985 *reach = (*reach).max(held.range.end);
986 }
987 None => costs.push((held.since, held.at, 1, held.range.end)),
988 }
989 }
990 }
991 costs.sort_unstable_by_key(|&(first, _, _, _)| first);
992 // A register the instructions in the way insist on for themselves is no use, because taking it
993 // over would put this value in a register it may not have.
994 let none = Active::default();
995 let chosen = costs
996 .iter()
997 .filter(|&&(_, at, _, reach)| {
998 reach > interval.range.end
999 && available(&none, blocked, interval, at, None, Want::Allowed)
1000 })
1001 .min_by_key(|&&(_, _, count, reach)| (count, Reverse(reach)))
1002 .map(|&(_, at, _, _)| at);
1003 active.costs = costs;
1004 match chosen {
1005 Some(at) => {
1006 active.evict(
1007 interval.class,
1008 at,
1009 |held| held.area.overlaps(interval.area),
1010 |held| assignment.spill(held.reg, held.class),
1011 );
1012 assignment.places[index(interval.reg)] = Some(Place::Reg(at));
1013 active.push(interval.reg, interval.class, interval.range, interval.area, at);
1014 }
1015 None => assignment.spill(interval.reg, interval.class),
1016 }
1017}
1018
1019/// The registers the instructions insist on, and where.
1020///
1021/// A physical register an operand names outright counts the same way. Nothing before allocation
1022/// writes one except an instruction that has to, and it has to for the length of that one
1023/// instruction, which is the same statement a fixed constraint makes.
1024pub(crate) fn blocked(func: &Func, order: &Order) -> Blocks {
1025 let mut blocked = Vec::new();
1026 let mut claimed: Vec<(RegClass, PhysReg)> = Vec::new();
1027 for block in func.blocks() {
1028 for inst in func.insts(block) {
1029 let operands = &func[func[inst].operands];
1030 claimed.clear();
1031 for operand in operands {
1032 if let Some(at) = insisted(operand) {
1033 let key = (operand.class, at);
1034 if !claimed.contains(&key) {
1035 claimed.push(key);
1036 }
1037 }
1038 }
1039 for &(class, at) in &claimed {
1040 // Both points, whether or not an operand is at them. A register an instruction
1041 // reads and does not write is still gone by the time the instruction is done as far
1042 // as anything here knows, which is what stops the value a call is passed in `rdi`
1043 // from staying in `rdi` over the call.
1044 for (point, role) in [(order.early(inst), Role::Use), (order.late(inst), Role::Def)]
1045 {
1046 let mut named = false;
1047 for operand in operands {
1048 let mine = insisted(operand) == Some(at) && operand.class == class;
1049 if !mine || !(operand.role == role || operand.role == Role::EarlyDef) {
1050 continue;
1051 }
1052 named = true;
1053 let by = operand.reg.is_virtual().then_some(operand.reg);
1054 let above = match operand.constraint {
1055 Constraint::Above(above) => Some(above),
1056 _ => None,
1057 };
1058 blocked.push(Blocked { class, at, point, by, above });
1059 }
1060 // A register no operand names where the operands are read is one the
1061 // instruction writes and does not read, which is what a clobber is, and the
1062 // seven registers a call destroys are the whole of why that case is worth
1063 // separating. Such a register is free right up to the point it is written, so a
1064 // value whose last read is this instruction may sit in one: it is read before
1065 // the instruction writes anything, the way any other operand is. Blocking it
1066 // where the operands are read as well would take every caller saved register
1067 // away from the value a call is passed, which is a value that dies at the call
1068 // and pays for a callee saved register it holds for two instructions. Anything
1069 // living past the instruction is still refused, by the block below.
1070 //
1071 // This is where a target's early definitions are paid for. An instruction that
1072 // fills a register before it has finished reading has to say so, because that
1073 // is the one thing a plain definition here no longer covers: a division on
1074 // x86-64 is a sign extension and then the division itself, so `rdx` is gone
1075 // before the divisor is read, and a divisor that went there would be read as
1076 // the dividend's own sign bits. `rucc_target::x86_64` writes both of them down
1077 // as early definitions for exactly that reason.
1078 if !named && role == Role::Def {
1079 blocked.push(Blocked { class, at, point, by: None, above: None });
1080 }
1081 }
1082 }
1083 }
1084 }
1085 // Program order already has the points ascending, but the registers one instruction claims are
1086 // walked outside the two points rather than inside them, so the list arrives in order by
1087 // instruction and not by register. A sort by the key the lookup searches on is what makes it
1088 // searchable, and it is stable so two constraints on one register at one point keep the order
1089 // the instruction wrote them in.
1090 blocked.sort_by_key(|one: &Blocked| (one.class, one.at, one.point));
1091 let widths = (0..func.vregs())
1092 .map(|number| func.width(Reg::virtual_reg(u32::try_from(number).ok()?)))
1093 .collect();
1094 let points = blocked.iter().map(|one| one.point).collect();
1095 let stride = blocked.iter().map(|one| usize::from(one.at.number()) + 1).max().unwrap_or(0);
1096 let classes = blocked.last().map_or(0, |one| usize::from(one.class.number()) + 1);
1097 let mut spans = vec![(0, 0); classes * stride];
1098 for (index, one) in blocked.iter().enumerate() {
1099 let key = usize::from(one.class.number()) * stride + usize::from(one.at.number());
1100 let span = &mut spans[key];
1101 if span.1 == 0 {
1102 span.0 = index;
1103 }
1104 span.1 = index + 1;
1105 }
1106 Blocks { all: blocked, points, spans, stride, widths }
1107}
1108
1109/// The register an operand has to be in, which is the one a constraint asks for or the one the
1110/// operand names outright.
1111fn insisted(operand: &Operand) -> Option<PhysReg> {
1112 match operand.constraint {
1113 Constraint::Fixed(at) => Some(at),
1114 _ => operand.reg.phys(),
1115 }
1116}
1117
1118/// The registers each value would rather be in, which are the ones the operands naming it insist on.
1119///
1120/// In the order the function writes them down, so the definition comes first where there is one,
1121/// since a value written into a fixed register and then moved somewhere else pays for the move at
1122/// the top of its life rather than at the bottom. The ones after it are worth keeping for the same
1123/// reason the first one is, and the value a call is passed is where that shows: its definition may
1124/// insist on the register a parameter arrived in, which the call it is handed to has usually taken
1125/// back for an argument of its own by then, and behind that is the register the convention passes
1126/// it in, which is free and is exactly where the value wants to end up.
1127pub(crate) fn hints(func: &Func) -> Vec<Vec<PhysReg>> {
1128 let mut hints = vec![Vec::new(); func.vregs()];
1129 for block in func.blocks() {
1130 for inst in func.insts(block) {
1131 for operand in &func[func[inst].operands] {
1132 let Constraint::Fixed(at) = operand.constraint else { continue };
1133 let number = operand.reg.number().and_then(|number| usize::try_from(number).ok());
1134 let Some(number) = number else { continue };
1135 let wanted: &mut Vec<PhysReg> = &mut hints[number];
1136 if func.class_of(operand.reg) == Some(operand.class) && !wanted.contains(&at) {
1137 wanted.push(at);
1138 }
1139 }
1140 }
1141 }
1142 hints
1143}
1144
1145/// The block parameters each value is passed to, by the virtual register passed.
1146pub(crate) fn passed(func: &Func) -> Vec<Vec<Reg>> {
1147 let mut passed = vec![Vec::new(); func.vregs()];
1148 for block in func.blocks() {
1149 for call in &func[block].succs {
1150 for (&arg, param) in call.args.iter().zip(&func[call.block].params) {
1151 let number = arg.number().and_then(|number| usize::try_from(number).ok());
1152 let Some(number) = number else { continue };
1153 let to: &mut Vec<Reg> = &mut passed[number];
1154 if !to.contains(¶m.reg) {
1155 to.push(param.reg);
1156 }
1157 }
1158 }
1159 }
1160 passed
1161}
1162
1163/// The values that have to be on the stack whatever else is true of them.
1164pub(crate) fn forced(func: &Func) -> Vec<Reg> {
1165 let mut forced = Vec::new();
1166 for block in func.blocks() {
1167 for inst in func.insts(block) {
1168 for operand in &func[func[inst].operands] {
1169 if operand.constraint == Constraint::Stack
1170 && operand.reg.is_virtual()
1171 && !forced.contains(&operand.reg)
1172 {
1173 forced.push(operand.reg);
1174 }
1175 }
1176 }
1177 }
1178 forced
1179}
1180
1181/// The value each two address instruction reuses, by the virtual register it writes.
1182pub(crate) fn reuses(func: &Func, order: &Order) -> Vec<Option<Reuse>> {
1183 let mut reuses = vec![None; func.vregs()];
1184 for block in func.blocks() {
1185 for inst in func.insts(block) {
1186 let operands = &func[func[inst].operands];
1187 for operand in operands {
1188 let Constraint::Reuse(other) = operand.constraint else { continue };
1189 let number = operand.reg.number().and_then(|number| usize::try_from(number).ok());
1190 let Some(number) = number else { continue };
1191 let source = operands[usize::from(other)].reg;
1192 let second = if func[inst].flags.contains(Flags::COMMUTES) {
1193 swappable(operands, usize::from(other))
1194 } else {
1195 None
1196 };
1197 reuses[number] = Some(Reuse { source, second, at: order.early(inst), inst });
1198 }
1199 }
1200 }
1201 reuses
1202}
1203
1204/// The second source of an instruction that reads its two sources either way round, when the
1205/// answer could go over it instead of over the first.
1206///
1207/// Only the shape of a two address instruction with two sources, the answer and then the two, with
1208/// the answer reusing the first. The second has to be a value of the same class that asks for
1209/// nothing more than a register, since after the swap it is the one the answer reuses.
1210fn swappable(operands: &[Operand], other: usize) -> Option<Reg> {
1211 let [answer, first, second] = operands else { return None };
1212 let same = second.class == first.class && second.class == answer.class;
1213 let plain = second.role == Role::Use && second.constraint == Constraint::Reg;
1214 (other == 1 && same && plain && second.reg.is_virtual() && second.reg != first.reg)
1215 .then_some(second.reg)
1216}
1217
1218/// A virtual register's number as a table index.
1219fn index(reg: Reg) -> usize {
1220 usize::try_from(reg.number().expect("a virtual register")).expect("a register number")
1221}
1222
1223#[cfg(test)]
1224mod tests {
1225 use rucc_base::Interner;
1226 use rucc_mir::{BlockCall, Opcode, Operand, Param};
1227 use rucc_target::x86_64::{GPR, R13, R14, R15, RAX, RCX, RDX, REGS, RSI, SYSV};
1228
1229 use super::*;
1230
1231 /// The x86-64 environment, with the last three of the allocation order held back as scratch.
1232 fn env() -> Env {
1233 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
1234 Env::new().with(GPR, order, scratch)
1235 }
1236
1237 /// An environment with that many general purpose registers, for putting a function under
1238 /// pressure without writing a hundred instructions.
1239 fn narrow(count: usize) -> Env {
1240 Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 1])
1241 }
1242
1243 /// What a place is called, which is what an assertion reads.
1244 fn named(place: Option<Place>) -> String {
1245 match place {
1246 Some(Place::Reg(reg)) => REGS.name(GPR, reg).expect("a register").to_string(),
1247 Some(Place::Slot(slot)) => format!("slot {slot}"),
1248 None => "nowhere".to_string(),
1249 }
1250 }
1251
1252 /// Where every value in a function went.
1253 fn places(func: &Func, env: &Env) -> Vec<String> {
1254 let order = Order::of(func);
1255 let live = Live::of(func, &order);
1256 let assignment = assign(func, &order, &live, env);
1257 (0..func.vregs())
1258 .map(|number| {
1259 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
1260 named(assignment.place(reg))
1261 })
1262 .collect()
1263 }
1264
1265 #[test]
1266 fn two_values_that_are_never_both_wanted_share_a_register() {
1267 let mut names = Interner::new();
1268 let mut func = Func::new(names.intern("f"));
1269 let opcode = Opcode::new(names.intern("x64.nop"));
1270 let block = func.create_block();
1271 let first = func.new_vreg(GPR);
1272 let second = func.new_vreg(GPR);
1273 func.build(block, opcode).def(first, GPR).finish();
1274 func.build(block, opcode).uses(first, GPR).finish();
1275 func.build(block, opcode).def(second, GPR).finish();
1276 func.build(block, opcode).uses(second, GPR).finish();
1277
1278 // The first register in the order, twice, because the first value is finished with before
1279 // the second one is written.
1280 assert_eq!(places(&func, &env()), ["rax", "rax"]);
1281 }
1282
1283 /// A value held over an instruction that writes the whole of the first register and the top
1284 /// of the second, which is a call on AArch64 and `v8` in small, with the value as wide as that.
1285 fn over_the_top(width: u32) -> Func {
1286 let mut names = Interner::new();
1287 let mut func = Func::new(names.intern("f"));
1288 let opcode = Opcode::new(names.intern("x64.nop"));
1289 let block = func.create_block();
1290 let held = func.new_vreg(GPR);
1291 func.set_width(held, width);
1292 func.build(block, opcode).def(held, GPR).finish();
1293 func.build(block, opcode)
1294 .operand(Operand::write(Reg::physical(RAX), GPR))
1295 .operand(Operand::write(Reg::physical(RCX), GPR).with(Constraint::Above(8)))
1296 .finish();
1297 func.build(block, opcode).uses(held, GPR).finish();
1298 func
1299 }
1300
1301 #[test]
1302 fn a_value_that_fits_under_what_an_instruction_writes_stays_in_the_register() {
1303 assert_eq!(places(&over_the_top(8), &narrow(2)), ["rcx"]);
1304 assert_eq!(places(&over_the_top(4), &narrow(2)), ["rcx"]);
1305 }
1306
1307 #[test]
1308 fn a_value_wider_than_that_or_of_no_known_width_does_not() {
1309 assert_eq!(places(&over_the_top(16), &narrow(2)), ["slot 0"]);
1310 assert_eq!(places(&over_the_top(0), &narrow(2)), ["slot 0"]);
1311 }
1312
1313 #[test]
1314 fn two_values_that_are_both_wanted_do_not() {
1315 let mut names = Interner::new();
1316 let mut func = Func::new(names.intern("f"));
1317 let opcode = Opcode::new(names.intern("x64.nop"));
1318 let block = func.create_block();
1319 let first = func.new_vreg(GPR);
1320 let second = func.new_vreg(GPR);
1321 func.build(block, opcode).def(first, GPR).finish();
1322 func.build(block, opcode).def(second, GPR).finish();
1323 func.build(block, opcode).uses(first, GPR).finish();
1324 func.build(block, opcode).uses(second, GPR).finish();
1325
1326 assert_eq!(places(&func, &env()), ["rax", "rcx"]);
1327 }
1328
1329 #[test]
1330 fn a_value_written_early_that_nothing_reads_still_holds_its_register() {
1331 let mut names = Interner::new();
1332 let mut func = Func::new(names.intern("f"));
1333 let opcode = Opcode::new(names.intern("x64.nop"));
1334 let block = func.create_block();
1335 let wanted = func.new_vreg(GPR);
1336 let spare = func.new_vreg(GPR);
1337 // A division: a remainder somebody wants, and a quotient nobody does. Both are written by
1338 // the one instruction and the quotient is written before the operands have been read.
1339 func.build(block, opcode)
1340 .def(wanted, GPR)
1341 .operand(Operand::write_early(spare, GPR))
1342 .finish();
1343 func.build(block, opcode).uses(wanted, GPR).finish();
1344
1345 // Two registers, not one. A value nothing reads is still somewhere, and the instruction
1346 // that wrote it wrote the other one too, so the two cannot be the same place. Handing them
1347 // the same register loses the remainder, because the copy that takes the quotient out of
1348 // the register the machine insisted on goes on top of it. The quotient gets the first
1349 // register because it is written first, which is the whole of what early means.
1350 assert_eq!(places(&func, &env()), ["rcx", "rax"]);
1351 }
1352
1353 #[test]
1354 fn the_value_wanted_longest_is_the_one_that_goes_to_the_stack() {
1355 let mut names = Interner::new();
1356 let mut func = Func::new(names.intern("f"));
1357 let opcode = Opcode::new(names.intern("x64.nop"));
1358 let block = func.create_block();
1359 let long = func.new_vreg(GPR);
1360 let short = func.new_vreg(GPR);
1361 let third = func.new_vreg(GPR);
1362 func.build(block, opcode).def(long, GPR).finish();
1363 func.build(block, opcode).def(short, GPR).finish();
1364 func.build(block, opcode).def(third, GPR).finish();
1365 func.build(block, opcode).uses(short, GPR).finish();
1366 func.build(block, opcode).uses(third, GPR).finish();
1367 func.build(block, opcode).uses(long, GPR).finish();
1368
1369 // Two registers between three values. The one still wanted at the end of the function is
1370 // the one whose register is worth the most to everybody else, so it is the one that goes.
1371 assert_eq!(places(&func, &narrow(2)), ["slot 0", "rcx", "rax"]);
1372 }
1373
1374 #[test]
1375 fn a_register_an_instruction_insists_on_goes_to_the_values_that_asked_for_it() {
1376 let mut names = Interner::new();
1377 let mut func = Func::new(names.intern("f"));
1378 let opcode = Opcode::new(names.intern("x64.nop"));
1379 let block = func.create_block();
1380 let across = func.new_vreg(GPR);
1381 let dividend = func.new_vreg(GPR);
1382 let quotient = func.new_vreg(GPR);
1383 let remainder = func.new_vreg(GPR);
1384 func.build(block, opcode).def(across, GPR).finish();
1385 func.build(block, opcode).def(dividend, GPR).finish();
1386 func.build(block, opcode)
1387 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
1388 .operand(Operand::write_early(remainder, GPR).with(Constraint::Fixed(RDX)))
1389 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
1390 .finish();
1391 func.build(block, opcode).uses(across, GPR).finish();
1392
1393 // The value that has to be across the division is nowhere near `rax` or `rdx`, and each of
1394 // the three the division names is in the register the division asked for it in. The
1395 // dividend and the quotient share `rax` because the first is read where the second is
1396 // written, which is what a division does.
1397 assert_eq!(places(&func, &env()), ["rcx", "rax", "rax", "rdx"]);
1398 }
1399
1400 /// A value read by an instruction that fills a register before it reads is kept out of that
1401 /// register, even though the read is the last thing the value is wanted for.
1402 ///
1403 /// The divisor of a division is the case. What the machine runs is `cltd` and then `idivl`, so
1404 /// `rdx` holds the top half of the dividend by the time the divisor is read, and a divisor
1405 /// sitting in `rdx` is read as the dividend's own sign bits. An early definition is how the
1406 /// target says a register goes before the operands are read, and this is where the allocator
1407 /// has to hear it, since a value dying at an instruction is otherwise free to sit in a
1408 /// register that instruction writes. tamnd/rucc#1232.
1409 #[test]
1410 fn a_value_that_dies_at_an_instruction_stays_out_of_what_it_fills_first() {
1411 let mut names = Interner::new();
1412 let mut func = Func::new(names.intern("f"));
1413 let opcode = Opcode::new(names.intern("x64.nop"));
1414 let block = func.create_block();
1415 let across = func.new_vreg(GPR);
1416 let dividend = func.new_vreg(GPR);
1417 let divisor = func.new_vreg(GPR);
1418 let remainder = func.new_vreg(GPR);
1419 func.build(block, opcode).def(across, GPR).finish();
1420 func.build(block, opcode).def(dividend, GPR).finish();
1421 func.build(block, opcode).def(divisor, GPR).finish();
1422 func.build(block, opcode)
1423 .operand(Operand::write_early(remainder, GPR).with(Constraint::Fixed(RDX)))
1424 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
1425 .operand(Operand::read(divisor, GPR))
1426 .finish();
1427 func.build(block, opcode).uses(across, GPR).uses(remainder, GPR).finish();
1428
1429 // Four registers for four values, and the divisor takes the fourth. `rdx` is free
1430 // everywhere in this function except at the instruction that is about to fill it, which is
1431 // the one instruction the divisor is wanted at.
1432 assert_eq!(places(&func, &narrow(4)), ["rcx", "rax", "rsi", "rdx"]);
1433 }
1434
1435 #[test]
1436 fn a_value_wanted_after_the_instruction_that_insists_does_not_get_that_register() {
1437 let mut names = Interner::new();
1438 let mut func = Func::new(names.intern("f"));
1439 let opcode = Opcode::new(names.intern("x64.nop"));
1440 let block = func.create_block();
1441 let dividend = func.new_vreg(GPR);
1442 let quotient = func.new_vreg(GPR);
1443 func.build(block, opcode).def(dividend, GPR).finish();
1444 func.build(block, opcode)
1445 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
1446 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
1447 .finish();
1448 func.build(block, opcode).uses(dividend, GPR).finish();
1449
1450 // The hint is a preference and not a claim. The dividend would rather be in `rax` and
1451 // cannot be, because the division writes `rax` and the dividend is wanted afterwards, so
1452 // it takes the next register and the quotient keeps the one it was promised.
1453 assert_eq!(places(&func, &env()), ["rcx", "rax"]);
1454 }
1455
1456 #[test]
1457 fn a_value_an_instruction_can_only_read_from_memory_is_on_the_stack() {
1458 let mut names = Interner::new();
1459 let mut func = Func::new(names.intern("f"));
1460 let opcode = Opcode::new(names.intern("x64.nop"));
1461 let block = func.create_block();
1462 let value = func.new_vreg(GPR);
1463 func.build(block, opcode).def(value, GPR).finish();
1464 func.build(block, opcode)
1465 .operand(Operand::read(value, GPR).with(Constraint::Stack))
1466 .finish();
1467
1468 assert_eq!(places(&func, &env()), ["slot 0"]);
1469 }
1470
1471 #[test]
1472 fn a_two_address_instruction_writes_the_register_it_read_when_it_can() {
1473 let mut names = Interner::new();
1474 let mut func = Func::new(names.intern("f"));
1475 let opcode = Opcode::new(names.intern("x64.nop"));
1476 let block = func.create_block();
1477 let left = func.new_vreg(GPR);
1478 let right = func.new_vreg(GPR);
1479 let sum = func.new_vreg(GPR);
1480 func.build(block, opcode).def(left, GPR).finish();
1481 func.build(block, opcode).def(right, GPR).finish();
1482 func.build(block, opcode)
1483 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1484 .uses(left, GPR)
1485 .uses(right, GPR)
1486 .finish();
1487 func.build(block, opcode).uses(right, GPR).finish();
1488
1489 // The addition reads the left value for the last time, so the answer goes where that was
1490 // and the instruction is two address without a move in front of it.
1491 assert_eq!(places(&func, &env()), ["rax", "rcx", "rax"]);
1492 }
1493
1494 #[test]
1495 fn a_two_address_instruction_that_cannot_gets_a_register_nothing_it_reads_is_in() {
1496 let mut names = Interner::new();
1497 let mut func = Func::new(names.intern("f"));
1498 let opcode = Opcode::new(names.intern("x64.nop"));
1499 let block = func.create_block();
1500 let left = func.new_vreg(GPR);
1501 let right = func.new_vreg(GPR);
1502 let sum = func.new_vreg(GPR);
1503 func.build(block, opcode).def(left, GPR).finish();
1504 func.build(block, opcode).def(right, GPR).finish();
1505 func.build(block, opcode)
1506 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1507 .uses(left, GPR)
1508 .uses(right, GPR)
1509 .finish();
1510 func.build(block, opcode).uses(left, GPR).finish();
1511
1512 // The left value is wanted afterwards, so the answer cannot have its register. It cannot
1513 // have the right one's either, because the rewrite is about to write a move into it before
1514 // the addition has read anything.
1515 assert_eq!(places(&func, &env()), ["rax", "rcx", "rdx"]);
1516 }
1517
1518 #[test]
1519 fn an_answer_that_commutes_goes_over_the_source_that_is_finished_with() {
1520 let mut names = Interner::new();
1521 let mut func = Func::new(names.intern("f"));
1522 let opcode = Opcode::new(names.intern("x64.nop"));
1523 let block = func.create_block();
1524 let left = func.new_vreg(GPR);
1525 let right = func.new_vreg(GPR);
1526 let sum = func.new_vreg(GPR);
1527 func.build(block, opcode).def(left, GPR).finish();
1528 func.build(block, opcode).def(right, GPR).finish();
1529 let add = func
1530 .build(block, opcode)
1531 .flags(Flags::COMMUTES)
1532 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1533 .uses(left, GPR)
1534 .uses(right, GPR)
1535 .finish();
1536 func.build(block, opcode).uses(left, GPR).uses(sum, GPR).finish();
1537
1538 // The same shape as the one above where the answer got a register of its own, except that
1539 // the addition reads its sources either way round, so the answer goes where the right one
1540 // was and the instruction is marked to be swapped.
1541 assert_eq!(places(&func, &env()), ["rax", "rcx", "rcx"]);
1542 let order = Order::of(&func);
1543 let live = Live::of(&func, &order);
1544 let assignment = assign(&func, &order, &live, &env());
1545 assert_eq!(assignment.commuted(), [add]);
1546
1547 // Once swapped, the instruction is an ordinary reuse of its first source, the checker and
1548 // the trace agree with it, and nothing has to be moved in front of it. The rewrite has put
1549 // the registers in by then, so the right one is `rcx` and the left one `rax`.
1550 let allocation = crate::run(&mut func, &env(), "f", true);
1551 assert!(allocation.edits.is_empty());
1552 let operands = &func[func[add].operands];
1553 let (first, second) = (operands[1].reg.phys(), operands[2].reg.phys());
1554 assert_eq!((first, second), (Some(RCX), Some(RAX)));
1555 }
1556
1557 #[test]
1558 fn an_answer_that_commutes_takes_the_source_something_after_it_wants() {
1559 let mut names = Interner::new();
1560 let mut func = Func::new(names.intern("f"));
1561 let opcode = Opcode::new(names.intern("x64.nop"));
1562 let block = func.create_block();
1563 let left = func.new_vreg(GPR);
1564 let right = func.new_vreg(GPR);
1565 let sum = func.new_vreg(GPR);
1566 func.build(block, opcode).def(left, GPR).finish();
1567 func.build(block, opcode)
1568 .operand(Operand::write(right, GPR).with(Constraint::Fixed(RAX)))
1569 .finish();
1570 let add = func
1571 .build(block, opcode)
1572 .flags(Flags::COMMUTES)
1573 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1574 .uses(left, GPR)
1575 .uses(right, GPR)
1576 .finish();
1577 func.build(block, opcode)
1578 .operand(Operand::read(sum, GPR).with(Constraint::Fixed(RAX)))
1579 .finish();
1580
1581 // Both sources are finished with, so either register would do for the answer. The one
1582 // reading it wants it in `rax`, which is where the right one already is, so it goes there
1583 // and nothing is moved in front of that reader.
1584 let names = places(&func, &env());
1585 assert_eq!(names[2], "rax");
1586 assert_ne!(names[0], "rax");
1587 let allocation = crate::run(&mut func, &env(), "f", true);
1588 assert!(allocation.edits.is_empty());
1589 assert_eq!(func[func[add].operands][1].reg.phys(), Some(RAX));
1590 }
1591
1592 #[test]
1593 fn an_answer_that_commutes_stays_where_the_loop_passes_it() {
1594 let mut names = Interner::new();
1595 let mut func = Func::new(names.intern("f"));
1596 let opcode = Opcode::new(names.intern("x64.nop"));
1597 let entry = func.create_block();
1598 let head = func.create_block();
1599 let out = func.create_block();
1600 let seed = func.new_vreg(GPR);
1601 let total = func.new_vreg(GPR);
1602 let term = func.new_vreg(GPR);
1603 let next = func.new_vreg(GPR);
1604 func.build(entry, opcode).def(seed, GPR).finish();
1605 *func.succs_mut(entry) = vec![BlockCall::with(head, vec![seed])];
1606 func.params_mut(head).push(Param { reg: total, class: GPR });
1607 func.build(head, opcode).def(term, GPR).finish();
1608 let add = func
1609 .build(head, opcode)
1610 .flags(Flags::COMMUTES)
1611 .operand(Operand::write(next, GPR).with(Constraint::Reuse(1)))
1612 .uses(total, GPR)
1613 .uses(term, GPR)
1614 .finish();
1615 func.build(head, opcode)
1616 .operand(Operand::read(next, GPR).with(Constraint::Fixed(RSI)))
1617 .finish();
1618 *func.succs_mut(head) = vec![BlockCall::with(head, vec![next]), BlockCall::to(out)];
1619
1620 // Both sources are finished with and the sum is wanted in `rsi` as well, but the loop
1621 // passes it back to `total`, so it goes where `total` is and the back edge has nothing to
1622 // copy.
1623 let order = Order::of(&func);
1624 let live = Live::of(&func, &order);
1625 let assignment = assign(&func, &order, &live, &env());
1626 assert_eq!(assignment.place(next), assignment.place(total));
1627 assert!(!assignment.commuted().contains(&add));
1628 }
1629
1630 #[test]
1631 fn an_answer_that_does_not_commute_leaves_its_sources_where_they_are() {
1632 let mut names = Interner::new();
1633 let mut func = Func::new(names.intern("f"));
1634 let opcode = Opcode::new(names.intern("x64.nop"));
1635 let block = func.create_block();
1636 let left = func.new_vreg(GPR);
1637 let right = func.new_vreg(GPR);
1638 let sum = func.new_vreg(GPR);
1639 func.build(block, opcode).def(left, GPR).finish();
1640 func.build(block, opcode).def(right, GPR).finish();
1641 func.build(block, opcode)
1642 .flags(Flags::COMMUTES)
1643 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1644 .uses(left, GPR)
1645 .uses(right, GPR)
1646 .finish();
1647 func.build(block, opcode).uses(right, GPR).finish();
1648
1649 // The left one is finished with, so the answer goes over it as it always did, and there is
1650 // nothing to swap.
1651 assert_eq!(places(&func, &env()), ["rax", "rcx", "rax"]);
1652 let order = Order::of(&func);
1653 let live = Live::of(&func, &order);
1654 assert!(assign(&func, &order, &live, &env()).commuted().is_empty());
1655 }
1656
1657 #[test]
1658 fn a_value_live_across_a_whole_loop_holds_its_register_over_all_of_it() {
1659 let mut names = Interner::new();
1660 let mut func = Func::new(names.intern("f"));
1661 let opcode = Opcode::new(names.intern("x64.nop"));
1662 let head = func.create_block();
1663 let body = func.create_block();
1664 let carried = func.new_vreg(GPR);
1665 let inside = func.new_vreg(GPR);
1666 func.build(head, opcode).def(carried, GPR).finish();
1667 *func.succs_mut(head) = vec![BlockCall::to(body)];
1668 func.build(body, opcode).def(inside, GPR).finish();
1669 func.build(body, opcode).uses(inside, GPR).uses(carried, GPR).finish();
1670 *func.succs_mut(body) = vec![BlockCall::to(body)];
1671
1672 // The value inside the loop cannot have the carried one's register, even though nothing
1673 // between the two definitions says so.
1674 assert_eq!(places(&func, &env()), ["rax", "rcx"]);
1675 }
1676
1677 #[test]
1678 fn a_two_address_answer_already_live_does_not_take_the_register_it_read() {
1679 let mut names = Interner::new();
1680 let mut func = Func::new(names.intern("f"));
1681 let opcode = Opcode::new(names.intern("x64.nop"));
1682 let head = func.create_block();
1683 let latch = func.create_block();
1684 let out = func.create_block();
1685 let source = func.new_vreg(GPR);
1686 let carried = func.new_vreg(GPR);
1687 func.build(head, opcode).def(source, GPR).finish();
1688 func.build(head, opcode).def(carried, GPR).finish();
1689 *func.succs_mut(head) = vec![BlockCall::to(latch)];
1690 // The bottom of the loop adds the source to the carried value and writes the answer back
1691 // over it, reusing the register the source is in. The next turn round redefines both.
1692 func.build(latch, opcode)
1693 .operand(Operand::write(carried, GPR).with(Constraint::Reuse(1)))
1694 .uses(source, GPR)
1695 .uses(carried, GPR)
1696 .finish();
1697 *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
1698 func.build(out, opcode).uses(carried, GPR).finish();
1699
1700 // The source is read here for the last time, which on its own is the shape the two address
1701 // shortcut is for, and taking it would be wrong. The carried value was written by the same
1702 // instruction on the last turn and is read by this one, so the two are both wanted where
1703 // the instruction reads and one register cannot hold both.
1704 assert_eq!(places(&func, &env()), ["rax", "rcx"]);
1705
1706 // And the checker has to agree, since it excused this pair on the same reasoning and so
1707 // would have let the answer through.
1708 let order = Order::of(&func);
1709 let live = Live::of(&func, &order);
1710 let assignment = assign(&func, &order, &live, &env());
1711 assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
1712 }
1713
1714 #[test]
1715 fn a_two_address_answer_with_a_hole_in_front_of_it_does_not_take_its_other_operand() {
1716 let mut names = Interner::new();
1717 let mut func = Func::new(names.intern("f"));
1718 let nop = Opcode::new(names.intern("x64.nop"));
1719 let add = Opcode::new(names.intern("x64.add"));
1720 let entry = func.create_block();
1721 let head = func.create_block();
1722 let arm = func.create_block();
1723 let latch = func.create_block();
1724 let out = func.create_block();
1725 let seed = func.new_vreg(GPR);
1726 let sum = func.new_vreg(GPR);
1727 let inside = func.new_vreg(GPR);
1728 let loaded = func.new_vreg(GPR);
1729 func.build(entry, nop).def(seed, GPR).finish();
1730 func.build(entry, nop).def(sum, GPR).finish();
1731 *func.succs_mut(entry) = vec![BlockCall::to(head)];
1732 func.build(head, nop).uses(sum, GPR).finish();
1733 *func.succs_mut(head) = vec![BlockCall::to(arm), BlockCall::to(latch)];
1734 func.build(arm, nop).def(inside, GPR).finish();
1735 func.build(arm, nop).uses(inside, GPR).finish();
1736 *func.succs_mut(arm) = vec![BlockCall::to(out)];
1737 func.build(latch, nop).def(loaded, GPR).finish();
1738 func.build(latch, add)
1739 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1740 .uses(seed, GPR)
1741 .uses(loaded, GPR)
1742 .finish();
1743 *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
1744
1745 // The answer is live in the entry and the head as well, and the arm between them is a hole
1746 // in it, so the piece the addition writes is not the first one. The value the addition reads
1747 // out of memory is still wanted where the addition reads, so it may not be in the register
1748 // the answer is about to be copied into, holes or no holes. tamnd/rucc#982.
1749 let places = places(&func, &env());
1750 assert_ne!(places[index(sum)], places[index(loaded)]);
1751
1752 let order = Order::of(&func);
1753 let live = Live::of(&func, &order);
1754 let assignment = assign(&func, &order, &live, &env());
1755 assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
1756 }
1757
1758 #[test]
1759 fn a_sum_a_loop_carries_round_keeps_its_register_past_an_arm_laid_out_after_it() {
1760 let mut names = Interner::new();
1761 let mut func = Func::new(names.intern("f"));
1762 let nop = Opcode::new(names.intern("x64.nop"));
1763 let add = Opcode::new(names.intern("x64.add"));
1764 let entry = func.create_block();
1765 let head = func.create_block();
1766 let join = func.create_block();
1767 let arm = func.create_block();
1768 let out = func.create_block();
1769 let seed = func.new_vreg(GPR);
1770 let term = func.new_vreg(GPR);
1771 let next = func.new_vreg(GPR);
1772 func.build(entry, nop).def(seed, GPR).finish();
1773 *func.succs_mut(entry) = vec![BlockCall::with(head, vec![seed])];
1774 let total = func.append_param(head, GPR);
1775 func.build(head, nop).def(term, GPR).finish();
1776 *func.succs_mut(head) = vec![BlockCall::to(join), BlockCall::to(arm)];
1777 func.build(join, add)
1778 .operand(Operand::write(next, GPR).with(Constraint::Reuse(1)))
1779 .uses(total, GPR)
1780 .uses(term, GPR)
1781 .finish();
1782 *func.succs_mut(join) =
1783 vec![BlockCall::with(head, vec![next]), BlockCall::with(out, vec![next])];
1784 // The default arm of a `switch`, laid out after the addition it joins back in above. The
1785 // sum is live in it and nothing in it or after it reads the sum again.
1786 func.build(arm, nop).def(term, GPR).finish();
1787 *func.succs_mut(arm) = vec![BlockCall::to(join)];
1788 let result = func.append_param(out, GPR);
1789 func.build(out, nop).uses(result, GPR).finish();
1790
1791 // The addition reads the sum for the last time, so the new sum goes where the old one was
1792 // and the edge back to the top of the loop has nothing to move. tamnd/rucc#1965.
1793 let places = places(&func, &env());
1794 assert_eq!(places[index(next)], places[index(total)]);
1795
1796 let order = Order::of(&func);
1797 let live = Live::of(&func, &order);
1798 let assignment = assign(&func, &order, &live, &env());
1799 assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
1800 }
1801
1802 /// Two blocks the entry chooses between, with the one the clobber is in written first. The two
1803 /// values written in the entry block are read in the other one, so their ranges cover the
1804 /// clobber whether or not either of them ever reaches it.
1805 fn arms(reaches: bool) -> Func {
1806 let mut names = Interner::new();
1807 let mut func = Func::new(names.intern("f"));
1808 let opcode = Opcode::new(names.intern("x64.nop"));
1809 let entry = func.create_block();
1810 let arm = func.create_block();
1811 let tail = func.create_block();
1812 let first = func.new_vreg(GPR);
1813 let second = func.new_vreg(GPR);
1814 func.build(entry, opcode).def(first, GPR).finish();
1815 func.build(entry, opcode).def(second, GPR).finish();
1816 *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
1817 // What a call looks like here: an instruction writing the registers the convention says it
1818 // destroys, named outright so that nothing else may be in them.
1819 func.build(arm, opcode).operand(Operand::write(Reg::physical(RAX), GPR)).finish();
1820 *func.succs_mut(arm) = if reaches { vec![BlockCall::to(tail)] } else { Vec::new() };
1821 func.build(tail, opcode).uses(first, GPR).uses(second, GPR).finish();
1822 func
1823 }
1824
1825 #[test]
1826 fn a_register_a_clobber_takes_beats_the_stack_for_a_value_not_live_in_that_block() {
1827 let func = arms(false);
1828
1829 // Two registers between two values, and a clobber in the arm that takes the first of them.
1830 // The intervals around both values cover the clobber, since the arm is written between the
1831 // two blocks they are live in, and the arm is a hole in both of their areas. So a value has
1832 // `rax` rather than a stack slot: the arm is a block its own path never goes through.
1833 // tamnd/rucc#982. It is the first value since tamnd/rucc#2202, because a clobber where a
1834 // value is dead leaves the register clear for it.
1835 assert_eq!(places(&func, &narrow(2)), ["rax", "rcx"]);
1836
1837 let order = Order::of(&func);
1838 let live = Live::of(&func, &order);
1839 let assignment = assign(&func, &order, &live, &narrow(2));
1840 assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
1841 }
1842
1843 #[test]
1844 fn a_register_a_clobber_takes_is_not_free_to_a_value_that_is_live_there() {
1845 let func = arms(true);
1846
1847 // The same blocks with an edge from the arm to the tail, which is all it takes: both values
1848 // now arrive at the read either way, so the clobber is on a path they are live over and the
1849 // one register left has to do for both of them.
1850 assert_eq!(places(&func, &narrow(2)), ["rcx", "slot 0"]);
1851 }
1852
1853 /// A value and the instruction that destroys a register, written one after the other, with the
1854 /// value read by that instruction or by the one after it.
1855 fn dies_at_the_clobber(here: bool) -> Func {
1856 let mut names = Interner::new();
1857 let mut func = Func::new(names.intern("f"));
1858 let opcode = Opcode::new(names.intern("x64.nop"));
1859 let entry = func.create_block();
1860 let value = func.new_vreg(GPR);
1861 func.build(entry, opcode).def(value, GPR).finish();
1862 let call = func.build(entry, opcode).operand(Operand::write(Reg::physical(RAX), GPR));
1863 if here {
1864 call.uses(value, GPR).finish();
1865 } else {
1866 call.finish();
1867 func.build(entry, opcode).uses(value, GPR).finish();
1868 }
1869 func
1870 }
1871
1872 /// A value whose last read is the instruction that destroys a register may be in that register,
1873 /// because the instruction reads what it is handed before it writes anything.
1874 ///
1875 /// The call is what this is about, and the value a call is passed is the case: seven registers
1876 /// on this machine are destroyed by one, every argument dies at the call that reads it, and
1877 /// refusing all seven to those values left them taking a callee saved register for a life two
1878 /// instructions long and paying for it in the prologue and the epilogue. tamnd/rucc#1232.
1879 #[test]
1880 fn a_value_that_dies_where_a_register_is_destroyed_may_be_in_that_register() {
1881 let func = dies_at_the_clobber(true);
1882 assert_eq!(places(&func, &narrow(1)), ["rax"]);
1883
1884 let order = Order::of(&func);
1885 let live = Live::of(&func, &order);
1886 let assignment = assign(&func, &order, &live, &narrow(1));
1887 assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
1888 }
1889
1890 /// And one read later than that is one the instruction really does destroy, which is the same
1891 /// function with the read moved down by one instruction.
1892 #[test]
1893 fn a_value_read_after_the_instruction_that_destroys_a_register_is_not_in_it() {
1894 let func = dies_at_the_clobber(false);
1895 assert_eq!(places(&func, &narrow(1)), ["slot 0"]);
1896 }
1897
1898 #[test]
1899 fn a_hint_is_followed_when_the_register_is_clear_and_not_when_it_is_merely_allowed() {
1900 let mut names = Interner::new();
1901 let mut func = Func::new(names.intern("f"));
1902 let opcode = Opcode::new(names.intern("x64.nop"));
1903 let entry = func.create_block();
1904 let mid = func.create_block();
1905 let tail = func.create_block();
1906 let first = func.new_vreg(GPR);
1907 let second = func.new_vreg(GPR);
1908 func.build(entry, opcode).def(first, GPR).finish();
1909 func.build(entry, opcode).def(second, GPR).finish();
1910 *func.succs_mut(entry) = vec![BlockCall::to(mid), BlockCall::to(tail)];
1911 // Two arms, each ending in an instruction that wants its own value in `rax`, which is what
1912 // a return out of either side of a branch looks like.
1913 func.build(mid, opcode)
1914 .operand(Operand::read(second, GPR).with(Constraint::Fixed(RAX)))
1915 .finish();
1916 func.build(tail, opcode)
1917 .operand(Operand::read(first, GPR).with(Constraint::Fixed(RAX)))
1918 .finish();
1919
1920 // The first value is hinted at `rax` and does not get it, because the other arm wants `rax`
1921 // for the other value and the first value's range reaches that far. Following the hint here
1922 // would save a move in the tail and cost one in the middle, and the second value gets `rax`
1923 // with nothing moved anywhere instead.
1924 assert_eq!(places(&func, &env()), ["rcx", "rax"]);
1925 }
1926
1927 #[test]
1928 fn a_value_living_in_a_hole_of_another_gets_the_same_register() {
1929 let mut names = Interner::new();
1930 let mut func = Func::new(names.intern("f"));
1931 let opcode = Opcode::new(names.intern("x64.nop"));
1932 let entry = func.create_block();
1933 let arm = func.create_block();
1934 let tail = func.create_block();
1935 let across = func.new_vreg(GPR);
1936 let inside = func.new_vreg(GPR);
1937 func.build(entry, opcode).def(across, GPR).finish();
1938 *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
1939 func.build(arm, opcode).def(inside, GPR).finish();
1940 func.build(arm, opcode).uses(inside, GPR).finish();
1941 func.build(tail, opcode).uses(across, GPR).finish();
1942
1943 // One register between the two of them, and one register is enough. Nothing in the arm can
1944 // reach the read in the tail, so the value the arm makes is welcome to the register the
1945 // value crossing the function is in. The interval around that value covers the arm and the
1946 // value is nowhere near it, which is what used to send one of the two to the stack.
1947 // tamnd/rucc#982.
1948 assert_eq!(places(&func, &narrow(1)), ["rax", "rax"]);
1949
1950 let order = Order::of(&func);
1951 let live = Live::of(&func, &order);
1952 let assignment = assign(&func, &order, &live, &narrow(1));
1953 assert_eq!(assignment.spilled(), 0);
1954 assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
1955 }
1956
1957 /// The blocks of [`arms`] with no edge from the arm to the tail, where the arm wants `rax` for
1958 /// a value of its own rather than destroying it.
1959 fn handed_in_the_arm() -> Func {
1960 let mut names = Interner::new();
1961 let mut func = Func::new(names.intern("f"));
1962 let opcode = Opcode::new(names.intern("x64.nop"));
1963 let entry = func.create_block();
1964 let arm = func.create_block();
1965 let tail = func.create_block();
1966 let first = func.new_vreg(GPR);
1967 let second = func.new_vreg(GPR);
1968 let own = func.new_vreg(GPR);
1969 func.build(entry, opcode).def(first, GPR).finish();
1970 func.build(entry, opcode).def(second, GPR).finish();
1971 *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
1972 func.build(arm, opcode).def(own, GPR).finish();
1973 func.build(arm, opcode)
1974 .operand(Operand::read(own, GPR).with(Constraint::Fixed(RAX)))
1975 .finish();
1976 func.build(tail, opcode).uses(first, GPR).uses(second, GPR).finish();
1977 func
1978 }
1979
1980 #[test]
1981 fn a_register_another_value_is_handed_is_the_last_one_offered_rather_than_the_first() {
1982 // With a register to spare the value takes the spare one. Being allowed a register some
1983 // instruction insists on is not the same as it being free: the instruction has to be handed
1984 // it in the end, and what hands it over is a move.
1985 let func = handed_in_the_arm();
1986 assert_eq!(places(&func, &narrow(3)), ["rcx", "rdx", "rax"]);
1987 }
1988
1989 #[test]
1990 fn a_register_a_clobber_takes_is_clear_to_a_value_dead_where_it_is_taken() {
1991 let func = arms(false);
1992
1993 // A clobber hands the register to nobody, so there is nothing to move in and nothing to
1994 // pay. The first value takes `rax` though the arm destroys it, since it is dead there, and
1995 // neither value needs a register past the third. tamnd/rucc#2202.
1996 assert_eq!(places(&func, &narrow(3)), ["rax", "rcx"]);
1997 }
1998
1999 #[test]
2000 fn a_frame_says_what_each_of_its_slots_is_for() {
2001 let mut names = Interner::new();
2002 let mut func = Func::new(names.intern("f"));
2003 let opcode = Opcode::new(names.intern("x64.nop"));
2004 let block = func.create_block();
2005 let first = func.new_vreg(GPR);
2006 let second = func.new_vreg(GPR);
2007 func.build(block, opcode).def(first, GPR).finish();
2008 func.build(block, opcode).def(second, GPR).finish();
2009 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
2010
2011 let order = Order::of(&func);
2012 let live = Live::of(&func, &order);
2013 let assignment = assign(&func, &order, &live, &narrow(1));
2014 assert_eq!(assignment.spilled(), 1);
2015 assert_eq!(assignment.slots(), [GPR]);
2016 // A register that is already a register is where it is, and this has nothing to say about
2017 // it.
2018 assert_eq!(assignment.place(Reg::physical(RCX)), None);
2019 assert_eq!(env().scratch(GPR), [R13, R14, R15]);
2020 }
2021
2022 /// Pieces of a value, from pairs of points.
2023 fn ranges(pairs: &[(Point, Point)]) -> Vec<Range> {
2024 pairs.iter().map(|&(start, end)| Range { start, end }).collect()
2025 }
2026
2027 #[test]
2028 fn the_pieces_of_a_register_answer_what_a_walk_over_its_values_would() {
2029 // Three values that take turns in one register, the first with a hole the third sits in.
2030 let held = [
2031 (Reg::virtual_reg(0), ranges(&[(0, 4), (20, 30)])),
2032 (Reg::virtual_reg(1), ranges(&[(5, 9)])),
2033 (Reg::virtual_reg(2), ranges(&[(10, 19), (31, 40)])),
2034 ];
2035 let mut pieces = Pieces::default();
2036 for (reg, list) in &held {
2037 for &piece in list {
2038 pieces.insert(piece, *reg);
2039 }
2040 }
2041 assert!(!pieces.broken);
2042 let asked = [
2043 ranges(&[(41, 50)]),
2044 ranges(&[(40, 50)]),
2045 ranges(&[(9, 9)]),
2046 ranges(&[(3, 3), (41, 42)]),
2047 ranges(&[(50, 60)]),
2048 ranges(&[(15, 15)]),
2049 ];
2050 for list in &asked {
2051 let area = Area::of_pieces(list);
2052 for except in [None, Some(Reg::virtual_reg(0)), Some(Reg::virtual_reg(2))] {
2053 let walked = held.iter().any(|(reg, pieces)| {
2054 Some(*reg) != except && Area::of_pieces(pieces).overlaps(area)
2055 });
2056 assert_eq!(pieces.touch(area, except), walked, "{list:?} except {except:?}");
2057 }
2058 }
2059 }
2060
2061 #[test]
2062 fn the_owners_of_the_pieces_an_area_touches_are_the_values_a_walk_would_find() {
2063 let held = [
2064 (Reg::virtual_reg(0), ranges(&[(0, 4), (20, 30)])),
2065 (Reg::virtual_reg(1), ranges(&[(5, 9)])),
2066 (Reg::virtual_reg(2), ranges(&[(10, 19), (30, 40)])),
2067 ];
2068 let mut pieces = Pieces::default();
2069 for (reg, list) in &held {
2070 for &piece in list {
2071 pieces.insert(piece, *reg);
2072 }
2073 }
2074 let asked = [
2075 ranges(&[(41, 50)]),
2076 ranges(&[(30, 30)]),
2077 ranges(&[(3, 12)]),
2078 ranges(&[(3, 3), (25, 42)]),
2079 ranges(&[(0, 50)]),
2080 ];
2081 for list in &asked {
2082 let area = Area::of_pieces(list);
2083 let walked: Vec<Reg> = held
2084 .iter()
2085 .filter(|(_, pieces)| Area::of_pieces(pieces).overlaps(area))
2086 .map(|&(reg, _)| reg)
2087 .collect();
2088 assert_eq!(pieces.owners(area), Some(walked), "{list:?}");
2089 }
2090 pieces.insert(Range { start: 2, end: 3 }, Reg::virtual_reg(3));
2091 assert_eq!(pieces.owners(Area::of_pieces(&asked[0])), None);
2092 }
2093
2094 #[test]
2095 fn pieces_that_end_out_of_order_are_marked() {
2096 let mut pieces = Pieces::default();
2097 pieces.insert(Range { start: 0, end: 10 }, Reg::virtual_reg(0));
2098 pieces.insert(Range { start: 12, end: 20 }, Reg::virtual_reg(1));
2099 assert!(!pieces.broken);
2100 // Inside the first, which two values in one register never are.
2101 pieces.insert(Range { start: 2, end: 3 }, Reg::virtual_reg(2));
2102 assert!(pieces.broken);
2103 }
2104
2105 #[test]
2106 fn a_value_taken_out_of_a_register_leaves_its_pieces_with_it() {
2107 let mut pieces = Pieces::default();
2108 let (first, second) = (Reg::virtual_reg(0), Reg::virtual_reg(1));
2109 pieces.insert(Range { start: 0, end: 10 }, first);
2110 pieces.insert(Range { start: 12, end: 20 }, second);
2111 let asked = ranges(&[(15, 16)]);
2112 assert!(pieces.touch(Area::of_pieces(&asked), None));
2113 pieces.remove(Range { start: 12, end: 20 }, second);
2114 assert!(!pieces.touch(Area::of_pieces(&asked), None));
2115 pieces.drop_before(11);
2116 assert!(pieces.list.is_empty());
2117 }
2118}