rucc_regalloc/assign.rs
1//! Which register each value lives in, and which values live on the stack instead.
2//!
3//! Design: `spec/10-backend.md` section 10.4.
4//!
5//! This is the `-O0` allocator's decision and nothing else. It is linear scan over the line
6//! [`crate::order`] lays the function out in: the values are taken in the order they are written,
7//! each is given a register that nothing else live at the same time is in, and when there is no
8//! such register one of the values in flight goes to the stack instead. There is no splitting and
9//! no coalescing, so a value gets one place for the whole of its range and keeps it. That produces
10//! mediocre code quickly, which is what `-O0` is for, and the allocator that produces good code
11//! slowly is a separate one, in M4.
12//!
13//! Which value is sent to the stack is the one whose range ends last, counting the value being
14//! placed among the candidates. A value wanted for a long time is the cheapest to spill per
15//! instruction it frees a register over, and it is the only heuristic here. What is picked is
16//! really a register and not a value, since two values that are never both wanted share one, and
17//! then every value in that register which is in this one's way goes.
18//!
19//! # Where the line is not the function
20//!
21//! The line is the order the blocks arrived in, and `crate::layout` puts them in a different one
22//! afterwards, so being between two blocks on the line says nothing about being between them in
23//! the code. A value live in one loop and live again in a later one is written down with
24//! everything in between inside the interval around it, and it is not live in any of it.
25//!
26//! Which is why what decides anything here is the area from `crate::live`, and the interval is
27//! only the sweep's bookkeeping: it says which values to compare and the areas say which of them
28//! actually collide. Three loops one after another in a function put a dozen values in flight at
29//! the same instant of the line and never at the same instant of the program, and asking the
30//! interval would spill the one this loop is walking for the sake of eleven values in the other
31//! two. tamnd/rucc#982.
32//!
33//! The same holds for a register an instruction insists on. A call destroys seven registers on
34//! x86-64, and a function whose blocks happen to arrive with a call written between the blocks of
35//! a loop would otherwise lose all seven for every value in that loop, for a call the loop never
36//! reaches, so that question is asked of the area and not of the interval either.
37//!
38//! Allowed is not the same as free, though, so the registers are offered in two passes. First the
39//! ones nothing insists on anywhere the range reaches, then the ones something insists on somewhere
40//! the value never goes. The second kind costs: the instruction that insists has to be handed the
41//! register in the end, and what hands it over is a move. A function that gives a value back has an
42//! operand fixed to `rax` at the end of it, and putting the busiest value in the function in `rax`
43//! because no path reaches the return with it live buys one register and pays a move at every
44//! return. Ordering the two passes is what keeps the register and drops the moves.
45//!
46//! The hint below is asked the first question rather than the second for the same reason. A value
47//! taking the register its own operand asked for saves a move, and taking one somebody else's
48//! operand asked for somewhere it never goes costs one, so a hint is worth following when the
49//! register is clear and not worth following when it is merely allowed.
50//!
51//! # What it does with a register an instruction insists on
52//!
53//! Two things. It stays out of that register for everybody else, and it tries that register first
54//! for the value the operand names. A division wants its dividend in `rax`, so `rax` is
55//! unavailable to every other value that is live where the division reads, and it is the first
56//! register offered to the dividend itself. When the dividend gets it there is no move on the way
57//! in, and when it does not the rewrite writes one and nothing else changes.
58//!
59//! That second half is the hint, and without it the register an instruction insists on is the one
60//! register the value in it can never have, since the value's own operand is what makes the
61//! register look busy. The effect is largest on returns, because a function that gives a value
62//! back has an operand fixed to `rax` at the end of it and most functions give a value back.
63//!
64//! What makes the hint safe is asking about the register at each of the instruction's two points
65//! rather than across the whole of it. An instruction reads at the first and writes at the second,
66//! so a register it insists on is one value's at the first, another value's at the second, and
67//! nobody else's at either. A division reads its dividend from `rax` and writes its quotient to
68//! `rax`, and those are different values that can both live there. A value passed to a call in
69//! `rdi` and wanted again afterwards cannot, because nothing writes `rdi` at the second point and
70//! a register the call does not write is a register the call is assumed to destroy.
71//!
72//! An operand that has to be in memory is the other way round. The value it names goes on the
73//! stack whatever else is true of it, because that is the only place the instruction could read it
74//! from.
75//!
76//! # What it does with a two address instruction
77//!
78//! An `add` on x86-64 writes one of the registers it reads, which the operand says as a reuse of
79//! another operand. The rewrite can always make that true by copying the source into the
80//! destination first, but only if the destination is a register the instruction does not otherwise
81//! read, so a value written by a reuse is treated here as live from where the instruction reads
82//! rather than from where it writes. Then the copy is always safe.
83//!
84//! The copy is also usually unnecessary, and the one place this looks past the interval it is
85//! placing is to see that: if the value being reused is read here for the last time and the value
86//! being written starts here, the second may have the first's register, and the instruction is
87//! already two address without anything being moved anywhere. That is the whole of the coalescing
88//! this allocator does, and it is worth the dozen lines, because otherwise every piece of
89//! arithmetic in the output carries a move in front of it.
90//!
91//! Both halves of that are needed. The second is the one a loop breaks: an instruction at the
92//! bottom of a loop can write a value the top of the loop reads on the next turn, and such a value
93//! is live on the way into the instruction that writes it as well as after. It is then wanted at
94//! the same time as the value it reuses, whatever is true of the reuse, and giving it the same
95//! register makes an addition read the answer to the last one instead of its own operand.
96//!
97//! # What it does not do
98//!
99//! It does not touch the function. What comes out is a table saying where each value went, and the
100//! pass that rewrites the operands and writes the moves reads it. Keeping the decision and the
101//! rewrite apart is what lets the decision be checked by looking at it, and it is the shape
102//! `spec/10-backend.md` section 10.4 asks for: an allocator is a function from a program to an
103//! assignment and the moves that make it true.
104
105use std::cmp::Reverse;
106
107use rucc_mir::{Constraint, Flags, Func, Inst, Operand, Reg, Role};
108use rucc_target::{PhysReg, RegClass};
109
110use crate::live::{Area, Live, Range};
111use crate::order::{Order, Point};
112
113/// Where a value lives.
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
115pub enum Place {
116 /// In a register, for the whole of its range.
117 Reg(PhysReg),
118 /// In a slot of the frame, which is what a value the allocator ran out of registers for gets,
119 /// and what a value an instruction can only read from memory gets.
120 Slot(u32),
121}
122
123/// What the allocator is allowed to use.
124///
125/// The order is the calling convention's, because which register to hand out first follows from
126/// which ones a call destroys, and `rucc-target` is where a convention says so. The scratch
127/// registers are held back out of the order and are what a spilled value is read into at each
128/// instruction that wants it, so a class needs as many of them as one of its instructions has
129/// register operands. Nothing here uses them, since a spilled value is only read once the rewrite
130/// is writing the instruction that reads it, but they are held back here because this is what
131/// decides what everything else may have.
132#[derive(Debug, Clone, Default)]
133pub struct Env {
134 classes: Vec<Class>,
135}
136
137/// What one class of registers offers.
138#[derive(Debug, Clone, Default)]
139struct Class {
140 order: Vec<PhysReg>,
141 scratch: Vec<PhysReg>,
142}
143
144impl Env {
145 /// An environment offering nothing, which is what a target that has said nothing offers.
146 #[must_use]
147 pub fn new() -> Self {
148 Self::default()
149 }
150
151 /// The same environment, with that class described.
152 #[must_use]
153 pub fn with(mut self, class: RegClass, order: &[PhysReg], scratch: &[PhysReg]) -> Self {
154 let index = usize::from(class.number());
155 if self.classes.len() <= index {
156 self.classes.resize(index + 1, Class::default());
157 }
158 self.classes[index] = Class { order: order.to_vec(), scratch: scratch.to_vec() };
159 self
160 }
161
162 /// The registers it may hand out in a class, in the order it prefers them.
163 #[must_use]
164 pub fn order(&self, class: RegClass) -> &[PhysReg] {
165 self.classes.get(usize::from(class.number())).map_or(&[], |class| &class.order)
166 }
167
168 /// The registers it may hand out, by class number, empty for a class it says nothing about.
169 pub(crate) fn offered(&self) -> impl Iterator<Item = &[PhysReg]> + '_ {
170 self.classes.iter().map(|class| class.order.as_slice())
171 }
172
173 /// The registers held back in a class for reading a spilled value into.
174 #[must_use]
175 pub fn scratch(&self, class: RegClass) -> &[PhysReg] {
176 self.classes.get(usize::from(class.number())).map_or(&[], |class| &class.scratch)
177 }
178}
179
180/// Where every value in a function went.
181#[derive(Debug, Clone)]
182pub struct Assignment {
183 places: Vec<Option<Place>>,
184 slots: Vec<RegClass>,
185 commuted: Vec<Inst>,
186}
187
188impl Assignment {
189 /// Records that the sources of `inst` are to be swapped, for an answer written over the second.
190 pub(crate) fn commute(&mut self, inst: Inst) {
191 self.commuted.push(inst);
192 }
193
194 /// An assignment that says nothing yet about a function with that many values.
195 ///
196 /// This and [`Assignment::put`] and [`Assignment::take_slot`] are how an allocator says what
197 /// it decided. There will be a second one in M4 and it will not reach its answer this way, so
198 /// what an assignment is has to be separable from how this file arrives at one, and the
199 /// checker in [`crate::check`] reads an assignment without caring which allocator wrote it.
200 #[must_use]
201 pub fn empty(vregs: usize) -> Self {
202 Self { places: vec![None; vregs], slots: Vec::new(), commuted: Vec::new() }
203 }
204
205 /// The two address instructions whose answer went into the register of their second source.
206 ///
207 /// Each has to have its two sources swapped before anything reads the assignment against the
208 /// function, which [`crate::run`] does. After that the answer reuses what is then the first
209 /// source, as every two address instruction does. tamnd/rucc#1895.
210 #[must_use]
211 pub fn commuted(&self) -> &[Inst] {
212 &self.commuted
213 }
214
215 /// Records where a value went.
216 ///
217 /// # Panics
218 ///
219 /// Panics on a physical register, which is somewhere already, and on a virtual one the
220 /// function never handed out.
221 pub fn put(&mut self, reg: Reg, place: Place) {
222 self.places[index(reg)] = Some(place);
223 }
224
225 /// Takes a slot of the frame, of that class, and gives back which one it is.
226 ///
227 /// # Panics
228 ///
229 /// Panics past four billion slots, which is a frame no machine has room for.
230 pub fn take_slot(&mut self, class: RegClass) -> u32 {
231 let slot = u32::try_from(self.slots.len()).expect("too many spilled values");
232 self.slots.push(class);
233 slot
234 }
235
236 /// Where a value lives, or `None` for a virtual register this function never mentions and for
237 /// a physical one, which is already where it is.
238 #[must_use]
239 pub fn place(&self, reg: Reg) -> Option<Place> {
240 self.places.get(usize::try_from(reg.number()?).ok()?).copied().flatten()
241 }
242
243 /// The class of each slot of the frame, which is what says how wide it has to be.
244 #[must_use]
245 pub fn slots(&self) -> &[RegClass] {
246 &self.slots
247 }
248
249 /// Every value that went somewhere, and where it went.
250 ///
251 /// The assignment read the other way round, which is what a caller wants when the question is
252 /// about the places rather than about the values. The stack slot allocator asks it that way,
253 /// since what it needs is which value is in each slot and the assignment is stored by value.
254 pub fn placed(&self) -> impl Iterator<Item = (Reg, Place)> + '_ {
255 self.places.iter().enumerate().filter_map(|(number, place)| {
256 let number = u32::try_from(number).ok()?;
257 Some((Reg::virtual_reg(number), (*place)?))
258 })
259 }
260
261 /// How many values went to the stack.
262 #[must_use]
263 pub fn spilled(&self) -> usize {
264 self.slots.len()
265 }
266
267 /// Puts a value on the stack, in a slot of its own.
268 pub(crate) fn spill(&mut self, reg: Reg, class: RegClass) {
269 let slot = self.take_slot(class);
270 self.put(reg, Place::Slot(slot));
271 }
272}
273
274/// One value waiting for a place.
275#[derive(Debug, Clone, Copy)]
276struct Interval<'a> {
277 reg: Reg,
278 class: RegClass,
279 /// The interval around the area, which is what the sweep below reads and what says which value
280 /// is wanted for longest when one of them has to go.
281 range: Range,
282 /// Everywhere the value is really live, which is what says whether two of them fit in one
283 /// register.
284 area: Area<'a>,
285}
286
287/// One value that has a register, for as long as it still wants it.
288#[derive(Debug, Clone, Copy)]
289struct Held<'a> {
290 reg: Reg,
291 class: RegClass,
292 range: Range,
293 area: Area<'a>,
294 at: PhysReg,
295 /// How many values were given a register before this one, which is the order the values in
296 /// flight are looked at in when one register has to be taken back.
297 since: usize,
298}
299
300/// The values that have a register, kept by the register each is in.
301///
302/// Nearly every question asked of them is about one register: whether it is free for an interval,
303/// or whether a value is still in it. They used to be one list walked from the start for every
304/// register tried, and on a function with a thousand values in flight that walk was most of the
305/// time the allocator took. Keeping them by register means asking about one reads only the values
306/// that are in it. Registers are numbered within their class, so two classes can share a list and
307/// the class is still checked.
308#[derive(Default)]
309struct Active<'a> {
310 by: Vec<Vec<Held<'a>>>,
311 /// For each register, a point no value in it ends before. Values are let go of at the start of
312 /// every interval, and most of those times nothing in most registers has ended, so a register
313 /// whose values all end at or after the point is not walked at all.
314 soonest: Vec<Point>,
315 /// How many values have been given a register so far.
316 count: usize,
317}
318
319impl<'a> Active<'a> {
320 /// The values in one register, in the order they were given it.
321 fn at(&self, at: PhysReg) -> &[Held<'a>] {
322 self.by.get(usize::from(at.number())).map_or(&[], Vec::as_slice)
323 }
324
325 fn push(&mut self, reg: Reg, class: RegClass, range: Range, area: Area<'a>, at: PhysReg) {
326 let slot = usize::from(at.number());
327 if self.by.len() <= slot {
328 self.by.resize_with(slot + 1, Vec::new);
329 self.soonest.resize(slot + 1, Point::MAX);
330 }
331 self.by[slot].push(Held { reg, class, range, area, at, since: self.count });
332 self.soonest[slot] = self.soonest[slot].min(range.end);
333 self.count += 1;
334 }
335
336 /// Lets go of every value whose interval ends before a point.
337 ///
338 /// Taking a value out of a register anywhere else leaves that register's soonest end where it
339 /// was, which is still a point nothing in it ends before, so only this and [`Active::push`]
340 /// have to keep it.
341 fn expire(&mut self, point: Point) {
342 for (held, soonest) in self.by.iter_mut().zip(&mut self.soonest) {
343 if *soonest >= point {
344 continue;
345 }
346 held.retain(|held| held.range.end >= point);
347 *soonest = held.iter().map(|held| held.range.end).min().unwrap_or(Point::MAX);
348 }
349 }
350}
351
352/// A register an instruction insists on, and where it insists on it.
353#[derive(Debug, Clone, Copy)]
354struct Blocked {
355 class: RegClass,
356 at: PhysReg,
357 /// One of the instruction's two points. Every register an instruction insists on has an entry
358 /// at each of them, because a register held at one of the two is a register nothing else may
359 /// be in across the instruction.
360 point: Point,
361 /// The one value that may be in it there, which is the value of an operand the instruction
362 /// reads at that point or writes at it. `None` means nothing may: an operand naming a physical
363 /// register outright claims it against everything, and a point no operand covers is a point
364 /// the instruction has the register to itself at.
365 by: Option<Reg>,
366 /// The byte the instruction writes the register from, when it leaves the bottom of it alone,
367 /// which is what a call does to a register AArch64 keeps the low half of. A value that fits
368 /// below it is not in the way. See [`Constraint::Above`].
369 above: Option<u8>,
370}
371
372impl Blocked {
373 /// Whether this is in the way of a value of that width, which it is unless it writes only
374 /// above everything the value takes.
375 fn reaches(&self, width: Option<u8>) -> bool {
376 match (self.above, width) {
377 (Some(above), Some(width)) => width > above,
378 _ => true,
379 }
380 }
381}
382
383/// A value written into the register another operand of the same instruction was read from.
384#[derive(Debug, Clone, Copy)]
385pub(crate) struct Reuse {
386 /// The value being read, which is the one whose register would do.
387 pub(crate) source: Reg,
388 /// The other value the instruction reads, when the instruction reads the two either way round
389 /// and so could write its answer over this one instead.
390 pub(crate) second: Option<Reg>,
391 /// Where the instruction reads it.
392 pub(crate) at: Point,
393 /// The instruction, which is swapped round if the answer takes the second value's register.
394 pub(crate) inst: Inst,
395}
396
397/// Decides where every value in a function lives.
398///
399/// # Panics
400///
401/// Panics if a class has no registers to hand out and something in the function is in that class,
402/// since that is a target description that does not describe the target the function is for.
403#[must_use]
404pub fn assign(func: &Func, order: &Order, live: &Live, env: &Env) -> Assignment {
405 let blocked = blocked(func, order);
406 let forced = forced(func);
407 let reuses = reuses(func, order);
408 let hints = hints(func);
409 let passed = passed(func);
410
411 let mut intervals = Vec::with_capacity(func.vregs());
412 for (number, reuse) in reuses.iter().enumerate() {
413 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
414 let (Some(mut area), Some(class)) = (live.area(reg), func.class_of(reg)) else {
415 continue;
416 };
417 if let Some(reuse) = reuse {
418 area = area.with(reuse.at);
419 }
420 intervals.push(Interval { reg, class, range: area.hull(), area });
421 }
422 intervals.sort_by_key(|interval| (interval.range.start, interval.reg));
423
424 let mut assignment = Assignment::empty(func.vregs());
425 let mut active = Active::default();
426 for interval in intervals {
427 active.expire(interval.range.start);
428 if forced.contains(&interval.reg) {
429 assignment.spill(interval.reg, interval.class);
430 continue;
431 }
432 // A class with no order is one the target says nothing allocates from, which on x86-64 is
433 // the x87 stack. A value of such a class is a mistake at the point it was made rather than
434 // a value with nowhere to go: what the target means is that the value lives in memory and
435 // that whatever operates on it takes an address. See `ClassInfo::allocatable`.
436 assert!(
437 !env.order(interval.class).is_empty(),
438 "a value in class {}, which the target hands out no registers from",
439 interval.class.number()
440 );
441 let reuse = reuses[index(interval.reg)];
442 let coalesced = |source| coalesce(&assignment, &active, &blocked, live, interval, source);
443 let first = reuse.and_then(|reuse| coalesced(reuse.source));
444 let second = reuse.and_then(|reuse| reuse.second).and_then(coalesced);
445 // An instruction that reads its sources either way round can write over the second one
446 // instead, which is what it needs when the first is read again later and the second is
447 // not. When both would do, the first is kept unless only the second is where something
448 // wants the answer, which saves the move in front of that reader.
449 //
450 // A block the answer is passed to wants it where that block's parameter already is. That
451 // has to count as much as an instruction asking for a register. A sum a loop carries is
452 // passed back to the parameter it was read from, and taking the register of the other
453 // source because the sum is also printed at the end moves the copy onto the back edge,
454 // where it runs every turn instead of once.
455 let hinted_at = |at: Option<PhysReg>| {
456 at.is_some_and(|at| {
457 hints[index(interval.reg)].contains(&at)
458 || passed[index(interval.reg)]
459 .iter()
460 .any(|¶m| assignment.place(param) == Some(Place::Reg(at)))
461 })
462 };
463 let commute =
464 second.is_some() && (first.is_none() || hinted_at(second) && !hinted_at(first));
465 let two_address = if commute { second } else { first };
466 if let (true, Some(reuse)) = (commute, reuse) {
467 assignment.commuted.push(reuse.inst);
468 }
469 // The reuse comes first, because a two address instruction that has to copy its left
470 // operand in pays for the copy whatever the hint says, and taking the hint here would buy
471 // one move at the cost of another.
472 let hinted = hints[index(interval.reg)].iter().copied().find(|&at| {
473 env.order(interval.class).contains(&at)
474 && available(&active, &blocked, interval, at, None, Want::Clear)
475 });
476 // A register nobody else wants anywhere near this value first, and one somebody wants
477 // somewhere the value never goes only when there is no other. Both are correct and the
478 // second is the worse buy, since the instruction that wants it has to be handed it and
479 // whatever this value is doing there has to move out of the way first.
480 let scan = |want| {
481 env.order(interval.class)
482 .iter()
483 .copied()
484 .find(|&at| available(&active, &blocked, interval, at, None, want))
485 };
486 let chosen =
487 two_address.or(hinted).or_else(|| scan(Want::Clear)).or_else(|| scan(Want::Allowed));
488 match chosen {
489 Some(at) => {
490 assignment.places[index(interval.reg)] = Some(Place::Reg(at));
491 active.push(interval.reg, interval.class, interval.range, interval.area, at);
492 }
493 None => spill_one(&mut assignment, &mut active, &blocked, interval),
494 }
495 }
496 assignment
497}
498
499/// How much a register suits an interval.
500#[derive(Debug, Clone, Copy, PartialEq, Eq)]
501pub(crate) enum Want {
502 /// Nothing insists on it anywhere the range reaches, so taking it costs nobody anything.
503 Clear,
504 /// Something insists on it somewhere the range reaches and nowhere the value is live, so taking
505 /// it is allowed and may still cost: the instruction that insists wants the register for a
506 /// value of its own, and that value now has to be moved into it.
507 Allowed,
508}
509
510/// Every register every instruction in the function insists on, arranged to be asked about.
511///
512/// Built once and never changed afterwards, and there is only one question ever asked of it: of the
513/// constraints naming one register of one class, is there one at a point some interval covers. So
514/// the entries are ordered by the register they name and then by the point, and the question is a
515/// binary search for the start of the interval followed by a walk that stops at its end.
516///
517/// It used to be a flat list walked from one end for every candidate register of every interval,
518/// which is quadratic in the size of a function and is most of the compile on a large one. See
519/// tamnd/rucc#1003 for the profile that found it.
520///
521/// The search is over the points alone and only among the one register's entries. A search over
522/// the whole list compares three fields of an entry several times its size at every step, and on a
523/// large function that is most of what asking costs, since every candidate register of every
524/// interval asks.
525pub(crate) struct Blocks {
526 /// The constraints, sorted by class, then by register, then by point.
527 all: Vec<Blocked>,
528 /// The point of each constraint, in the same order, which is what the search reads.
529 points: Vec<Point>,
530 /// Where each register's constraints start and end in the list, by class times `stride` plus
531 /// the register's number.
532 spans: Vec<(usize, usize)>,
533 /// One more than the highest register number anything insists on.
534 stride: usize,
535 /// How many bytes of its register each virtual register's value takes, by number, which is
536 /// what [`Blocked::reaches`] asks.
537 widths: Vec<Option<u8>>,
538}
539
540impl Blocks {
541 /// Whether an instruction insists on `at` where a value over `area` would be in its way: at any
542 /// point the value's range reaches when the register is wanted clear, and only at a point the
543 /// value is live at when it is merely wanted allowed. The value's own operands never count.
544 pub(crate) fn insists(
545 &self,
546 reg: Reg,
547 class: RegClass,
548 area: Area<'_>,
549 range: Range,
550 at: PhysReg,
551 want: Want,
552 ) -> bool {
553 self.over(class, at, range).any(|one| {
554 one.by != Some(reg)
555 && one.reaches(self.width(reg))
556 && (want == Want::Clear || area.covers(one.point))
557 })
558 }
559
560 /// How many bytes of its register a value takes, or `None` for all of it.
561 fn width(&self, reg: Reg) -> Option<u8> {
562 let number = usize::try_from(reg.number()?).ok()?;
563 self.widths.get(number).copied().flatten()
564 }
565
566 /// Every register an instruction takes for itself where no value may be in it, with the class
567 /// and the point, sorted by class, then by register, then by point.
568 ///
569 /// Not one it writes only the top of, since a value narrow enough may still be in that.
570 pub(crate) fn taken(&self) -> impl Iterator<Item = (RegClass, PhysReg, Point)> + '_ {
571 self.all
572 .iter()
573 .filter(|one| one.by.is_none() && one.above.is_none())
574 .map(|one| (one.class, one.at, one.point))
575 }
576
577 /// The constraints on one register of one class at the points an interval covers.
578 ///
579 /// Both ends of the walk come from the ordering rather than from a test, so what comes back is
580 /// exactly what the old `covers` call used to keep and in the same order.
581 fn over(
582 &self,
583 class: RegClass,
584 at: PhysReg,
585 range: Range,
586 ) -> impl Iterator<Item = &Blocked> + '_ {
587 let (low, high) = if usize::from(at.number()) < self.stride {
588 let key = usize::from(class.number()) * self.stride + usize::from(at.number());
589 self.spans.get(key).copied().unwrap_or((0, 0))
590 } else {
591 (0, 0)
592 };
593 let first = low + self.points[low..high].partition_point(|&point| point < range.start);
594 self.all[first..high].iter().take_while(move |one| one.point <= range.end)
595 }
596}
597
598/// Whether a register is one this interval could have.
599///
600/// The exception is the value a reuse is coalescing with, which holds the register right up to the
601/// point the new value takes it over and is the one thing that may overlap.
602///
603/// The sweep only keeps a value in `active` while the interval around it reaches this one, so the
604/// areas still have to be compared: two values whose intervals cross can have holes that let them
605/// share a register anyway, which on a function with several loops in it is most of them.
606fn available(
607 active: &Active<'_>,
608 blocked: &Blocks,
609 interval: Interval<'_>,
610 at: PhysReg,
611 except: Option<Reg>,
612 want: Want,
613) -> bool {
614 let taken = active.at(at).iter().any(|held| {
615 held.class == interval.class
616 && Some(held.reg) != except
617 && held.area.overlaps(interval.area)
618 });
619 let width = blocked.width(interval.reg);
620 let insisted = blocked.over(interval.class, at, interval.range).any(|one| {
621 one.by != Some(interval.reg)
622 && one.reaches(width)
623 && (want == Want::Clear || interval.area.covers(one.point))
624 });
625 !taken && !insisted
626}
627
628/// The register the value being reused is in, when the value being written is never live at the
629/// same time as it and the register is otherwise free.
630fn coalesce(
631 assignment: &Assignment,
632 active: &Active<'_>,
633 blocked: &Blocks,
634 live: &Live,
635 interval: Interval<'_>,
636 source: Reg,
637) -> Option<PhysReg> {
638 let Some(Place::Reg(at)) = assignment.place(source) else { return None };
639 active.at(at).iter().find(|held| held.reg == source)?;
640 // The two have to be apart everywhere, asked of the areas liveness worked out and without the
641 // point the reuse adds, since that point is the one they are allowed to share.
642 //
643 // That covers both ways it can go wrong. A value read again later needs its register after
644 // this instruction would have overwritten it. And a value being written that is live where the
645 // instruction reads already is what a loop carrying its own result round looks like: the
646 // instruction writes it at the bottom and the top of the loop reads what the last turn wrote.
647 // Either way the two are wanted at once, and no register holds both.
648 //
649 // It used to be asked of the end of the interval around the value being read, and that is not
650 // the same question. A block laid out after this instruction where the value is still live,
651 // such as the default arm of a `switch` that joins back in above it, stretches the interval
652 // past this point when nothing past it reads the value at all. The sum a loop carries round
653 // then went into a new register and was copied back at the bottom of every turn.
654 // tamnd/rucc#1965.
655 let free = available(active, blocked, interval, at, Some(source), Want::Allowed);
656 (apart(live, source, interval.reg) && free).then_some(at)
657}
658
659/// Whether two values are never live at the same time, going by what liveness worked out.
660pub(crate) fn apart(live: &Live, first: Reg, second: Reg) -> bool {
661 match (live.area(first), live.area(second)) {
662 (Some(first), Some(second)) => !first.overlaps(second),
663 _ => false,
664 }
665}
666
667/// Sends values to the stack to free a register: the ones wanted for longest, since a register
668/// held that long pays for itself over the most instructions.
669///
670/// What is chosen is a register rather than a value, because two values whose areas miss each
671/// other share one and taking it means every value in it this one is really on top of has to go.
672/// A register holding two of those costs twice as much to take as one holding a single value, so
673/// the cheap ones are looked at first and the reach only settles ties.
674fn spill_one<'a>(
675 assignment: &mut Assignment,
676 active: &mut Active<'a>,
677 blocked: &Blocks,
678 interval: Interval<'a>,
679) {
680 // What each register would cost: how many values would go, and the furthest any of them
681 // reaches. The list is one entry per register of the class, so walking it for each value in
682 // flight is the same shape as everything else here. The first number is when the earliest of
683 // them was given the register, and sorting by it puts the registers in the order the values
684 // were given them, which is what settles a tie.
685 let mut costs: Vec<(usize, PhysReg, usize, Point)> = Vec::new();
686 for held in active.by.iter().flatten() {
687 if held.class != interval.class || !held.area.overlaps(interval.area) {
688 continue;
689 }
690 match costs.iter_mut().find(|(_, at, _, _)| *at == held.at) {
691 Some((first, _, count, reach)) => {
692 *first = (*first).min(held.since);
693 *count += 1;
694 *reach = (*reach).max(held.range.end);
695 }
696 None => costs.push((held.since, held.at, 1, held.range.end)),
697 }
698 }
699 costs.sort_unstable_by_key(|&(first, _, _, _)| first);
700 // A register the instructions in the way insist on for themselves is no use, because taking it
701 // over would put this value in a register it may not have.
702 let none = Active::default();
703 let chosen = costs
704 .iter()
705 .filter(|&&(_, at, _, reach)| {
706 reach > interval.range.end
707 && available(&none, blocked, interval, at, None, Want::Allowed)
708 })
709 .min_by_key(|&&(_, _, count, reach)| (count, Reverse(reach)))
710 .map(|&(_, at, _, _)| at);
711 match chosen {
712 Some(at) => {
713 active.by[usize::from(at.number())].retain(|held| {
714 let goes = held.class == interval.class && held.area.overlaps(interval.area);
715 if goes {
716 assignment.spill(held.reg, held.class);
717 }
718 !goes
719 });
720 assignment.places[index(interval.reg)] = Some(Place::Reg(at));
721 active.push(interval.reg, interval.class, interval.range, interval.area, at);
722 }
723 None => assignment.spill(interval.reg, interval.class),
724 }
725}
726
727/// The registers the instructions insist on, and where.
728///
729/// A physical register an operand names outright counts the same way. Nothing before allocation
730/// writes one except an instruction that has to, and it has to for the length of that one
731/// instruction, which is the same statement a fixed constraint makes.
732pub(crate) fn blocked(func: &Func, order: &Order) -> Blocks {
733 let mut blocked = Vec::new();
734 let mut claimed: Vec<(RegClass, PhysReg)> = Vec::new();
735 for block in func.blocks() {
736 for inst in func.insts(block) {
737 let operands = &func[func[inst].operands];
738 claimed.clear();
739 for operand in operands {
740 if let Some(at) = insisted(operand) {
741 let key = (operand.class, at);
742 if !claimed.contains(&key) {
743 claimed.push(key);
744 }
745 }
746 }
747 for &(class, at) in &claimed {
748 // Both points, whether or not an operand is at them. A register an instruction
749 // reads and does not write is still gone by the time the instruction is done as far
750 // as anything here knows, which is what stops the value a call is passed in `rdi`
751 // from staying in `rdi` over the call.
752 for (point, role) in [(order.early(inst), Role::Use), (order.late(inst), Role::Def)]
753 {
754 let mut named = false;
755 for operand in operands {
756 let mine = insisted(operand) == Some(at) && operand.class == class;
757 if !mine || !(operand.role == role || operand.role == Role::EarlyDef) {
758 continue;
759 }
760 named = true;
761 let by = operand.reg.is_virtual().then_some(operand.reg);
762 let above = match operand.constraint {
763 Constraint::Above(above) => Some(above),
764 _ => None,
765 };
766 blocked.push(Blocked { class, at, point, by, above });
767 }
768 // A register no operand names where the operands are read is one the
769 // instruction writes and does not read, which is what a clobber is, and the
770 // seven registers a call destroys are the whole of why that case is worth
771 // separating. Such a register is free right up to the point it is written, so a
772 // value whose last read is this instruction may sit in one: it is read before
773 // the instruction writes anything, the way any other operand is. Blocking it
774 // where the operands are read as well would take every caller saved register
775 // away from the value a call is passed, which is a value that dies at the call
776 // and pays for a callee saved register it holds for two instructions. Anything
777 // living past the instruction is still refused, by the block below.
778 //
779 // This is where a target's early definitions are paid for. An instruction that
780 // fills a register before it has finished reading has to say so, because that
781 // is the one thing a plain definition here no longer covers: a division on
782 // x86-64 is a sign extension and then the division itself, so `rdx` is gone
783 // before the divisor is read, and a divisor that went there would be read as
784 // the dividend's own sign bits. `rucc_target::x86_64` writes both of them down
785 // as early definitions for exactly that reason.
786 if !named && role == Role::Def {
787 blocked.push(Blocked { class, at, point, by: None, above: None });
788 }
789 }
790 }
791 }
792 }
793 // Program order already has the points ascending, but the registers one instruction claims are
794 // walked outside the two points rather than inside them, so the list arrives in order by
795 // instruction and not by register. A sort by the key the lookup searches on is what makes it
796 // searchable, and it is stable so two constraints on one register at one point keep the order
797 // the instruction wrote them in.
798 blocked.sort_by_key(|one: &Blocked| (one.class, one.at, one.point));
799 let widths = (0..func.vregs())
800 .map(|number| func.width(Reg::virtual_reg(u32::try_from(number).ok()?)))
801 .collect();
802 let points = blocked.iter().map(|one| one.point).collect();
803 let stride = blocked.iter().map(|one| usize::from(one.at.number()) + 1).max().unwrap_or(0);
804 let classes = blocked.last().map_or(0, |one| usize::from(one.class.number()) + 1);
805 let mut spans = vec![(0, 0); classes * stride];
806 for (index, one) in blocked.iter().enumerate() {
807 let key = usize::from(one.class.number()) * stride + usize::from(one.at.number());
808 let span = &mut spans[key];
809 if span.1 == 0 {
810 span.0 = index;
811 }
812 span.1 = index + 1;
813 }
814 Blocks { all: blocked, points, spans, stride, widths }
815}
816
817/// The register an operand has to be in, which is the one a constraint asks for or the one the
818/// operand names outright.
819fn insisted(operand: &Operand) -> Option<PhysReg> {
820 match operand.constraint {
821 Constraint::Fixed(at) => Some(at),
822 _ => operand.reg.phys(),
823 }
824}
825
826/// The registers each value would rather be in, which are the ones the operands naming it insist on.
827///
828/// In the order the function writes them down, so the definition comes first where there is one,
829/// since a value written into a fixed register and then moved somewhere else pays for the move at
830/// the top of its life rather than at the bottom. The ones after it are worth keeping for the same
831/// reason the first one is, and the value a call is passed is where that shows: its definition may
832/// insist on the register a parameter arrived in, which the call it is handed to has usually taken
833/// back for an argument of its own by then, and behind that is the register the convention passes
834/// it in, which is free and is exactly where the value wants to end up.
835pub(crate) fn hints(func: &Func) -> Vec<Vec<PhysReg>> {
836 let mut hints = vec![Vec::new(); func.vregs()];
837 for block in func.blocks() {
838 for inst in func.insts(block) {
839 for operand in &func[func[inst].operands] {
840 let Constraint::Fixed(at) = operand.constraint else { continue };
841 let number = operand.reg.number().and_then(|number| usize::try_from(number).ok());
842 let Some(number) = number else { continue };
843 let wanted: &mut Vec<PhysReg> = &mut hints[number];
844 if func.class_of(operand.reg) == Some(operand.class) && !wanted.contains(&at) {
845 wanted.push(at);
846 }
847 }
848 }
849 }
850 hints
851}
852
853/// The block parameters each value is passed to, by the virtual register passed.
854pub(crate) fn passed(func: &Func) -> Vec<Vec<Reg>> {
855 let mut passed = vec![Vec::new(); func.vregs()];
856 for block in func.blocks() {
857 for call in &func[block].succs {
858 for (&arg, param) in call.args.iter().zip(&func[call.block].params) {
859 let number = arg.number().and_then(|number| usize::try_from(number).ok());
860 let Some(number) = number else { continue };
861 let to: &mut Vec<Reg> = &mut passed[number];
862 if !to.contains(¶m.reg) {
863 to.push(param.reg);
864 }
865 }
866 }
867 }
868 passed
869}
870
871/// The values that have to be on the stack whatever else is true of them.
872pub(crate) fn forced(func: &Func) -> Vec<Reg> {
873 let mut forced = Vec::new();
874 for block in func.blocks() {
875 for inst in func.insts(block) {
876 for operand in &func[func[inst].operands] {
877 if operand.constraint == Constraint::Stack
878 && operand.reg.is_virtual()
879 && !forced.contains(&operand.reg)
880 {
881 forced.push(operand.reg);
882 }
883 }
884 }
885 }
886 forced
887}
888
889/// The value each two address instruction reuses, by the virtual register it writes.
890pub(crate) fn reuses(func: &Func, order: &Order) -> Vec<Option<Reuse>> {
891 let mut reuses = vec![None; func.vregs()];
892 for block in func.blocks() {
893 for inst in func.insts(block) {
894 let operands = &func[func[inst].operands];
895 for operand in operands {
896 let Constraint::Reuse(other) = operand.constraint else { continue };
897 let number = operand.reg.number().and_then(|number| usize::try_from(number).ok());
898 let Some(number) = number else { continue };
899 let source = operands[usize::from(other)].reg;
900 let second = if func[inst].flags.contains(Flags::COMMUTES) {
901 swappable(operands, usize::from(other))
902 } else {
903 None
904 };
905 reuses[number] = Some(Reuse { source, second, at: order.early(inst), inst });
906 }
907 }
908 }
909 reuses
910}
911
912/// The second source of an instruction that reads its two sources either way round, when the
913/// answer could go over it instead of over the first.
914///
915/// Only the shape of a two address instruction with two sources, the answer and then the two, with
916/// the answer reusing the first. The second has to be a value of the same class that asks for
917/// nothing more than a register, since after the swap it is the one the answer reuses.
918fn swappable(operands: &[Operand], other: usize) -> Option<Reg> {
919 let [answer, first, second] = operands else { return None };
920 let same = second.class == first.class && second.class == answer.class;
921 let plain = second.role == Role::Use && second.constraint == Constraint::Reg;
922 (other == 1 && same && plain && second.reg.is_virtual() && second.reg != first.reg)
923 .then_some(second.reg)
924}
925
926/// A virtual register's number as a table index.
927fn index(reg: Reg) -> usize {
928 usize::try_from(reg.number().expect("a virtual register")).expect("a register number")
929}
930
931#[cfg(test)]
932mod tests {
933 use rucc_base::Interner;
934 use rucc_mir::{BlockCall, Opcode, Operand, Param};
935 use rucc_target::x86_64::{GPR, R13, R14, R15, RAX, RCX, RDX, REGS, RSI, SYSV};
936
937 use super::*;
938
939 /// The x86-64 environment, with the last three of the allocation order held back as scratch.
940 fn env() -> Env {
941 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
942 Env::new().with(GPR, order, scratch)
943 }
944
945 /// An environment with that many general purpose registers, for putting a function under
946 /// pressure without writing a hundred instructions.
947 fn narrow(count: usize) -> Env {
948 Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 1])
949 }
950
951 /// What a place is called, which is what an assertion reads.
952 fn named(place: Option<Place>) -> String {
953 match place {
954 Some(Place::Reg(reg)) => REGS.name(GPR, reg).expect("a register").to_string(),
955 Some(Place::Slot(slot)) => format!("slot {slot}"),
956 None => "nowhere".to_string(),
957 }
958 }
959
960 /// Where every value in a function went.
961 fn places(func: &Func, env: &Env) -> Vec<String> {
962 let order = Order::of(func);
963 let live = Live::of(func, &order);
964 let assignment = assign(func, &order, &live, env);
965 (0..func.vregs())
966 .map(|number| {
967 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
968 named(assignment.place(reg))
969 })
970 .collect()
971 }
972
973 #[test]
974 fn two_values_that_are_never_both_wanted_share_a_register() {
975 let mut names = Interner::new();
976 let mut func = Func::new(names.intern("f"));
977 let opcode = Opcode::new(names.intern("x64.nop"));
978 let block = func.create_block();
979 let first = func.new_vreg(GPR);
980 let second = func.new_vreg(GPR);
981 func.build(block, opcode).def(first, GPR).finish();
982 func.build(block, opcode).uses(first, GPR).finish();
983 func.build(block, opcode).def(second, GPR).finish();
984 func.build(block, opcode).uses(second, GPR).finish();
985
986 // The first register in the order, twice, because the first value is finished with before
987 // the second one is written.
988 assert_eq!(places(&func, &env()), ["rax", "rax"]);
989 }
990
991 /// A value held over an instruction that writes the whole of the first register and the top
992 /// of the second, which is a call on AArch64 and `v8` in small, with the value as wide as that.
993 fn over_the_top(width: u32) -> Func {
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 held = func.new_vreg(GPR);
999 func.set_width(held, width);
1000 func.build(block, opcode).def(held, GPR).finish();
1001 func.build(block, opcode)
1002 .operand(Operand::write(Reg::physical(RAX), GPR))
1003 .operand(Operand::write(Reg::physical(RCX), GPR).with(Constraint::Above(8)))
1004 .finish();
1005 func.build(block, opcode).uses(held, GPR).finish();
1006 func
1007 }
1008
1009 #[test]
1010 fn a_value_that_fits_under_what_an_instruction_writes_stays_in_the_register() {
1011 assert_eq!(places(&over_the_top(8), &narrow(2)), ["rcx"]);
1012 assert_eq!(places(&over_the_top(4), &narrow(2)), ["rcx"]);
1013 }
1014
1015 #[test]
1016 fn a_value_wider_than_that_or_of_no_known_width_does_not() {
1017 assert_eq!(places(&over_the_top(16), &narrow(2)), ["slot 0"]);
1018 assert_eq!(places(&over_the_top(0), &narrow(2)), ["slot 0"]);
1019 }
1020
1021 #[test]
1022 fn two_values_that_are_both_wanted_do_not() {
1023 let mut names = Interner::new();
1024 let mut func = Func::new(names.intern("f"));
1025 let opcode = Opcode::new(names.intern("x64.nop"));
1026 let block = func.create_block();
1027 let first = func.new_vreg(GPR);
1028 let second = func.new_vreg(GPR);
1029 func.build(block, opcode).def(first, GPR).finish();
1030 func.build(block, opcode).def(second, GPR).finish();
1031 func.build(block, opcode).uses(first, GPR).finish();
1032 func.build(block, opcode).uses(second, GPR).finish();
1033
1034 assert_eq!(places(&func, &env()), ["rax", "rcx"]);
1035 }
1036
1037 #[test]
1038 fn a_value_written_early_that_nothing_reads_still_holds_its_register() {
1039 let mut names = Interner::new();
1040 let mut func = Func::new(names.intern("f"));
1041 let opcode = Opcode::new(names.intern("x64.nop"));
1042 let block = func.create_block();
1043 let wanted = func.new_vreg(GPR);
1044 let spare = func.new_vreg(GPR);
1045 // A division: a remainder somebody wants, and a quotient nobody does. Both are written by
1046 // the one instruction and the quotient is written before the operands have been read.
1047 func.build(block, opcode)
1048 .def(wanted, GPR)
1049 .operand(Operand::write_early(spare, GPR))
1050 .finish();
1051 func.build(block, opcode).uses(wanted, GPR).finish();
1052
1053 // Two registers, not one. A value nothing reads is still somewhere, and the instruction
1054 // that wrote it wrote the other one too, so the two cannot be the same place. Handing them
1055 // the same register loses the remainder, because the copy that takes the quotient out of
1056 // the register the machine insisted on goes on top of it. The quotient gets the first
1057 // register because it is written first, which is the whole of what early means.
1058 assert_eq!(places(&func, &env()), ["rcx", "rax"]);
1059 }
1060
1061 #[test]
1062 fn the_value_wanted_longest_is_the_one_that_goes_to_the_stack() {
1063 let mut names = Interner::new();
1064 let mut func = Func::new(names.intern("f"));
1065 let opcode = Opcode::new(names.intern("x64.nop"));
1066 let block = func.create_block();
1067 let long = func.new_vreg(GPR);
1068 let short = func.new_vreg(GPR);
1069 let third = func.new_vreg(GPR);
1070 func.build(block, opcode).def(long, GPR).finish();
1071 func.build(block, opcode).def(short, GPR).finish();
1072 func.build(block, opcode).def(third, GPR).finish();
1073 func.build(block, opcode).uses(short, GPR).finish();
1074 func.build(block, opcode).uses(third, GPR).finish();
1075 func.build(block, opcode).uses(long, GPR).finish();
1076
1077 // Two registers between three values. The one still wanted at the end of the function is
1078 // the one whose register is worth the most to everybody else, so it is the one that goes.
1079 assert_eq!(places(&func, &narrow(2)), ["slot 0", "rcx", "rax"]);
1080 }
1081
1082 #[test]
1083 fn a_register_an_instruction_insists_on_goes_to_the_values_that_asked_for_it() {
1084 let mut names = Interner::new();
1085 let mut func = Func::new(names.intern("f"));
1086 let opcode = Opcode::new(names.intern("x64.nop"));
1087 let block = func.create_block();
1088 let across = func.new_vreg(GPR);
1089 let dividend = func.new_vreg(GPR);
1090 let quotient = func.new_vreg(GPR);
1091 let remainder = func.new_vreg(GPR);
1092 func.build(block, opcode).def(across, GPR).finish();
1093 func.build(block, opcode).def(dividend, GPR).finish();
1094 func.build(block, opcode)
1095 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
1096 .operand(Operand::write_early(remainder, GPR).with(Constraint::Fixed(RDX)))
1097 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
1098 .finish();
1099 func.build(block, opcode).uses(across, GPR).finish();
1100
1101 // The value that has to be across the division is nowhere near `rax` or `rdx`, and each of
1102 // the three the division names is in the register the division asked for it in. The
1103 // dividend and the quotient share `rax` because the first is read where the second is
1104 // written, which is what a division does.
1105 assert_eq!(places(&func, &env()), ["rcx", "rax", "rax", "rdx"]);
1106 }
1107
1108 /// A value read by an instruction that fills a register before it reads is kept out of that
1109 /// register, even though the read is the last thing the value is wanted for.
1110 ///
1111 /// The divisor of a division is the case. What the machine runs is `cltd` and then `idivl`, so
1112 /// `rdx` holds the top half of the dividend by the time the divisor is read, and a divisor
1113 /// sitting in `rdx` is read as the dividend's own sign bits. An early definition is how the
1114 /// target says a register goes before the operands are read, and this is where the allocator
1115 /// has to hear it, since a value dying at an instruction is otherwise free to sit in a
1116 /// register that instruction writes. tamnd/rucc#1232.
1117 #[test]
1118 fn a_value_that_dies_at_an_instruction_stays_out_of_what_it_fills_first() {
1119 let mut names = Interner::new();
1120 let mut func = Func::new(names.intern("f"));
1121 let opcode = Opcode::new(names.intern("x64.nop"));
1122 let block = func.create_block();
1123 let across = func.new_vreg(GPR);
1124 let dividend = func.new_vreg(GPR);
1125 let divisor = func.new_vreg(GPR);
1126 let remainder = func.new_vreg(GPR);
1127 func.build(block, opcode).def(across, GPR).finish();
1128 func.build(block, opcode).def(dividend, GPR).finish();
1129 func.build(block, opcode).def(divisor, GPR).finish();
1130 func.build(block, opcode)
1131 .operand(Operand::write_early(remainder, GPR).with(Constraint::Fixed(RDX)))
1132 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
1133 .operand(Operand::read(divisor, GPR))
1134 .finish();
1135 func.build(block, opcode).uses(across, GPR).uses(remainder, GPR).finish();
1136
1137 // Four registers for four values, and the divisor takes the fourth. `rdx` is free
1138 // everywhere in this function except at the instruction that is about to fill it, which is
1139 // the one instruction the divisor is wanted at.
1140 assert_eq!(places(&func, &narrow(4)), ["rcx", "rax", "rsi", "rdx"]);
1141 }
1142
1143 #[test]
1144 fn a_value_wanted_after_the_instruction_that_insists_does_not_get_that_register() {
1145 let mut names = Interner::new();
1146 let mut func = Func::new(names.intern("f"));
1147 let opcode = Opcode::new(names.intern("x64.nop"));
1148 let block = func.create_block();
1149 let dividend = func.new_vreg(GPR);
1150 let quotient = func.new_vreg(GPR);
1151 func.build(block, opcode).def(dividend, GPR).finish();
1152 func.build(block, opcode)
1153 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
1154 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
1155 .finish();
1156 func.build(block, opcode).uses(dividend, GPR).finish();
1157
1158 // The hint is a preference and not a claim. The dividend would rather be in `rax` and
1159 // cannot be, because the division writes `rax` and the dividend is wanted afterwards, so
1160 // it takes the next register and the quotient keeps the one it was promised.
1161 assert_eq!(places(&func, &env()), ["rcx", "rax"]);
1162 }
1163
1164 #[test]
1165 fn a_value_an_instruction_can_only_read_from_memory_is_on_the_stack() {
1166 let mut names = Interner::new();
1167 let mut func = Func::new(names.intern("f"));
1168 let opcode = Opcode::new(names.intern("x64.nop"));
1169 let block = func.create_block();
1170 let value = func.new_vreg(GPR);
1171 func.build(block, opcode).def(value, GPR).finish();
1172 func.build(block, opcode)
1173 .operand(Operand::read(value, GPR).with(Constraint::Stack))
1174 .finish();
1175
1176 assert_eq!(places(&func, &env()), ["slot 0"]);
1177 }
1178
1179 #[test]
1180 fn a_two_address_instruction_writes_the_register_it_read_when_it_can() {
1181 let mut names = Interner::new();
1182 let mut func = Func::new(names.intern("f"));
1183 let opcode = Opcode::new(names.intern("x64.nop"));
1184 let block = func.create_block();
1185 let left = func.new_vreg(GPR);
1186 let right = func.new_vreg(GPR);
1187 let sum = func.new_vreg(GPR);
1188 func.build(block, opcode).def(left, GPR).finish();
1189 func.build(block, opcode).def(right, GPR).finish();
1190 func.build(block, opcode)
1191 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1192 .uses(left, GPR)
1193 .uses(right, GPR)
1194 .finish();
1195 func.build(block, opcode).uses(right, GPR).finish();
1196
1197 // The addition reads the left value for the last time, so the answer goes where that was
1198 // and the instruction is two address without a move in front of it.
1199 assert_eq!(places(&func, &env()), ["rax", "rcx", "rax"]);
1200 }
1201
1202 #[test]
1203 fn a_two_address_instruction_that_cannot_gets_a_register_nothing_it_reads_is_in() {
1204 let mut names = Interner::new();
1205 let mut func = Func::new(names.intern("f"));
1206 let opcode = Opcode::new(names.intern("x64.nop"));
1207 let block = func.create_block();
1208 let left = func.new_vreg(GPR);
1209 let right = func.new_vreg(GPR);
1210 let sum = func.new_vreg(GPR);
1211 func.build(block, opcode).def(left, GPR).finish();
1212 func.build(block, opcode).def(right, GPR).finish();
1213 func.build(block, opcode)
1214 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1215 .uses(left, GPR)
1216 .uses(right, GPR)
1217 .finish();
1218 func.build(block, opcode).uses(left, GPR).finish();
1219
1220 // The left value is wanted afterwards, so the answer cannot have its register. It cannot
1221 // have the right one's either, because the rewrite is about to write a move into it before
1222 // the addition has read anything.
1223 assert_eq!(places(&func, &env()), ["rax", "rcx", "rdx"]);
1224 }
1225
1226 #[test]
1227 fn an_answer_that_commutes_goes_over_the_source_that_is_finished_with() {
1228 let mut names = Interner::new();
1229 let mut func = Func::new(names.intern("f"));
1230 let opcode = Opcode::new(names.intern("x64.nop"));
1231 let block = func.create_block();
1232 let left = func.new_vreg(GPR);
1233 let right = func.new_vreg(GPR);
1234 let sum = func.new_vreg(GPR);
1235 func.build(block, opcode).def(left, GPR).finish();
1236 func.build(block, opcode).def(right, GPR).finish();
1237 let add = func
1238 .build(block, opcode)
1239 .flags(Flags::COMMUTES)
1240 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1241 .uses(left, GPR)
1242 .uses(right, GPR)
1243 .finish();
1244 func.build(block, opcode).uses(left, GPR).uses(sum, GPR).finish();
1245
1246 // The same shape as the one above where the answer got a register of its own, except that
1247 // the addition reads its sources either way round, so the answer goes where the right one
1248 // was and the instruction is marked to be swapped.
1249 assert_eq!(places(&func, &env()), ["rax", "rcx", "rcx"]);
1250 let order = Order::of(&func);
1251 let live = Live::of(&func, &order);
1252 let assignment = assign(&func, &order, &live, &env());
1253 assert_eq!(assignment.commuted(), [add]);
1254
1255 // Once swapped, the instruction is an ordinary reuse of its first source, the checker and
1256 // the trace agree with it, and nothing has to be moved in front of it. The rewrite has put
1257 // the registers in by then, so the right one is `rcx` and the left one `rax`.
1258 let allocation = crate::run(&mut func, &env(), "f", true);
1259 assert!(allocation.edits.is_empty());
1260 let operands = &func[func[add].operands];
1261 let (first, second) = (operands[1].reg.phys(), operands[2].reg.phys());
1262 assert_eq!((first, second), (Some(RCX), Some(RAX)));
1263 }
1264
1265 #[test]
1266 fn an_answer_that_commutes_takes_the_source_something_after_it_wants() {
1267 let mut names = Interner::new();
1268 let mut func = Func::new(names.intern("f"));
1269 let opcode = Opcode::new(names.intern("x64.nop"));
1270 let block = func.create_block();
1271 let left = func.new_vreg(GPR);
1272 let right = func.new_vreg(GPR);
1273 let sum = func.new_vreg(GPR);
1274 func.build(block, opcode).def(left, GPR).finish();
1275 func.build(block, opcode)
1276 .operand(Operand::write(right, GPR).with(Constraint::Fixed(RAX)))
1277 .finish();
1278 let add = func
1279 .build(block, opcode)
1280 .flags(Flags::COMMUTES)
1281 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1282 .uses(left, GPR)
1283 .uses(right, GPR)
1284 .finish();
1285 func.build(block, opcode)
1286 .operand(Operand::read(sum, GPR).with(Constraint::Fixed(RAX)))
1287 .finish();
1288
1289 // Both sources are finished with, so either register would do for the answer. The one
1290 // reading it wants it in `rax`, which is where the right one already is, so it goes there
1291 // and nothing is moved in front of that reader.
1292 let names = places(&func, &env());
1293 assert_eq!(names[2], "rax");
1294 assert_ne!(names[0], "rax");
1295 let allocation = crate::run(&mut func, &env(), "f", true);
1296 assert!(allocation.edits.is_empty());
1297 assert_eq!(func[func[add].operands][1].reg.phys(), Some(RAX));
1298 }
1299
1300 #[test]
1301 fn an_answer_that_commutes_stays_where_the_loop_passes_it() {
1302 let mut names = Interner::new();
1303 let mut func = Func::new(names.intern("f"));
1304 let opcode = Opcode::new(names.intern("x64.nop"));
1305 let entry = func.create_block();
1306 let head = func.create_block();
1307 let out = func.create_block();
1308 let seed = func.new_vreg(GPR);
1309 let total = func.new_vreg(GPR);
1310 let term = func.new_vreg(GPR);
1311 let next = func.new_vreg(GPR);
1312 func.build(entry, opcode).def(seed, GPR).finish();
1313 *func.succs_mut(entry) = vec![BlockCall::with(head, vec![seed])];
1314 func.params_mut(head).push(Param { reg: total, class: GPR });
1315 func.build(head, opcode).def(term, GPR).finish();
1316 let add = func
1317 .build(head, opcode)
1318 .flags(Flags::COMMUTES)
1319 .operand(Operand::write(next, GPR).with(Constraint::Reuse(1)))
1320 .uses(total, GPR)
1321 .uses(term, GPR)
1322 .finish();
1323 func.build(head, opcode)
1324 .operand(Operand::read(next, GPR).with(Constraint::Fixed(RSI)))
1325 .finish();
1326 *func.succs_mut(head) = vec![BlockCall::with(head, vec![next]), BlockCall::to(out)];
1327
1328 // Both sources are finished with and the sum is wanted in `rsi` as well, but the loop
1329 // passes it back to `total`, so it goes where `total` is and the back edge has nothing to
1330 // copy.
1331 let order = Order::of(&func);
1332 let live = Live::of(&func, &order);
1333 let assignment = assign(&func, &order, &live, &env());
1334 assert_eq!(assignment.place(next), assignment.place(total));
1335 assert!(!assignment.commuted().contains(&add));
1336 }
1337
1338 #[test]
1339 fn an_answer_that_does_not_commute_leaves_its_sources_where_they_are() {
1340 let mut names = Interner::new();
1341 let mut func = Func::new(names.intern("f"));
1342 let opcode = Opcode::new(names.intern("x64.nop"));
1343 let block = func.create_block();
1344 let left = func.new_vreg(GPR);
1345 let right = func.new_vreg(GPR);
1346 let sum = func.new_vreg(GPR);
1347 func.build(block, opcode).def(left, GPR).finish();
1348 func.build(block, opcode).def(right, GPR).finish();
1349 func.build(block, opcode)
1350 .flags(Flags::COMMUTES)
1351 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1352 .uses(left, GPR)
1353 .uses(right, GPR)
1354 .finish();
1355 func.build(block, opcode).uses(right, GPR).finish();
1356
1357 // The left one is finished with, so the answer goes over it as it always did, and there is
1358 // nothing to swap.
1359 assert_eq!(places(&func, &env()), ["rax", "rcx", "rax"]);
1360 let order = Order::of(&func);
1361 let live = Live::of(&func, &order);
1362 assert!(assign(&func, &order, &live, &env()).commuted().is_empty());
1363 }
1364
1365 #[test]
1366 fn a_value_live_across_a_whole_loop_holds_its_register_over_all_of_it() {
1367 let mut names = Interner::new();
1368 let mut func = Func::new(names.intern("f"));
1369 let opcode = Opcode::new(names.intern("x64.nop"));
1370 let head = func.create_block();
1371 let body = func.create_block();
1372 let carried = func.new_vreg(GPR);
1373 let inside = func.new_vreg(GPR);
1374 func.build(head, opcode).def(carried, GPR).finish();
1375 *func.succs_mut(head) = vec![BlockCall::to(body)];
1376 func.build(body, opcode).def(inside, GPR).finish();
1377 func.build(body, opcode).uses(inside, GPR).uses(carried, GPR).finish();
1378 *func.succs_mut(body) = vec![BlockCall::to(body)];
1379
1380 // The value inside the loop cannot have the carried one's register, even though nothing
1381 // between the two definitions says so.
1382 assert_eq!(places(&func, &env()), ["rax", "rcx"]);
1383 }
1384
1385 #[test]
1386 fn a_two_address_answer_already_live_does_not_take_the_register_it_read() {
1387 let mut names = Interner::new();
1388 let mut func = Func::new(names.intern("f"));
1389 let opcode = Opcode::new(names.intern("x64.nop"));
1390 let head = func.create_block();
1391 let latch = func.create_block();
1392 let out = func.create_block();
1393 let source = func.new_vreg(GPR);
1394 let carried = func.new_vreg(GPR);
1395 func.build(head, opcode).def(source, GPR).finish();
1396 func.build(head, opcode).def(carried, GPR).finish();
1397 *func.succs_mut(head) = vec![BlockCall::to(latch)];
1398 // The bottom of the loop adds the source to the carried value and writes the answer back
1399 // over it, reusing the register the source is in. The next turn round redefines both.
1400 func.build(latch, opcode)
1401 .operand(Operand::write(carried, GPR).with(Constraint::Reuse(1)))
1402 .uses(source, GPR)
1403 .uses(carried, GPR)
1404 .finish();
1405 *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
1406 func.build(out, opcode).uses(carried, GPR).finish();
1407
1408 // The source is read here for the last time, which on its own is the shape the two address
1409 // shortcut is for, and taking it would be wrong. The carried value was written by the same
1410 // instruction on the last turn and is read by this one, so the two are both wanted where
1411 // the instruction reads and one register cannot hold both.
1412 assert_eq!(places(&func, &env()), ["rax", "rcx"]);
1413
1414 // And the checker has to agree, since it excused this pair on the same reasoning and so
1415 // would have let the answer through.
1416 let order = Order::of(&func);
1417 let live = Live::of(&func, &order);
1418 let assignment = assign(&func, &order, &live, &env());
1419 assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
1420 }
1421
1422 #[test]
1423 fn a_two_address_answer_with_a_hole_in_front_of_it_does_not_take_its_other_operand() {
1424 let mut names = Interner::new();
1425 let mut func = Func::new(names.intern("f"));
1426 let nop = Opcode::new(names.intern("x64.nop"));
1427 let add = Opcode::new(names.intern("x64.add"));
1428 let entry = func.create_block();
1429 let head = func.create_block();
1430 let arm = func.create_block();
1431 let latch = func.create_block();
1432 let out = func.create_block();
1433 let seed = func.new_vreg(GPR);
1434 let sum = func.new_vreg(GPR);
1435 let inside = func.new_vreg(GPR);
1436 let loaded = func.new_vreg(GPR);
1437 func.build(entry, nop).def(seed, GPR).finish();
1438 func.build(entry, nop).def(sum, GPR).finish();
1439 *func.succs_mut(entry) = vec![BlockCall::to(head)];
1440 func.build(head, nop).uses(sum, GPR).finish();
1441 *func.succs_mut(head) = vec![BlockCall::to(arm), BlockCall::to(latch)];
1442 func.build(arm, nop).def(inside, GPR).finish();
1443 func.build(arm, nop).uses(inside, GPR).finish();
1444 *func.succs_mut(arm) = vec![BlockCall::to(out)];
1445 func.build(latch, nop).def(loaded, GPR).finish();
1446 func.build(latch, add)
1447 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1448 .uses(seed, GPR)
1449 .uses(loaded, GPR)
1450 .finish();
1451 *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
1452
1453 // The answer is live in the entry and the head as well, and the arm between them is a hole
1454 // in it, so the piece the addition writes is not the first one. The value the addition reads
1455 // out of memory is still wanted where the addition reads, so it may not be in the register
1456 // the answer is about to be copied into, holes or no holes. tamnd/rucc#982.
1457 let places = places(&func, &env());
1458 assert_ne!(places[index(sum)], places[index(loaded)]);
1459
1460 let order = Order::of(&func);
1461 let live = Live::of(&func, &order);
1462 let assignment = assign(&func, &order, &live, &env());
1463 assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
1464 }
1465
1466 #[test]
1467 fn a_sum_a_loop_carries_round_keeps_its_register_past_an_arm_laid_out_after_it() {
1468 let mut names = Interner::new();
1469 let mut func = Func::new(names.intern("f"));
1470 let nop = Opcode::new(names.intern("x64.nop"));
1471 let add = Opcode::new(names.intern("x64.add"));
1472 let entry = func.create_block();
1473 let head = func.create_block();
1474 let join = func.create_block();
1475 let arm = func.create_block();
1476 let out = func.create_block();
1477 let seed = func.new_vreg(GPR);
1478 let term = func.new_vreg(GPR);
1479 let next = func.new_vreg(GPR);
1480 func.build(entry, nop).def(seed, GPR).finish();
1481 *func.succs_mut(entry) = vec![BlockCall::with(head, vec![seed])];
1482 let total = func.append_param(head, GPR);
1483 func.build(head, nop).def(term, GPR).finish();
1484 *func.succs_mut(head) = vec![BlockCall::to(join), BlockCall::to(arm)];
1485 func.build(join, add)
1486 .operand(Operand::write(next, GPR).with(Constraint::Reuse(1)))
1487 .uses(total, GPR)
1488 .uses(term, GPR)
1489 .finish();
1490 *func.succs_mut(join) =
1491 vec![BlockCall::with(head, vec![next]), BlockCall::with(out, vec![next])];
1492 // The default arm of a `switch`, laid out after the addition it joins back in above. The
1493 // sum is live in it and nothing in it or after it reads the sum again.
1494 func.build(arm, nop).def(term, GPR).finish();
1495 *func.succs_mut(arm) = vec![BlockCall::to(join)];
1496 let result = func.append_param(out, GPR);
1497 func.build(out, nop).uses(result, GPR).finish();
1498
1499 // The addition reads the sum for the last time, so the new sum goes where the old one was
1500 // and the edge back to the top of the loop has nothing to move. tamnd/rucc#1965.
1501 let places = places(&func, &env());
1502 assert_eq!(places[index(next)], places[index(total)]);
1503
1504 let order = Order::of(&func);
1505 let live = Live::of(&func, &order);
1506 let assignment = assign(&func, &order, &live, &env());
1507 assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
1508 }
1509
1510 /// Two blocks the entry chooses between, with the one the clobber is in written first. The two
1511 /// values written in the entry block are read in the other one, so their ranges cover the
1512 /// clobber whether or not either of them ever reaches it.
1513 fn arms(reaches: bool) -> Func {
1514 let mut names = Interner::new();
1515 let mut func = Func::new(names.intern("f"));
1516 let opcode = Opcode::new(names.intern("x64.nop"));
1517 let entry = func.create_block();
1518 let arm = func.create_block();
1519 let tail = func.create_block();
1520 let first = func.new_vreg(GPR);
1521 let second = func.new_vreg(GPR);
1522 func.build(entry, opcode).def(first, GPR).finish();
1523 func.build(entry, opcode).def(second, GPR).finish();
1524 *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
1525 // What a call looks like here: an instruction writing the registers the convention says it
1526 // destroys, named outright so that nothing else may be in them.
1527 func.build(arm, opcode).operand(Operand::write(Reg::physical(RAX), GPR)).finish();
1528 *func.succs_mut(arm) = if reaches { vec![BlockCall::to(tail)] } else { Vec::new() };
1529 func.build(tail, opcode).uses(first, GPR).uses(second, GPR).finish();
1530 func
1531 }
1532
1533 #[test]
1534 fn a_register_a_clobber_takes_beats_the_stack_for_a_value_not_live_in_that_block() {
1535 let func = arms(false);
1536
1537 // Two registers between two values, and a clobber in the arm that takes the first of them.
1538 // The intervals around both values cover the clobber, since the arm is written between the
1539 // two blocks they are live in, and the arm is a hole in both of their areas. So the second
1540 // value has `rax` rather than a stack slot: the arm is a block its own path never goes
1541 // through. tamnd/rucc#982.
1542 assert_eq!(places(&func, &narrow(2)), ["rcx", "rax"]);
1543
1544 let order = Order::of(&func);
1545 let live = Live::of(&func, &order);
1546 let assignment = assign(&func, &order, &live, &narrow(2));
1547 assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
1548 }
1549
1550 #[test]
1551 fn a_register_a_clobber_takes_is_not_free_to_a_value_that_is_live_there() {
1552 let func = arms(true);
1553
1554 // The same blocks with an edge from the arm to the tail, which is all it takes: both values
1555 // now arrive at the read either way, so the clobber is on a path they are live over and the
1556 // one register left has to do for both of them.
1557 assert_eq!(places(&func, &narrow(2)), ["rcx", "slot 0"]);
1558 }
1559
1560 /// A value and the instruction that destroys a register, written one after the other, with the
1561 /// value read by that instruction or by the one after it.
1562 fn dies_at_the_clobber(here: bool) -> Func {
1563 let mut names = Interner::new();
1564 let mut func = Func::new(names.intern("f"));
1565 let opcode = Opcode::new(names.intern("x64.nop"));
1566 let entry = func.create_block();
1567 let value = func.new_vreg(GPR);
1568 func.build(entry, opcode).def(value, GPR).finish();
1569 let call = func.build(entry, opcode).operand(Operand::write(Reg::physical(RAX), GPR));
1570 if here {
1571 call.uses(value, GPR).finish();
1572 } else {
1573 call.finish();
1574 func.build(entry, opcode).uses(value, GPR).finish();
1575 }
1576 func
1577 }
1578
1579 /// A value whose last read is the instruction that destroys a register may be in that register,
1580 /// because the instruction reads what it is handed before it writes anything.
1581 ///
1582 /// The call is what this is about, and the value a call is passed is the case: seven registers
1583 /// on this machine are destroyed by one, every argument dies at the call that reads it, and
1584 /// refusing all seven to those values left them taking a callee saved register for a life two
1585 /// instructions long and paying for it in the prologue and the epilogue. tamnd/rucc#1232.
1586 #[test]
1587 fn a_value_that_dies_where_a_register_is_destroyed_may_be_in_that_register() {
1588 let func = dies_at_the_clobber(true);
1589 assert_eq!(places(&func, &narrow(1)), ["rax"]);
1590
1591 let order = Order::of(&func);
1592 let live = Live::of(&func, &order);
1593 let assignment = assign(&func, &order, &live, &narrow(1));
1594 assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
1595 }
1596
1597 /// And one read later than that is one the instruction really does destroy, which is the same
1598 /// function with the read moved down by one instruction.
1599 #[test]
1600 fn a_value_read_after_the_instruction_that_destroys_a_register_is_not_in_it() {
1601 let func = dies_at_the_clobber(false);
1602 assert_eq!(places(&func, &narrow(1)), ["slot 0"]);
1603 }
1604
1605 #[test]
1606 fn a_hint_is_followed_when_the_register_is_clear_and_not_when_it_is_merely_allowed() {
1607 let mut names = Interner::new();
1608 let mut func = Func::new(names.intern("f"));
1609 let opcode = Opcode::new(names.intern("x64.nop"));
1610 let entry = func.create_block();
1611 let mid = func.create_block();
1612 let tail = func.create_block();
1613 let first = func.new_vreg(GPR);
1614 let second = func.new_vreg(GPR);
1615 func.build(entry, opcode).def(first, GPR).finish();
1616 func.build(entry, opcode).def(second, GPR).finish();
1617 *func.succs_mut(entry) = vec![BlockCall::to(mid), BlockCall::to(tail)];
1618 // Two arms, each ending in an instruction that wants its own value in `rax`, which is what
1619 // a return out of either side of a branch looks like.
1620 func.build(mid, opcode)
1621 .operand(Operand::read(second, GPR).with(Constraint::Fixed(RAX)))
1622 .finish();
1623 func.build(tail, opcode)
1624 .operand(Operand::read(first, GPR).with(Constraint::Fixed(RAX)))
1625 .finish();
1626
1627 // The first value is hinted at `rax` and does not get it, because the other arm wants `rax`
1628 // for the other value and the first value's range reaches that far. Following the hint here
1629 // would save a move in the tail and cost one in the middle, and the second value gets `rax`
1630 // with nothing moved anywhere instead.
1631 assert_eq!(places(&func, &env()), ["rcx", "rax"]);
1632 }
1633
1634 #[test]
1635 fn a_value_living_in_a_hole_of_another_gets_the_same_register() {
1636 let mut names = Interner::new();
1637 let mut func = Func::new(names.intern("f"));
1638 let opcode = Opcode::new(names.intern("x64.nop"));
1639 let entry = func.create_block();
1640 let arm = func.create_block();
1641 let tail = func.create_block();
1642 let across = func.new_vreg(GPR);
1643 let inside = func.new_vreg(GPR);
1644 func.build(entry, opcode).def(across, GPR).finish();
1645 *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
1646 func.build(arm, opcode).def(inside, GPR).finish();
1647 func.build(arm, opcode).uses(inside, GPR).finish();
1648 func.build(tail, opcode).uses(across, GPR).finish();
1649
1650 // One register between the two of them, and one register is enough. Nothing in the arm can
1651 // reach the read in the tail, so the value the arm makes is welcome to the register the
1652 // value crossing the function is in. The interval around that value covers the arm and the
1653 // value is nowhere near it, which is what used to send one of the two to the stack.
1654 // tamnd/rucc#982.
1655 assert_eq!(places(&func, &narrow(1)), ["rax", "rax"]);
1656
1657 let order = Order::of(&func);
1658 let live = Live::of(&func, &order);
1659 let assignment = assign(&func, &order, &live, &narrow(1));
1660 assert_eq!(assignment.spilled(), 0);
1661 assert!(crate::check::check(&func, &order, &live, &assignment).is_empty());
1662 }
1663
1664 #[test]
1665 fn a_register_a_clobber_takes_is_the_last_one_offered_rather_than_the_first() {
1666 let func = arms(false);
1667
1668 // With a register to spare the value takes the spare one. Being allowed a register some
1669 // instruction insists on is not the same as it being free: the instruction has to be handed
1670 // it in the end, and what hands it over is a move.
1671 assert_eq!(places(&func, &narrow(3)), ["rcx", "rdx"]);
1672 }
1673
1674 #[test]
1675 fn a_frame_says_what_each_of_its_slots_is_for() {
1676 let mut names = Interner::new();
1677 let mut func = Func::new(names.intern("f"));
1678 let opcode = Opcode::new(names.intern("x64.nop"));
1679 let block = func.create_block();
1680 let first = func.new_vreg(GPR);
1681 let second = func.new_vreg(GPR);
1682 func.build(block, opcode).def(first, GPR).finish();
1683 func.build(block, opcode).def(second, GPR).finish();
1684 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
1685
1686 let order = Order::of(&func);
1687 let live = Live::of(&func, &order);
1688 let assignment = assign(&func, &order, &live, &narrow(1));
1689 assert_eq!(assignment.spilled(), 1);
1690 assert_eq!(assignment.slots(), [GPR]);
1691 // A register that is already a register is where it is, and this has nothing to say about
1692 // it.
1693 assert_eq!(assignment.place(Reg::physical(RCX)), None);
1694 assert_eq!(env().scratch(GPR), [R13, R14, R15]);
1695 }
1696}