Skip to main content

rucc_regalloc/
check.rs

1//! The allocation checker: whether an assignment is one the machine can actually run.
2//!
3//! Design: `spec/10-backend.md` section 10.4, which asks for this in debug and CI builds.
4//!
5//! A register allocator is the pass whose bugs are hardest to find from the outside. It does not
6//! change what a program means, so a wrong allocation compiles, links and runs, and then produces
7//! the wrong number in one function of one program under one register pressure. The stack trace
8//! points at the arithmetic, the arithmetic is right, and the value it read was overwritten four
9//! instructions earlier by something unrelated. A checker turns all of that into an assertion at
10//! the point the mistake was made, naming the two values and the register they were both put in.
11//!
12//! # What it asks
13//!
14//! Five questions, and they are the whole of what an assignment has to get right.
15//!
16//! Every value the function reads is written first, on every path that reaches the read. Every
17//! value the function uses has somewhere to live. Two values that are both wanted at the same
18//! point are not in the same register or the same slot. Nothing is sitting in a register that an
19//! instruction insists on for itself, because that register belongs to the instruction for as long
20//! as it runs. A value an instruction can only read from memory is in memory.
21//!
22//! The first of those is not about the allocation at all, since the value would be read before it
23//! was written whatever register it went to. It is asked here because this is where the answer is
24//! already computed: a value read before it is written is a value live on the way into the entry
25//! block, and the liveness the allocator needs anyway says which those are. A function that gets
26//! this wrong is one the allocator will happily place, and what comes out reads a stack slot
27//! nothing ever stored to.
28//!
29//! # What it does not ask
30//!
31//! Whether the allocation is any good. A function with every value on the stack passes, and so it
32//! should: it is slow and it is correct, and this is the thing that says which of the two a
33//! problem is. Quality is what the numbers in `spec/14-target-ladder.md` are for.
34//!
35//! It also does not read the rewrite. It runs on the assignment, before [`crate::rewrite`] has
36//! touched the function, because the assignment is the decision and the rewrite is a
37//! transcription of it. A rewrite that transcribes a good decision badly is a different bug, and
38//! [`crate::trace`] is the checker that catches it by following each value from the instruction
39//! that wrote it to the instructions that read it.
40//!
41//! # Why it repeats work
42//!
43//! The two address instructions are worked out again here rather than borrowed from
44//! [`crate::assign`], and that is deliberate. A checker that shares its reasoning with the thing
45//! it checks agrees with it about everything, including the mistakes, and the one bug it can never
46//! find is the one in the code they share. Fifteen lines is a cheap price for a second opinion.
47//!
48//! It is also allowed to be slow. Looking for a value in a register an instruction wants is the
49//! plain product of the values and the constrained operands, with no index over either, because a
50//! checker runs in debug builds and in CI and the thing it is checking is the thing that has to be
51//! fast.
52
53use std::fmt;
54
55use rucc_mir::{Constraint, Func, Inst, Reg, Role};
56use rucc_target::{PhysReg, RegClass};
57
58use crate::assign::{Assignment, Place};
59use crate::live::{Area, Live, Range};
60use crate::order::{Order, Point};
61
62/// One thing wrong with an allocation.
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum Problem {
65    /// A value the function reads or writes was given no place at all.
66    Nowhere {
67        /// The value with nowhere to be.
68        reg: Reg,
69    },
70    /// Two values that are both live somewhere were put in the same place, so whichever is written
71    /// second destroys the other.
72    Shared {
73        /// The value that was there first.
74        first: Reg,
75        /// The value that was put on top of it.
76        second: Reg,
77        /// The place they were both given.
78        place: Place,
79    },
80    /// A value was left in a register an instruction claims for itself, over the instruction that
81    /// claims it, so the moves around that instruction overwrite the value.
82    InTheWay {
83        /// The value in the way.
84        reg: Reg,
85        /// The register the instruction insists on.
86        at: PhysReg,
87        /// The instruction that insists on it.
88        inst: Inst,
89    },
90    /// A value an instruction can only read from memory was put in a register.
91    NotOnTheStack {
92        /// The value that has to be in memory.
93        reg: Reg,
94        /// The instruction that says so.
95        inst: Inst,
96    },
97    /// A value is read on some path from the entry block without anything on that path having
98    /// written it, so what the instruction reading it gets is whatever was left there.
99    NeverWritten {
100        /// The value nothing writes.
101        reg: Reg,
102    },
103}
104
105impl fmt::Display for Problem {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        match self {
108            Problem::Nowhere { reg } => write!(f, "{} has nowhere to live", name(*reg)),
109            Problem::Shared { first, second, place } => {
110                let (first, second) = (name(*first), name(*second));
111                write!(f, "{first} and {second} are both live and both in {}", place_name(*place))
112            }
113            Problem::InTheWay { reg, at, inst } => {
114                let reg = name(*reg);
115                let inst = inst.index();
116                write!(f, "{reg} is in register {}, which instruction {inst} wants", at.number())
117            }
118            Problem::NotOnTheStack { reg, inst } => {
119                let reg = name(*reg);
120                write!(f, "{reg} is not on the stack, and instruction {} needs it", inst.index())
121            }
122            Problem::NeverWritten { reg } => {
123                write!(f, "{} is read before anything writes it", name(*reg))
124            }
125        }
126    }
127}
128
129/// Everything wrong with an allocation, in an order a person can read.
130///
131/// An empty answer is the one every allocation is supposed to give. Anything else is a compiler
132/// bug rather than a program the compiler cannot handle, which is why [`crate::run`] asserts on it
133/// instead of reporting it as a diagnostic.
134///
135/// # Panics
136///
137/// Panics on a function with two billion virtual registers in it, which is a function no machine
138/// has the memory to hold.
139#[must_use]
140pub fn check(func: &Func, order: &Order, live: &Live, assignment: &Assignment) -> Vec<Problem> {
141    let mut problems = Vec::new();
142    // What arrives live in the entry block is what the function reads without writing, since
143    // nothing runs in front of the entry block to have written it.
144    if let Some(entry) = func.entry() {
145        for reg in live.live_in(entry) {
146            problems.push(Problem::NeverWritten { reg });
147        }
148    }
149    let reuses = reuses(func, order);
150    let mut values = Vec::new();
151    for (number, reuse) in reuses.iter().enumerate() {
152        let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
153        let (Some(mut area), Some(class)) = (live.area(reg), func.class_of(reg)) else {
154            continue;
155        };
156        let Some(place) = assignment.place(reg) else {
157            problems.push(Problem::Nowhere { reg });
158            continue;
159        };
160        // A two address instruction writes its answer into a register it read, so the answer is
161        // really in that register from the moment the instruction starts and not from the moment
162        // it ends. Reading its area any other way lets it share the register with something the
163        // same instruction is still reading.
164        if let Some(reuse) = reuse {
165            area = area.with(reuse.at);
166        }
167        values.push(Value { reg, class, range: area.hull(), area, place });
168    }
169    overlaps(&values, &reuses, live, &mut problems);
170    instructions(func, order, assignment, &values, &reuses, &mut problems);
171    problems
172}
173
174/// Everything wrong with an allocation, as an assertion message.
175#[must_use]
176pub fn report(problems: &[Problem]) -> String {
177    let places = if problems.len() == 1 { "place" } else { "places" };
178    let mut report = format!("the allocation is wrong in {} {places}", problems.len());
179    for problem in problems {
180        report.push_str("\n  ");
181        report.push_str(&problem.to_string());
182    }
183    report
184}
185
186/// One value, where it is wanted and where it was put.
187#[derive(Debug, Clone, Copy)]
188struct Value<'a> {
189    reg: Reg,
190    class: RegClass,
191    /// The interval around the area, which is what the sweep below reads.
192    range: Range,
193    /// Everywhere the value is really live, which is what says whether sharing a place with
194    /// another value is a mistake.
195    area: Area<'a>,
196    place: Place,
197}
198
199/// A value written into the register another operand of the same instruction was read from.
200#[derive(Debug, Clone, Copy)]
201struct Reuse {
202    source: Reg,
203    at: Point,
204}
205
206/// Looks for two values that are both live somewhere and were put in the same place.
207///
208/// A sweep in the order the values start, holding the ones whose interval still reaches this one,
209/// so the pairs it compares are the pairs that can be wrong rather than all of them. The interval
210/// is generous, so a pair that survives the sweep is then asked whether the areas inside those
211/// intervals really meet.
212fn overlaps(
213    values: &[Value<'_>],
214    reuses: &[Option<Reuse>],
215    live: &Live,
216    problems: &mut Vec<Problem>,
217) {
218    let mut sorted = values.to_vec();
219    sorted.sort_by_key(|value| (value.range.start, value.reg));
220    let mut active: Vec<Value<'_>> = Vec::new();
221    for value in sorted {
222        active.retain(|held| held.range.end >= value.range.start);
223        for held in &active {
224            if !together(*held, value)
225                || !held.area.overlaps(value.area)
226                || coalesced(*held, value, reuses, live)
227            {
228                continue;
229            }
230            problems.push(Problem::Shared {
231                first: held.reg,
232                second: value.reg,
233                place: value.place,
234            });
235        }
236        active.push(value);
237    }
238}
239
240/// Whether two values were put in the same place.
241///
242/// Two registers of different classes are different registers even when they are the same number,
243/// which is what a class is. Two slots are the same slot whatever is in them, because a frame is
244/// one piece of memory.
245fn together(first: Value<'_>, second: Value<'_>) -> bool {
246    match (first.place, second.place) {
247        (Place::Reg(first_at), Place::Reg(second_at)) => {
248            first_at == second_at && first.class == second.class
249        }
250        (Place::Slot(first_slot), Place::Slot(second_slot)) => first_slot == second_slot,
251        _ => false,
252    }
253}
254
255/// Whether one of the two is the answer a two address instruction wrote into the register it read
256/// the other from, which is the one overlap that is not a mistake.
257///
258/// It only holds when the two are not live at the same time anywhere, asked of the areas liveness
259/// worked out, without the extra point the reuse adds. A value read again afterwards needs its
260/// register afterwards, so writing over it is the plain bug this whole file exists to find. And a
261/// value that covers the reuse point on its own is one that was already live on the way in. That
262/// is what a loop carrying its own answer round looks like: written at the bottom and read by the
263/// next turn. Such a value is wanted where the instruction reads as well as after it, so it is
264/// genuinely on top of the one it reuses and no excuse at the one instruction they share makes
265/// them fit in a single register.
266fn coalesced(first: Value<'_>, second: Value<'_>, reuses: &[Option<Reuse>], live: &Live) -> bool {
267    let pair = |source: Value<'_>, dest: Value<'_>| {
268        let Some(reuse) = reuses[index(dest.reg)] else { return false };
269        reuse.source == source.reg && crate::assign::apart(live, source.reg, dest.reg)
270    };
271    pair(first, second) || pair(second, first)
272}
273
274/// Looks for a value in a register an instruction wants, and for a value that had to be in memory
275/// and is not.
276fn instructions(
277    func: &Func,
278    order: &Order,
279    assignment: &Assignment,
280    values: &[Value<'_>],
281    reuses: &[Option<Reuse>],
282    problems: &mut Vec<Problem>,
283) {
284    for block in func.blocks() {
285        for inst in func.insts(block) {
286            for operand in &func[func[inst].operands] {
287                if operand.constraint == Constraint::Stack
288                    && matches!(assignment.place(operand.reg), Some(Place::Reg(_)))
289                {
290                    problems.push(Problem::NotOnTheStack { reg: operand.reg, inst });
291                }
292                // A physical register an operand names outright is claimed exactly as firmly as
293                // one a constraint asks for, since nothing before allocation writes one except an
294                // instruction that has no choice.
295                let at = match operand.constraint {
296                    Constraint::Fixed(at) => Some(at),
297                    _ => operand.reg.phys(),
298                };
299                let Some(at) = at else { continue };
300                let early = order.early(inst);
301                let point = if operand.role == Role::Def { order.late(inst) } else { early };
302                for value in values {
303                    let mine = value.reg == operand.reg
304                        || reuses[index(value.reg)].is_some_and(|reuse| {
305                            reuse.source == operand.reg
306                                && reuse.at == early
307                                && value.place == Place::Reg(at)
308                        });
309                    if mine || value.class != operand.class {
310                        continue;
311                    }
312                    // A write of only the top of the register, which a value that fits under it
313                    // survives. See [`Constraint::Above`].
314                    let width = func.width(value.reg);
315                    let under = |above| width.is_some_and(|width| width <= above);
316                    if matches!(operand.constraint, Constraint::Above(above) if under(above)) {
317                        continue;
318                    }
319                    // The interval around a value covers blocks the value never reaches, so what
320                    // decides this is the area inside it, which says whether the value is live at
321                    // this point rather than whether the point is between its ends.
322                    // tamnd/rucc#982.
323                    if value.place == Place::Reg(at) && value.area.covers(point) {
324                        problems.push(Problem::InTheWay { reg: value.reg, at, inst });
325                    }
326                }
327            }
328        }
329    }
330}
331
332/// The value each two address instruction reuses, by the virtual register it writes.
333fn reuses(func: &Func, order: &Order) -> Vec<Option<Reuse>> {
334    let mut reuses = vec![None; func.vregs()];
335    for block in func.blocks() {
336        for inst in func.insts(block) {
337            let operands = &func[func[inst].operands];
338            for operand in operands {
339                let Constraint::Reuse(other) = operand.constraint else { continue };
340                let number = operand.reg.number().and_then(|number| usize::try_from(number).ok());
341                let Some(number) = number else { continue };
342                let source = operands[usize::from(other)].reg;
343                reuses[number] = Some(Reuse { source, at: order.early(inst) });
344            }
345        }
346    }
347    reuses
348}
349
350/// A virtual register's number as a table index, and zero for a physical one, which never has an
351/// entry of its own and is never what a reuse writes.
352fn index(reg: Reg) -> usize {
353    reg.number().and_then(|number| usize::try_from(number).ok()).unwrap_or(0)
354}
355
356/// What a value is called in a report.
357fn name(reg: Reg) -> String {
358    match reg.number() {
359        Some(number) => format!("%{number}"),
360        None => format!("register {}", reg.phys().expect("a physical register").number()),
361    }
362}
363
364/// What a place is called in a report, without the target's name for it, since this crate holds
365/// nothing of any target.
366fn place_name(place: Place) -> String {
367    match place {
368        Place::Reg(at) => format!("register {}", at.number()),
369        Place::Slot(slot) => format!("slot {slot}"),
370    }
371}
372
373#[cfg(test)]
374mod tests {
375    use rucc_base::Interner;
376    use rucc_mir::{BlockCall, Opcode, Operand};
377    use rucc_target::x86_64::{GPR, RAX, RCX, RDX, SYSV};
378
379    use super::*;
380    use crate::assign::{Env, assign};
381
382    /// The x86-64 environment, with the last three of the allocation order held back as scratch.
383    fn env() -> Env {
384        let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
385        Env::new().with(GPR, order, scratch)
386    }
387
388    /// What the checker says about the allocation the single pass allocator works out, which is
389    /// supposed to be nothing at all.
390    fn allocated(func: &Func) -> Vec<String> {
391        let order = Order::of(func);
392        let live = Live::of(func, &order);
393        let assignment = assign(func, &order, &live, &env());
394        said(func, &order, &live, &assignment)
395    }
396
397    /// What the checker says about an allocation somebody wrote by hand.
398    fn said(func: &Func, order: &Order, live: &Live, assignment: &Assignment) -> Vec<String> {
399        check(func, order, live, assignment).iter().map(ToString::to_string).collect()
400    }
401
402    /// The order and the liveness of a function, which every hand written case needs both of.
403    fn read(func: &Func) -> (Order, Live) {
404        let order = Order::of(func);
405        let live = Live::of(func, &order);
406        (order, live)
407    }
408
409    /// A value in `rcx` over an instruction that writes `rcx` from its eighth byte up.
410    fn under_the_top(width: u32) -> Vec<String> {
411        let mut names = Interner::new();
412        let mut func = Func::new(names.intern("f"));
413        let opcode = Opcode::new(names.intern("x64.nop"));
414        let block = func.create_block();
415        let held = func.new_vreg(GPR);
416        func.set_width(held, width);
417        func.build(block, opcode).def(held, GPR).finish();
418        func.build(block, opcode)
419            .operand(Operand::write(Reg::physical(RCX), GPR).with(Constraint::Above(8)))
420            .finish();
421        func.build(block, opcode).uses(held, GPR).finish();
422        let (order, live) = read(&func);
423        let mut assignment = Assignment::empty(func.vregs());
424        assignment.put(held, Place::Reg(RCX));
425        said(&func, &order, &live, &assignment)
426    }
427
428    #[test]
429    fn a_value_under_the_part_an_instruction_writes_is_not_in_its_way() {
430        assert!(under_the_top(8).is_empty(), "{:?}", under_the_top(8));
431        let wide = under_the_top(16);
432        assert!(wide.len() == 1 && wide[0].contains("%0"), "{wide:?}");
433        assert_eq!(under_the_top(0), wide);
434    }
435
436    #[test]
437    fn an_allocation_the_allocator_worked_out_has_nothing_wrong_with_it() {
438        let mut names = Interner::new();
439        let mut func = Func::new(names.intern("f"));
440        let opcode = Opcode::new(names.intern("x64.nop"));
441        let block = func.create_block();
442        let first = func.new_vreg(GPR);
443        let second = func.new_vreg(GPR);
444        func.build(block, opcode).def(first, GPR).finish();
445        func.build(block, opcode).def(second, GPR).finish();
446        func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
447
448        assert_eq!(allocated(&func), Vec::<String>::new());
449    }
450
451    #[test]
452    fn a_value_with_nowhere_to_live_is_found() {
453        let mut names = Interner::new();
454        let mut func = Func::new(names.intern("f"));
455        let opcode = Opcode::new(names.intern("x64.nop"));
456        let block = func.create_block();
457        let only = func.new_vreg(GPR);
458        func.build(block, opcode).def(only, GPR).finish();
459        func.build(block, opcode).uses(only, GPR).finish();
460
461        let (order, live) = read(&func);
462        let assignment = Assignment::empty(func.vregs());
463
464        assert_eq!(said(&func, &order, &live, &assignment), ["%0 has nowhere to live"]);
465    }
466
467    #[test]
468    fn a_value_read_before_anything_writes_it_is_found() {
469        let mut names = Interner::new();
470        let mut func = Func::new(names.intern("f"));
471        let opcode = Opcode::new(names.intern("x64.nop"));
472        let block = func.create_block();
473        let never = func.new_vreg(GPR);
474        func.build(block, opcode).uses(never, GPR).finish();
475
476        let (order, live) = read(&func);
477        let mut assignment = Assignment::empty(func.vregs());
478        assignment.put(never, Place::Reg(RAX));
479
480        assert_eq!(
481            said(&func, &order, &live, &assignment),
482            ["%0 is read before anything writes it"]
483        );
484    }
485
486    #[test]
487    fn two_values_that_are_both_wanted_and_share_a_register_are_found() {
488        let mut names = Interner::new();
489        let mut func = Func::new(names.intern("f"));
490        let opcode = Opcode::new(names.intern("x64.nop"));
491        let block = func.create_block();
492        let first = func.new_vreg(GPR);
493        let second = func.new_vreg(GPR);
494        func.build(block, opcode).def(first, GPR).finish();
495        func.build(block, opcode).def(second, GPR).finish();
496        func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
497
498        let (order, live) = read(&func);
499        let mut assignment = Assignment::empty(func.vregs());
500        assignment.put(first, Place::Reg(RAX));
501        assignment.put(second, Place::Reg(RAX));
502
503        let said = said(&func, &order, &live, &assignment);
504        assert_eq!(said, ["%0 and %1 are both live and both in register 0"]);
505    }
506
507    #[test]
508    fn a_value_that_lives_in_a_hole_of_another_may_share_its_register() {
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 entry = func.create_block();
513        let arm = func.create_block();
514        let tail = func.create_block();
515        let across = func.new_vreg(GPR);
516        let inside = func.new_vreg(GPR);
517        func.build(entry, opcode).def(across, GPR).finish();
518        *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
519        func.build(arm, opcode).def(inside, GPR).finish();
520        func.build(arm, opcode).uses(inside, GPR).finish();
521        func.build(tail, opcode).uses(across, GPR).finish();
522
523        let (order, live) = read(&func);
524        let mut assignment = Assignment::empty(func.vregs());
525        assignment.put(across, Place::Reg(RAX));
526        assignment.put(inside, Place::Reg(RAX));
527
528        // The arm is written between the two blocks the first value is live in and is a block that
529        // value's own path never goes through, so the second is not sitting on top of it and the
530        // interval around the first saying so is not what decides this. A checker that read the
531        // intervals would call every register the allocator has learned to share a value written
532        // over another. tamnd/rucc#982.
533        assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
534    }
535
536    #[test]
537    fn two_values_that_are_both_wanted_and_share_a_slot_are_found() {
538        let mut names = Interner::new();
539        let mut func = Func::new(names.intern("f"));
540        let opcode = Opcode::new(names.intern("x64.nop"));
541        let block = func.create_block();
542        let first = func.new_vreg(GPR);
543        let second = func.new_vreg(GPR);
544        func.build(block, opcode).def(first, GPR).finish();
545        func.build(block, opcode).def(second, GPR).finish();
546        func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
547
548        let (order, live) = read(&func);
549        let mut assignment = Assignment::empty(func.vregs());
550        let slot = assignment.take_slot(GPR);
551        assignment.put(first, Place::Slot(slot));
552        assignment.put(second, Place::Slot(slot));
553
554        let said = said(&func, &order, &live, &assignment);
555        assert_eq!(said, ["%0 and %1 are both live and both in slot 0"]);
556    }
557
558    #[test]
559    fn two_values_that_are_never_both_wanted_may_share_anything() {
560        let mut names = Interner::new();
561        let mut func = Func::new(names.intern("f"));
562        let opcode = Opcode::new(names.intern("x64.nop"));
563        let block = func.create_block();
564        let first = func.new_vreg(GPR);
565        let second = func.new_vreg(GPR);
566        func.build(block, opcode).def(first, GPR).finish();
567        func.build(block, opcode).uses(first, GPR).finish();
568        func.build(block, opcode).def(second, GPR).finish();
569        func.build(block, opcode).uses(second, GPR).finish();
570
571        let (order, live) = read(&func);
572        let mut assignment = Assignment::empty(func.vregs());
573        assignment.put(first, Place::Reg(RAX));
574        assignment.put(second, Place::Reg(RAX));
575
576        assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
577    }
578
579    #[test]
580    fn a_value_left_in_a_register_an_instruction_wants_is_found() {
581        let mut names = Interner::new();
582        let mut func = Func::new(names.intern("f"));
583        let nop = Opcode::new(names.intern("x64.nop"));
584        let divide = Opcode::new(names.intern("x64.idiv"));
585        let block = func.create_block();
586        let held = func.new_vreg(GPR);
587        let dividend = func.new_vreg(GPR);
588        func.build(block, nop).def(held, GPR).finish();
589        func.build(block, nop).def(dividend, GPR).finish();
590        // The division reads its dividend out of one register and no other, so anything still
591        // wanted afterwards has to be somewhere else while it runs.
592        func.build(block, divide)
593            .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
594            .finish();
595        func.build(block, nop).uses(held, GPR).finish();
596
597        let (order, live) = read(&func);
598        let mut assignment = Assignment::empty(func.vregs());
599        assignment.put(held, Place::Reg(RAX));
600        assignment.put(dividend, Place::Reg(RCX));
601
602        let said = said(&func, &order, &live, &assignment);
603        assert_eq!(said, ["%0 is in register 0, which instruction 2 wants"]);
604    }
605
606    #[test]
607    fn the_value_an_instruction_wants_a_register_for_may_be_in_it_already() {
608        let mut names = Interner::new();
609        let mut func = Func::new(names.intern("f"));
610        let nop = Opcode::new(names.intern("x64.nop"));
611        let divide = Opcode::new(names.intern("x64.idiv"));
612        let block = func.create_block();
613        let dividend = func.new_vreg(GPR);
614        func.build(block, nop).def(dividend, GPR).finish();
615        func.build(block, divide)
616            .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
617            .finish();
618
619        let (order, live) = read(&func);
620        let mut assignment = Assignment::empty(func.vregs());
621        assignment.put(dividend, Place::Reg(RAX));
622
623        // Being in the register the instruction wanted is the best answer, not a problem, and the
624        // rewrite writes no move at all for it.
625        assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
626    }
627
628    #[test]
629    fn a_value_that_can_only_be_read_from_memory_and_is_in_a_register_is_found() {
630        let mut names = Interner::new();
631        let mut func = Func::new(names.intern("f"));
632        let nop = Opcode::new(names.intern("x64.nop"));
633        let wide = Opcode::new(names.intern("x64.wide"));
634        let block = func.create_block();
635        let only = func.new_vreg(GPR);
636        func.build(block, nop).def(only, GPR).finish();
637        func.build(block, wide).operand(Operand::read(only, GPR).with(Constraint::Stack)).finish();
638
639        let (order, live) = read(&func);
640        let mut assignment = Assignment::empty(func.vregs());
641        assignment.put(only, Place::Reg(RAX));
642
643        let said = said(&func, &order, &live, &assignment);
644        assert_eq!(said, ["%0 is not on the stack, and instruction 1 needs it"]);
645    }
646
647    #[test]
648    fn a_two_address_instruction_may_write_the_register_it_read_a_finished_value_from() {
649        let mut names = Interner::new();
650        let mut func = Func::new(names.intern("f"));
651        let nop = Opcode::new(names.intern("x64.nop"));
652        let add = Opcode::new(names.intern("x64.add"));
653        let block = func.create_block();
654        let left = func.new_vreg(GPR);
655        let right = func.new_vreg(GPR);
656        let sum = func.new_vreg(GPR);
657        func.build(block, nop).def(left, GPR).finish();
658        func.build(block, nop).def(right, GPR).finish();
659        func.build(block, add)
660            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
661            .uses(left, GPR)
662            .uses(right, GPR)
663            .finish();
664        func.build(block, nop).uses(sum, GPR).finish();
665
666        let (order, live) = read(&func);
667        let mut assignment = Assignment::empty(func.vregs());
668        assignment.put(left, Place::Reg(RAX));
669        assignment.put(right, Place::Reg(RCX));
670        assignment.put(sum, Place::Reg(RAX));
671
672        // The left operand is finished with at the addition, so the sum takes its register and
673        // the addition is the one instruction rather than a move and an instruction.
674        assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
675    }
676
677    #[test]
678    fn a_two_address_instruction_may_not_write_over_a_value_wanted_afterwards() {
679        let mut names = Interner::new();
680        let mut func = Func::new(names.intern("f"));
681        let nop = Opcode::new(names.intern("x64.nop"));
682        let add = Opcode::new(names.intern("x64.add"));
683        let block = func.create_block();
684        let left = func.new_vreg(GPR);
685        let right = func.new_vreg(GPR);
686        let sum = func.new_vreg(GPR);
687        func.build(block, nop).def(left, GPR).finish();
688        func.build(block, nop).def(right, GPR).finish();
689        func.build(block, add)
690            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
691            .uses(left, GPR)
692            .uses(right, GPR)
693            .finish();
694        func.build(block, nop).uses(sum, GPR).uses(left, GPR).finish();
695
696        let (order, live) = read(&func);
697        let mut assignment = Assignment::empty(func.vregs());
698        assignment.put(left, Place::Reg(RAX));
699        assignment.put(right, Place::Reg(RCX));
700        assignment.put(sum, Place::Reg(RAX));
701
702        // The left operand is read again after the addition, so the addition may not have its
703        // register even though it is the one the addition reads.
704        let said = said(&func, &order, &live, &assignment);
705        assert_eq!(said, ["%0 and %2 are both live and both in register 0"]);
706    }
707
708    #[test]
709    fn a_two_address_instruction_may_not_write_the_register_it_reads_its_other_operand_from() {
710        let mut names = Interner::new();
711        let mut func = Func::new(names.intern("f"));
712        let nop = Opcode::new(names.intern("x64.nop"));
713        let add = Opcode::new(names.intern("x64.add"));
714        let block = func.create_block();
715        let left = func.new_vreg(GPR);
716        let right = func.new_vreg(GPR);
717        let sum = func.new_vreg(GPR);
718        func.build(block, nop).def(left, GPR).finish();
719        func.build(block, nop).def(right, GPR).finish();
720        func.build(block, add)
721            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
722            .uses(left, GPR)
723            .uses(right, GPR)
724            .finish();
725        func.build(block, nop).uses(sum, GPR).finish();
726
727        let (order, live) = read(&func);
728        let mut assignment = Assignment::empty(func.vregs());
729        assignment.put(left, Place::Reg(RAX));
730        assignment.put(right, Place::Reg(RCX));
731        assignment.put(sum, Place::Reg(RCX));
732
733        // Copying the left operand into the sum's register would destroy the right operand before
734        // the addition has read it, even though both are finished with at the addition.
735        let said = said(&func, &order, &live, &assignment);
736        assert_eq!(said, ["%1 and %2 are both live and both in register 1"]);
737    }
738
739    #[test]
740    fn a_two_address_instruction_may_not_write_the_register_it_read_over_its_own_last_answer() {
741        let mut names = Interner::new();
742        let mut func = Func::new(names.intern("f"));
743        let nop = Opcode::new(names.intern("x64.nop"));
744        let add = Opcode::new(names.intern("x64.add"));
745        let head = func.create_block();
746        let latch = func.create_block();
747        let out = func.create_block();
748        let source = func.new_vreg(GPR);
749        let carried = func.new_vreg(GPR);
750        func.build(head, nop).def(source, GPR).finish();
751        func.build(head, nop).def(carried, GPR).finish();
752        *func.succs_mut(head) = vec![BlockCall::to(latch)];
753        func.build(latch, add)
754            .operand(Operand::write(carried, GPR).with(Constraint::Reuse(1)))
755            .uses(source, GPR)
756            .uses(carried, GPR)
757            .finish();
758        *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
759        func.build(out, nop).uses(carried, GPR).finish();
760
761        let (order, live) = read(&func);
762        let mut assignment = Assignment::empty(func.vregs());
763        assignment.put(source, Place::Reg(RAX));
764        assignment.put(carried, Place::Reg(RAX));
765
766        // The source is finished with at the addition, which is what would normally let the answer
767        // have its register. It does not here, because the answer is the one the last turn round
768        // the loop wrote and the addition reads it too, so both are wanted where it reads.
769        let said = said(&func, &order, &live, &assignment);
770        assert_eq!(said, ["%0 and %1 are both live and both in register 0"]);
771    }
772
773    #[test]
774    fn a_two_address_answer_with_a_hole_in_front_of_it_still_may_not_take_the_other_operand() {
775        let mut names = Interner::new();
776        let mut func = Func::new(names.intern("f"));
777        let nop = Opcode::new(names.intern("x64.nop"));
778        let add = Opcode::new(names.intern("x64.add"));
779        let entry = func.create_block();
780        let head = func.create_block();
781        let arm = func.create_block();
782        let latch = func.create_block();
783        let out = func.create_block();
784        let seed = func.new_vreg(GPR);
785        let sum = func.new_vreg(GPR);
786        let inside = func.new_vreg(GPR);
787        let loaded = func.new_vreg(GPR);
788        func.build(entry, nop).def(seed, GPR).finish();
789        func.build(entry, nop).def(sum, GPR).finish();
790        *func.succs_mut(entry) = vec![BlockCall::to(head)];
791        func.build(head, nop).uses(sum, GPR).finish();
792        *func.succs_mut(head) = vec![BlockCall::to(arm), BlockCall::to(latch)];
793        func.build(arm, nop).def(inside, GPR).finish();
794        func.build(arm, nop).uses(inside, GPR).finish();
795        *func.succs_mut(arm) = vec![BlockCall::to(out)];
796        func.build(latch, nop).def(loaded, GPR).finish();
797        func.build(latch, add)
798            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
799            .uses(seed, GPR)
800            .uses(loaded, GPR)
801            .finish();
802        *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
803
804        let (order, live) = read(&func);
805        let mut assignment = Assignment::empty(func.vregs());
806        assignment.put(seed, Place::Reg(RCX));
807        assignment.put(sum, Place::Reg(RAX));
808        assignment.put(inside, Place::Reg(RDX));
809        assignment.put(loaded, Place::Reg(RAX));
810
811        // The answer is live in the entry and the head too, so the piece the addition writes is not
812        // the first one and the arm in between is a hole. Copying the left operand into the answer's
813        // register still destroys the right operand before the addition reads it, and a checker that
814        // added the extra point to the first piece rather than the piece the addition writes saw
815        // nothing wrong with any of it. tamnd/rucc#982.
816        let said = said(&func, &order, &live, &assignment);
817        assert_eq!(said, ["%1 and %3 are both live and both in register 0"]);
818    }
819
820    #[test]
821    fn a_report_names_every_problem() {
822        let mut names = Interner::new();
823        let mut func = Func::new(names.intern("f"));
824        let opcode = Opcode::new(names.intern("x64.nop"));
825        let block = func.create_block();
826        let first = func.new_vreg(GPR);
827        let second = func.new_vreg(GPR);
828        func.build(block, opcode).def(first, GPR).finish();
829        func.build(block, opcode).def(second, GPR).finish();
830        func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
831
832        let (order, live) = read(&func);
833        let mut assignment = Assignment::empty(func.vregs());
834        assignment.put(first, Place::Reg(RAX));
835        assignment.put(second, Place::Reg(RAX));
836
837        let problems = check(&func, &order, &live, &assignment);
838        assert_eq!(
839            report(&problems),
840            "the allocation is wrong in 1 place\n  %0 and %1 are both live and both in register 0"
841        );
842        assert_eq!(report(&[]), "the allocation is wrong in 0 places");
843    }
844}