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