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. 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, Func, 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 held back in a class for reading a spilled value into.
169    #[must_use]
170    pub fn scratch(&self, class: RegClass) -> &[PhysReg] {
171        self.classes.get(usize::from(class.number())).map_or(&[], |class| &class.scratch)
172    }
173}
174
175/// Where every value in a function went.
176#[derive(Debug, Clone)]
177pub struct Assignment {
178    places: Vec<Option<Place>>,
179    slots: Vec<RegClass>,
180}
181
182impl Assignment {
183    /// An assignment that says nothing yet about a function with that many values.
184    ///
185    /// This and [`Assignment::put`] and [`Assignment::take_slot`] are how an allocator says what
186    /// it decided. There will be a second one in M4 and it will not reach its answer this way, so
187    /// what an assignment is has to be separable from how this file arrives at one, and the
188    /// checker in [`crate::check`] reads an assignment without caring which allocator wrote it.
189    #[must_use]
190    pub fn empty(vregs: usize) -> Self {
191        Self { places: vec![None; vregs], slots: Vec::new() }
192    }
193
194    /// Records where a value went.
195    ///
196    /// # Panics
197    ///
198    /// Panics on a physical register, which is somewhere already, and on a virtual one the
199    /// function never handed out.
200    pub fn put(&mut self, reg: Reg, place: Place) {
201        self.places[index(reg)] = Some(place);
202    }
203
204    /// Takes a slot of the frame, of that class, and gives back which one it is.
205    ///
206    /// # Panics
207    ///
208    /// Panics past four billion slots, which is a frame no machine has room for.
209    pub fn take_slot(&mut self, class: RegClass) -> u32 {
210        let slot = u32::try_from(self.slots.len()).expect("too many spilled values");
211        self.slots.push(class);
212        slot
213    }
214
215    /// Where a value lives, or `None` for a virtual register this function never mentions and for
216    /// a physical one, which is already where it is.
217    #[must_use]
218    pub fn place(&self, reg: Reg) -> Option<Place> {
219        self.places.get(usize::try_from(reg.number()?).ok()?).copied().flatten()
220    }
221
222    /// The class of each slot of the frame, which is what says how wide it has to be.
223    #[must_use]
224    pub fn slots(&self) -> &[RegClass] {
225        &self.slots
226    }
227
228    /// How many values went to the stack.
229    #[must_use]
230    pub fn spilled(&self) -> usize {
231        self.slots.len()
232    }
233
234    /// Puts a value on the stack, in a slot of its own.
235    fn spill(&mut self, reg: Reg, class: RegClass) {
236        let slot = self.take_slot(class);
237        self.put(reg, Place::Slot(slot));
238    }
239}
240
241/// One value waiting for a place.
242#[derive(Debug, Clone, Copy)]
243struct Interval<'a> {
244    reg: Reg,
245    class: RegClass,
246    /// The interval around the area, which is what the sweep below reads and what says which value
247    /// is wanted for longest when one of them has to go.
248    range: Range,
249    /// Everywhere the value is really live, which is what says whether two of them fit in one
250    /// register.
251    area: Area<'a>,
252}
253
254/// One value that has a register, for as long as it still wants it.
255#[derive(Debug, Clone, Copy)]
256struct Held<'a> {
257    reg: Reg,
258    class: RegClass,
259    range: Range,
260    area: Area<'a>,
261    at: PhysReg,
262}
263
264/// A register an instruction insists on, and where it insists on it.
265#[derive(Debug, Clone, Copy)]
266struct Blocked {
267    class: RegClass,
268    at: PhysReg,
269    /// One of the instruction's two points. Every register an instruction insists on has an entry
270    /// at each of them, because a register held at one of the two is a register nothing else may
271    /// be in across the instruction.
272    point: Point,
273    /// The one value that may be in it there, which is the value of an operand the instruction
274    /// reads at that point or writes at it. `None` means nothing may: an operand naming a physical
275    /// register outright claims it against everything, and a point no operand covers is a point
276    /// the instruction has the register to itself at.
277    by: Option<Reg>,
278}
279
280/// A value written into the register another operand of the same instruction was read from.
281#[derive(Debug, Clone, Copy)]
282struct Reuse {
283    /// The value being read, which is the one whose register would do.
284    source: Reg,
285    /// Where the instruction reads it.
286    at: Point,
287}
288
289/// Decides where every value in a function lives.
290///
291/// # Panics
292///
293/// Panics if a class has no registers to hand out and something in the function is in that class,
294/// since that is a target description that does not describe the target the function is for.
295#[must_use]
296pub fn assign(func: &Func, order: &Order, live: &Live, env: &Env) -> Assignment {
297    let blocked = blocked(func, order);
298    let forced = forced(func);
299    let reuses = reuses(func, order);
300    let hints = hints(func);
301
302    let mut intervals = Vec::with_capacity(func.vregs());
303    for (number, reuse) in reuses.iter().enumerate() {
304        let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
305        let (Some(mut area), Some(class)) = (live.area(reg), func.class_of(reg)) else {
306            continue;
307        };
308        if let Some(reuse) = reuse {
309            area = area.with(reuse.at);
310        }
311        intervals.push(Interval { reg, class, range: area.hull(), area });
312    }
313    intervals.sort_by_key(|interval| (interval.range.start, interval.reg));
314
315    let mut assignment = Assignment::empty(func.vregs());
316    let mut active: Vec<Held<'_>> = Vec::new();
317    for interval in intervals {
318        active.retain(|held| held.range.end >= interval.range.start);
319        if forced.contains(&interval.reg) {
320            assignment.spill(interval.reg, interval.class);
321            continue;
322        }
323        // A class with no order is one the target says nothing allocates from, which on x86-64 is
324        // the x87 stack. A value of such a class is a mistake at the point it was made rather than
325        // a value with nowhere to go: what the target means is that the value lives in memory and
326        // that whatever operates on it takes an address. See `ClassInfo::allocatable`.
327        assert!(
328            !env.order(interval.class).is_empty(),
329            "a value in class {}, which the target hands out no registers from",
330            interval.class.number()
331        );
332        let two_address = reuses[index(interval.reg)]
333            .and_then(|reuse| coalesce(&assignment, &active, &blocked, live, interval, reuse));
334        // The reuse comes first, because a two address instruction that has to copy its left
335        // operand in pays for the copy whatever the hint says, and taking the hint here would buy
336        // one move at the cost of another.
337        let hinted = hints[index(interval.reg)].filter(|&at| {
338            env.order(interval.class).contains(&at)
339                && available(&active, &blocked, interval, at, None, Want::Clear)
340        });
341        // A register nobody else wants anywhere near this value first, and one somebody wants
342        // somewhere the value never goes only when there is no other. Both are correct and the
343        // second is the worse buy, since the instruction that wants it has to be handed it and
344        // whatever this value is doing there has to move out of the way first.
345        let scan = |want| {
346            env.order(interval.class)
347                .iter()
348                .copied()
349                .find(|&at| available(&active, &blocked, interval, at, None, want))
350        };
351        let chosen =
352            two_address.or(hinted).or_else(|| scan(Want::Clear)).or_else(|| scan(Want::Allowed));
353        match chosen {
354            Some(at) => {
355                assignment.places[index(interval.reg)] = Some(Place::Reg(at));
356                active.push(Held {
357                    reg: interval.reg,
358                    class: interval.class,
359                    range: interval.range,
360                    area: interval.area,
361                    at,
362                });
363            }
364            None => spill_one(&mut assignment, &mut active, &blocked, interval),
365        }
366    }
367    assignment
368}
369
370/// How much a register suits an interval.
371#[derive(Debug, Clone, Copy, PartialEq, Eq)]
372enum Want {
373    /// Nothing insists on it anywhere the range reaches, so taking it costs nobody anything.
374    Clear,
375    /// Something insists on it somewhere the range reaches and nowhere the value is live, so taking
376    /// it is allowed and may still cost: the instruction that insists wants the register for a
377    /// value of its own, and that value now has to be moved into it.
378    Allowed,
379}
380
381/// Every register every instruction in the function insists on, arranged to be asked about.
382///
383/// Built once and never changed afterwards, and there is only one question ever asked of it: of the
384/// constraints naming one register of one class, is there one at a point some interval covers. So
385/// the entries are ordered by the register they name and then by the point, and the question is a
386/// binary search for the start of the interval followed by a walk that stops at its end.
387///
388/// It used to be a flat list walked from one end for every candidate register of every interval,
389/// which is quadratic in the size of a function and is most of the compile on a large one. See
390/// tamnd/rucc#1003 for the profile that found it.
391struct Blocks {
392    /// The constraints, sorted by class, then by register, then by point.
393    all: Vec<Blocked>,
394}
395
396impl Blocks {
397    /// The constraints on one register of one class at the points an interval covers.
398    ///
399    /// Both ends of the walk come from the ordering rather than from a test, so what comes back is
400    /// exactly what the old `covers` call used to keep and in the same order.
401    fn over(
402        &self,
403        class: RegClass,
404        at: PhysReg,
405        range: Range,
406    ) -> impl Iterator<Item = &Blocked> + '_ {
407        let first = self
408            .all
409            .partition_point(|one| (one.class, one.at, one.point) < (class, at, range.start));
410        self.all[first..]
411            .iter()
412            .take_while(move |one| one.class == class && one.at == at && one.point <= range.end)
413    }
414}
415
416/// Whether a register is one this interval could have.
417///
418/// The exception is the value a reuse is coalescing with, which holds the register right up to the
419/// point the new value takes it over and is the one thing that may overlap.
420///
421/// The sweep only keeps a value in `active` while the interval around it reaches this one, so the
422/// areas still have to be compared: two values whose intervals cross can have holes that let them
423/// share a register anyway, which on a function with several loops in it is most of them.
424fn available(
425    active: &[Held<'_>],
426    blocked: &Blocks,
427    interval: Interval<'_>,
428    at: PhysReg,
429    except: Option<Reg>,
430    want: Want,
431) -> bool {
432    let taken = active.iter().any(|held| {
433        held.at == at
434            && held.class == interval.class
435            && Some(held.reg) != except
436            && held.area.overlaps(interval.area)
437    });
438    let insisted = blocked.over(interval.class, at, interval.range).any(|one| {
439        one.by != Some(interval.reg) && (want == Want::Clear || interval.area.covers(one.point))
440    });
441    !taken && !insisted
442}
443
444/// The register the value being reused is in, when this instruction is the last thing that reads
445/// it, the value being written starts here, and the register is otherwise free.
446fn coalesce(
447    assignment: &Assignment,
448    active: &[Held<'_>],
449    blocked: &Blocks,
450    live: &Live,
451    interval: Interval<'_>,
452    reuse: Reuse,
453) -> Option<PhysReg> {
454    let Some(Place::Reg(at)) = assignment.place(reuse.source) else { return None };
455    let source = active.iter().find(|held| held.reg == reuse.source)?;
456    // A value read again later needs its register after this instruction would have overwritten
457    // it, so the two really do have to be different and the rewrite really does have to copy.
458    let dies = source.range.end == reuse.at;
459    // And the value being written must not be live where the instruction reads already. The area
460    // asked here is the one liveness worked out, without the point the reuse adds, so a value that
461    // covers the reuse point on its own is one that was live on the way into this instruction. That
462    // is what a loop carrying its own result round looks like: the instruction writes it at the
463    // bottom and the top of the loop reads what the last turn wrote. Such a value overlaps the one
464    // it reuses over the whole loop, so the two cannot be the same register no matter that the read
465    // here is the last one.
466    let begins = live.area(interval.reg).is_some_and(|area| !area.covers(reuse.at));
467    let free = available(active, blocked, interval, at, Some(reuse.source), Want::Allowed);
468    (dies && begins && free).then_some(at)
469}
470
471/// Sends values to the stack to free a register: the ones wanted for longest, since a register
472/// held that long pays for itself over the most instructions.
473///
474/// What is chosen is a register rather than a value, because two values whose areas miss each
475/// other share one and taking it means every value in it this one is really on top of has to go.
476/// A register holding two of those costs twice as much to take as one holding a single value, so
477/// the cheap ones are looked at first and the reach only settles ties.
478fn spill_one<'a>(
479    assignment: &mut Assignment,
480    active: &mut Vec<Held<'a>>,
481    blocked: &Blocks,
482    interval: Interval<'a>,
483) {
484    // What each register would cost: how many values would go, and the furthest any of them
485    // reaches. The list is one entry per register of the class, so walking it for each value in
486    // flight is the same shape as everything else here.
487    let mut costs: Vec<(PhysReg, usize, Point)> = Vec::new();
488    for held in active.iter() {
489        if held.class != interval.class || !held.area.overlaps(interval.area) {
490            continue;
491        }
492        match costs.iter_mut().find(|(at, _, _)| *at == held.at) {
493            Some((_, count, reach)) => {
494                *count += 1;
495                *reach = (*reach).max(held.range.end);
496            }
497            None => costs.push((held.at, 1, held.range.end)),
498        }
499    }
500    // A register the instructions in the way insist on for themselves is no use, because taking it
501    // over would put this value in a register it may not have.
502    let chosen = costs
503        .iter()
504        .filter(|&&(at, _, reach)| {
505            reach > interval.range.end && available(&[], blocked, interval, at, None, Want::Allowed)
506        })
507        .min_by_key(|&&(_, count, reach)| (count, Reverse(reach)))
508        .map(|&(at, _, _)| at);
509    match chosen {
510        Some(at) => {
511            active.retain(|held| {
512                let goes = held.at == at
513                    && held.class == interval.class
514                    && held.area.overlaps(interval.area);
515                if goes {
516                    assignment.spill(held.reg, held.class);
517                }
518                !goes
519            });
520            assignment.places[index(interval.reg)] = Some(Place::Reg(at));
521            active.push(Held {
522                reg: interval.reg,
523                class: interval.class,
524                range: interval.range,
525                area: interval.area,
526                at,
527            });
528        }
529        None => assignment.spill(interval.reg, interval.class),
530    }
531}
532
533/// The registers the instructions insist on, and where.
534///
535/// A physical register an operand names outright counts the same way. Nothing before allocation
536/// writes one except an instruction that has to, and it has to for the length of that one
537/// instruction, which is the same statement a fixed constraint makes.
538fn blocked(func: &Func, order: &Order) -> Blocks {
539    let mut blocked = Vec::new();
540    let mut claimed: Vec<(RegClass, PhysReg)> = Vec::new();
541    for block in func.blocks() {
542        for inst in func.insts(block) {
543            let operands = &func[func[inst].operands];
544            claimed.clear();
545            for operand in operands {
546                if let Some(at) = insisted(operand) {
547                    let key = (operand.class, at);
548                    if !claimed.contains(&key) {
549                        claimed.push(key);
550                    }
551                }
552            }
553            for &(class, at) in &claimed {
554                // Both points, whether or not an operand is at them. A register an instruction
555                // reads and does not write is destroyed by the time the instruction is done as far
556                // as anything here knows, which is what stops the value a call is passed in `rdi`
557                // from staying in `rdi` over the call.
558                for (point, role) in [(order.early(inst), Role::Use), (order.late(inst), Role::Def)]
559                {
560                    let mut named = false;
561                    for operand in operands {
562                        let mine = insisted(operand) == Some(at) && operand.class == class;
563                        if !mine || !(operand.role == role || operand.role == Role::EarlyDef) {
564                            continue;
565                        }
566                        named = true;
567                        let by = operand.reg.is_virtual().then_some(operand.reg);
568                        blocked.push(Blocked { class, at, point, by });
569                    }
570                    if !named {
571                        blocked.push(Blocked { class, at, point, by: None });
572                    }
573                }
574            }
575        }
576    }
577    // Program order already has the points ascending, but the registers one instruction claims are
578    // walked outside the two points rather than inside them, so the list arrives in order by
579    // instruction and not by register. A sort by the key the lookup searches on is what makes it
580    // searchable, and it is stable so two constraints on one register at one point keep the order
581    // the instruction wrote them in.
582    blocked.sort_by_key(|one: &Blocked| (one.class, one.at, one.point));
583    Blocks { all: blocked }
584}
585
586/// The register an operand has to be in, which is the one a constraint asks for or the one the
587/// operand names outright.
588fn insisted(operand: &Operand) -> Option<PhysReg> {
589    match operand.constraint {
590        Constraint::Fixed(at) => Some(at),
591        _ => operand.reg.phys(),
592    }
593}
594
595/// The register each value would rather be in, which is the one an operand naming it insists on.
596///
597/// A value with two of them keeps the first the function writes down, which is the definition when
598/// there is one, since a value written into a fixed register and then moved somewhere else pays
599/// for the move at the top of its life rather than at the bottom. Two different fixed registers on
600/// one value is rare enough that the second is not worth carrying a list for.
601fn hints(func: &Func) -> Vec<Option<PhysReg>> {
602    let mut hints = vec![None; func.vregs()];
603    for block in func.blocks() {
604        for inst in func.insts(block) {
605            for operand in &func[func[inst].operands] {
606                let Constraint::Fixed(at) = operand.constraint else { continue };
607                let number = operand.reg.number().and_then(|number| usize::try_from(number).ok());
608                let Some(number) = number else { continue };
609                if func.class_of(operand.reg) == Some(operand.class) && hints[number].is_none() {
610                    hints[number] = Some(at);
611                }
612            }
613        }
614    }
615    hints
616}
617
618/// The values that have to be on the stack whatever else is true of them.
619fn forced(func: &Func) -> Vec<Reg> {
620    let mut forced = Vec::new();
621    for block in func.blocks() {
622        for inst in func.insts(block) {
623            for operand in &func[func[inst].operands] {
624                if operand.constraint == Constraint::Stack
625                    && operand.reg.is_virtual()
626                    && !forced.contains(&operand.reg)
627                {
628                    forced.push(operand.reg);
629                }
630            }
631        }
632    }
633    forced
634}
635
636/// The value each two address instruction reuses, by the virtual register it writes.
637fn reuses(func: &Func, order: &Order) -> Vec<Option<Reuse>> {
638    let mut reuses = vec![None; func.vregs()];
639    for block in func.blocks() {
640        for inst in func.insts(block) {
641            let operands = &func[func[inst].operands];
642            for operand in operands {
643                let Constraint::Reuse(other) = operand.constraint else { continue };
644                let number = operand.reg.number().and_then(|number| usize::try_from(number).ok());
645                let Some(number) = number else { continue };
646                let source = operands[usize::from(other)].reg;
647                reuses[number] = Some(Reuse { source, at: order.early(inst) });
648            }
649        }
650    }
651    reuses
652}
653
654/// A virtual register's number as a table index.
655fn index(reg: Reg) -> usize {
656    usize::try_from(reg.number().expect("a virtual register")).expect("a register number")
657}
658
659#[cfg(test)]
660mod tests {
661    use rucc_base::Interner;
662    use rucc_mir::{BlockCall, Opcode, Operand};
663    use rucc_target::x86_64::{GPR, R13, R14, R15, RAX, RCX, RDX, REGS, SYSV};
664
665    use super::*;
666
667    /// The x86-64 environment, with the last three of the allocation order held back as scratch.
668    fn env() -> Env {
669        let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
670        Env::new().with(GPR, order, scratch)
671    }
672
673    /// An environment with that many general purpose registers, for putting a function under
674    /// pressure without writing a hundred instructions.
675    fn narrow(count: usize) -> Env {
676        Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 1])
677    }
678
679    /// What a place is called, which is what an assertion reads.
680    fn named(place: Option<Place>) -> String {
681        match place {
682            Some(Place::Reg(reg)) => REGS.name(GPR, reg).expect("a register").to_string(),
683            Some(Place::Slot(slot)) => format!("slot {slot}"),
684            None => "nowhere".to_string(),
685        }
686    }
687
688    /// Where every value in a function went.
689    fn places(func: &Func, env: &Env) -> Vec<String> {
690        let order = Order::of(func);
691        let live = Live::of(func, &order);
692        let assignment = assign(func, &order, &live, env);
693        (0..func.vregs())
694            .map(|number| {
695                let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
696                named(assignment.place(reg))
697            })
698            .collect()
699    }
700
701    #[test]
702    fn two_values_that_are_never_both_wanted_share_a_register() {
703        let mut names = Interner::new();
704        let mut func = Func::new(names.intern("f"));
705        let opcode = Opcode::new(names.intern("x64.nop"));
706        let block = func.create_block();
707        let first = func.new_vreg(GPR);
708        let second = func.new_vreg(GPR);
709        func.build(block, opcode).def(first, GPR).finish();
710        func.build(block, opcode).uses(first, GPR).finish();
711        func.build(block, opcode).def(second, GPR).finish();
712        func.build(block, opcode).uses(second, GPR).finish();
713
714        // The first register in the order, twice, because the first value is finished with before
715        // the second one is written.
716        assert_eq!(places(&func, &env()), ["rax", "rax"]);
717    }
718
719    #[test]
720    fn two_values_that_are_both_wanted_do_not() {
721        let mut names = Interner::new();
722        let mut func = Func::new(names.intern("f"));
723        let opcode = Opcode::new(names.intern("x64.nop"));
724        let block = func.create_block();
725        let first = func.new_vreg(GPR);
726        let second = func.new_vreg(GPR);
727        func.build(block, opcode).def(first, GPR).finish();
728        func.build(block, opcode).def(second, GPR).finish();
729        func.build(block, opcode).uses(first, GPR).finish();
730        func.build(block, opcode).uses(second, GPR).finish();
731
732        assert_eq!(places(&func, &env()), ["rax", "rcx"]);
733    }
734
735    #[test]
736    fn a_value_written_early_that_nothing_reads_still_holds_its_register() {
737        let mut names = Interner::new();
738        let mut func = Func::new(names.intern("f"));
739        let opcode = Opcode::new(names.intern("x64.nop"));
740        let block = func.create_block();
741        let wanted = func.new_vreg(GPR);
742        let spare = func.new_vreg(GPR);
743        // A division: a remainder somebody wants, and a quotient nobody does. Both are written by
744        // the one instruction and the quotient is written before the operands have been read.
745        func.build(block, opcode)
746            .def(wanted, GPR)
747            .operand(Operand::write_early(spare, GPR))
748            .finish();
749        func.build(block, opcode).uses(wanted, GPR).finish();
750
751        // Two registers, not one. A value nothing reads is still somewhere, and the instruction
752        // that wrote it wrote the other one too, so the two cannot be the same place. Handing them
753        // the same register loses the remainder, because the copy that takes the quotient out of
754        // the register the machine insisted on goes on top of it. The quotient gets the first
755        // register because it is written first, which is the whole of what early means.
756        assert_eq!(places(&func, &env()), ["rcx", "rax"]);
757    }
758
759    #[test]
760    fn the_value_wanted_longest_is_the_one_that_goes_to_the_stack() {
761        let mut names = Interner::new();
762        let mut func = Func::new(names.intern("f"));
763        let opcode = Opcode::new(names.intern("x64.nop"));
764        let block = func.create_block();
765        let long = func.new_vreg(GPR);
766        let short = func.new_vreg(GPR);
767        let third = func.new_vreg(GPR);
768        func.build(block, opcode).def(long, GPR).finish();
769        func.build(block, opcode).def(short, GPR).finish();
770        func.build(block, opcode).def(third, GPR).finish();
771        func.build(block, opcode).uses(short, GPR).finish();
772        func.build(block, opcode).uses(third, GPR).finish();
773        func.build(block, opcode).uses(long, GPR).finish();
774
775        // Two registers between three values. The one still wanted at the end of the function is
776        // the one whose register is worth the most to everybody else, so it is the one that goes.
777        assert_eq!(places(&func, &narrow(2)), ["slot 0", "rcx", "rax"]);
778    }
779
780    #[test]
781    fn a_register_an_instruction_insists_on_goes_to_the_values_that_asked_for_it() {
782        let mut names = Interner::new();
783        let mut func = Func::new(names.intern("f"));
784        let opcode = Opcode::new(names.intern("x64.nop"));
785        let block = func.create_block();
786        let across = func.new_vreg(GPR);
787        let dividend = func.new_vreg(GPR);
788        let quotient = func.new_vreg(GPR);
789        let remainder = func.new_vreg(GPR);
790        func.build(block, opcode).def(across, GPR).finish();
791        func.build(block, opcode).def(dividend, GPR).finish();
792        func.build(block, opcode)
793            .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
794            .operand(Operand::write_early(remainder, GPR).with(Constraint::Fixed(RDX)))
795            .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
796            .finish();
797        func.build(block, opcode).uses(across, GPR).finish();
798
799        // The value that has to be across the division is nowhere near `rax` or `rdx`, and each of
800        // the three the division names is in the register the division asked for it in. The
801        // dividend and the quotient share `rax` because the first is read where the second is
802        // written, which is what a division does.
803        assert_eq!(places(&func, &env()), ["rcx", "rax", "rax", "rdx"]);
804    }
805
806    #[test]
807    fn a_value_wanted_after_the_instruction_that_insists_does_not_get_that_register() {
808        let mut names = Interner::new();
809        let mut func = Func::new(names.intern("f"));
810        let opcode = Opcode::new(names.intern("x64.nop"));
811        let block = func.create_block();
812        let dividend = func.new_vreg(GPR);
813        let quotient = func.new_vreg(GPR);
814        func.build(block, opcode).def(dividend, GPR).finish();
815        func.build(block, opcode)
816            .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
817            .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
818            .finish();
819        func.build(block, opcode).uses(dividend, GPR).finish();
820
821        // The hint is a preference and not a claim. The dividend would rather be in `rax` and
822        // cannot be, because the division writes `rax` and the dividend is wanted afterwards, so
823        // it takes the next register and the quotient keeps the one it was promised.
824        assert_eq!(places(&func, &env()), ["rcx", "rax"]);
825    }
826
827    #[test]
828    fn a_value_an_instruction_can_only_read_from_memory_is_on_the_stack() {
829        let mut names = Interner::new();
830        let mut func = Func::new(names.intern("f"));
831        let opcode = Opcode::new(names.intern("x64.nop"));
832        let block = func.create_block();
833        let value = func.new_vreg(GPR);
834        func.build(block, opcode).def(value, GPR).finish();
835        func.build(block, opcode)
836            .operand(Operand::read(value, GPR).with(Constraint::Stack))
837            .finish();
838
839        assert_eq!(places(&func, &env()), ["slot 0"]);
840    }
841
842    #[test]
843    fn a_two_address_instruction_writes_the_register_it_read_when_it_can() {
844        let mut names = Interner::new();
845        let mut func = Func::new(names.intern("f"));
846        let opcode = Opcode::new(names.intern("x64.nop"));
847        let block = func.create_block();
848        let left = func.new_vreg(GPR);
849        let right = func.new_vreg(GPR);
850        let sum = func.new_vreg(GPR);
851        func.build(block, opcode).def(left, GPR).finish();
852        func.build(block, opcode).def(right, GPR).finish();
853        func.build(block, opcode)
854            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
855            .uses(left, GPR)
856            .uses(right, GPR)
857            .finish();
858        func.build(block, opcode).uses(right, GPR).finish();
859
860        // The addition reads the left value for the last time, so the answer goes where that was
861        // and the instruction is two address without a move in front of it.
862        assert_eq!(places(&func, &env()), ["rax", "rcx", "rax"]);
863    }
864
865    #[test]
866    fn a_two_address_instruction_that_cannot_gets_a_register_nothing_it_reads_is_in() {
867        let mut names = Interner::new();
868        let mut func = Func::new(names.intern("f"));
869        let opcode = Opcode::new(names.intern("x64.nop"));
870        let block = func.create_block();
871        let left = func.new_vreg(GPR);
872        let right = func.new_vreg(GPR);
873        let sum = func.new_vreg(GPR);
874        func.build(block, opcode).def(left, GPR).finish();
875        func.build(block, opcode).def(right, GPR).finish();
876        func.build(block, opcode)
877            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
878            .uses(left, GPR)
879            .uses(right, GPR)
880            .finish();
881        func.build(block, opcode).uses(left, GPR).finish();
882
883        // The left value is wanted afterwards, so the answer cannot have its register. It cannot
884        // have the right one's either, because the rewrite is about to write a move into it before
885        // the addition has read anything.
886        assert_eq!(places(&func, &env()), ["rax", "rcx", "rdx"]);
887    }
888
889    #[test]
890    fn a_value_live_across_a_whole_loop_holds_its_register_over_all_of_it() {
891        let mut names = Interner::new();
892        let mut func = Func::new(names.intern("f"));
893        let opcode = Opcode::new(names.intern("x64.nop"));
894        let head = func.create_block();
895        let body = func.create_block();
896        let carried = func.new_vreg(GPR);
897        let inside = func.new_vreg(GPR);
898        func.build(head, opcode).def(carried, GPR).finish();
899        *func.succs_mut(head) = vec![BlockCall::to(body)];
900        func.build(body, opcode).def(inside, GPR).finish();
901        func.build(body, opcode).uses(inside, GPR).uses(carried, GPR).finish();
902        *func.succs_mut(body) = vec![BlockCall::to(body)];
903
904        // The value inside the loop cannot have the carried one's register, even though nothing
905        // between the two definitions says so.
906        assert_eq!(places(&func, &env()), ["rax", "rcx"]);
907    }
908
909    #[test]
910    fn a_two_address_answer_already_live_does_not_take_the_register_it_read() {
911        let mut names = Interner::new();
912        let mut func = Func::new(names.intern("f"));
913        let opcode = Opcode::new(names.intern("x64.nop"));
914        let head = func.create_block();
915        let latch = func.create_block();
916        let out = func.create_block();
917        let source = func.new_vreg(GPR);
918        let carried = func.new_vreg(GPR);
919        func.build(head, opcode).def(source, GPR).finish();
920        func.build(head, opcode).def(carried, GPR).finish();
921        *func.succs_mut(head) = vec![BlockCall::to(latch)];
922        // The bottom of the loop adds the source to the carried value and writes the answer back
923        // over it, reusing the register the source is in. The next turn round redefines both.
924        func.build(latch, opcode)
925            .operand(Operand::write(carried, GPR).with(Constraint::Reuse(1)))
926            .uses(source, GPR)
927            .uses(carried, GPR)
928            .finish();
929        *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
930        func.build(out, opcode).uses(carried, GPR).finish();
931
932        // The source is read here for the last time, which on its own is the shape the two address
933        // shortcut is for, and taking it would be wrong. The carried value was written by the same
934        // instruction on the last turn and is read by this one, so the two are both wanted where
935        // the instruction reads and one register cannot hold both.
936        assert_eq!(places(&func, &env()), ["rax", "rcx"]);
937
938        // And the checker has to agree, since it excused this pair on the same reasoning and so
939        // would have let the answer through.
940        let order = Order::of(&func);
941        let live = Live::of(&func, &order);
942        let assignment = assign(&func, &order, &live, &env());
943        assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
944    }
945
946    #[test]
947    fn a_two_address_answer_with_a_hole_in_front_of_it_does_not_take_its_other_operand() {
948        let mut names = Interner::new();
949        let mut func = Func::new(names.intern("f"));
950        let nop = Opcode::new(names.intern("x64.nop"));
951        let add = Opcode::new(names.intern("x64.add"));
952        let entry = func.create_block();
953        let head = func.create_block();
954        let arm = func.create_block();
955        let latch = func.create_block();
956        let out = func.create_block();
957        let seed = func.new_vreg(GPR);
958        let sum = func.new_vreg(GPR);
959        let inside = func.new_vreg(GPR);
960        let loaded = func.new_vreg(GPR);
961        func.build(entry, nop).def(seed, GPR).finish();
962        func.build(entry, nop).def(sum, GPR).finish();
963        *func.succs_mut(entry) = vec![BlockCall::to(head)];
964        func.build(head, nop).uses(sum, GPR).finish();
965        *func.succs_mut(head) = vec![BlockCall::to(arm), BlockCall::to(latch)];
966        func.build(arm, nop).def(inside, GPR).finish();
967        func.build(arm, nop).uses(inside, GPR).finish();
968        *func.succs_mut(arm) = vec![BlockCall::to(out)];
969        func.build(latch, nop).def(loaded, GPR).finish();
970        func.build(latch, add)
971            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
972            .uses(seed, GPR)
973            .uses(loaded, GPR)
974            .finish();
975        *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
976
977        // The answer is live in the entry and the head as well, and the arm between them is a hole
978        // in it, so the piece the addition writes is not the first one. The value the addition reads
979        // out of memory is still wanted where the addition reads, so it may not be in the register
980        // the answer is about to be copied into, holes or no holes. tamnd/rucc#982.
981        let places = places(&func, &env());
982        assert_ne!(places[index(sum)], places[index(loaded)]);
983
984        let order = Order::of(&func);
985        let live = Live::of(&func, &order);
986        let assignment = assign(&func, &order, &live, &env());
987        assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
988    }
989
990    /// Two blocks the entry chooses between, with the one the clobber is in written first. The two
991    /// values written in the entry block are read in the other one, so their ranges cover the
992    /// clobber whether or not either of them ever reaches it.
993    fn arms(reaches: bool) -> Func {
994        let mut names = Interner::new();
995        let mut func = Func::new(names.intern("f"));
996        let opcode = Opcode::new(names.intern("x64.nop"));
997        let entry = func.create_block();
998        let arm = func.create_block();
999        let tail = func.create_block();
1000        let first = func.new_vreg(GPR);
1001        let second = func.new_vreg(GPR);
1002        func.build(entry, opcode).def(first, GPR).finish();
1003        func.build(entry, opcode).def(second, GPR).finish();
1004        *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
1005        // What a call looks like here: an instruction writing the registers the convention says it
1006        // destroys, named outright so that nothing else may be in them.
1007        func.build(arm, opcode).operand(Operand::write(Reg::physical(RAX), GPR)).finish();
1008        *func.succs_mut(arm) = if reaches { vec![BlockCall::to(tail)] } else { Vec::new() };
1009        func.build(tail, opcode).uses(first, GPR).uses(second, GPR).finish();
1010        func
1011    }
1012
1013    #[test]
1014    fn a_register_a_clobber_takes_beats_the_stack_for_a_value_not_live_in_that_block() {
1015        let func = arms(false);
1016
1017        // Two registers between two values, and a clobber in the arm that takes the first of them.
1018        // The intervals around both values cover the clobber, since the arm is written between the
1019        // two blocks they are live in, and the arm is a hole in both of their areas. So the second
1020        // value has `rax` rather than a stack slot: the arm is a block its own path never goes
1021        // through. tamnd/rucc#982.
1022        assert_eq!(places(&func, &narrow(2)), ["rcx", "rax"]);
1023
1024        let order = Order::of(&func);
1025        let live = Live::of(&func, &order);
1026        let assignment = assign(&func, &order, &live, &narrow(2));
1027        assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
1028    }
1029
1030    #[test]
1031    fn a_register_a_clobber_takes_is_not_free_to_a_value_that_is_live_there() {
1032        let func = arms(true);
1033
1034        // The same blocks with an edge from the arm to the tail, which is all it takes: both values
1035        // now arrive at the read either way, so the clobber is on a path they are live over and the
1036        // one register left has to do for both of them.
1037        assert_eq!(places(&func, &narrow(2)), ["rcx", "slot 0"]);
1038    }
1039
1040    #[test]
1041    fn a_hint_is_followed_when_the_register_is_clear_and_not_when_it_is_merely_allowed() {
1042        let mut names = Interner::new();
1043        let mut func = Func::new(names.intern("f"));
1044        let opcode = Opcode::new(names.intern("x64.nop"));
1045        let entry = func.create_block();
1046        let mid = func.create_block();
1047        let tail = func.create_block();
1048        let first = func.new_vreg(GPR);
1049        let second = func.new_vreg(GPR);
1050        func.build(entry, opcode).def(first, GPR).finish();
1051        func.build(entry, opcode).def(second, GPR).finish();
1052        *func.succs_mut(entry) = vec![BlockCall::to(mid), BlockCall::to(tail)];
1053        // Two arms, each ending in an instruction that wants its own value in `rax`, which is what
1054        // a return out of either side of a branch looks like.
1055        func.build(mid, opcode)
1056            .operand(Operand::read(second, GPR).with(Constraint::Fixed(RAX)))
1057            .finish();
1058        func.build(tail, opcode)
1059            .operand(Operand::read(first, GPR).with(Constraint::Fixed(RAX)))
1060            .finish();
1061
1062        // The first value is hinted at `rax` and does not get it, because the other arm wants `rax`
1063        // for the other value and the first value's range reaches that far. Following the hint here
1064        // would save a move in the tail and cost one in the middle, and the second value gets `rax`
1065        // with nothing moved anywhere instead.
1066        assert_eq!(places(&func, &env()), ["rcx", "rax"]);
1067    }
1068
1069    #[test]
1070    fn a_value_living_in_a_hole_of_another_gets_the_same_register() {
1071        let mut names = Interner::new();
1072        let mut func = Func::new(names.intern("f"));
1073        let opcode = Opcode::new(names.intern("x64.nop"));
1074        let entry = func.create_block();
1075        let arm = func.create_block();
1076        let tail = func.create_block();
1077        let across = func.new_vreg(GPR);
1078        let inside = func.new_vreg(GPR);
1079        func.build(entry, opcode).def(across, GPR).finish();
1080        *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
1081        func.build(arm, opcode).def(inside, GPR).finish();
1082        func.build(arm, opcode).uses(inside, GPR).finish();
1083        func.build(tail, opcode).uses(across, GPR).finish();
1084
1085        // One register between the two of them, and one register is enough. Nothing in the arm can
1086        // reach the read in the tail, so the value the arm makes is welcome to the register the
1087        // value crossing the function is in. The interval around that value covers the arm and the
1088        // value is nowhere near it, which is what used to send one of the two to the stack.
1089        // tamnd/rucc#982.
1090        assert_eq!(places(&func, &narrow(1)), ["rax", "rax"]);
1091
1092        let order = Order::of(&func);
1093        let live = Live::of(&func, &order);
1094        let assignment = assign(&func, &order, &live, &narrow(1));
1095        assert_eq!(assignment.spilled(), 0);
1096        assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
1097    }
1098
1099    #[test]
1100    fn a_register_a_clobber_takes_is_the_last_one_offered_rather_than_the_first() {
1101        let func = arms(false);
1102
1103        // With a register to spare the value takes the spare one. Being allowed a register some
1104        // instruction insists on is not the same as it being free: the instruction has to be handed
1105        // it in the end, and what hands it over is a move.
1106        assert_eq!(places(&func, &narrow(3)), ["rcx", "rdx"]);
1107    }
1108
1109    #[test]
1110    fn a_frame_says_what_each_of_its_slots_is_for() {
1111        let mut names = Interner::new();
1112        let mut func = Func::new(names.intern("f"));
1113        let opcode = Opcode::new(names.intern("x64.nop"));
1114        let block = func.create_block();
1115        let first = func.new_vreg(GPR);
1116        let second = func.new_vreg(GPR);
1117        func.build(block, opcode).def(first, GPR).finish();
1118        func.build(block, opcode).def(second, GPR).finish();
1119        func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
1120
1121        let order = Order::of(&func);
1122        let live = Live::of(&func, &order);
1123        let assignment = assign(&func, &order, &live, &narrow(1));
1124        assert_eq!(assignment.spilled(), 1);
1125        assert_eq!(assignment.slots(), [GPR]);
1126        // A register that is already a register is where it is, and this has nothing to say about
1127        // it.
1128        assert_eq!(assignment.place(Reg::physical(RCX)), None);
1129        assert_eq!(env().scratch(GPR), [R13, R14, R15]);
1130    }
1131}