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