Skip to main content

rucc_regalloc/
backtrack.rs

1//! Which register each value lives in, decided in the order the values are hardest to place, and
2//! undone when a value that would cost more to lose finds its register taken.
3//!
4//! Design: `spec/optimizer/39-register-allocation.md` section 39.7, and tamnd/rucc#1177.
5//!
6//! [`crate::assign`] is the `-O0` answer. It walks the line once, and when it runs out of
7//! registers the value that goes to the stack is the one whose range ends last. That is a guess
8//! about cost made from a fact about length, and it is wrong exactly where it matters: a value read
9//! in every turn of a loop and wanted again after the loop ends last, so it is the one that goes,
10//! and every turn of the loop pays a load for it.
11//!
12//! This answers the same question with the cost in it. Every value gets a weight, which is how
13//! often it is read or written, each time counted by how often the block it happens in runs, over
14//! how much of the function it is live across. A value with a high weight is one that would cost
15//! a lot to keep in memory for little register in return, and that is the one to keep.
16//!
17//! # The order values are placed in
18//!
19//! Longest first. A long value meets more of the others than a short one does, so it has the
20//! fewest registers to choose from, and giving it first choice is what leaves the short ones
21//! something to fit into. That is the order LLVM's greedy allocator takes them in, for the same
22//! reason.
23//!
24//! # Backtracking
25//!
26//! Going longest first means a long cold value takes a register before a short hot one has been
27//! looked at. When the hot one comes and finds nothing free, it asks what it would cost to take a
28//! register back. For each register the answer is the values in it that are in the way, and the
29//! register can be taken when every one of them weighs less than the value asking. Of the
30//! registers that can, the one taken is the one whose heaviest value in the way is lightest. The
31//! values that lose it go back in the queue and look for another register, and a value that has
32//! lost one [`ROUNDS`] times goes to the stack instead of looking again.
33//!
34//! That rule is also why it stops. A value only ever takes a register from values lighter than
35//! itself, and each value is put back a bounded number of times.
36//!
37//! # What it keeps from the linear scan
38//!
39//! Everything that says what a register may hold. The registers an instruction insists on, the
40//! values an instruction can only read from memory, the two address instructions and the hints
41//! are all read the way [`crate::assign`] reads them, from the same functions, so the two
42//! allocators cannot disagree about what the machine allows. They only disagree about who gets
43//! the register, and [`crate::check`] asks the same questions of either answer.
44//!
45//! A two address instruction is coalesced from both ends here. The linear scan only ever meets the
46//! answer after its source, since the source is written first. Here either can be placed first, so
47//! a source looks at where the answer that reuses it went as well as the other way round.
48//!
49//! # When it gives up
50//!
51//! Every question of whether two values are both wanted is counted, and a function that asks more
52//! than [`BUDGET`] of them is handed to the linear scan instead. The answer is worse and it comes
53//! out in time, which is what the section asks of a pathological function. It is a count of work
54//! rather than of values because a function with many short values that never meet is cheap
55//! however many there are.
56//!
57//! # What the spill phase adds
58//!
59//! It runs twice when [`crate::pressure`] finds a point with more values live than registers. Once
60//! as above, where a value goes to the stack only when the queue reaches it and it can take no
61//! register back, and once with the values [`crate::spill`] picked sent to the stack before the
62//! queue starts. The first is better where the pressure is brief and eviction settles it in a few
63//! moves. The second is better where it is long, since the values that go are picked by weight
64//! across every point that is over rather than by which one the queue met last. Neither wins
65//! everywhere, so both are costed.
66//!
67//! It also gives up when it would lose. The linear scan runs as well, which is cheap next to this,
68//! and the answer kept is the one with the lower [`cost`]: the loads and stores of the values on
69//! the stack and the copies between the ends of each tie left apart, each counted by how often its
70//! block runs. Placing the long values first is right where registers are fought over in a loop,
71//! and it can be worse in a long straight run of arithmetic, where the order the linear scan walks
72//! in is also the order that lets each answer follow its source.
73
74use std::cmp::Reverse;
75use std::collections::{BinaryHeap, HashMap, HashSet};
76
77use rucc_mir::{Func, Inst, Reg};
78use rucc_target::{PhysReg, RegClass};
79
80use crate::assign::{self, Assignment, Blocks, Env, Place, Reuse, Want};
81use crate::live::{Area, Live, Range};
82use crate::order::Order;
83use crate::pressure::Pressure;
84use crate::spill;
85
86/// How many questions of whether two values are both wanted a function may ask before it is handed
87/// to the linear scan.
88pub const BUDGET: u64 = 50_000_000;
89
90/// How many times a value may lose its register and look for another before it goes to the stack.
91pub const ROUNDS: u32 = 4;
92
93/// One value, as the queue sees it.
94#[derive(Debug, Clone, Copy)]
95struct Value<'a> {
96    reg: Reg,
97    class: RegClass,
98    area: Area<'a>,
99    range: Range,
100    weight: u128,
101    size: u32,
102}
103
104/// Where every value goes, in the order that places the hard ones first and takes a register back
105/// when a heavier value wants it.
106///
107/// A function that asks more than [`BUDGET`] questions gets the linear scan's answer instead, and
108/// so does one where the linear scan's answer has the lower [`cost`].
109#[must_use]
110pub fn assign(func: &Func, order: &Order, live: &Live, env: &Env) -> Assignment {
111    let linear = assign::assign(func, order, live, env);
112    let mut best = cost(func, order, &linear);
113    let mut kept = linear;
114    let pressure = Pressure::of(func, order, live, env);
115    let spilled = spill::choose(func, live, &pressure);
116    // With nothing sent ahead the second try would be the first one again.
117    let tries: &[&[Reg]] = if spilled.is_empty() { &[&[]] } else { &[&[], &spilled] };
118    for &early in tries {
119        let Some(ours) = placed(func, order, live, env, BUDGET, early) else { break };
120        let spent = cost(func, order, &ours);
121        if spent <= best {
122            best = spent;
123            kept = ours;
124        }
125    }
126    kept
127}
128
129/// What an assignment is expected to cost a function, in instructions each counted by how often
130/// its block runs.
131///
132/// A value on the stack costs a load or a store every time it is read or written. A two address
133/// instruction whose answer is not where its source is costs the copy between them, and so does a
134/// block parameter that is not where the value passed to it is. Moves the machine needs whatever
135/// the assignment, like those into the registers a call insists on, are left out, since they are
136/// the same for any answer.
137///
138/// # Panics
139///
140/// Panics on a function with more values than a register number can name, as
141/// [`Reg::virtual_reg`] does.
142#[must_use]
143pub fn cost(func: &Func, order: &Order, assignment: &Assignment) -> u128 {
144    let costs = costs(func);
145    let mut total = 0;
146    for (reg, place) in assignment.placed() {
147        if !matches!(place, Place::Reg(_)) {
148            total += costs[index(reg)];
149        }
150    }
151    let mut weights = HashMap::new();
152    for block in func.blocks() {
153        let weight = u128::from(func[block].weight.raw().max(1));
154        for inst in func.insts(block) {
155            weights.insert(inst, weight);
156        }
157        for call in &func[block].succs {
158            for (&arg, param) in call.args.iter().zip(&func[call.block].params) {
159                if assignment.place(arg) != assignment.place(param.reg) {
160                    total += weight;
161                }
162            }
163        }
164    }
165    let commuted: HashSet<Inst> = assignment.commuted().iter().copied().collect();
166    for (number, reuse) in assign::reuses(func, order).iter().enumerate() {
167        let Some(reuse) = reuse else { continue };
168        let answer = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
169        let tied = if commuted.contains(&reuse.inst) { reuse.second } else { Some(reuse.source) };
170        let Some(at) = assignment.place(answer) else { continue };
171        if tied.and_then(|tied| assignment.place(tied)) != Some(at) {
172            total += weights.get(&reuse.inst).copied().unwrap_or(1);
173        }
174    }
175    total
176}
177
178/// The same, with the budget said, or `None` for a function that went over it.
179///
180/// # Panics
181///
182/// Panics on a value in a class the environment hands out no registers from, as the linear scan
183/// does.
184#[must_use]
185pub fn within(
186    func: &Func,
187    order: &Order,
188    live: &Live,
189    env: &Env,
190    budget: u64,
191) -> Option<Assignment> {
192    placed(func, order, live, env, budget, &[])
193}
194
195fn placed(
196    func: &Func,
197    order: &Order,
198    live: &Live,
199    env: &Env,
200    budget: u64,
201    early: &[Reg],
202) -> Option<Assignment> {
203    let blocked = assign::blocked(func, order);
204    let forced = assign::forced(func);
205    let reuses = assign::reuses(func, order);
206    let hints = assign::hints(func);
207    let passed = assign::passed(func);
208    let received = received(func);
209    let reused = reused(&reuses);
210    let costs = costs(func);
211
212    let count = func.vregs();
213    let mut values: Vec<Option<Value<'_>>> = vec![None; count];
214    let mut queue = BinaryHeap::new();
215    for (number, reuse) in reuses.iter().enumerate() {
216        let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
217        let (Some(mut area), Some(class)) = (live.area(reg), func.class_of(reg)) else {
218            continue;
219        };
220        if let Some(reuse) = reuse {
221            area = area.with(reuse.at);
222        }
223        let size = size(area);
224        let weight = costs[number] * 1024 / u128::from(size + 8);
225        values[number] = Some(Value { reg, class, area, range: area.hull(), weight, size });
226        queue.push((size, Reverse(number)));
227    }
228
229    let mut state = State {
230        live,
231        blocked: &blocked,
232        reuses: &reuses,
233        values: &values,
234        held: Vec::new(),
235        at: vec![None; count],
236        commuted: vec![None; count],
237        work: 0,
238        budget,
239    };
240    let mut assignment = Assignment::empty(count);
241    let mut lost = vec![0u32; count];
242    while let Some((_, Reverse(number))) = queue.pop() {
243        let Some(value) = values[number] else { continue };
244        if forced.contains(&value.reg) || early.contains(&value.reg) {
245            assignment.spill(value.reg, value.class);
246            continue;
247        }
248        assert!(
249            !env.order(value.class).is_empty(),
250            "a value in class {}, which the target hands out no registers from",
251            value.class.number()
252        );
253        let chosen = state
254            .coalesced(value, &reused[number])
255            .or_else(|| state.hinted(value, &hints[number], Want::Clear))
256            .or_else(|| {
257                let partners = passed[number].iter().chain(&received[number]);
258                let partners: Vec<PhysReg> =
259                    partners.filter_map(|&other| state.reg_of(other)).collect();
260                state.hinted(value, &partners, Want::Clear)
261            })
262            .or_else(|| {
263                let mut ties = reused[number].clone();
264                let source = reuses[number].and_then(|reuse| reuse.source.number());
265                ties.extend(source.and_then(|source| usize::try_from(source).ok()));
266                ties.retain(|&tie| state.at[tie].is_none() && values[tie].is_some());
267                let tied = ties.iter().flat_map(|&tie| hints[tie].iter());
268                let wanted: Vec<PhysReg> = tied.chain(env.order(value.class)).copied().collect();
269                state.together(value, &ties, &wanted)
270            })
271            .or_else(|| state.hinted(value, env.order(value.class), Want::Clear))
272            .or_else(|| state.hinted(value, env.order(value.class), Want::Allowed));
273        if state.work > state.budget {
274            return None;
275        }
276        if let Some(at) = chosen {
277            state.take(value, at);
278            continue;
279        }
280        match state.cheapest(value, env.order(value.class)) {
281            Some(at) => {
282                for other in state.evict(value, at) {
283                    lost[other] += 1;
284                    let Some(evicted) = values[other] else { continue };
285                    if lost[other] > ROUNDS {
286                        assignment.spill(evicted.reg, evicted.class);
287                    } else {
288                        queue.push((evicted.size, Reverse(other)));
289                    }
290                }
291                state.take(value, at);
292            }
293            None => assignment.spill(value.reg, value.class),
294        }
295        if state.work > state.budget {
296            return None;
297        }
298    }
299    state.settle(&reused, &passed, &received);
300    if state.work > state.budget {
301        return None;
302    }
303    for (number, at) in state.at.iter().enumerate() {
304        let Some(at) = *at else { continue };
305        let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
306        assignment.put(reg, Place::Reg(at));
307    }
308    for inst in state.commuted.iter().flatten() {
309        assignment.commute(*inst);
310    }
311    Some(assignment)
312}
313
314/// What the allocation knows while it runs.
315struct State<'a, 'v> {
316    live: &'a Live,
317    blocked: &'a Blocks,
318    reuses: &'a [Option<Reuse>],
319    values: &'v [Option<Value<'a>>],
320    /// Which values are in each register of each class, by number.
321    held: Vec<((RegClass, PhysReg), Vec<usize>)>,
322    /// Which register each value is in now, if one.
323    at: Vec<Option<PhysReg>>,
324    /// The instruction whose sources were swapped for the answer written by each value, if its
325    /// register is the second source's.
326    commuted: Vec<Option<Inst>>,
327    work: u64,
328    budget: u64,
329}
330
331impl<'a> State<'a, '_> {
332    fn reg_of(&self, reg: Reg) -> Option<PhysReg> {
333        self.at.get(usize::try_from(reg.number()?).ok()?).copied().flatten()
334    }
335
336    fn slot(&mut self, class: RegClass, at: PhysReg) -> &mut Vec<usize> {
337        let found = self.held.iter().position(|(key, _)| *key == (class, at));
338        let index = found.unwrap_or_else(|| {
339            self.held.push(((class, at), Vec::new()));
340            self.held.len() - 1
341        });
342        &mut self.held[index].1
343    }
344
345    /// The values in `at` that are wanted while `value` is, leaving out the one a two address
346    /// instruction lets it share the register with.
347    fn clashes(&mut self, value: Value<'_>, at: PhysReg) -> Vec<usize> {
348        let Some(index) = self.held.iter().position(|(key, _)| *key == (value.class, at)) else {
349            return Vec::new();
350        };
351        let mut clashes = Vec::new();
352        for &other in &self.held[index].1 {
353            let Some(held) = self.values[other] else { continue };
354            self.work += 1;
355            if !held.range.overlaps(value.range) || !held.area.overlaps(value.area) {
356                continue;
357            }
358            if !self.shares(value.reg, held.reg) {
359                clashes.push(other);
360            }
361        }
362        clashes
363    }
364
365    /// Whether two values that are both wanted at one instruction may still be in one register,
366    /// which is when that instruction reads one for the last time and writes the other over it.
367    fn shares(&self, one: Reg, two: Reg) -> bool {
368        let reads = |answer: Reg, source: Reg| {
369            let Some(number) = answer.number().and_then(|n| usize::try_from(n).ok()) else {
370                return false;
371            };
372            let Some(reuse) = self.reuses[number] else { return false };
373            match self.commuted[number] {
374                Some(_) => reuse.second == Some(source),
375                None => reuse.source == source,
376            }
377        };
378        (reads(one, two) || reads(two, one)) && assign::apart(self.live, one, two)
379    }
380
381    fn free(&mut self, value: Value<'_>, at: PhysReg, want: Want) -> bool {
382        !self.blocked.insists(value.reg, value.class, value.area, value.range, at, want)
383            && self.clashes(value, at).is_empty()
384    }
385
386    /// The first register of `wanted` that is free for `value`.
387    fn hinted(&mut self, value: Value<'_>, wanted: &[PhysReg], want: Want) -> Option<PhysReg> {
388        wanted.iter().copied().find(|&at| self.work <= self.budget && self.free(value, at, want))
389    }
390
391    /// The first register of `order` free for `value` and for each value in `ties`, which are the
392    /// values a two address instruction ties it to that have no register yet. Taking one of these
393    /// leaves the tied value a register to follow it into when its turn comes. The registers the
394    /// tied values are hinted to come first in `order`, so that a parameter's answer waits for it in
395    /// the register the parameter arrives in.
396    fn together(&mut self, value: Value<'_>, ties: &[usize], order: &[PhysReg]) -> Option<PhysReg> {
397        if ties.is_empty() {
398            return None;
399        }
400        for &at in order {
401            if self.work > self.budget {
402                return None;
403            }
404            if !self.free(value, at, Want::Clear) {
405                continue;
406            }
407            let values = self.values;
408            let mut tied = ties.iter().filter_map(|&tie| values[tie]);
409            if tied.all(|other| self.free(other, at, Want::Allowed)) {
410                return Some(at);
411            }
412        }
413        None
414    }
415
416    /// The register of a value a two address instruction ties this one to, if it can have it.
417    ///
418    /// Asked of the answer, it is the register of the source, or of the second source if the
419    /// instruction reads its sources either way round. Asked of a source, it is the register of an
420    /// answer that reuses it and was placed first.
421    fn coalesced(&mut self, value: Value<'_>, answers: &[usize]) -> Option<PhysReg> {
422        let number = index(value.reg);
423        if let Some(reuse) = self.reuses[number] {
424            if let Some(at) = self.reg_of(reuse.source) {
425                if self.free(value, at, Want::Allowed) {
426                    return Some(at);
427                }
428            }
429            if let Some(second) = reuse.second {
430                if let Some(at) = self.reg_of(second) {
431                    self.commuted[number] = Some(reuse.inst);
432                    if self.free(value, at, Want::Allowed) {
433                        return Some(at);
434                    }
435                    self.commuted[number] = None;
436                }
437            }
438        }
439        for &answer in answers {
440            let Some(at) = self.at[answer] else { continue };
441            if self.commuted[answer].is_none() && self.free(value, at, Want::Allowed) {
442                return Some(at);
443            }
444        }
445        None
446    }
447
448    /// The register that is cheapest to take back for `value`, if any is cheaper than sending
449    /// `value` to the stack.
450    fn cheapest(&mut self, value: Value<'_>, order: &[PhysReg]) -> Option<PhysReg> {
451        let mut best: Option<(u128, u128, PhysReg)> = None;
452        for &at in order {
453            if self.blocked.insists(
454                value.reg,
455                value.class,
456                value.area,
457                value.range,
458                at,
459                Want::Allowed,
460            ) {
461                continue;
462            }
463            let clashes = self.clashes(value, at);
464            let weights = clashes.iter().filter_map(|&other| self.values[other]).map(|v| v.weight);
465            let (heaviest, total) = weights.fold((0, 0), |(most, sum), w| (most.max(w), sum + w));
466            if heaviest >= value.weight {
467                continue;
468            }
469            if best.is_none_or(|(most, sum, _)| (heaviest, total) < (most, sum)) {
470                best = Some((heaviest, total, at));
471            }
472        }
473        best.map(|(_, _, at)| at)
474    }
475
476    /// Takes `at` back from the values in it that are in the way of `value`, and says which.
477    fn evict(&mut self, value: Value<'_>, at: PhysReg) -> Vec<usize> {
478        let clashes = self.clashes(value, at);
479        self.slot(value.class, at).retain(|other| !clashes.contains(other));
480        for &other in &clashes {
481            self.at[other] = None;
482            self.commuted[other] = None;
483        }
484        clashes
485    }
486
487    /// Moves each value into the register the most values tied to it are in, of those that are free
488    /// for it and hold more of them than where it is now.
489    ///
490    /// A tie is a two address instruction or a block parameter, and each one met is a copy the
491    /// rewrite does not have to write. Placing values in priority order can leave the two ends of a
492    /// tie apart though the register one of them is in stayed free for the other, because the other
493    /// was placed first and had nothing yet to follow. Every move meets more ties than it breaks, so
494    /// this ends.
495    fn settle(&mut self, reused: &[Vec<usize>], passed: &[Vec<Reg>], received: &[Vec<Reg>]) {
496        let mut moved = true;
497        while moved && self.work <= self.budget {
498            moved = false;
499            for number in 0..self.at.len() {
500                let (Some(value), Some(now)) = (self.values[number], self.at[number]) else {
501                    continue;
502                };
503                let reuse = self.reuses[number];
504                let source = reuse.and_then(|reuse| self.reg_of(reuse.source));
505                let second =
506                    reuse.and_then(|reuse| reuse.second).and_then(|second| self.reg_of(second));
507                let mut wanted: Vec<PhysReg> = source.into_iter().chain(second).collect();
508                for &answer in &reused[number] {
509                    if self.commuted[answer].is_none() {
510                        wanted.extend(self.at[answer]);
511                    }
512                }
513                let partners = passed[number].iter().chain(&received[number]);
514                wanted.extend(partners.filter_map(|&other| self.reg_of(other)));
515                let met = |at: PhysReg| wanted.iter().filter(|&&reg| reg == at).count();
516                let here = met(now);
517                let mut better: Vec<(usize, PhysReg)> = Vec::new();
518                for &at in &wanted {
519                    let count = met(at);
520                    if count > here && !better.contains(&(count, at)) {
521                        better.push((count, at));
522                    }
523                }
524                if better.is_empty() {
525                    continue;
526                }
527                better.sort_by_key(|&(count, _)| Reverse(count));
528                self.slot(value.class, now).retain(|&other| other != number);
529                self.at[number] = None;
530                let was = self.commuted[number];
531                let mut to = now;
532                for (_, at) in better {
533                    self.commuted[number] = match reuse {
534                        Some(reuse) if second == Some(at) && source != Some(at) => Some(reuse.inst),
535                        _ => None,
536                    };
537                    if self.free(value, at, Want::Allowed) {
538                        to = at;
539                        moved = true;
540                        break;
541                    }
542                }
543                if to == now {
544                    self.commuted[number] = was;
545                }
546                self.take(value, to);
547            }
548        }
549    }
550
551    fn take(&mut self, value: Value<'_>, at: PhysReg) {
552        let number = index(value.reg);
553        self.at[number] = Some(at);
554        self.slot(value.class, at).push(number);
555    }
556}
557
558/// How much of the line a value is live over.
559pub(crate) fn size(area: Area<'_>) -> u32 {
560    area.pieces().map(|piece| piece.end - piece.start + 1).sum()
561}
562
563/// How often each value is read or written, each time counted by how often its block runs.
564///
565/// A block that says nothing about how often it runs counts as running once, so a function with no
566/// weights on it is counted by how many times each value is named.
567pub(crate) fn costs(func: &Func) -> Vec<u128> {
568    let mut costs = vec![0u128; func.vregs()];
569    let mut add = |reg: Reg, weight: u128| {
570        let number = reg.number().and_then(|number| usize::try_from(number).ok());
571        if let Some(cost) = number.and_then(|number| costs.get_mut(number)) {
572            *cost += weight;
573        }
574    };
575    for block in func.blocks() {
576        let weight = u128::from(func[block].weight.raw().max(1));
577        for param in &func[block].params {
578            add(param.reg, weight);
579        }
580        for inst in func.insts(block) {
581            for operand in &func[func[inst].operands] {
582                add(operand.reg, weight);
583            }
584        }
585        for call in &func[block].succs {
586            for &arg in &call.args {
587                add(arg, weight);
588            }
589        }
590    }
591    costs
592}
593
594/// For each parameter of a block, the values the edges into it pass it.
595fn received(func: &Func) -> Vec<Vec<Reg>> {
596    let mut received = vec![Vec::new(); func.vregs()];
597    for block in func.blocks() {
598        for call in &func[block].succs {
599            for (&arg, param) in call.args.iter().zip(&func[call.block].params) {
600                let number = param.reg.number().and_then(|number| usize::try_from(number).ok());
601                let Some(from) = number.and_then(|number| received.get_mut(number)) else {
602                    continue;
603                };
604                if !from.contains(&arg) {
605                    from.push(arg);
606                }
607            }
608        }
609    }
610    received
611}
612
613/// For each value, the answers of two address instructions that reuse it as their first source.
614fn reused(reuses: &[Option<Reuse>]) -> Vec<Vec<usize>> {
615    let mut reused = vec![Vec::new(); reuses.len()];
616    for (answer, reuse) in reuses.iter().enumerate() {
617        let Some(reuse) = reuse else { continue };
618        let number = reuse.source.number().and_then(|number| usize::try_from(number).ok());
619        if let Some(answers) = number.and_then(|number| reused.get_mut(number)) {
620            answers.push(answer);
621        }
622    }
623    reused
624}
625
626fn index(reg: Reg) -> usize {
627    usize::try_from(reg.number().expect("a virtual register")).expect("a register number")
628}
629
630#[cfg(test)]
631mod tests {
632    use rucc_base::Interner;
633    use rucc_mir::{BlockCall, Constraint, Flags, Opcode, Operand, Param};
634    use rucc_target::x86_64::{GPR, REGS, SYSV};
635
636    use super::*;
637    use crate::check;
638
639    fn env() -> Env {
640        let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
641        Env::new().with(GPR, order, scratch)
642    }
643
644    fn narrow(count: usize) -> Env {
645        Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 1])
646    }
647
648    fn named(place: Option<Place>) -> String {
649        match place {
650            Some(Place::Reg(reg)) => REGS.name(GPR, reg).expect("a register").to_string(),
651            Some(Place::Slot(_)) => "slot".to_string(),
652            None => "nowhere".to_string(),
653        }
654    }
655
656    /// Where every value went, after asking the checker whether the machine could run it.
657    fn places(func: &mut Func, env: &Env) -> Vec<String> {
658        let order = Order::of(func);
659        let live = Live::of(func, &order);
660        let assignment = within(func, &order, &live, env, BUDGET).expect("inside the budget");
661        // The sources of an instruction that commutes are swapped before the check, as `run_with`
662        // swaps them.
663        for &inst in assignment.commuted() {
664            let list = func[inst].operands;
665            func[list].swap(1, 2);
666        }
667        let problems = check::check(func, &order, &live, &assignment);
668        assert!(problems.is_empty(), "{}", check::report(&problems));
669        (0..func.vregs())
670            .map(|number| {
671                let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
672                named(assignment.place(reg))
673            })
674            .collect()
675    }
676
677    fn linear(func: &Func, env: &Env) -> Vec<String> {
678        let order = Order::of(func);
679        let live = Live::of(func, &order);
680        let assignment = assign::assign(func, &order, &live, env);
681        (0..func.vregs())
682            .map(|number| {
683                let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
684                named(assignment.place(reg))
685            })
686            .collect()
687    }
688
689    #[test]
690    fn a_value_the_spill_phase_picked_goes_to_the_stack_and_the_rest_fit() {
691        let mut names = Interner::new();
692        let mut func = Func::new(names.intern("f"));
693        let opcode = Opcode::new(names.intern("x64.nop"));
694        let block = func.create_block();
695        let busy = func.new_vreg(GPR);
696        let once = func.new_vreg(GPR);
697        let other = func.new_vreg(GPR);
698        func.build(block, opcode).def(busy, GPR).finish();
699        func.build(block, opcode).def(once, GPR).finish();
700        func.build(block, opcode).def(other, GPR).finish();
701        for _ in 0..3 {
702            func.build(block, opcode).uses(busy, GPR).uses(other, GPR).finish();
703        }
704        func.build(block, opcode).uses(once, GPR).uses(busy, GPR).uses(other, GPR).finish();
705
706        let env = narrow(2);
707        let order = Order::of(&func);
708        let live = Live::of(&func, &order);
709        let pressure = Pressure::of(&func, &order, &live, &env);
710        let early = spill::choose(&func, &live, &pressure);
711        assert_eq!(early, [once]);
712        let assignment = placed(&func, &order, &live, &env, BUDGET, &early).expect("in budget");
713        let problems = check::check(&func, &order, &live, &assignment);
714        assert!(problems.is_empty(), "{}", check::report(&problems));
715        assert_eq!(named(assignment.place(once)), "slot");
716        assert_eq!(assignment.spilled(), 1);
717    }
718
719    #[test]
720    fn two_values_that_are_never_both_wanted_share_a_register() {
721        let mut names = Interner::new();
722        let mut func = Func::new(names.intern("f"));
723        let opcode = Opcode::new(names.intern("x64.nop"));
724        let block = func.create_block();
725        let first = func.new_vreg(GPR);
726        let second = func.new_vreg(GPR);
727        func.build(block, opcode).def(first, GPR).finish();
728        func.build(block, opcode).uses(first, GPR).finish();
729        func.build(block, opcode).def(second, GPR).finish();
730        func.build(block, opcode).uses(second, GPR).finish();
731
732        assert_eq!(places(&mut func, &env()), ["rax", "rax"]);
733    }
734
735    #[test]
736    fn the_value_read_most_often_keeps_its_register_though_it_is_wanted_longest() {
737        let mut names = Interner::new();
738        let mut func = Func::new(names.intern("f"));
739        let opcode = Opcode::new(names.intern("x64.nop"));
740        let block = func.create_block();
741        let busy = func.new_vreg(GPR);
742        let once = func.new_vreg(GPR);
743        func.build(block, opcode).def(busy, GPR).finish();
744        func.build(block, opcode).def(once, GPR).finish();
745        for _ in 0..6 {
746            func.build(block, opcode).uses(busy, GPR).finish();
747        }
748        func.build(block, opcode).uses(once, GPR).finish();
749        func.build(block, opcode).uses(busy, GPR).finish();
750
751        // With one register the linear scan sends the value that ends last to the stack, which is
752        // the one read seven times. Weighing them keeps that one and sends the one read once.
753        assert_eq!(linear(&func, &narrow(1)), ["slot", "rax"]);
754        assert_eq!(places(&mut func, &narrow(1)), ["rax", "slot"]);
755    }
756
757    #[test]
758    fn a_value_read_in_a_loop_keeps_its_register_over_one_read_outside_it() {
759        let mut names = Interner::new();
760        let mut func = Func::new(names.intern("f"));
761        let opcode = Opcode::new(names.intern("x64.nop"));
762        let entry = func.create_block();
763        let body = func.create_block();
764        let out = func.create_block();
765        let step = func.new_vreg(GPR);
766        let cold = func.new_vreg(GPR);
767        func.build(entry, opcode).def(cold, GPR).finish();
768        func.build(entry, opcode).def(step, GPR).finish();
769        *func.succs_mut(entry) = vec![BlockCall::to(body)];
770        func.build(body, opcode).uses(step, GPR).finish();
771        *func.succs_mut(body) = vec![BlockCall::to(body), BlockCall::to(out)];
772        func.set_weight(body, rucc_mir::Weight::parts(100 * rucc_mir::Weight::SCALE));
773        func.build(out, opcode).uses(cold, GPR).finish();
774        func.build(out, opcode).uses(step, GPR).finish();
775
776        // Read once in the loop and once after it, against a value read once after it: the count
777        // of reads is the same and it is how often the loop runs that decides.
778        assert_eq!(places(&mut func, &narrow(1)), ["rax", "slot"]);
779    }
780
781    #[test]
782    fn a_long_value_gives_its_register_back_to_a_short_busy_one() {
783        let mut names = Interner::new();
784        let mut func = Func::new(names.intern("f"));
785        let opcode = Opcode::new(names.intern("x64.nop"));
786        let block = func.create_block();
787        let long = func.new_vreg(GPR);
788        let short = func.new_vreg(GPR);
789        func.build(block, opcode).def(long, GPR).finish();
790        for _ in 0..4 {
791            func.build(block, opcode).finish();
792        }
793        func.build(block, opcode).def(short, GPR).finish();
794        for _ in 0..4 {
795            func.build(block, opcode).uses(short, GPR).finish();
796        }
797        func.build(block, opcode).uses(long, GPR).finish();
798
799        // The long one is placed first, since it is the harder to place, and then loses the one
800        // register to the short one, which weighs more, and has nowhere else to go.
801        assert_eq!(places(&mut func, &narrow(1)), ["slot", "rax"]);
802    }
803
804    #[test]
805    fn the_answer_of_a_two_address_instruction_goes_where_its_source_ends() {
806        let mut names = Interner::new();
807        let mut func = Func::new(names.intern("f"));
808        let opcode = Opcode::new(names.intern("x64.nop"));
809        let block = func.create_block();
810        let left = func.new_vreg(GPR);
811        let right = func.new_vreg(GPR);
812        let sum = func.new_vreg(GPR);
813        func.build(block, opcode).def(left, GPR).finish();
814        func.build(block, opcode).def(right, GPR).finish();
815        func.build(block, opcode)
816            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
817            .uses(left, GPR)
818            .uses(right, GPR)
819            .finish();
820        func.build(block, opcode).uses(right, GPR).finish();
821        func.build(block, opcode).uses(sum, GPR).finish();
822
823        let places = places(&mut func, &env());
824        assert_eq!(places[2], places[0]);
825        assert_ne!(places[1], places[0]);
826    }
827
828    #[test]
829    fn a_source_placed_after_its_answer_goes_where_the_answer_is() {
830        let mut names = Interner::new();
831        let mut func = Func::new(names.intern("f"));
832        let opcode = Opcode::new(names.intern("x64.nop"));
833        let block = func.create_block();
834        let left = func.new_vreg(GPR);
835        let sum = func.new_vreg(GPR);
836        func.build(block, opcode).def(left, GPR).finish();
837        func.build(block, opcode)
838            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
839            .uses(left, GPR)
840            .finish();
841        for _ in 0..6 {
842            func.build(block, opcode).uses(sum, GPR).finish();
843        }
844
845        // The answer is the longer of the two, so it is placed first, and the source finds it.
846        let places = places(&mut func, &env());
847        assert_eq!(places[0], places[1]);
848    }
849
850    #[test]
851    fn an_answer_that_commutes_stays_where_the_loop_passes_it() {
852        let mut names = Interner::new();
853        let mut func = Func::new(names.intern("f"));
854        let opcode = Opcode::new(names.intern("x64.nop"));
855        let entry = func.create_block();
856        let head = func.create_block();
857        let out = func.create_block();
858        let seed = func.new_vreg(GPR);
859        let total = func.new_vreg(GPR);
860        let term = func.new_vreg(GPR);
861        let next = func.new_vreg(GPR);
862        func.build(entry, opcode).def(seed, GPR).finish();
863        *func.succs_mut(entry) = vec![BlockCall::with(head, vec![seed])];
864        func.params_mut(head).push(Param { reg: total, class: GPR });
865        func.build(head, opcode).def(term, GPR).finish();
866        func.build(head, opcode)
867            .flags(Flags::COMMUTES)
868            .operand(Operand::write(next, GPR).with(Constraint::Reuse(1)))
869            .uses(total, GPR)
870            .uses(term, GPR)
871            .finish();
872        *func.succs_mut(head) = vec![BlockCall::with(head, vec![next]), BlockCall::to(out)];
873
874        let places = places(&mut func, &env());
875        assert_eq!(places[3], places[1]);
876    }
877
878    #[test]
879    fn an_answer_whose_first_source_lives_on_goes_where_the_second_one_ends() {
880        let mut names = Interner::new();
881        let mut func = Func::new(names.intern("f"));
882        let opcode = Opcode::new(names.intern("x64.nop"));
883        let block = func.create_block();
884        let base = func.new_vreg(GPR);
885        let entry = func.new_vreg(GPR);
886        let target = func.new_vreg(GPR);
887        func.build(block, opcode).def(base, GPR).finish();
888        func.build(block, opcode).def(entry, GPR).finish();
889        func.build(block, opcode)
890            .flags(Flags::COMMUTES)
891            .operand(Operand::write(target, GPR).with(Constraint::Reuse(1)))
892            .uses(base, GPR)
893            .uses(entry, GPR)
894            .finish();
895        func.build(block, opcode).uses(target, GPR).finish();
896        func.build(block, opcode).uses(base, GPR).finish();
897
898        let places = places(&mut func, &env());
899        assert_eq!(places[2], places[1]);
900        assert_ne!(places[2], places[0]);
901    }
902
903    #[test]
904    fn a_register_an_instruction_insists_on_is_left_to_the_value_it_names() {
905        let mut names = Interner::new();
906        let mut func = Func::new(names.intern("f"));
907        let opcode = Opcode::new(names.intern("x64.nop"));
908        let block = func.create_block();
909        let kept = func.new_vreg(GPR);
910        let passed = func.new_vreg(GPR);
911        func.build(block, opcode).def(kept, GPR).finish();
912        func.build(block, opcode).def(passed, GPR).finish();
913        func.build(block, opcode)
914            .operand(Operand::read(passed, GPR).with(Constraint::Fixed(rucc_target::x86_64::RAX)))
915            .finish();
916        func.build(block, opcode).uses(kept, GPR).finish();
917
918        let places = places(&mut func, &env());
919        assert_eq!(places[1], "rax");
920        assert_ne!(places[0], "rax");
921    }
922
923    #[test]
924    fn a_value_only_memory_can_hold_goes_to_the_stack() {
925        let mut names = Interner::new();
926        let mut func = Func::new(names.intern("f"));
927        let opcode = Opcode::new(names.intern("x64.nop"));
928        let block = func.create_block();
929        let value = func.new_vreg(GPR);
930        func.build(block, opcode).def(value, GPR).finish();
931        func.build(block, opcode)
932            .operand(Operand::read(value, GPR).with(Constraint::Stack))
933            .finish();
934
935        assert_eq!(places(&mut func, &env()), ["slot"]);
936    }
937
938    #[test]
939    fn a_function_over_the_budget_gets_the_linear_scan_answer() {
940        let mut names = Interner::new();
941        let mut func = Func::new(names.intern("f"));
942        let opcode = Opcode::new(names.intern("x64.nop"));
943        let block = func.create_block();
944        let busy = func.new_vreg(GPR);
945        let once = func.new_vreg(GPR);
946        func.build(block, opcode).def(busy, GPR).finish();
947        func.build(block, opcode).def(once, GPR).finish();
948        for _ in 0..6 {
949            func.build(block, opcode).uses(busy, GPR).finish();
950        }
951        func.build(block, opcode).uses(once, GPR).finish();
952        func.build(block, opcode).uses(busy, GPR).finish();
953
954        let order = Order::of(&func);
955        let live = Live::of(&func, &order);
956        assert!(within(&func, &order, &live, &narrow(1), 0).is_none());
957        let fallen = linear(&func, &narrow(1));
958        assert_eq!(fallen, ["slot", "rax"]);
959    }
960
961    #[test]
962    fn the_cheaper_of_the_two_answers_is_the_one_kept() {
963        let mut names = Interner::new();
964        let mut func = Func::new(names.intern("f"));
965        let opcode = Opcode::new(names.intern("x64.nop"));
966        let block = func.create_block();
967        let busy = func.new_vreg(GPR);
968        let once = func.new_vreg(GPR);
969        func.build(block, opcode).def(busy, GPR).finish();
970        func.build(block, opcode).def(once, GPR).finish();
971        for _ in 0..6 {
972            func.build(block, opcode).uses(busy, GPR).finish();
973        }
974        func.build(block, opcode).uses(once, GPR).finish();
975        func.build(block, opcode).uses(busy, GPR).finish();
976
977        let order = Order::of(&func);
978        let live = Live::of(&func, &order);
979        let env = narrow(1);
980        let linear = assign::assign(&func, &order, &live, &env);
981        let ours = within(&func, &order, &live, &env, BUDGET).expect("inside the budget");
982        // The linear scan puts `busy` on the stack, which is its write and its seven reads. This
983        // puts `once` there, which is one write and one read.
984        let once_through = u128::from(func[block].weight.raw());
985        assert_eq!(cost(&func, &order, &linear), 8 * once_through);
986        assert_eq!(cost(&func, &order, &ours), 2 * once_through);
987        let kept = assign(&func, &order, &live, &env);
988        assert_eq!(kept.place(busy), ours.place(busy));
989        assert_eq!(kept.place(once), ours.place(once));
990    }
991
992    #[test]
993    fn a_copy_left_between_a_two_address_answer_and_its_source_is_counted() {
994        let mut names = Interner::new();
995        let mut func = Func::new(names.intern("f"));
996        let opcode = Opcode::new(names.intern("x64.nop"));
997        let block = func.create_block();
998        let left = func.new_vreg(GPR);
999        let right = func.new_vreg(GPR);
1000        let sum = func.new_vreg(GPR);
1001        func.build(block, opcode).def(left, GPR).finish();
1002        func.build(block, opcode).def(right, GPR).finish();
1003        func.build(block, opcode)
1004            .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1005            .uses(left, GPR)
1006            .uses(right, GPR)
1007            .finish();
1008        func.build(block, opcode).uses(sum, GPR).finish();
1009
1010        let order = Order::of(&func);
1011        let live = Live::of(&func, &order);
1012        let chosen = assign(&func, &order, &live, &env());
1013        assert_eq!(cost(&func, &order, &chosen), 0);
1014        let mut apart = chosen.clone();
1015        apart.put(sum, chosen.place(right).expect("a place for right"));
1016        assert_eq!(cost(&func, &order, &apart), u128::from(func[block].weight.raw()));
1017    }
1018
1019    #[test]
1020    fn many_values_in_few_registers_come_out_as_something_the_machine_can_run() {
1021        let mut names = Interner::new();
1022        let mut func = Func::new(names.intern("f"));
1023        let opcode = Opcode::new(names.intern("x64.nop"));
1024        let block = func.create_block();
1025        let regs: Vec<Reg> = (0..24).map(|_| func.new_vreg(GPR)).collect();
1026        for &reg in &regs {
1027            func.build(block, opcode).def(reg, GPR).finish();
1028        }
1029        for (at, &reg) in regs.iter().enumerate().rev() {
1030            for _ in 0..(at % 5) {
1031                func.build(block, opcode).uses(reg, GPR).finish();
1032            }
1033            func.build(block, opcode).uses(reg, GPR).finish();
1034        }
1035
1036        let places = places(&mut func, &narrow(4));
1037        assert_eq!(places.iter().filter(|place| *place != "slot").count(), 4);
1038    }
1039}