Skip to main content

rucc_regalloc/
legalize.rs

1//! What an instruction needs around it for the machine to accept the places its operands were
2//! given.
3//!
4//! Design: `spec/optimizer/39-register-allocation.md` section 39.7, the legalization phase, and
5//! tamnd/rucc#1177.
6//!
7//! The assignment says where every value lives, and that is not yet something the machine will
8//! run. A value on the stack has to be read into a register before an instruction that wants it in
9//! one, and an answer written into a register has to be stored to the slot it lives in. An operand
10//! the instruction insists on having in one register has to be moved there and back when its
11//! value lives in another. A two address instruction writes one of the registers it reads, so the
12//! value it reads has to be copied into the register the answer lives in first when the two are
13//! not already the same. This works all of that out for one instruction at a time and hands back
14//! the operands as the machine will read them and the moves either side, in the order they have to
15//! be made in. [`crate::rewrite`] files them, and writes the operands into the function.
16//!
17//! Keeping it apart from the rewrite is what lets it be asked about one instruction and checked
18//! against what comes back, rather than only through the function the whole rewrite produces.
19//!
20//! # How many scratch registers one instruction wants
21//!
22//! Two of a class, and a target holds two of each back for exactly this. The instruction that asks
23//! for most reads two values and writes a third with nothing of the three in a register, and the
24//! arithmetic works out because the two reads are what use the two scratch registers and the answer
25//! is written back into one of them. Writing over it destroys nothing, since it holds a copy of a
26//! value whose home is a stack slot and the instruction has already read it, and the answer is
27//! stored away from it afterwards. Giving the answer a scratch register of its own would want a
28//! third, which a program with enough live values around a call reaches, and that was issue #350.
29//!
30//! Which register the answer goes back into depends on what wrote it. A two address instruction
31//! writes the register the operand it reuses was read into, because that is what two address means.
32//! A three address one, which is `lea` and the compare and set pairs, writes a register that is
33//! none of its operands, and there the answer takes the first scratch register of the class again:
34//! the reads are done by the time the write happens, so the two uses of that register do not meet.
35//! Counting the two jobs in one running number is what made a three address instruction with every
36//! end on the stack ask for a third register and abort, which was issue #726.
37//!
38//! It is only a scratch register the answer may have either way. Where the operand a two address
39//! instruction reuses is in a register the assignment gave out, the value in it may be wanted after
40//! the instruction, and the assignment only lets one be written over when it is not, which it says
41//! by giving the answer that register in the first place. So the answer takes a scratch register
42//! there and the two address copy fills it. That one is filled in front of the instruction rather
43//! than by it, so it cannot share with a read, and the count still comes to two, because an operand
44//! that is in a register is not holding a scratch register.
45//!
46//! Deciding either way needs to know where the operand it reuses went, so an operand that reuses
47//! another and has no register of its own is placed in a second pass over the operands.
48//!
49//! The count is per class. An instruction reading a spilled value out of each of two files wants
50//! the first register of each, since a class holds its own back and nothing on the instruction is
51//! in the other's.
52//!
53//! # What happens when two is not enough after all
54//!
55//! Two runs out on an instruction that reads three registers and writes none, because then there is
56//! no answer to fold back into a register an operand arrived in and the arithmetic above has nothing
57//! to work on. The instruction that does this on x86-64 is the indexed store, whose base, index and
58//! value are three registers it only reads, and at `-O0` all three of them can be stack slots. That
59//! is tamnd/rucc#913, and it stopped brotli and cmocka on the first file that held one.
60//!
61//! What answers it is borrowing: a register of the class the instruction has not named is
62//! taken, whatever is in it is put in a slot of the frame in front of the instruction, and it is
63//! brought back behind it. That asks nothing at all of the register, so it does not matter whether
64//! the value in it is wanted afterwards, whether the callee owes it back, or whether an argument
65//! travels in it, which are the three things that make a register held back hard to find. A third
66//! register held back would cost every function in the program one, and on x86-64 the only one
67//! available is `rax`, which is the return value, so the bill would be a move at every return. This
68//! costs two memory accesses at the one instruction that wanted it and a slot most functions never
69//! take.
70//!
71//! # What a fixed register turns into
72//!
73//! A move each way. The assignment deliberately gave the value some other register, so a division
74//! whose dividend has to be in `rax` gets a move into `rax` in front of it and a move out of `rax`
75//! behind it. That is the cost of the rule the assignment follows, and it is the rule that keeps
76//! the `-O0` allocator one pass.
77//!
78
79use rucc_mir::{Constraint, Func, Inst, Operand, Reg, Role};
80use rucc_target::{PhysReg, RegClass};
81
82use crate::assign::{Assignment, Env, Place};
83use crate::moves::Move;
84
85/// What one instruction becomes.
86#[derive(Debug, Clone, PartialEq, Eq)]
87pub struct Legal {
88    /// The operands, each naming the physical register the instruction reads or writes it in.
89    pub operands: Vec<Operand>,
90    /// The moves in front of the instruction, with the class of each, in the order they are made.
91    pub before: Vec<(Move<Place>, RegClass)>,
92    /// The moves behind it, the same way.
93    pub after: Vec<(Move<Place>, RegClass)>,
94}
95
96/// Works out what one instruction's operands are rewritten to and what has to happen either side of
97/// it for the machine to accept the places the assignment gave them.
98///
99/// The assignment is taken by reference and may gain a slot, which is where a register borrowed at
100/// an instruction with more spilled operands than the class holds registers back for waits.
101/// `spare` is the function's list of those slots, shared by every instruction in it.
102///
103/// # Panics
104///
105/// Panics on an instruction naming every register of a class at once, which leaves nothing to
106/// borrow, and on an operand whose register the assignment says nothing about and which is not a
107/// physical register either.
108#[must_use]
109pub fn instruction(
110    func: &Func,
111    assignment: &mut Assignment,
112    env: &Env,
113    spare: &mut Spare,
114    inst: Inst,
115) -> Legal {
116    let list = func[inst].operands;
117    let mut operands: Vec<Operand> = func[list].to_vec();
118    let mut before = Moves::new();
119    let mut after = Moves::new();
120    let mut taken = Taken::new();
121
122    // Where the assignment put each operand's value, taken before anything is rewritten, since
123    // rewriting an operand is what loses that. The second pass below reads it.
124    let places: Vec<Place> =
125        operands.iter().map(|operand| place(assignment, operand.reg)).collect();
126
127    // A spilled operand that reuses another is left for the second pass, because where it goes
128    // depends on where the operand it reuses went and that is not known until every operand ahead
129    // of it has been placed.
130    let mut reusing: Vec<usize> = Vec::new();
131
132    // Every register the instruction has named for itself, which is one an operand's value is
133    // already in and one a fixed constraint asked for. Taken before anything is rewritten, for the
134    // same reason the places above are: rewriting is what turns an operand's register into a
135    // physical one and loses which of the two it was.
136    let mut claimed = Claimed::default();
137    for (operand, place) in operands.iter().zip(&places) {
138        if let Place::Reg(at) = *place {
139            claimed.named(operand, at);
140        }
141        if let Constraint::Fixed(at) = operand.constraint {
142            claimed.named(operand, at);
143        }
144    }
145    let mut scratch = Scratch::new(env, assignment, spare, claimed);
146
147    for (index, operand) in operands.iter_mut().enumerate() {
148        let fixed = match operand.constraint {
149            Constraint::Fixed(at) => Some(at),
150            _ => None,
151        };
152        let at = match (place(scratch.assignment, operand.reg), fixed) {
153            (Place::Reg(at), None) => at,
154            (Place::Reg(at), Some(fixed)) => {
155                if at != fixed {
156                    let (there, here) = (Place::Reg(fixed), Place::Reg(at));
157                    push(&mut before, &mut after, operand, Move::new(there, here));
158                }
159                fixed
160            }
161            (Place::Slot(_), None) if matches!(operand.constraint, Constraint::Reuse(_)) => {
162                reusing.push(index);
163                continue;
164            }
165            (Place::Slot(slot), fixed) => {
166                // Which of the two jobs this register is for. An operand the instruction only
167                // writes wants one from the instruction onwards, and an operand it reads wants one
168                // from before the instruction until it reads it, so the same register does both
169                // and the two are counted apart.
170                let at = match fixed {
171                    Some(fixed) => fixed,
172                    None if operand.role.is_def() => {
173                        taken.written_into(operand.class, &mut scratch)
174                    }
175                    None => taken.read_into(operand.class, &mut scratch),
176                };
177                push(
178                    &mut before,
179                    &mut after,
180                    operand,
181                    Move::new(Place::Reg(at), Place::Slot(slot)),
182                );
183                at
184            }
185        };
186        operand.reg = Reg::physical(at);
187    }
188
189    for index in reusing {
190        let Constraint::Reuse(other) = operands[index].constraint else {
191            unreachable!("only an operand that reuses another was left for this pass")
192        };
193        let Place::Slot(slot) = places[index] else {
194            unreachable!("only a spilled operand was left for this pass")
195        };
196        // Where the operand it reuses was read into, if it was read into anywhere. A scratch
197        // register holds a copy of a value that lives on the stack, so writing over it destroys
198        // nothing and the instruction can have it. A register the assignment gave out is a
199        // different matter: the value in it may be wanted after the instruction, and the
200        // assignment only lets one be written over when it is not, which it says by giving the
201        // answer that register. So a fresh scratch register there, and the copy below fills it.
202        //
203        // Either way this shape wants two of the class and no more. If the operand it reuses is on
204        // the stack then it is holding one of them already, and if it is not then it is not
205        // holding one at all.
206        //
207        // This one is asked for as a read even though the instruction writes it, because the copy
208        // that fills it goes in front of the instruction. It is live from there, which is the same
209        // span a value read in off the stack is live for, so it cannot share with one.
210        let other = usize::from(other);
211        let at = match places[other] {
212            Place::Slot(_) => phys(operands[other].reg),
213            Place::Reg(_) => taken.read_into(operands[index].class, &mut scratch),
214        };
215        push(
216            &mut before,
217            &mut after,
218            &operands[index],
219            Move::new(Place::Reg(at), Place::Slot(slot)),
220        );
221        operands[index].reg = Reg::physical(at);
222    }
223
224    // A two address instruction writes one of the registers it reads, and the copy that makes that
225    // true goes after everything else in front of the instruction, since what it reads may be a
226    // value that was itself only just read in from the stack.
227    for index in 0..operands.len() {
228        let Constraint::Reuse(other) = operands[index].constraint else { continue };
229        let (to, from) = (operands[index], operands[usize::from(other)]);
230        if to.reg != from.reg {
231            let mov = Move::new(Place::Reg(phys(to.reg)), Place::Reg(phys(from.reg)));
232            before.push((mov, to.class));
233        }
234    }
235
236    // A borrowed register is put away in front of everything else and brought back behind
237    // everything else, since what happens in between is the instruction using it and the moves
238    // that carry its operands in and out. Nothing borrowed at one instruction is still borrowed at
239    // the next, which is what lets the slot be shared.
240    let (saves, restores) = scratch.finish();
241
242    let mut first = saves;
243    first.extend(before);
244    let mut last = after;
245    last.extend(restores);
246    Legal { operands, before: first, after: last }
247}
248
249/// How many scratch registers of each class one instruction has been handed, in each of the two
250/// jobs they do.
251///
252/// Counted per class rather than in one running number, because the classes hold their own back
253/// and an instruction reading a spilled value out of each of two files would otherwise skip the
254/// first register of the second file for no reason.
255///
256/// Counted per job as well, and that is the part that keeps the count down. A register a spilled
257/// value is read into is live from in front of the instruction until the instruction reads it. A
258/// register the instruction writes its answer into is live from the instruction until the store
259/// behind it. Those two spans do not meet, so one register does both jobs and the counting starts
260/// again rather than carrying on. What that rests on is the machine reading its operands before it
261/// writes its answer, which is true of every instruction the backends here emit and is the same
262/// thing that makes `addq %rax, %rax` mean what it looks like.
263///
264/// Where the count runs out is an instruction that reads three registers and writes none, because
265/// then there is no answer to fold back into a register an operand arrived in and the trick above
266/// has nothing to work on. On x86-64 that instruction is the indexed store, whose base, index and
267/// value are three registers it only reads, and at `-O0` all three of them can be stack slots. That
268/// is tamnd/rucc#913, and what answers it is [`Scratch::borrow`] rather than a third register held
269/// back, since holding a third back costs every function a register and this costs only the
270/// instruction that wanted one.
271#[derive(Debug, Default)]
272struct Taken {
273    /// How many of each class hold a value read in ahead of the instruction.
274    read: Vec<usize>,
275    /// How many of each class hold an answer the instruction writes.
276    written: Vec<usize>,
277}
278
279impl Taken {
280    /// Nothing handed out yet.
281    fn new() -> Self {
282        Self::default()
283    }
284
285    /// A register of a class for a value read in ahead of the instruction.
286    fn read_into(&mut self, class: RegClass, scratch: &mut Scratch<'_>) -> PhysReg {
287        Self::take(&mut self.read, class, scratch, Role::Use)
288    }
289
290    /// A register of a class for an answer the instruction writes.
291    fn written_into(&mut self, class: RegClass, scratch: &mut Scratch<'_>) -> PhysReg {
292        Self::take(&mut self.written, class, scratch, Role::Def)
293    }
294
295    /// The next register of a class out of one of the two counts, passing over any the instruction
296    /// has already named itself for a value travelling the same way and borrowing one when the held
297    /// back ones run out.
298    ///
299    /// An operand with a fixed constraint names a register the instruction has to have its value
300    /// in, and the move that puts it there is in the same list as the move that would fill a
301    /// scratch register. So handing the same register out for both would lose one of the two
302    /// values, quietly and at run time. It is passed over instead.
303    ///
304    /// Which way the value travels is what decides whether there is a clash at all, and [`Claimed`]
305    /// says why. A register the instruction only writes is free to carry a value in, which is what a
306    /// call wants: a call names every caller saved register as one it writes, and those are the very
307    /// registers held back for scratch.
308    ///
309    /// A clash comes up on a machine where a register held back is one an instruction can also
310    /// insist on, and on x86-64 the way in is inline assembly naming `r10` or `r11`.
311    fn take(
312        counts: &mut Vec<usize>,
313        class: RegClass,
314        scratch: &mut Scratch<'_>,
315        role: Role,
316    ) -> PhysReg {
317        let index = usize::from(class.number());
318        if counts.len() <= index {
319            counts.resize(index + 1, 0);
320        }
321        let held: &[PhysReg] = scratch.env.scratch(class);
322        while held.get(counts[index]).is_some_and(|&reg| scratch.claimed.clashes(role, class, reg))
323        {
324            counts[index] += 1;
325        }
326        if let Some(&at) = held.get(counts[index]) {
327            counts[index] += 1;
328            return at;
329        }
330        scratch.borrow(class)
331    }
332}
333
334/// The registers the instruction has named for itself, which scratch has to work around.
335///
336/// A register is kept with the class it was named in, because a register number is only a number
337/// into one file and the same one means a different register in another: a call names sixteen vector
338/// registers numbered nought to fifteen and sixteen general purpose ones numbered the same, and
339/// reading the two lists as one leaves the general purpose file looking entirely spoken for.
340///
341/// Reading and writing are kept apart because they clash with different things. A register a value
342/// arrives in is one no move in front of the instruction may write, and a register an answer leaves
343/// in is one no move behind it may write. A call is the case that makes the difference matter: it
344/// names every caller saved register as one it writes, `r10` and `r11` among them, and an indirect
345/// call through a pointer on the stack has to read that pointer into one of exactly those two.
346#[derive(Debug, Default)]
347struct Claimed {
348    /// The registers a value arrives in, with the class each was named in.
349    reads: Vec<(RegClass, PhysReg)>,
350    /// The registers an answer leaves in, with the class each was named in.
351    writes: Vec<(RegClass, PhysReg)>,
352}
353
354impl Claimed {
355    /// Records a register an operand named, on the side its value travels.
356    fn named(&mut self, operand: &Operand, at: PhysReg) {
357        self.side_mut(operand.role).push((operand.class, at));
358    }
359
360    /// Records a register nothing may be handed for the rest of the instruction, which is one
361    /// [`Scratch::borrow`] has just taken.
362    fn taken(&mut self, class: RegClass, at: PhysReg) {
363        self.reads.push((class, at));
364        self.writes.push((class, at));
365    }
366
367    /// Whether handing that register out for a value travelling that way would lose a value.
368    fn clashes(&self, role: Role, class: RegClass, at: PhysReg) -> bool {
369        self.side(role).contains(&(class, at))
370    }
371
372    /// Whether the instruction names that register at all, which is what borrowing has to keep off:
373    /// what is borrowed is put back behind the instruction, over anything left there.
374    fn names(&self, class: RegClass, at: PhysReg) -> bool {
375        self.reads.contains(&(class, at)) || self.writes.contains(&(class, at))
376    }
377
378    /// The list for values travelling that way. The lists are one instruction's long, so a scan
379    /// beats a set.
380    fn side(&self, role: Role) -> &Vec<(RegClass, PhysReg)> {
381        if role.is_def() { &self.writes } else { &self.reads }
382    }
383
384    /// The same, to write to.
385    fn side_mut(&mut self, role: Role) -> &mut Vec<(RegClass, PhysReg)> {
386        if role.is_def() { &mut self.writes } else { &mut self.reads }
387    }
388}
389
390/// Moves waiting to be filed, each with the class of the value it moves.
391///
392/// The class travels with the move because an [`Edit`] carries one and the consumer needs it to pick
393/// the instruction that does the move, and by the time a move is filed the operand it came from is
394/// out of reach.
395type Moves = Vec<(Move<Place>, RegClass)>;
396
397/// The frame slots a borrowed register's value waits in, one list per class.
398///
399/// They belong to the function rather than to an instruction, because a borrowed register is given
400/// back before the next instruction starts and the slot is dead in between, so one slot serves
401/// every instruction in the function that borrows. Most functions never take one at all.
402pub type Spare = Vec<Vec<u32>>;
403
404/// What it takes to hand a register to one instruction.
405///
406/// It is a struct rather than four arguments because [`Scratch::borrow`] writes to all of them at
407/// once: it reads the environment, takes a slot off the assignment, remembers the register so a
408/// second borrow at the same instruction does not land on it, and files the two moves that make it
409/// safe.
410struct Scratch<'a> {
411    env: &'a Env,
412    /// Where every value went, and where a slot for a borrowed register comes from.
413    assignment: &'a mut Assignment,
414    /// The function's slots for borrowed registers, reused at every instruction.
415    spare: &'a mut Spare,
416    /// Every register the instruction has named, and then every one borrowed here as it is borrowed.
417    claimed: Claimed,
418    /// How many of each class have been borrowed at this instruction, which says which slot the
419    /// next one uses.
420    borrowed: Vec<usize>,
421    /// The moves that put a borrowed register's value away, which go in front of everything else.
422    saves: Moves,
423    /// The moves that bring it back, which go behind everything else.
424    restores: Moves,
425}
426
427impl<'a> Scratch<'a> {
428    /// Nothing borrowed yet at an instruction claiming those registers.
429    fn new(
430        env: &'a Env,
431        assignment: &'a mut Assignment,
432        spare: &'a mut Spare,
433        claimed: Claimed,
434    ) -> Self {
435        Self {
436            env,
437            assignment,
438            spare,
439            claimed,
440            borrowed: Vec::new(),
441            saves: Vec::new(),
442            restores: Vec::new(),
443        }
444    }
445
446    /// A register of the class the instruction is not using, with whatever is in it put away in
447    /// front of the instruction and brought back behind it.
448    ///
449    /// This is what a class runs out to, and it works on any machine because it asks nothing at all
450    /// of the register it takes. Whatever was in it is somewhere else for the length of one
451    /// instruction, so it does not matter whether that value is wanted afterwards, whether the
452    /// callee owes the register back, or whether an argument travels in it, which are the three
453    /// things that make a register held back hard to find. What it costs is two memory accesses at
454    /// the one instruction that wanted it and one slot of the frame, against a register taken off
455    /// every function in the program, and `rucc_codegen::pipeline` says why that trade goes this
456    /// way round on x86-64.
457    ///
458    /// The register is any of the class the instruction has not claimed for itself. A register the
459    /// allocator gave a value that is live right across the instruction is as good as an idle one,
460    /// which is the whole point of putting the contents away first.
461    ///
462    /// # Panics
463    ///
464    /// Panics if the class has no register the instruction has not already claimed, which is an
465    /// instruction naming every register of a file at once.
466    fn borrow(&mut self, class: RegClass) -> PhysReg {
467        let index = usize::from(class.number());
468        let at = *self
469            .env
470            .order(class)
471            .iter()
472            .find(|&&reg| !self.claimed.names(class, reg))
473            .expect("an instruction naming every register of its class at once");
474
475        if self.borrowed.len() <= index {
476            self.borrowed.resize(index + 1, 0);
477        }
478        if self.spare.len() <= index {
479            self.spare.resize(index + 1, Vec::new());
480        }
481        let nth = self.borrowed[index];
482        if self.spare[index].len() <= nth {
483            let slot = self.assignment.take_slot(class);
484            self.spare[index].push(slot);
485        }
486        let slot = self.spare[index][nth];
487
488        self.borrowed[index] = nth + 1;
489        self.claimed.taken(class, at);
490        self.saves.push((Move::new(Place::Slot(slot), Place::Reg(at)), class));
491        self.restores.push((Move::new(Place::Reg(at), Place::Slot(slot)), class));
492        at
493    }
494
495    /// The moves either side of the instruction, once every register has been handed out.
496    fn finish(self) -> (Moves, Moves) {
497        (self.saves, self.restores)
498    }
499}
500
501/// Files a move in front of the instruction or behind it, and turns it round for a value the
502/// instruction writes, since that one travels the other way.
503fn push(before: &mut Moves, after: &mut Moves, operand: &Operand, mov: Move<Place>) {
504    if operand.role.is_def() {
505        after.push((Move::new(mov.from, mov.to), operand.class));
506    } else {
507        before.push((mov, operand.class));
508    }
509}
510
511/// Where a register is, whether the allocator put it there or it was already somewhere.
512pub(crate) fn place(assignment: &Assignment, reg: Reg) -> Place {
513    assignment.place(reg).unwrap_or_else(|| Place::Reg(phys(reg)))
514}
515
516/// The physical register a register is, once it has to be one.
517pub(crate) fn phys(reg: Reg) -> PhysReg {
518    reg.phys().expect("a register the assignment says nothing about and that is not a register")
519}
520
521#[cfg(test)]
522mod tests {
523    use rucc_base::Interner;
524    use rucc_mir::Opcode;
525    use rucc_target::x86_64::{GPR, RAX, RCX, SYSV};
526
527    use super::*;
528
529    /// Two registers to hand out and two held back after them.
530    fn env() -> Env {
531        Env::new().with(GPR, &SYSV.int_order[..2], &SYSV.int_order[2..4])
532    }
533
534    fn func() -> (Func, Opcode, rucc_mir::Block) {
535        let mut names = Interner::new();
536        let mut func = Func::new(names.intern("f"));
537        let opcode = Opcode::new(names.intern("x64.nop"));
538        let block = func.create_block();
539        (func, opcode, block)
540    }
541
542    fn legal(func: &Func, assignment: &mut Assignment, inst: Inst) -> Legal {
543        instruction(func, assignment, &env(), &mut Spare::new(), inst)
544    }
545
546    fn regs(legal: &Legal) -> Vec<PhysReg> {
547        legal.operands.iter().map(|operand| phys(operand.reg)).collect()
548    }
549
550    #[test]
551    fn an_instruction_whose_values_are_where_it_wants_them_needs_nothing() {
552        let (mut func, opcode, block) = func();
553        let value = func.new_vreg(GPR);
554        let inst = func.build(block, opcode).uses(value, GPR).finish();
555        let mut assignment = Assignment::empty(func.vregs());
556        assignment.put(value, Place::Reg(RCX));
557
558        let legal = legal(&func, &mut assignment, inst);
559        assert_eq!(regs(&legal), [RCX]);
560        assert!(legal.before.is_empty() && legal.after.is_empty());
561    }
562
563    #[test]
564    fn a_register_the_instruction_insists_on_is_filled_in_front_of_it() {
565        let (mut func, opcode, block) = func();
566        let value = func.new_vreg(GPR);
567        let inst = func
568            .build(block, opcode)
569            .operand(Operand::read(value, GPR).with(Constraint::Fixed(RAX)))
570            .finish();
571        let mut assignment = Assignment::empty(func.vregs());
572        assignment.put(value, Place::Reg(RCX));
573
574        let legal = legal(&func, &mut assignment, inst);
575        assert_eq!(regs(&legal), [RAX]);
576        assert_eq!(legal.before, [(Move::new(Place::Reg(RAX), Place::Reg(RCX)), GPR)]);
577        assert!(legal.after.is_empty());
578    }
579
580    #[test]
581    fn an_answer_written_where_it_does_not_live_is_taken_away_behind_it() {
582        let (mut func, opcode, block) = func();
583        let value = func.new_vreg(GPR);
584        let inst = func
585            .build(block, opcode)
586            .operand(Operand::write(value, GPR).with(Constraint::Fixed(RAX)))
587            .finish();
588        let mut assignment = Assignment::empty(func.vregs());
589        assignment.put(value, Place::Reg(RCX));
590
591        let legal = legal(&func, &mut assignment, inst);
592        assert_eq!(regs(&legal), [RAX]);
593        assert!(legal.before.is_empty());
594        assert_eq!(legal.after, [(Move::new(Place::Reg(RCX), Place::Reg(RAX)), GPR)]);
595    }
596
597    #[test]
598    fn a_value_on_the_stack_is_read_into_a_register_held_back() {
599        let (mut func, opcode, block) = func();
600        let value = func.new_vreg(GPR);
601        let inst = func.build(block, opcode).uses(value, GPR).finish();
602        let mut assignment = Assignment::empty(func.vregs());
603        let slot = assignment.take_slot(GPR);
604        assignment.put(value, Place::Slot(slot));
605
606        let legal = legal(&func, &mut assignment, inst);
607        let scratch = env().scratch(GPR)[0];
608        assert_eq!(regs(&legal), [scratch]);
609        assert_eq!(legal.before, [(Move::new(Place::Reg(scratch), Place::Slot(slot)), GPR)]);
610    }
611
612    #[test]
613    fn a_two_address_answer_apart_from_its_source_is_copied_into_first() {
614        let (mut func, opcode, block) = func();
615        let source = func.new_vreg(GPR);
616        let answer = func.new_vreg(GPR);
617        let inst = func
618            .build(block, opcode)
619            .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
620            .uses(source, GPR)
621            .finish();
622        let mut assignment = Assignment::empty(func.vregs());
623        assignment.put(source, Place::Reg(RCX));
624        assignment.put(answer, Place::Reg(RAX));
625
626        let legal = legal(&func, &mut assignment, inst);
627        assert_eq!(regs(&legal), [RAX, RCX]);
628        assert_eq!(legal.before, [(Move::new(Place::Reg(RAX), Place::Reg(RCX)), GPR)]);
629    }
630
631    #[test]
632    fn a_third_value_on_the_stack_borrows_a_register_and_gives_it_back() {
633        let (mut func, opcode, block) = func();
634        let values: Vec<Reg> = (0..3).map(|_| func.new_vreg(GPR)).collect();
635        let build = func.build(block, opcode);
636        let inst = values.iter().fold(build, |build, &value| build.uses(value, GPR)).finish();
637        let mut assignment = Assignment::empty(func.vregs());
638        for &value in &values {
639            let slot = assignment.take_slot(GPR);
640            assignment.put(value, Place::Slot(slot));
641        }
642
643        let legal = legal(&func, &mut assignment, inst);
644        let borrowed = regs(&legal)[2];
645        assert!(!env().scratch(GPR).contains(&borrowed));
646        // The one borrowed is put away first and brought back last, around everything else.
647        let spare = Place::Slot(3);
648        assert_eq!(legal.before[0], (Move::new(spare, Place::Reg(borrowed)), GPR));
649        assert_eq!(legal.after.last(), Some(&(Move::new(Place::Reg(borrowed), spare), GPR)));
650        assert_eq!(assignment.spilled(), 4);
651    }
652}