Skip to main content

rucc_regalloc/
rewrite.rs

1//! Making an assignment true in the function it was worked out for.
2//!
3//! Design: `spec/10-backend.md` section 10.4.
4//!
5//! [`crate::assign`] says where every value goes and touches nothing. This is the other half: every
6//! operand is rewritten to the place its value was given, and the moves that the places do not
7//! already say are collected. After it the function names no virtual register and no block asks
8//! for anything, which is the point at which machine IR stops being in SSA form and starts being
9//! something an encoder could read.
10//!
11//! # Why the moves are handed back rather than written
12//!
13//! A move is an instruction, and an instruction has an opcode, and an opcode belongs to a target.
14//! `spec/10-backend.md` section 10.8 says no pipeline crate holds target specific code, so this
15//! crate is not the one that can write `x64.mov`. What it hands back is an [`Edit`]: a move
16//! between two places, the class it is in, and where in the function it goes. `rucc-codegen` turns
17//! each one into whatever its target moves a register with, which for a value on the stack is a
18//! load or a store rather than a move at all.
19//!
20//! The edits at any one place are in the order they have to be made in. That matters in two
21//! places: a spilled operand is read into a scratch register before the instruction that wants it,
22//! and a two address instruction's copy has to come after that read, because what it is copying
23//! may be the thing that was just read in.
24//!
25//! What each instruction needs around it for the machine to accept the places its operands were
26//! given is worked out in [`crate::legalize`], and this files what that says as edits.
27//!
28//! # A value put away around an instruction
29//!
30//! A value [`crate::backtrack`] kept in a register an instruction destroys is stored to its slot
31//! in front of everything else the instruction needs and loaded back behind everything else. In
32//! front because the moves that hand the instruction its operands read what they move and never
33//! write the value's register, which nothing at the instruction insists on, and behind because the
34//! moves that take its answers away may read the register the call returned something in. Either
35//! way round would be correct for the value. These are the only places it is not.
36//!
37//! # What an edge turns into
38//!
39//! The moves that write the block's parameters, in an order they can be made in one at a time,
40//! which is what [`crate::moves`] is for. Where they go depends on the shape of the edge. A block
41//! with one successor puts them at its own end, in front of the branch it finishes with, and a
42//! block with several puts them at the start of the block the edge goes to, which is safe exactly
43//! because that block has no other predecessor. An edge that is critical has neither place to put
44//! them and has to have been split before allocation ran, which this checks rather than assumes.
45//!
46//! An edge is also the one place a value can be asked to go from one stack slot to another, which
47//! happens when a spilled value is passed to a parameter that was itself spilled. No machine here
48//! has that instruction, so the move goes through a register, and the register is a second scratch
49//! rather than the one the ordering may be holding a value in for the length of a cycle. Expanding
50//! it here rather than leaving it to the target is the same decision as everything else in this
51//! file: a move through a temporary is a fact about places, and which register is free to be the
52//! temporary is a fact only this crate has.
53
54use rucc_base::hash::Map;
55use rucc_mir::{Block, Func, Inst, Param, Reg};
56use rucc_target::RegClass;
57
58use crate::assign::{Assignment, Env, Place};
59use crate::legalize::{self, Spare, place};
60use crate::moves::{self, Move};
61
62/// One move the places did not already make true.
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub struct Edit {
65    /// Where in the function it goes.
66    pub at: At,
67    /// What it moves, and where to.
68    pub mov: Move<Place>,
69    /// The class both places are in, which is what says how wide the move is.
70    pub class: RegClass,
71}
72
73/// Where an edit goes.
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
75pub enum At {
76    /// In front of an instruction, which is where a value it reads is put where it wants it.
77    Before(Inst),
78    /// Behind an instruction, which is where a value it wrote somewhere it insisted on is taken
79    /// away to where it lives.
80    After(Inst),
81    /// At the start of a block, in front of everything in it.
82    StartOf(Block),
83    /// At the end of a block, behind everything in it. Only ever a block with one edge out of
84    /// it, since a block with two puts an edge's moves at the start of the block it goes to.
85    EndOf(Block),
86}
87
88/// Rewrites a function to the places it was given, and says what moves are still wanted.
89///
90/// # Panics
91///
92/// Panics if the entry block has parameters, since there is no edge into it for their moves to go
93/// on and what arrives in a function is the ABI lowering's to say. Panics on a critical edge, on
94/// an edge carrying the wrong number of arguments, and if a class has fewer than two registers on
95/// an edge that moves a spilled value into a spilled parameter, all of which are the caller handing
96/// it something it was told not to.
97///
98/// The assignment is taken by reference and may gain a slot, which is the one the register borrowed
99/// at an instruction with more spilled operands than the class holds registers back for waits in.
100/// The section above says what the borrowing is, and the slot is asked for here rather than planned
101/// before allocation because most functions never want one.
102#[must_use]
103pub fn rewrite(func: &mut Func, assignment: &mut Assignment, env: &Env) -> Vec<Edit> {
104    let blocks: Vec<Block> = func.blocks().collect();
105    assert!(
106        func.entry().is_none_or(|entry| func[entry].params.is_empty()),
107        "what arrives in a function is not a block parameter"
108    );
109
110    let mut edits = Vec::new();
111    let mut spare = Spare::default();
112    // Collected once for the whole function rather than once a block, since rewriting an
113    // instruction needs the function and the walk over the blocks would be borrowing it.
114    let insts: Vec<Inst> = blocks.iter().flat_map(|&block| func.insts(block)).collect();
115    let saves = saves(func, assignment);
116    for inst in insts {
117        let saved = saves.get(&inst).map_or(&[][..], Vec::as_slice);
118        instruction(func, assignment, env, &mut spare, inst, saved, &mut edits);
119    }
120
121    let preds = preds(func, &blocks);
122    for &block in &blocks {
123        behind_the_end(func, block, &preds, &mut edits);
124    }
125    for &block in &blocks {
126        edges(func, assignment, env, block, &preds, &mut edits);
127    }
128    for &block in &blocks {
129        func.params_mut(block).clear();
130        for call in func.succs_mut(block) {
131            call.args.clear();
132        }
133    }
134    edits
135}
136
137/// The stores that put away the values kept in a register each instruction destroys, by the
138/// instruction. Each load that brings one back is the same move the other way round.
139fn saves(func: &Func, assignment: &Assignment) -> Map<Inst, Vec<(Move<Place>, RegClass)>> {
140    let mut saves: Map<Inst, Vec<(Move<Place>, RegClass)>> = Map::default();
141    for save in assignment.saves() {
142        let (Some(Place::Reg(at)), Some(class)) =
143            (assignment.place(save.reg), func.class_of(save.reg))
144        else {
145            continue;
146        };
147        let store = Move::new(Place::Slot(save.slot), Place::Reg(at));
148        saves.entry(save.inst).or_default().push((store, class));
149    }
150    saves
151}
152
153/// Rewrites one instruction's operands, and files what has to happen either side of it.
154fn instruction(
155    func: &mut Func,
156    assignment: &mut Assignment,
157    env: &Env,
158    spare: &mut Spare,
159    inst: Inst,
160    saved: &[(Move<Place>, RegClass)],
161    edits: &mut Vec<Edit>,
162) {
163    let legal = legalize::instruction(func, assignment, env, spare, inst);
164    let list = func[inst].operands;
165    func[list].copy_from_slice(&legal.operands);
166    let before = saved.iter().copied().chain(legal.before);
167    edits.extend(before.map(|(mov, class)| Edit { at: At::Before(inst), mov, class }));
168    let back = saved.iter().map(|&(store, class)| (Move::new(store.from, store.to), class));
169    edits.extend(legal.after.into_iter().chain(back).map(|(mov, class)| Edit {
170        at: At::After(inst),
171        mov,
172        class,
173    }));
174}
175
176/// Moves what has to happen behind the last instruction of a block that leaves several ways to the
177/// start of every block it goes to.
178///
179/// That instruction is an `asm goto`, the one thing that both writes values and ends a block with
180/// more than one edge out of it. Behind it in its own block is the fall through only, since the
181/// template has already jumped to a label by then when it was going to, so a value it wrote that
182/// has to be taken somewhere else has to be taken there on every edge. Each of those goes to a
183/// block with no other way in, which `rucc_codegen::split::critical` sees to, and these are filed
184/// before the edge's own moves because those may read what these put in place.
185///
186/// A branch writes nothing and has nothing behind it, and neither does any other instruction a
187/// block that leaves several ways ends in, so this is nothing for all of those.
188fn behind_the_end(func: &Func, block: Block, preds: &[usize], edits: &mut Vec<Edit>) {
189    let succs = &func[block].succs;
190    if succs.len() < 2 {
191        return;
192    }
193    let Some(last) = func.insts(block).last() else { return };
194    if !func[func[last].operands].iter().any(|operand| operand.role.is_def()) {
195        return;
196    }
197    let behind: Vec<Edit> =
198        edits.iter().filter(|edit| edit.at == At::After(last)).copied().collect();
199    if behind.is_empty() {
200        return;
201    }
202    edits.retain(|edit| edit.at != At::After(last));
203    for call in succs {
204        assert!(
205            preds[call.block.index()] == 1,
206            "an edge out of an asm goto that writes something has to be split before allocation"
207        );
208        edits.extend(behind.iter().map(|&edit| Edit { at: At::StartOf(call.block), ..edit }));
209    }
210}
211
212/// The moves the edges out of a block turn into.
213fn edges(
214    func: &mut Func,
215    assignment: &Assignment,
216    env: &Env,
217    block: Block,
218    preds: &[usize],
219    edits: &mut Vec<Edit>,
220) {
221    let succs = func[block].succs.clone();
222    let single = succs.len() == 1;
223    for call in &succs {
224        let params = func[call.block].params.clone();
225        assert_eq!(
226            params.len(),
227            call.args.len(),
228            "an edge carries what the block it goes to asks for"
229        );
230        if params.is_empty() {
231            continue;
232        }
233        assert!(
234            single || preds[call.block.index()] == 1,
235            "a critical edge has nowhere to put its moves and has to be split before allocation"
236        );
237        let at = if single { At::EndOf(block) } else { At::StartOf(call.block) };
238        edits.extend(edge(assignment, env, &params, &call.args, at));
239    }
240}
241
242/// The moves one edge turns into, in the order they can be made in.
243fn edge(assignment: &Assignment, env: &Env, params: &[Param], args: &[Reg], at: At) -> Vec<Edit> {
244    let mut classes: Vec<RegClass> = params.iter().map(|param| param.class).collect();
245    classes.sort_unstable();
246    classes.dedup();
247
248    let mut edits = Vec::new();
249    for class in classes {
250        // One class at a time, because a scratch register is per class and a value never crosses
251        // from one to another on an edge.
252        //
253        // A class with no scratch register is one [`fits`] said goes round in no cycle, so the
254        // ordering never reaches for the place it is given to break one with, and that place is
255        // nothing rather than a register.
256        let parallel: Vec<Move<Option<Place>>> = params
257            .iter()
258            .zip(args)
259            .filter(|(param, _)| param.class == class)
260            .map(|(param, &arg)| {
261                Move::new(Some(place(assignment, param.reg)), Some(place(assignment, arg)))
262            })
263            .collect();
264        let scratch = env.scratch(class);
265        let cycle = scratch.first().map(|&reg| Place::Reg(reg));
266        for mov in moves::sequence(&parallel, cycle) {
267            let (Some(to), Some(from)) = (mov.to, mov.from) else {
268                panic!(
269                    "a class whose values go round in a cycle on an edge and which has no scratch \
270                     register"
271                )
272            };
273            let mov = Move::new(to, from);
274            match (mov.to, mov.from) {
275                // No machine here moves one piece of memory into another, so the value goes
276                // through a register, and it is a second scratch rather than the one the ordering
277                // above may be holding a value in for the length of a cycle.
278                (Place::Slot(_), Place::Slot(_)) => {
279                    let through = Place::Reg(*scratch.get(1).expect(
280                        "a class passing a spilled value to a spilled parameter and having only \
281                         one scratch register",
282                    ));
283                    edits.push(Edit { at, mov: Move::new(through, mov.from), class });
284                    edits.push(Edit { at, mov: Move::new(mov.to, through), class });
285                }
286                _ => edits.push(Edit { at, mov, class }),
287            }
288        }
289    }
290    edits
291}
292
293/// Whether the rewrite can write an assignment down with no more than the scratch registers `env`
294/// holds back.
295///
296/// Only a class that holds none back can fail. A value of that class on the stack has nowhere to
297/// be read into at an instruction that wants it, and an edge whose moves of that class go round in
298/// a cycle has nowhere to keep one value while the others move. Those are the only two things the
299/// rewrite takes a scratch register for, so an assignment with neither is one it never asks for one
300/// in. A slot a value waits in around a call is not either of them, since the value is in its
301/// register on both sides and the moves are between that register and the slot.
302#[must_use]
303pub fn fits(func: &Func, assignment: &Assignment, env: &Env) -> bool {
304    let bare = |class: RegClass| env.scratch(class).is_empty();
305    let slots = assignment.slots();
306    let stacked = assignment.placed().any(|(_, at)| match at {
307        Place::Slot(slot) => usize::try_from(slot)
308            .ok()
309            .and_then(|slot| slots.get(slot))
310            .is_some_and(|&class| bare(class)),
311        Place::Reg(_) => false,
312    });
313    if stacked {
314        return false;
315    }
316    for block in func.blocks() {
317        for call in &func[block].succs {
318            let params = &func[call.block].params;
319            let mut classes: Vec<RegClass> =
320                params.iter().map(|param| param.class).filter(|&class| bare(class)).collect();
321            classes.sort_unstable();
322            classes.dedup();
323            for class in classes {
324                // The ordering is given a place that is not one to break a cycle with, so a move
325                // out of it in what comes back is where it would have taken a scratch register.
326                let parallel: Vec<Move<Option<Place>>> = params
327                    .iter()
328                    .zip(&call.args)
329                    .filter(|(param, _)| param.class == class)
330                    .map(|(param, &arg)| {
331                        Move::new(Some(place(assignment, param.reg)), Some(place(assignment, arg)))
332                    })
333                    .collect();
334                if moves::sequence(&parallel, None).iter().any(|mov| mov.from.is_none()) {
335                    return false;
336                }
337            }
338        }
339    }
340    true
341}
342
343/// How many edges arrive in each block.
344fn preds(func: &Func, blocks: &[Block]) -> Vec<usize> {
345    let mut preds = vec![0; func.block_count()];
346    for &block in blocks {
347        for call in &func[block].succs {
348            preds[call.block.index()] += 1;
349        }
350    }
351    preds
352}
353
354#[cfg(test)]
355mod tests {
356    use rucc_base::Interner;
357    use rucc_mir::{BlockCall, Constraint, Opcode, Operand};
358    use rucc_target::x86_64::{GPR, RAX, RCX, RDX, REGS, RSI, SYSV, XMM};
359
360    use super::*;
361    use crate::assign::assign;
362    use crate::legalize::phys;
363    use crate::live::Live;
364    use crate::order::Order;
365
366    /// The x86-64 environment, with the last three of the allocation order held back as scratch.
367    fn env() -> Env {
368        let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
369        Env::new().with(GPR, order, scratch)
370    }
371
372    /// An environment with that many general purpose registers and two scratch after them.
373    fn narrow(count: usize) -> Env {
374        Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 2])
375    }
376
377    /// What a place is called, which is what an assertion reads.
378    ///
379    /// The class comes in because a register is a number within its class and the two files here
380    /// number from zero, so nothing but the class tells `rcx` from `xmm1`.
381    fn named(class: RegClass, place: Place) -> String {
382        match place {
383            Place::Reg(reg) => REGS.name(class, reg).expect("a register").to_string(),
384            Place::Slot(slot) => format!("slot{slot}"),
385        }
386    }
387
388    /// Runs both halves and reports the edits as lines an assertion can read.
389    fn run(func: &mut Func, env: &Env) -> Vec<String> {
390        let order = Order::of(func);
391        let live = Live::of(func, &order);
392        let mut assignment = assign(func, &order, &live, env);
393        rewrite(func, &mut assignment, env)
394            .into_iter()
395            .map(|edit| {
396                let at = match edit.at {
397                    At::Before(inst) => format!("before {}", inst.index()),
398                    At::After(inst) => format!("after {}", inst.index()),
399                    At::StartOf(block) => format!("start of {}", block.index()),
400                    At::EndOf(block) => format!("end of {}", block.index()),
401                };
402                format!(
403                    "{at}: {} = {}",
404                    named(edit.class, edit.mov.to),
405                    named(edit.class, edit.mov.from)
406                )
407            })
408            .collect()
409    }
410
411    /// The registers an instruction's operands ended up naming.
412    fn operands(func: &Func, inst: Inst) -> Vec<String> {
413        func[func[inst].operands]
414            .iter()
415            .map(|operand| named(operand.class, Place::Reg(phys(operand.reg))))
416            .collect()
417    }
418
419    #[test]
420    fn every_operand_ends_up_naming_the_register_its_value_was_given() {
421        let mut names = Interner::new();
422        let mut func = Func::new(names.intern("f"));
423        let opcode = Opcode::new(names.intern("x64.nop"));
424        let block = func.create_block();
425        let first = func.new_vreg(GPR);
426        let second = func.new_vreg(GPR);
427        func.build(block, opcode).def(first, GPR).finish();
428        func.build(block, opcode).def(second, GPR).finish();
429        let read = func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
430
431        assert_eq!(run(&mut func, &env()), Vec::<String>::new());
432        assert_eq!(operands(&func, read), ["rax", "rcx"]);
433    }
434
435    #[test]
436    fn a_register_an_instruction_insists_on_costs_nothing_when_the_values_can_have_it() {
437        let mut names = Interner::new();
438        let mut func = Func::new(names.intern("f"));
439        let opcode = Opcode::new(names.intern("x64.nop"));
440        let block = func.create_block();
441        let dividend = func.new_vreg(GPR);
442        let quotient = func.new_vreg(GPR);
443        func.build(block, opcode).def(dividend, GPR).finish();
444        let divide = func
445            .build(block, opcode)
446            .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
447            .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
448            .finish();
449        func.build(block, opcode).uses(quotient, GPR).finish();
450
451        // Nothing either side of the division. The dividend is read out of `rax` for the last
452        // time and the quotient is written into it afterwards, so both of them live there and the
453        // moves that used to carry the value in and the answer out are not written.
454        assert_eq!(run(&mut func, &env()), Vec::<String>::new());
455        assert_eq!(operands(&func, divide), ["rax", "rax"]);
456    }
457
458    #[test]
459    fn a_register_an_instruction_insists_on_is_moved_into_when_the_value_cannot_have_it() {
460        let mut names = Interner::new();
461        let mut func = Func::new(names.intern("f"));
462        let opcode = Opcode::new(names.intern("x64.nop"));
463        let block = func.create_block();
464        let dividend = func.new_vreg(GPR);
465        let quotient = func.new_vreg(GPR);
466        func.build(block, opcode).def(dividend, GPR).finish();
467        let divide = func
468            .build(block, opcode)
469            .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
470            .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
471            .finish();
472        func.build(block, opcode).uses(quotient, GPR).finish();
473        func.build(block, opcode).uses(dividend, GPR).finish();
474
475        // This time the dividend is wanted after the division, so it cannot be in the register the
476        // division writes and the value is moved in. The answer still comes out of `rax` without
477        // a move, which is the half of it the hint bought.
478        assert_eq!(run(&mut func, &env()), ["before 1: rax = rcx"]);
479        assert_eq!(operands(&func, divide), ["rax", "rax"]);
480    }
481
482    #[test]
483    fn a_two_address_instruction_that_did_not_get_its_register_copies_first() {
484        let mut names = Interner::new();
485        let mut func = Func::new(names.intern("f"));
486        let opcode = Opcode::new(names.intern("x64.nop"));
487        let block = func.create_block();
488        let left = func.new_vreg(GPR);
489        let right = func.new_vreg(GPR);
490        let sum = func.new_vreg(GPR);
491        func.build(block, opcode).def(left, GPR).finish();
492        func.build(block, opcode).def(right, GPR).finish();
493        let add = func
494            .build(block, opcode)
495            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
496            .uses(left, GPR)
497            .uses(right, GPR)
498            .finish();
499        func.build(block, opcode).uses(left, GPR).finish();
500
501        // The left value is wanted afterwards, so the answer could not have its register and the
502        // copy in front of the addition is what makes the instruction two address.
503        assert_eq!(run(&mut func, &env()), ["before 2: rdx = rax"]);
504        assert_eq!(operands(&func, add), ["rdx", "rax", "rcx"]);
505    }
506
507    #[test]
508    fn a_two_address_instruction_that_did_get_its_register_copies_nothing() {
509        let mut names = Interner::new();
510        let mut func = Func::new(names.intern("f"));
511        let opcode = Opcode::new(names.intern("x64.nop"));
512        let block = func.create_block();
513        let left = func.new_vreg(GPR);
514        let right = func.new_vreg(GPR);
515        let sum = func.new_vreg(GPR);
516        func.build(block, opcode).def(left, GPR).finish();
517        func.build(block, opcode).def(right, GPR).finish();
518        let add = func
519            .build(block, opcode)
520            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
521            .uses(left, GPR)
522            .uses(right, GPR)
523            .finish();
524        func.build(block, opcode).uses(right, GPR).finish();
525
526        assert_eq!(run(&mut func, &env()), Vec::<String>::new());
527        assert_eq!(operands(&func, add), ["rax", "rax", "rcx"]);
528    }
529
530    #[test]
531    fn a_spilled_value_is_read_into_a_scratch_register_at_each_instruction_that_wants_it() {
532        let mut names = Interner::new();
533        let mut func = Func::new(names.intern("f"));
534        let opcode = Opcode::new(names.intern("x64.nop"));
535        let block = func.create_block();
536        let first = func.new_vreg(GPR);
537        let second = func.new_vreg(GPR);
538        func.build(block, opcode).def(first, GPR).finish();
539        func.build(block, opcode).def(second, GPR).finish();
540        let read = func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
541
542        // One register between two values, so one of them goes to the stack. It is written there
543        // where it is computed and read back where it is wanted, and both ends of that go through
544        // the scratch register that is held out of the allocation order for exactly this.
545        assert_eq!(run(&mut func, &narrow(1)), ["after 1: slot0 = rcx", "before 2: rcx = slot0"]);
546        assert_eq!(operands(&func, read), ["rax", "rcx"]);
547    }
548
549    /// A two address instruction with nothing in a register is two scratch registers and not three.
550    ///
551    /// The answer has no register of its own to be in, so what it is written into is whichever one
552    /// the operand it reuses was read into, and it is stored away from there afterwards. Handing it
553    /// a scratch register of its own would want a third, and a class holds two back, which is issue
554    /// #350: a program with enough live values around a call reached it and the compiler aborted.
555    #[test]
556    fn a_two_address_instruction_whose_answer_and_operands_are_all_spilled_wants_two_registers() {
557        let mut names = Interner::new();
558        let mut func = Func::new(names.intern("f"));
559        let opcode = Opcode::new(names.intern("x64.nop"));
560        let block = func.create_block();
561        let keeper = func.new_vreg(GPR);
562        let left = func.new_vreg(GPR);
563        let right = func.new_vreg(GPR);
564        let sum = func.new_vreg(GPR);
565        func.build(block, opcode).def(keeper, GPR).finish();
566        func.build(block, opcode)
567            .operand(Operand::write(left, GPR).with(Constraint::Stack))
568            .finish();
569        func.build(block, opcode)
570            .operand(Operand::write(right, GPR).with(Constraint::Stack))
571            .finish();
572        let add = func
573            .build(block, opcode)
574            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
575            .uses(left, GPR)
576            .uses(right, GPR)
577            .finish();
578        func.build(block, opcode).uses(keeper, GPR).finish();
579        func.build(block, opcode).uses(sum, GPR).finish();
580
581        // Both operands are read in, the answer is written into the register the operand it
582        // reuses arrived in, and it is stored away from there. Two scratch registers, which is
583        // what the class holds back. Asking for one of its own would be a third and would abort.
584        assert_eq!(
585            run(&mut func, &narrow(1)),
586            [
587                "after 1: slot0 = rcx",
588                "after 2: slot1 = rcx",
589                "before 3: rcx = slot0",
590                "before 3: rdx = slot1",
591                "after 3: slot2 = rcx",
592                "before 5: rcx = slot2",
593            ]
594        );
595        assert_eq!(operands(&func, add), ["rcx", "rcx", "rdx"]);
596    }
597
598    /// A three address instruction with nothing in a register is two scratch registers, not three.
599    ///
600    /// The case #726 aborted on. `x64.lea_64` and the `x64.cmp_set_*` family read two values and
601    /// write a third that is neither of them, and when all three ends are on the stack there are
602    /// three operands wanting a register at one instruction. Counting them in one running number
603    /// asks for a third scratch register and the class holds two back.
604    ///
605    /// Two is enough because the answer's register is not wanted until the instruction writes it,
606    /// by which time the registers the operands were read into have been read. So the answer goes
607    /// back into the first of them and is stored away from there.
608    #[test]
609    fn a_three_address_instruction_whose_answer_and_operands_are_all_spilled_wants_two_registers() {
610        let mut names = Interner::new();
611        let mut func = Func::new(names.intern("f"));
612        let opcode = Opcode::new(names.intern("x64.nop"));
613        let block = func.create_block();
614        let keeper = func.new_vreg(GPR);
615        let base = func.new_vreg(GPR);
616        let index = func.new_vreg(GPR);
617        let address = func.new_vreg(GPR);
618        func.build(block, opcode).def(keeper, GPR).finish();
619        func.build(block, opcode)
620            .operand(Operand::write(base, GPR).with(Constraint::Stack))
621            .finish();
622        func.build(block, opcode)
623            .operand(Operand::write(index, GPR).with(Constraint::Stack))
624            .finish();
625        let lea =
626            func.build(block, opcode).def(address, GPR).uses(base, GPR).uses(index, GPR).finish();
627        func.build(block, opcode).uses(keeper, GPR).finish();
628        func.build(block, opcode).uses(address, GPR).finish();
629
630        // Both operands are read in, the answer is written into the first of the two registers
631        // they arrived in, and it is stored away from there. Two, which is what the class holds.
632        assert_eq!(
633            run(&mut func, &narrow(1)),
634            [
635                "after 1: slot0 = rcx",
636                "after 2: slot1 = rcx",
637                "before 3: rcx = slot0",
638                "before 3: rdx = slot1",
639                "after 3: slot2 = rcx",
640                "before 5: rcx = slot2",
641            ]
642        );
643        assert_eq!(operands(&func, lea), ["rcx", "rcx", "rdx"]);
644    }
645
646    /// A spilled answer takes a scratch register where the operand it reuses is in a real one.
647    ///
648    /// The value in that register may be wanted after the instruction, and the assignment is the
649    /// only thing that knows whether it is. It says so by giving the answer that register, and here
650    /// it did not, so writing over it would destroy a value. The count still comes to two, because
651    /// an operand that is in a register is not holding a scratch register.
652    #[test]
653    fn a_spilled_answer_does_not_write_over_a_register_the_assignment_gave_to_something_else() {
654        let mut names = Interner::new();
655        let mut func = Func::new(names.intern("f"));
656        let opcode = Opcode::new(names.intern("x64.nop"));
657        let block = func.create_block();
658        let left = func.new_vreg(GPR);
659        let right = func.new_vreg(GPR);
660        let sum = func.new_vreg(GPR);
661        func.build(block, opcode).def(left, GPR).finish();
662        func.build(block, opcode)
663            .operand(Operand::write(right, GPR).with(Constraint::Stack))
664            .finish();
665        let add = func
666            .build(block, opcode)
667            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
668            .uses(left, GPR)
669            .uses(right, GPR)
670            .finish();
671        func.build(block, opcode).uses(left, GPR).finish();
672        func.build(block, opcode).uses(sum, GPR).finish();
673
674        // The left value is in `rax` and is read again afterwards, so the answer is copied into a
675        // scratch register and written there instead.
676        assert_eq!(
677            run(&mut func, &narrow(1)),
678            [
679                "after 1: slot0 = rcx",
680                "before 2: rcx = slot0",
681                "before 2: rdx = rax",
682                "after 2: slot1 = rdx",
683                "before 4: rcx = slot1",
684            ]
685        );
686        assert_eq!(operands(&func, add), ["rdx", "rax", "rcx"]);
687    }
688
689    /// The count of scratch registers handed out is per class and not one number for all of them.
690    ///
691    /// An instruction reading a spilled value out of each of two files wants the first register of
692    /// each, since the files hold their own back and nothing on the instruction is in the other's.
693    #[test]
694    fn an_instruction_reading_out_of_two_files_takes_the_first_scratch_register_of_each() {
695        let mut names = Interner::new();
696        let mut func = Func::new(names.intern("f"));
697        let opcode = Opcode::new(names.intern("x64.nop"));
698        let block = func.create_block();
699        let integer = func.new_vreg(GPR);
700        let number = func.new_vreg(XMM);
701        let spare = func.new_vreg(GPR);
702        let other = func.new_vreg(XMM);
703        func.build(block, opcode).def(integer, GPR).finish();
704        func.build(block, opcode).def(number, XMM).finish();
705        func.build(block, opcode).def(spare, GPR).finish();
706        func.build(block, opcode).def(other, XMM).finish();
707        func.build(block, opcode).uses(integer, GPR).uses(number, XMM).finish();
708        let read = func.build(block, opcode).uses(spare, GPR).uses(other, XMM).finish();
709
710        // One register in each file, so the value of each that is wanted later goes to the stack
711        // and is read back at the instruction that wants it.
712        let env = Env::new().with(GPR, &SYSV.int_order[..1], &SYSV.int_order[1..3]).with(
713            XMM,
714            &SYSV.sse_order[..1],
715            &SYSV.sse_order[1..3],
716        );
717        assert_eq!(
718            run(&mut func, &env),
719            [
720                "after 2: slot0 = rcx",
721                "after 3: slot1 = xmm1",
722                "before 5: rcx = slot0",
723                "before 5: xmm1 = slot1",
724            ]
725        );
726        assert_eq!(operands(&func, read), ["rcx", "xmm1"]);
727    }
728
729    #[test]
730    fn an_edge_out_of_a_block_with_one_way_to_go_moves_at_the_end_of_it() {
731        let mut names = Interner::new();
732        let mut func = Func::new(names.intern("f"));
733        let opcode = Opcode::new(names.intern("x64.nop"));
734        let head = func.create_block();
735        let tail = func.create_block();
736        let held = func.new_vreg(GPR);
737        let carried = func.new_vreg(GPR);
738        func.build(head, opcode).def(held, GPR).finish();
739        func.build(head, opcode).def(carried, GPR).finish();
740        func.build(head, opcode).uses(held, GPR).finish();
741        let param = func.append_param(tail, GPR);
742        *func.succs_mut(head) = vec![BlockCall::with(tail, vec![carried])];
743        let read = func.build(tail, opcode).uses(param, GPR).finish();
744
745        // The value the edge carries is in the second register, because the first was busy where
746        // the value was written, and the parameter it arrives as is in the first, because by then
747        // it is not. So the edge is a move, and it goes at the end of the block it leaves.
748        assert_eq!(run(&mut func, &env()), ["end of 0: rax = rcx"]);
749        assert_eq!(operands(&func, read), ["rax"]);
750        // Nothing arrives in a block any more and no edge carries anything, which is where SSA
751        // form stops.
752        assert!(func[tail].params.is_empty());
753        assert!(func[head].succs[0].args.is_empty());
754    }
755
756    #[test]
757    fn an_edge_out_of_a_block_with_a_choice_moves_at_the_start_of_where_it_goes() {
758        let mut names = Interner::new();
759        let mut func = Func::new(names.intern("f"));
760        let opcode = Opcode::new(names.intern("x64.nop"));
761        let head = func.create_block();
762        let left = func.create_block();
763        let right = func.create_block();
764        let held = func.new_vreg(GPR);
765        let carried = func.new_vreg(GPR);
766        func.build(head, opcode).def(held, GPR).finish();
767        func.build(head, opcode).def(carried, GPR).finish();
768        func.build(head, opcode).uses(held, GPR).finish();
769        let taken = func.append_param(left, GPR);
770        *func.succs_mut(head) = vec![BlockCall::with(left, vec![carried]), BlockCall::to(right)];
771        func.build(left, opcode).uses(taken, GPR).finish();
772
773        // The move cannot go at the end of the block it leaves, because the other way out of that
774        // block does not want it. It goes at the start of the block it arrives in, which is safe
775        // because nothing else arrives there.
776        assert_eq!(run(&mut func, &env()), ["start of 1: rax = rcx"]);
777    }
778
779    #[test]
780    fn two_values_that_swap_on_an_edge_get_an_order_and_a_scratch_register() {
781        let mut names = Interner::new();
782        let mut func = Func::new(names.intern("f"));
783        let opcode = Opcode::new(names.intern("x64.nop"));
784        let head = func.create_block();
785        let body = func.create_block();
786        let first = func.new_vreg(GPR);
787        let second = func.new_vreg(GPR);
788        func.build(head, opcode).def(first, GPR).finish();
789        func.build(head, opcode).def(second, GPR).finish();
790        let left = func.append_param(body, GPR);
791        let right = func.append_param(body, GPR);
792        *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
793        func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
794        *func.succs_mut(body) = vec![BlockCall::with(body, vec![right, left])];
795
796        // The loop hands each value back the other way round, which is the case no order of two
797        // moves answers, so one of them goes through the scratch register. The edge into the loop
798        // moves nothing, because each value is already where the parameter it feeds lives.
799        assert_eq!(
800            run(&mut func, &env()),
801            ["end of 1: r13 = rcx", "end of 1: rcx = rax", "end of 1: rax = r13"]
802        );
803    }
804
805    #[test]
806    fn a_spilled_value_handed_to_a_spilled_parameter_goes_through_a_register() {
807        let mut names = Interner::new();
808        let mut func = Func::new(names.intern("f"));
809        let opcode = Opcode::new(names.intern("x64.nop"));
810        let head = func.create_block();
811        let body = func.create_block();
812        let first = func.new_vreg(GPR);
813        let second = func.new_vreg(GPR);
814        func.build(head, opcode).def(first, GPR).finish();
815        func.build(head, opcode).def(second, GPR).finish();
816        let left = func.append_param(body, GPR);
817        let right = func.append_param(body, GPR);
818        *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
819        func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
820
821        // One register between the values and the parameters, so a value on the stack is handed to
822        // a parameter on the stack, and no machine here has that instruction. It goes through the
823        // second scratch register rather than the first, which is the one the ordering above is
824        // entitled to be holding a value in.
825        assert_eq!(
826            run(&mut func, &narrow(1)),
827            [
828                "after 1: slot0 = rcx",
829                "before 2: rcx = slot1",
830                "end of 0: rdx = slot0",
831                "end of 0: slot1 = rdx",
832            ]
833        );
834    }
835
836    /// Three values read and none written wants a third register, which is tamnd/rucc#913.
837    ///
838    /// There is no answer here to fold back into the register an operand arrived in, so the trick
839    /// that keeps a two address instruction down to two has nothing to work on and each of the
840    /// three wants a register of its own. The instruction is the indexed store: `a[i] = v` reads a
841    /// base, an index and a value, and at `-O0`, where nothing is coalesced, all three of them are
842    /// stack slots. The rewriter aborted on it, which stopped brotli and cmocka on the first file
843    /// that held one and sqlite3 on `fts5Init`.
844    ///
845    /// The third register is borrowed rather than held back, and the borrowing is what this is
846    /// really about: it takes a register the allocator gave to a value that is live right across
847    /// the instruction, which is safe because that value is put in a slot in front of the
848    /// instruction and brought back behind it.
849    #[test]
850    fn an_instruction_reading_three_spilled_values_borrows_a_register_for_the_third() {
851        let mut names = Interner::new();
852        let mut func = Func::new(names.intern("f"));
853        let opcode = Opcode::new(names.intern("x64.nop"));
854        let block = func.create_block();
855        let keeper = func.new_vreg(GPR);
856        let base = func.new_vreg(GPR);
857        let index = func.new_vreg(GPR);
858        let value = func.new_vreg(GPR);
859        func.build(block, opcode).def(keeper, GPR).finish();
860        for reg in [base, index, value] {
861            func.build(block, opcode)
862                .operand(Operand::write(reg, GPR).with(Constraint::Stack))
863                .finish();
864        }
865        let store =
866            func.build(block, opcode).uses(base, GPR).uses(index, GPR).uses(value, GPR).finish();
867        func.build(block, opcode).uses(keeper, GPR).finish();
868
869        assert_eq!(
870            run(&mut func, &narrow(2)),
871            [
872                "after 1: slot0 = rdx",
873                "after 2: slot1 = rdx",
874                "after 3: slot2 = rdx",
875                "before 4: slot3 = rax",
876                "before 4: rdx = slot0",
877                "before 4: rsi = slot1",
878                "before 4: rax = slot2",
879                "after 4: rax = slot3",
880            ]
881        );
882        assert_eq!(operands(&func, store), ["rdx", "rsi", "rax"]);
883    }
884
885    /// A register the instruction only writes still carries a value in.
886    ///
887    /// A call names every caller saved register as one it writes, and on x86-64 the two held back for
888    /// scratch are both caller saved, so an indirect call through a pointer on the stack has nowhere
889    /// to read the pointer into unless a register named only on the way out is still free on the way
890    /// in. Reading them as spoken for stopped cmocka on its first file.
891    #[test]
892    fn a_register_the_instruction_only_writes_still_carries_a_value_in() {
893        let mut names = Interner::new();
894        let mut func = Func::new(names.intern("f"));
895        let opcode = Opcode::new(names.intern("x64.nop"));
896        let block = func.create_block();
897        let target = func.new_vreg(GPR);
898        func.build(block, opcode)
899            .operand(Operand::write(target, GPR).with(Constraint::Stack))
900            .finish();
901        let call = func
902            .build(block, opcode)
903            .def(Reg::physical(RDX), GPR)
904            .def(Reg::physical(RSI), GPR)
905            .uses(target, GPR)
906            .finish();
907
908        assert_eq!(run(&mut func, &narrow(2)), ["after 0: slot0 = rdx", "before 1: rdx = slot0"]);
909        assert_eq!(operands(&func, call), ["rdx", "rsi", "rdx"]);
910    }
911
912    /// A scratch register the instruction has already named for itself is passed over.
913    ///
914    /// The move that carries a value into a register a fixed constraint asks for and the move that
915    /// fills a scratch register both go in front of the instruction, so handing the same register
916    /// out twice would lose one of the two values without anything saying so. On x86-64 the way
917    /// into this is inline assembly naming `r10` or `r11`, which are the two the file holds back.
918    #[test]
919    fn a_register_the_instruction_already_named_is_not_handed_out_as_scratch() {
920        let mut names = Interner::new();
921        let mut func = Func::new(names.intern("f"));
922        let opcode = Opcode::new(names.intern("x64.nop"));
923        let block = func.create_block();
924        let wanted = func.new_vreg(GPR);
925        let other = func.new_vreg(GPR);
926        for reg in [wanted, other] {
927            func.build(block, opcode)
928                .operand(Operand::write(reg, GPR).with(Constraint::Stack))
929                .finish();
930        }
931        let read = func
932            .build(block, opcode)
933            .operand(Operand::read(wanted, GPR).with(Constraint::Fixed(RCX)))
934            .uses(other, GPR)
935            .finish();
936
937        // `rcx` is both the first scratch register here and the one the instruction insists on, so
938        // the value it did not ask for by name starts at the second one instead.
939        assert_eq!(
940            run(&mut func, &narrow(1)),
941            [
942                "after 0: slot0 = rcx",
943                "after 1: slot1 = rcx",
944                "before 2: rcx = slot0",
945                "before 2: rdx = slot1"
946            ]
947        );
948        assert_eq!(operands(&func, read), ["rcx", "rdx"]);
949    }
950
951    #[test]
952    #[should_panic(expected = "a critical edge has nowhere to put its moves")]
953    fn a_critical_edge_is_refused() {
954        let mut names = Interner::new();
955        let mut func = Func::new(names.intern("f"));
956        let opcode = Opcode::new(names.intern("x64.nop"));
957        let head = func.create_block();
958        let other = func.create_block();
959        let join = func.create_block();
960        let value = func.new_vreg(GPR);
961        func.build(head, opcode).def(value, GPR).finish();
962        let param = func.append_param(join, GPR);
963        *func.succs_mut(head) = vec![BlockCall::with(join, vec![value]), BlockCall::to(other)];
964        *func.succs_mut(other) = vec![BlockCall::with(join, vec![value])];
965        func.build(join, opcode).uses(param, GPR).finish();
966
967        let _ = run(&mut func, &env());
968    }
969
970    #[test]
971    #[should_panic(expected = "what arrives in a function is not a block parameter")]
972    fn a_parameter_on_the_entry_block_is_refused() {
973        let mut names = Interner::new();
974        let mut func = Func::new(names.intern("f"));
975        let block = func.create_block();
976        let param = func.append_param(block, GPR);
977        let opcode = Opcode::new(names.intern("x64.nop"));
978        func.build(block, opcode).uses(param, GPR).finish();
979
980        let _ = run(&mut func, &env());
981    }
982
983    #[test]
984    fn a_value_already_in_a_register_is_left_where_it_is() {
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 inst = func.build(block, opcode).uses(Reg::physical(RDX), GPR).finish();
990
991        assert_eq!(run(&mut func, &env()), Vec::<String>::new());
992        assert_eq!(operands(&func, inst), ["rdx"]);
993    }
994}