Skip to main content

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.
16//!
17//! # Where the line is not the function
18//!
19//! The line is the order the blocks arrived in, and `crate::layout` puts them in a different one
20//! afterwards, so being between two blocks on the line says nothing about being between them in
21//! the code. A range has no holes either, so a value live in two blocks looks live in every block
22//! written between them.
23//!
24//! Both of those are fine for deciding that two values cannot share a register, which is the
25//! question a range was built to answer and which it answers by being generous. Neither is fine
26//! for deciding that a register an instruction insists on is unavailable, because there the
27//! generosity has a price: a call destroys seven registers on x86-64, and a function whose blocks
28//! happen to arrive with a call written between the blocks of a loop would otherwise lose all
29//! seven for every value in that loop, for a call the loop never reaches. So that one question is
30//! asked of the liveness rather than of the range: a value is live where an instruction insists on
31//! something when its range covers the point and it is live in that block, which is a fact about
32//! the function rather than about the order it was written down in. tamnd/rucc#982.
33//!
34//! Allowed is not the same as free, though, so the registers are offered in two passes. First the
35//! ones nothing insists on anywhere the range reaches, then the ones something insists on somewhere
36//! the value never goes. The second kind costs: the instruction that insists has to be handed the
37//! register in the end, and what hands it over is a move. A function that gives a value back has an
38//! operand fixed to `rax` at the end of it, and putting the busiest value in the function in `rax`
39//! because no path reaches the return with it live buys one register and pays a move at every
40//! return. Ordering the two passes is what keeps the register and drops the moves.
41//!
42//! The hint below is asked the first question rather than the second for the same reason. A value
43//! taking the register its own operand asked for saves a move, and taking one somebody else's
44//! operand asked for somewhere it never goes costs one, so a hint is worth following when the
45//! register is clear and not worth following when it is merely allowed.
46//!
47//! # What it does with a register an instruction insists on
48//!
49//! Two things. It stays out of that register for everybody else, and it tries that register first
50//! for the value the operand names. A division wants its dividend in `rax`, so `rax` is
51//! unavailable to every other value that is live where the division reads, and it is the first
52//! register offered to the dividend itself. When the dividend gets it there is no move on the way
53//! in, and when it does not the rewrite writes one and nothing else changes.
54//!
55//! That second half is the hint, and without it the register an instruction insists on is the one
56//! register the value in it can never have, since the value's own operand is what makes the
57//! register look busy. The effect is largest on returns, because a function that gives a value
58//! back has an operand fixed to `rax` at the end of it and most functions give a value back.
59//!
60//! What makes the hint safe is asking about the register at each of the instruction's two points
61//! rather than across the whole of it. An instruction reads at the first and writes at the second,
62//! so a register it insists on is one value's at the first, another value's at the second, and
63//! nobody else's at either. A division reads its dividend from `rax` and writes its quotient to
64//! `rax`, and those are different values that can both live there. A value passed to a call in
65//! `rdi` and wanted again afterwards cannot, because nothing writes `rdi` at the second point and
66//! a register the call does not write is a register the call is assumed to destroy.
67//!
68//! An operand that has to be in memory is the other way round. The value it names goes on the
69//! stack whatever else is true of it, because that is the only place the instruction could read it
70//! from.
71//!
72//! # What it does with a two address instruction
73//!
74//! An `add` on x86-64 writes one of the registers it reads, which the operand says as a reuse of
75//! another operand. The rewrite can always make that true by copying the source into the
76//! destination first, but only if the destination is a register the instruction does not otherwise
77//! read, so a value written by a reuse is treated here as live from where the instruction reads
78//! rather than from where it writes. Then the copy is always safe.
79//!
80//! The copy is also usually unnecessary, and the one place this looks past the interval it is
81//! placing is to see that: if the value being reused is read here for the last time and the value
82//! being written starts here, the second may have the first's register, and the instruction is
83//! already two address without anything being moved anywhere. That is the whole of the coalescing
84//! this allocator does, and it is worth the dozen lines, because otherwise every piece of
85//! arithmetic in the output carries a move in front of it.
86//!
87//! Both halves of that are needed. The second is the one a loop breaks: an instruction at the
88//! bottom of a loop can write a value the top of the loop reads on the next turn, and such a value
89//! is live on the way into the instruction that writes it as well as after. It is then wanted at
90//! the same time as the value it reuses, whatever is true of the reuse, and giving it the same
91//! register makes an addition read the answer to the last one instead of its own operand.
92//!
93//! # What it does not do
94//!
95//! It does not touch the function. What comes out is a table saying where each value went, and the
96//! pass that rewrites the operands and writes the moves reads it. Keeping the decision and the
97//! rewrite apart is what lets the decision be checked by looking at it, and it is the shape
98//! `spec/10-backend.md` section 10.4 asks for: an allocator is a function from a program to an
99//! assignment and the moves that make it true.
100
101use rucc_mir::{Block, Constraint, Func, Operand, Reg, Role};
102use rucc_target::{PhysReg, RegClass};
103
104use crate::live::{Live, Range};
105use crate::order::{Order, Point};
106
107/// Where a value lives.
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub enum Place {
110    /// In a register, for the whole of its range.
111    Reg(PhysReg),
112    /// In a slot of the frame, which is what a value the allocator ran out of registers for gets,
113    /// and what a value an instruction can only read from memory gets.
114    Slot(u32),
115}
116
117/// What the allocator is allowed to use.
118///
119/// The order is the calling convention's, because which register to hand out first follows from
120/// which ones a call destroys, and `rucc-target` is where a convention says so. The scratch
121/// registers are held back out of the order and are what a spilled value is read into at each
122/// instruction that wants it, so a class needs as many of them as one of its instructions has
123/// register operands. Nothing here uses them, since a spilled value is only read once the rewrite
124/// is writing the instruction that reads it, but they are held back here because this is what
125/// decides what everything else may have.
126#[derive(Debug, Clone, Default)]
127pub struct Env {
128    classes: Vec<Class>,
129}
130
131/// What one class of registers offers.
132#[derive(Debug, Clone, Default)]
133struct Class {
134    order: Vec<PhysReg>,
135    scratch: Vec<PhysReg>,
136}
137
138impl Env {
139    /// An environment offering nothing, which is what a target that has said nothing offers.
140    #[must_use]
141    pub fn new() -> Self {
142        Self::default()
143    }
144
145    /// The same environment, with that class described.
146    #[must_use]
147    pub fn with(mut self, class: RegClass, order: &[PhysReg], scratch: &[PhysReg]) -> Self {
148        let index = usize::from(class.number());
149        if self.classes.len() <= index {
150            self.classes.resize(index + 1, Class::default());
151        }
152        self.classes[index] = Class { order: order.to_vec(), scratch: scratch.to_vec() };
153        self
154    }
155
156    /// The registers it may hand out in a class, in the order it prefers them.
157    #[must_use]
158    pub fn order(&self, class: RegClass) -> &[PhysReg] {
159        self.classes.get(usize::from(class.number())).map_or(&[], |class| &class.order)
160    }
161
162    /// The registers held back in a class for reading a spilled value into.
163    #[must_use]
164    pub fn scratch(&self, class: RegClass) -> &[PhysReg] {
165        self.classes.get(usize::from(class.number())).map_or(&[], |class| &class.scratch)
166    }
167}
168
169/// Where every value in a function went.
170#[derive(Debug, Clone)]
171pub struct Assignment {
172    places: Vec<Option<Place>>,
173    slots: Vec<RegClass>,
174}
175
176impl Assignment {
177    /// An assignment that says nothing yet about a function with that many values.
178    ///
179    /// This and [`Assignment::put`] and [`Assignment::take_slot`] are how an allocator says what
180    /// it decided. There will be a second one in M4 and it will not reach its answer this way, so
181    /// what an assignment is has to be separable from how this file arrives at one, and the
182    /// checker in [`crate::check`] reads an assignment without caring which allocator wrote it.
183    #[must_use]
184    pub fn empty(vregs: usize) -> Self {
185        Self { places: vec![None; vregs], slots: Vec::new() }
186    }
187
188    /// Records where a value went.
189    ///
190    /// # Panics
191    ///
192    /// Panics on a physical register, which is somewhere already, and on a virtual one the
193    /// function never handed out.
194    pub fn put(&mut self, reg: Reg, place: Place) {
195        self.places[index(reg)] = Some(place);
196    }
197
198    /// Takes a slot of the frame, of that class, and gives back which one it is.
199    ///
200    /// # Panics
201    ///
202    /// Panics past four billion slots, which is a frame no machine has room for.
203    pub fn take_slot(&mut self, class: RegClass) -> u32 {
204        let slot = u32::try_from(self.slots.len()).expect("too many spilled values");
205        self.slots.push(class);
206        slot
207    }
208
209    /// Where a value lives, or `None` for a virtual register this function never mentions and for
210    /// a physical one, which is already where it is.
211    #[must_use]
212    pub fn place(&self, reg: Reg) -> Option<Place> {
213        self.places.get(usize::try_from(reg.number()?).ok()?).copied().flatten()
214    }
215
216    /// The class of each slot of the frame, which is what says how wide it has to be.
217    #[must_use]
218    pub fn slots(&self) -> &[RegClass] {
219        &self.slots
220    }
221
222    /// How many values went to the stack.
223    #[must_use]
224    pub fn spilled(&self) -> usize {
225        self.slots.len()
226    }
227
228    /// Puts a value on the stack, in a slot of its own.
229    fn spill(&mut self, reg: Reg, class: RegClass) {
230        let slot = self.take_slot(class);
231        self.put(reg, Place::Slot(slot));
232    }
233}
234
235/// One value waiting for a place.
236#[derive(Debug, Clone, Copy)]
237struct Interval {
238    reg: Reg,
239    class: RegClass,
240    range: Range,
241}
242
243/// One value that has a register, for as long as it still wants it.
244#[derive(Debug, Clone, Copy)]
245struct Held {
246    reg: Reg,
247    class: RegClass,
248    range: Range,
249    at: PhysReg,
250}
251
252/// A register an instruction insists on, and where it insists on it.
253#[derive(Debug, Clone, Copy)]
254struct Blocked {
255    class: RegClass,
256    at: PhysReg,
257    /// One of the instruction's two points. Every register an instruction insists on has an entry
258    /// at each of them, because a register held at one of the two is a register nothing else may
259    /// be in across the instruction.
260    point: Point,
261    /// The one value that may be in it there, which is the value of an operand the instruction
262    /// reads at that point or writes at it. `None` means nothing may: an operand naming a physical
263    /// register outright claims it against everything, and a point no operand covers is a point
264    /// the instruction has the register to itself at.
265    by: Option<Reg>,
266    /// The block the point is in, which is what says whether a value whose interval covers the
267    /// point is really live there. See `crate::live::Live::anywhere_in`.
268    block: Block,
269}
270
271/// A value written into the register another operand of the same instruction was read from.
272#[derive(Debug, Clone, Copy)]
273struct Reuse {
274    /// The value being read, which is the one whose register would do.
275    source: Reg,
276    /// Where the instruction reads it.
277    at: Point,
278}
279
280/// Decides where every value in a function lives.
281///
282/// # Panics
283///
284/// Panics if a class has no registers to hand out and something in the function is in that class,
285/// since that is a target description that does not describe the target the function is for.
286#[must_use]
287pub fn assign(func: &Func, order: &Order, live: &Live, env: &Env) -> Assignment {
288    let blocked = blocked(func, order);
289    let forced = forced(func);
290    let reuses = reuses(func, order);
291    let hints = hints(func);
292
293    let mut intervals = Vec::with_capacity(func.vregs());
294    for (number, reuse) in reuses.iter().enumerate() {
295        let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
296        let (Some(mut range), Some(class)) = (live.range(reg), func.class_of(reg)) else {
297            continue;
298        };
299        if let Some(reuse) = reuse {
300            range.start = range.start.min(reuse.at);
301        }
302        intervals.push(Interval { reg, class, range });
303    }
304    intervals.sort_by_key(|interval| (interval.range.start, interval.reg));
305
306    let mut assignment = Assignment::empty(func.vregs());
307    let mut active: Vec<Held> = Vec::new();
308    for interval in intervals {
309        active.retain(|held| held.range.end >= interval.range.start);
310        if forced.contains(&interval.reg) {
311            assignment.spill(interval.reg, interval.class);
312            continue;
313        }
314        // A class with no order is one the target says nothing allocates from, which on x86-64 is
315        // the x87 stack. A value of such a class is a mistake at the point it was made rather than
316        // a value with nowhere to go: what the target means is that the value lives in memory and
317        // that whatever operates on it takes an address. See `ClassInfo::allocatable`.
318        assert!(
319            !env.order(interval.class).is_empty(),
320            "a value in class {}, which the target hands out no registers from",
321            interval.class.number()
322        );
323        let two_address = reuses[index(interval.reg)]
324            .and_then(|reuse| coalesce(&assignment, &active, &blocked, live, interval, reuse));
325        // The reuse comes first, because a two address instruction that has to copy its left
326        // operand in pays for the copy whatever the hint says, and taking the hint here would buy
327        // one move at the cost of another.
328        let hinted = hints[index(interval.reg)].filter(|&at| {
329            env.order(interval.class).contains(&at)
330                && available(&active, &blocked, live, interval, at, None, Want::Clear)
331        });
332        // A register nobody else wants anywhere near this value first, and one somebody wants
333        // somewhere the value never goes only when there is no other. Both are correct and the
334        // second is the worse buy, since the instruction that wants it has to be handed it and
335        // whatever this value is doing there has to move out of the way first.
336        let scan = |want| {
337            env.order(interval.class)
338                .iter()
339                .copied()
340                .find(|&at| available(&active, &blocked, live, interval, at, None, want))
341        };
342        let chosen =
343            two_address.or(hinted).or_else(|| scan(Want::Clear)).or_else(|| scan(Want::Allowed));
344        match chosen {
345            Some(at) => {
346                assignment.places[index(interval.reg)] = Some(Place::Reg(at));
347                let reg = interval.reg;
348                active.push(Held { reg, class: interval.class, range: interval.range, at });
349            }
350            None => spill_one(&mut assignment, &mut active, &blocked, live, interval),
351        }
352    }
353    assignment
354}
355
356/// How much a register suits an interval.
357#[derive(Debug, Clone, Copy, PartialEq, Eq)]
358enum Want {
359    /// Nothing insists on it anywhere the range reaches, so taking it costs nobody anything.
360    Clear,
361    /// Something insists on it somewhere the range reaches and nowhere the value is live, so taking
362    /// it is allowed and may still cost: the instruction that insists wants the register for a
363    /// value of its own, and that value now has to be moved into it.
364    Allowed,
365}
366
367/// Every register every instruction in the function insists on, arranged to be asked about.
368///
369/// Built once and never changed afterwards, and there is only one question ever asked of it: of the
370/// constraints naming one register of one class, is there one at a point some interval covers. So
371/// the entries are ordered by the register they name and then by the point, and the question is a
372/// binary search for the start of the interval followed by a walk that stops at its end.
373///
374/// It used to be a flat list walked from one end for every candidate register of every interval,
375/// which is quadratic in the size of a function and is most of the compile on a large one. See
376/// tamnd/rucc#1003 for the profile that found it.
377struct Blocks {
378    /// The constraints, sorted by class, then by register, then by point.
379    all: Vec<Blocked>,
380}
381
382impl Blocks {
383    /// The constraints on one register of one class at the points an interval covers.
384    ///
385    /// Both ends of the walk come from the ordering rather than from a test, so what comes back is
386    /// exactly what the old `covers` call used to keep and in the same order.
387    fn over(
388        &self,
389        class: RegClass,
390        at: PhysReg,
391        range: Range,
392    ) -> impl Iterator<Item = &Blocked> + '_ {
393        let first = self
394            .all
395            .partition_point(|one| (one.class, one.at, one.point) < (class, at, range.start));
396        self.all[first..]
397            .iter()
398            .take_while(move |one| one.class == class && one.at == at && one.point <= range.end)
399    }
400}
401
402/// Whether a register is one this interval could have.
403///
404/// The exception is the value a reuse is coalescing with, which holds the register right up to the
405/// point the new value takes it over and is the one thing that may overlap.
406fn available(
407    active: &[Held],
408    blocked: &Blocks,
409    live: &Live,
410    interval: Interval,
411    at: PhysReg,
412    except: Option<Reg>,
413    want: Want,
414) -> bool {
415    let taken = active
416        .iter()
417        .any(|held| held.at == at && held.class == interval.class && Some(held.reg) != except);
418    let insisted = blocked.over(interval.class, at, interval.range).any(|one| {
419        one.by != Some(interval.reg)
420            && (want == Want::Clear || live.anywhere_in(interval.reg, one.block))
421    });
422    !taken && !insisted
423}
424
425/// The register the value being reused is in, when this instruction is the last thing that reads
426/// it, the value being written starts here, and the register is otherwise free.
427fn coalesce(
428    assignment: &Assignment,
429    active: &[Held],
430    blocked: &Blocks,
431    live: &Live,
432    interval: Interval,
433    reuse: Reuse,
434) -> Option<PhysReg> {
435    let Some(Place::Reg(at)) = assignment.place(reuse.source) else { return None };
436    let source = active.iter().find(|held| held.reg == reuse.source)?;
437    // A value read again later needs its register after this instruction would have overwritten
438    // it, so the two really do have to be different and the rewrite really does have to copy.
439    let dies = source.range.end == reuse.at;
440    // And the value being written has to begin here. The interval start was already pulled back to
441    // the reuse point above, so a start still earlier than that is a value that was live on the way
442    // into this instruction, which is what a loop carrying its own result round looks like: the
443    // instruction writes it at the bottom and the top of the loop reads what the last turn wrote.
444    // Such a value overlaps the one it reuses over the whole loop, so the two cannot be the same
445    // register no matter that the read here is the last one.
446    let begins = interval.range.start == reuse.at;
447    let free = available(active, blocked, live, interval, at, Some(reuse.source), Want::Allowed);
448    (dies && begins && free).then_some(at)
449}
450
451/// Sends one value to the stack: the one wanted for longest, since its register pays for itself
452/// over the most instructions.
453fn spill_one(
454    assignment: &mut Assignment,
455    active: &mut Vec<Held>,
456    blocked: &Blocks,
457    live: &Live,
458    interval: Interval,
459) {
460    // A value whose register the instructions in the way insist on for themselves is no use as a
461    // victim, because taking it over would put this value in a register it may not have.
462    let victim = active
463        .iter()
464        .enumerate()
465        .filter(|(_, held)| held.class == interval.class)
466        .filter(|(_, held)| available(&[], blocked, live, interval, held.at, None, Want::Allowed))
467        .max_by_key(|(_, held)| held.range.end)
468        .map(|(at, held)| (at, held.at, held.range.end));
469    match victim {
470        Some((victim, at, end)) if end > interval.range.end => {
471            let held = active.remove(victim);
472            assignment.spill(held.reg, held.class);
473            assignment.places[index(interval.reg)] = Some(Place::Reg(at));
474            let reg = interval.reg;
475            active.push(Held { reg, class: interval.class, range: interval.range, at });
476        }
477        _ => assignment.spill(interval.reg, interval.class),
478    }
479}
480
481/// The registers the instructions insist on, and where.
482///
483/// A physical register an operand names outright counts the same way. Nothing before allocation
484/// writes one except an instruction that has to, and it has to for the length of that one
485/// instruction, which is the same statement a fixed constraint makes.
486fn blocked(func: &Func, order: &Order) -> Blocks {
487    let mut blocked = Vec::new();
488    let mut claimed: Vec<(RegClass, PhysReg)> = Vec::new();
489    for block in func.blocks() {
490        for inst in func.insts(block) {
491            let operands = &func[func[inst].operands];
492            claimed.clear();
493            for operand in operands {
494                if let Some(at) = insisted(operand) {
495                    let key = (operand.class, at);
496                    if !claimed.contains(&key) {
497                        claimed.push(key);
498                    }
499                }
500            }
501            for &(class, at) in &claimed {
502                // Both points, whether or not an operand is at them. A register an instruction
503                // reads and does not write is destroyed by the time the instruction is done as far
504                // as anything here knows, which is what stops the value a call is passed in `rdi`
505                // from staying in `rdi` over the call.
506                for (point, role) in [(order.early(inst), Role::Use), (order.late(inst), Role::Def)]
507                {
508                    let mut named = false;
509                    for operand in operands {
510                        let mine = insisted(operand) == Some(at) && operand.class == class;
511                        if !mine || !(operand.role == role || operand.role == Role::EarlyDef) {
512                            continue;
513                        }
514                        named = true;
515                        let by = operand.reg.is_virtual().then_some(operand.reg);
516                        blocked.push(Blocked { class, at, point, by, block });
517                    }
518                    if !named {
519                        blocked.push(Blocked { class, at, point, by: None, block });
520                    }
521                }
522            }
523        }
524    }
525    // Program order already has the points ascending, but the registers one instruction claims are
526    // walked outside the two points rather than inside them, so the list arrives in order by
527    // instruction and not by register. A sort by the key the lookup searches on is what makes it
528    // searchable, and it is stable so two constraints on one register at one point keep the order
529    // the instruction wrote them in.
530    blocked.sort_by_key(|one: &Blocked| (one.class, one.at, one.point));
531    Blocks { all: blocked }
532}
533
534/// The register an operand has to be in, which is the one a constraint asks for or the one the
535/// operand names outright.
536fn insisted(operand: &Operand) -> Option<PhysReg> {
537    match operand.constraint {
538        Constraint::Fixed(at) => Some(at),
539        _ => operand.reg.phys(),
540    }
541}
542
543/// The register each value would rather be in, which is the one an operand naming it insists on.
544///
545/// A value with two of them keeps the first the function writes down, which is the definition when
546/// there is one, since a value written into a fixed register and then moved somewhere else pays
547/// for the move at the top of its life rather than at the bottom. Two different fixed registers on
548/// one value is rare enough that the second is not worth carrying a list for.
549fn hints(func: &Func) -> Vec<Option<PhysReg>> {
550    let mut hints = vec![None; func.vregs()];
551    for block in func.blocks() {
552        for inst in func.insts(block) {
553            for operand in &func[func[inst].operands] {
554                let Constraint::Fixed(at) = operand.constraint else { continue };
555                let number = operand.reg.number().and_then(|number| usize::try_from(number).ok());
556                let Some(number) = number else { continue };
557                if func.class_of(operand.reg) == Some(operand.class) && hints[number].is_none() {
558                    hints[number] = Some(at);
559                }
560            }
561        }
562    }
563    hints
564}
565
566/// The values that have to be on the stack whatever else is true of them.
567fn forced(func: &Func) -> Vec<Reg> {
568    let mut forced = Vec::new();
569    for block in func.blocks() {
570        for inst in func.insts(block) {
571            for operand in &func[func[inst].operands] {
572                if operand.constraint == Constraint::Stack
573                    && operand.reg.is_virtual()
574                    && !forced.contains(&operand.reg)
575                {
576                    forced.push(operand.reg);
577                }
578            }
579        }
580    }
581    forced
582}
583
584/// The value each two address instruction reuses, by the virtual register it writes.
585fn reuses(func: &Func, order: &Order) -> Vec<Option<Reuse>> {
586    let mut reuses = vec![None; func.vregs()];
587    for block in func.blocks() {
588        for inst in func.insts(block) {
589            let operands = &func[func[inst].operands];
590            for operand in operands {
591                let Constraint::Reuse(other) = operand.constraint else { continue };
592                let number = operand.reg.number().and_then(|number| usize::try_from(number).ok());
593                let Some(number) = number else { continue };
594                let source = operands[usize::from(other)].reg;
595                reuses[number] = Some(Reuse { source, at: order.early(inst) });
596            }
597        }
598    }
599    reuses
600}
601
602/// A virtual register's number as a table index.
603fn index(reg: Reg) -> usize {
604    usize::try_from(reg.number().expect("a virtual register")).expect("a register number")
605}
606
607#[cfg(test)]
608mod tests {
609    use rucc_base::Interner;
610    use rucc_mir::{BlockCall, Opcode, Operand};
611    use rucc_target::x86_64::{GPR, R13, R14, R15, RAX, RCX, RDX, REGS, SYSV};
612
613    use super::*;
614
615    /// The x86-64 environment, with the last three of the allocation order held back as scratch.
616    fn env() -> Env {
617        let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
618        Env::new().with(GPR, order, scratch)
619    }
620
621    /// An environment with that many general purpose registers, for putting a function under
622    /// pressure without writing a hundred instructions.
623    fn narrow(count: usize) -> Env {
624        Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 1])
625    }
626
627    /// What a place is called, which is what an assertion reads.
628    fn named(place: Option<Place>) -> String {
629        match place {
630            Some(Place::Reg(reg)) => REGS.name(GPR, reg).expect("a register").to_string(),
631            Some(Place::Slot(slot)) => format!("slot {slot}"),
632            None => "nowhere".to_string(),
633        }
634    }
635
636    /// Where every value in a function went.
637    fn places(func: &Func, env: &Env) -> Vec<String> {
638        let order = Order::of(func);
639        let live = Live::of(func, &order);
640        let assignment = assign(func, &order, &live, env);
641        (0..func.vregs())
642            .map(|number| {
643                let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
644                named(assignment.place(reg))
645            })
646            .collect()
647    }
648
649    #[test]
650    fn two_values_that_are_never_both_wanted_share_a_register() {
651        let mut names = Interner::new();
652        let mut func = Func::new(names.intern("f"));
653        let opcode = Opcode::new(names.intern("x64.nop"));
654        let block = func.create_block();
655        let first = func.new_vreg(GPR);
656        let second = func.new_vreg(GPR);
657        func.build(block, opcode).def(first, GPR).finish();
658        func.build(block, opcode).uses(first, GPR).finish();
659        func.build(block, opcode).def(second, GPR).finish();
660        func.build(block, opcode).uses(second, GPR).finish();
661
662        // The first register in the order, twice, because the first value is finished with before
663        // the second one is written.
664        assert_eq!(places(&func, &env()), ["rax", "rax"]);
665    }
666
667    #[test]
668    fn two_values_that_are_both_wanted_do_not() {
669        let mut names = Interner::new();
670        let mut func = Func::new(names.intern("f"));
671        let opcode = Opcode::new(names.intern("x64.nop"));
672        let block = func.create_block();
673        let first = func.new_vreg(GPR);
674        let second = func.new_vreg(GPR);
675        func.build(block, opcode).def(first, GPR).finish();
676        func.build(block, opcode).def(second, GPR).finish();
677        func.build(block, opcode).uses(first, GPR).finish();
678        func.build(block, opcode).uses(second, GPR).finish();
679
680        assert_eq!(places(&func, &env()), ["rax", "rcx"]);
681    }
682
683    #[test]
684    fn a_value_written_early_that_nothing_reads_still_holds_its_register() {
685        let mut names = Interner::new();
686        let mut func = Func::new(names.intern("f"));
687        let opcode = Opcode::new(names.intern("x64.nop"));
688        let block = func.create_block();
689        let wanted = func.new_vreg(GPR);
690        let spare = func.new_vreg(GPR);
691        // A division: a remainder somebody wants, and a quotient nobody does. Both are written by
692        // the one instruction and the quotient is written before the operands have been read.
693        func.build(block, opcode)
694            .def(wanted, GPR)
695            .operand(Operand::write_early(spare, GPR))
696            .finish();
697        func.build(block, opcode).uses(wanted, GPR).finish();
698
699        // Two registers, not one. A value nothing reads is still somewhere, and the instruction
700        // that wrote it wrote the other one too, so the two cannot be the same place. Handing them
701        // the same register loses the remainder, because the copy that takes the quotient out of
702        // the register the machine insisted on goes on top of it. The quotient gets the first
703        // register because it is written first, which is the whole of what early means.
704        assert_eq!(places(&func, &env()), ["rcx", "rax"]);
705    }
706
707    #[test]
708    fn the_value_wanted_longest_is_the_one_that_goes_to_the_stack() {
709        let mut names = Interner::new();
710        let mut func = Func::new(names.intern("f"));
711        let opcode = Opcode::new(names.intern("x64.nop"));
712        let block = func.create_block();
713        let long = func.new_vreg(GPR);
714        let short = func.new_vreg(GPR);
715        let third = func.new_vreg(GPR);
716        func.build(block, opcode).def(long, GPR).finish();
717        func.build(block, opcode).def(short, GPR).finish();
718        func.build(block, opcode).def(third, GPR).finish();
719        func.build(block, opcode).uses(short, GPR).finish();
720        func.build(block, opcode).uses(third, GPR).finish();
721        func.build(block, opcode).uses(long, GPR).finish();
722
723        // Two registers between three values. The one still wanted at the end of the function is
724        // the one whose register is worth the most to everybody else, so it is the one that goes.
725        assert_eq!(places(&func, &narrow(2)), ["slot 0", "rcx", "rax"]);
726    }
727
728    #[test]
729    fn a_register_an_instruction_insists_on_goes_to_the_values_that_asked_for_it() {
730        let mut names = Interner::new();
731        let mut func = Func::new(names.intern("f"));
732        let opcode = Opcode::new(names.intern("x64.nop"));
733        let block = func.create_block();
734        let across = func.new_vreg(GPR);
735        let dividend = func.new_vreg(GPR);
736        let quotient = func.new_vreg(GPR);
737        let remainder = func.new_vreg(GPR);
738        func.build(block, opcode).def(across, GPR).finish();
739        func.build(block, opcode).def(dividend, GPR).finish();
740        func.build(block, opcode)
741            .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
742            .operand(Operand::write_early(remainder, GPR).with(Constraint::Fixed(RDX)))
743            .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
744            .finish();
745        func.build(block, opcode).uses(across, GPR).finish();
746
747        // The value that has to be across the division is nowhere near `rax` or `rdx`, and each of
748        // the three the division names is in the register the division asked for it in. The
749        // dividend and the quotient share `rax` because the first is read where the second is
750        // written, which is what a division does.
751        assert_eq!(places(&func, &env()), ["rcx", "rax", "rax", "rdx"]);
752    }
753
754    #[test]
755    fn a_value_wanted_after_the_instruction_that_insists_does_not_get_that_register() {
756        let mut names = Interner::new();
757        let mut func = Func::new(names.intern("f"));
758        let opcode = Opcode::new(names.intern("x64.nop"));
759        let block = func.create_block();
760        let dividend = func.new_vreg(GPR);
761        let quotient = func.new_vreg(GPR);
762        func.build(block, opcode).def(dividend, GPR).finish();
763        func.build(block, opcode)
764            .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
765            .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
766            .finish();
767        func.build(block, opcode).uses(dividend, GPR).finish();
768
769        // The hint is a preference and not a claim. The dividend would rather be in `rax` and
770        // cannot be, because the division writes `rax` and the dividend is wanted afterwards, so
771        // it takes the next register and the quotient keeps the one it was promised.
772        assert_eq!(places(&func, &env()), ["rcx", "rax"]);
773    }
774
775    #[test]
776    fn a_value_an_instruction_can_only_read_from_memory_is_on_the_stack() {
777        let mut names = Interner::new();
778        let mut func = Func::new(names.intern("f"));
779        let opcode = Opcode::new(names.intern("x64.nop"));
780        let block = func.create_block();
781        let value = func.new_vreg(GPR);
782        func.build(block, opcode).def(value, GPR).finish();
783        func.build(block, opcode)
784            .operand(Operand::read(value, GPR).with(Constraint::Stack))
785            .finish();
786
787        assert_eq!(places(&func, &env()), ["slot 0"]);
788    }
789
790    #[test]
791    fn a_two_address_instruction_writes_the_register_it_read_when_it_can() {
792        let mut names = Interner::new();
793        let mut func = Func::new(names.intern("f"));
794        let opcode = Opcode::new(names.intern("x64.nop"));
795        let block = func.create_block();
796        let left = func.new_vreg(GPR);
797        let right = func.new_vreg(GPR);
798        let sum = func.new_vreg(GPR);
799        func.build(block, opcode).def(left, GPR).finish();
800        func.build(block, opcode).def(right, GPR).finish();
801        func.build(block, opcode)
802            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
803            .uses(left, GPR)
804            .uses(right, GPR)
805            .finish();
806        func.build(block, opcode).uses(right, GPR).finish();
807
808        // The addition reads the left value for the last time, so the answer goes where that was
809        // and the instruction is two address without a move in front of it.
810        assert_eq!(places(&func, &env()), ["rax", "rcx", "rax"]);
811    }
812
813    #[test]
814    fn a_two_address_instruction_that_cannot_gets_a_register_nothing_it_reads_is_in() {
815        let mut names = Interner::new();
816        let mut func = Func::new(names.intern("f"));
817        let opcode = Opcode::new(names.intern("x64.nop"));
818        let block = func.create_block();
819        let left = func.new_vreg(GPR);
820        let right = func.new_vreg(GPR);
821        let sum = func.new_vreg(GPR);
822        func.build(block, opcode).def(left, GPR).finish();
823        func.build(block, opcode).def(right, GPR).finish();
824        func.build(block, opcode)
825            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
826            .uses(left, GPR)
827            .uses(right, GPR)
828            .finish();
829        func.build(block, opcode).uses(left, GPR).finish();
830
831        // The left value is wanted afterwards, so the answer cannot have its register. It cannot
832        // have the right one's either, because the rewrite is about to write a move into it before
833        // the addition has read anything.
834        assert_eq!(places(&func, &env()), ["rax", "rcx", "rdx"]);
835    }
836
837    #[test]
838    fn a_value_live_across_a_whole_loop_holds_its_register_over_all_of_it() {
839        let mut names = Interner::new();
840        let mut func = Func::new(names.intern("f"));
841        let opcode = Opcode::new(names.intern("x64.nop"));
842        let head = func.create_block();
843        let body = func.create_block();
844        let carried = func.new_vreg(GPR);
845        let inside = func.new_vreg(GPR);
846        func.build(head, opcode).def(carried, GPR).finish();
847        *func.succs_mut(head) = vec![BlockCall::to(body)];
848        func.build(body, opcode).def(inside, GPR).finish();
849        func.build(body, opcode).uses(inside, GPR).uses(carried, GPR).finish();
850        *func.succs_mut(body) = vec![BlockCall::to(body)];
851
852        // The value inside the loop cannot have the carried one's register, even though nothing
853        // between the two definitions says so.
854        assert_eq!(places(&func, &env()), ["rax", "rcx"]);
855    }
856
857    #[test]
858    fn a_two_address_answer_already_live_does_not_take_the_register_it_read() {
859        let mut names = Interner::new();
860        let mut func = Func::new(names.intern("f"));
861        let opcode = Opcode::new(names.intern("x64.nop"));
862        let head = func.create_block();
863        let latch = func.create_block();
864        let out = func.create_block();
865        let source = func.new_vreg(GPR);
866        let carried = func.new_vreg(GPR);
867        func.build(head, opcode).def(source, GPR).finish();
868        func.build(head, opcode).def(carried, GPR).finish();
869        *func.succs_mut(head) = vec![BlockCall::to(latch)];
870        // The bottom of the loop adds the source to the carried value and writes the answer back
871        // over it, reusing the register the source is in. The next turn round redefines both.
872        func.build(latch, opcode)
873            .operand(Operand::write(carried, GPR).with(Constraint::Reuse(1)))
874            .uses(source, GPR)
875            .uses(carried, GPR)
876            .finish();
877        *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
878        func.build(out, opcode).uses(carried, GPR).finish();
879
880        // The source is read here for the last time, which on its own is the shape the two address
881        // shortcut is for, and taking it would be wrong. The carried value was written by the same
882        // instruction on the last turn and is read by this one, so the two are both wanted where
883        // the instruction reads and one register cannot hold both.
884        assert_eq!(places(&func, &env()), ["rax", "rcx"]);
885
886        // And the checker has to agree, since it excused this pair on the same reasoning and so
887        // would have let the answer through.
888        let order = Order::of(&func);
889        let live = Live::of(&func, &order);
890        let assignment = assign(&func, &order, &live, &env());
891        assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
892    }
893
894    /// Two blocks the entry chooses between, with the one the clobber is in written first. The two
895    /// values written in the entry block are read in the other one, so their ranges cover the
896    /// clobber whether or not either of them ever reaches it.
897    fn arms(reaches: bool) -> Func {
898        let mut names = Interner::new();
899        let mut func = Func::new(names.intern("f"));
900        let opcode = Opcode::new(names.intern("x64.nop"));
901        let entry = func.create_block();
902        let arm = func.create_block();
903        let tail = func.create_block();
904        let first = func.new_vreg(GPR);
905        let second = func.new_vreg(GPR);
906        func.build(entry, opcode).def(first, GPR).finish();
907        func.build(entry, opcode).def(second, GPR).finish();
908        *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
909        // What a call looks like here: an instruction writing the registers the convention says it
910        // destroys, named outright so that nothing else may be in them.
911        func.build(arm, opcode).operand(Operand::write(Reg::physical(RAX), GPR)).finish();
912        *func.succs_mut(arm) = if reaches { vec![BlockCall::to(tail)] } else { Vec::new() };
913        func.build(tail, opcode).uses(first, GPR).uses(second, GPR).finish();
914        func
915    }
916
917    #[test]
918    fn a_register_a_clobber_takes_beats_the_stack_for_a_value_not_live_in_that_block() {
919        let func = arms(false);
920
921        // Two registers between two values, and a clobber in the arm that takes the first of them.
922        // The ranges both cover the clobber, since ranges have no holes and the arm is written
923        // between the two blocks the values are live in, and neither value is live in the arm. So
924        // the second value has `rax` rather than a stack slot: the arm is a block its own path
925        // never goes through. tamnd/rucc#982.
926        assert_eq!(places(&func, &narrow(2)), ["rcx", "rax"]);
927
928        let order = Order::of(&func);
929        let live = Live::of(&func, &order);
930        let assignment = assign(&func, &order, &live, &narrow(2));
931        assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
932    }
933
934    #[test]
935    fn a_register_a_clobber_takes_is_not_free_to_a_value_that_is_live_there() {
936        let func = arms(true);
937
938        // The same blocks with an edge from the arm to the tail, which is all it takes: both values
939        // now arrive at the read either way, so the clobber is on a path they are live over and the
940        // one register left has to do for both of them.
941        assert_eq!(places(&func, &narrow(2)), ["rcx", "slot 0"]);
942    }
943
944    #[test]
945    fn a_hint_is_followed_when_the_register_is_clear_and_not_when_it_is_merely_allowed() {
946        let mut names = Interner::new();
947        let mut func = Func::new(names.intern("f"));
948        let opcode = Opcode::new(names.intern("x64.nop"));
949        let entry = func.create_block();
950        let mid = func.create_block();
951        let tail = func.create_block();
952        let first = func.new_vreg(GPR);
953        let second = func.new_vreg(GPR);
954        func.build(entry, opcode).def(first, GPR).finish();
955        func.build(entry, opcode).def(second, GPR).finish();
956        *func.succs_mut(entry) = vec![BlockCall::to(mid), BlockCall::to(tail)];
957        // Two arms, each ending in an instruction that wants its own value in `rax`, which is what
958        // a return out of either side of a branch looks like.
959        func.build(mid, opcode)
960            .operand(Operand::read(second, GPR).with(Constraint::Fixed(RAX)))
961            .finish();
962        func.build(tail, opcode)
963            .operand(Operand::read(first, GPR).with(Constraint::Fixed(RAX)))
964            .finish();
965
966        // The first value is hinted at `rax` and does not get it, because the other arm wants `rax`
967        // for the other value and the first value's range reaches that far. Following the hint here
968        // would save a move in the tail and cost one in the middle, and the second value gets `rax`
969        // with nothing moved anywhere instead.
970        assert_eq!(places(&func, &env()), ["rcx", "rax"]);
971    }
972
973    #[test]
974    fn a_register_a_clobber_takes_is_the_last_one_offered_rather_than_the_first() {
975        let func = arms(false);
976
977        // With a register to spare the value takes the spare one. Being allowed a register some
978        // instruction insists on is not the same as it being free: the instruction has to be handed
979        // it in the end, and what hands it over is a move.
980        assert_eq!(places(&func, &narrow(3)), ["rcx", "rdx"]);
981    }
982
983    #[test]
984    fn a_frame_says_what_each_of_its_slots_is_for() {
985        let mut names = Interner::new();
986        let mut func = Func::new(names.intern("f"));
987        let opcode = Opcode::new(names.intern("x64.nop"));
988        let block = func.create_block();
989        let first = func.new_vreg(GPR);
990        let second = func.new_vreg(GPR);
991        func.build(block, opcode).def(first, GPR).finish();
992        func.build(block, opcode).def(second, GPR).finish();
993        func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
994
995        let order = Order::of(&func);
996        let live = Live::of(&func, &order);
997        let assignment = assign(&func, &order, &live, &narrow(1));
998        assert_eq!(assignment.spilled(), 1);
999        assert_eq!(assignment.slots(), [GPR]);
1000        // A register that is already a register is where it is, and this has nothing to say about
1001        // it.
1002        assert_eq!(assignment.place(Reg::physical(RCX)), None);
1003        assert_eq!(env().scratch(GPR), [R13, R14, R15]);
1004    }
1005}