rucc_regalloc/rewrite.rs
1//! Making an assignment true in the function it was worked out for.
2//!
3//! Design: `spec/10-backend.md` section 10.4.
4//!
5//! [`crate::assign`] says where every value goes and touches nothing. This is the other half: every
6//! operand is rewritten to the place its value was given, and the moves that the places do not
7//! already say are collected. After it the function names no virtual register and no block asks
8//! for anything, which is the point at which machine IR stops being in SSA form and starts being
9//! something an encoder could read.
10//!
11//! # Why the moves are handed back rather than written
12//!
13//! A move is an instruction, and an instruction has an opcode, and an opcode belongs to a target.
14//! `spec/10-backend.md` section 10.8 says no pipeline crate holds target specific code, so this
15//! crate is not the one that can write `x64.mov`. What it hands back is an [`Edit`]: a move
16//! between two places, the class it is in, and where in the function it goes. `rucc-codegen` turns
17//! each one into whatever its target moves a register with, which for a value on the stack is a
18//! load or a store rather than a move at all.
19//!
20//! The edits at any one place are in the order they have to be made in. That matters in two
21//! places: a spilled operand is read into a scratch register before the instruction that wants it,
22//! and a two address instruction's copy has to come after that read, because what it is copying
23//! may be the thing that was just read in.
24//!
25//! # How many scratch registers one instruction wants
26//!
27//! Two of a class, and a target holds two of each back for exactly this. The instruction that asks
28//! for most reads two values and writes a third with nothing of the three in a register, and the
29//! arithmetic works out because the two reads are what use the two scratch registers and the answer
30//! is written back into one of them. Writing over it destroys nothing, since it holds a copy of a
31//! value whose home is a stack slot and the instruction has already read it, and the answer is
32//! stored away from it afterwards. Giving the answer a scratch register of its own would want a
33//! third, which a program with enough live values around a call reaches, and that was issue #350.
34//!
35//! Which register the answer goes back into depends on what wrote it. A two address instruction
36//! writes the register the operand it reuses was read into, because that is what two address means.
37//! A three address one, which is `lea` and the compare and set pairs, writes a register that is
38//! none of its operands, and there the answer takes the first scratch register of the class again:
39//! the reads are done by the time the write happens, so the two uses of that register do not meet.
40//! Counting the two jobs in one running number is what made a three address instruction with every
41//! end on the stack ask for a third register and abort, which was issue #726.
42//!
43//! It is only a scratch register the answer may have either way. Where the operand a two address
44//! instruction reuses is in a register the assignment gave out, the value in it may be wanted after
45//! the instruction, and the assignment only lets one be written over when it is not, which it says
46//! by giving the answer that register in the first place. So the answer takes a scratch register
47//! there and the two address copy fills it. That one is filled in front of the instruction rather
48//! than by it, so it cannot share with a read, and the count still comes to two, because an operand
49//! that is in a register is not holding a scratch register.
50//!
51//! Deciding either way needs to know where the operand it reuses went, so an operand that reuses
52//! another and has no register of its own is placed in a second pass over the operands.
53//!
54//! The count is per class. An instruction reading a spilled value out of each of two files wants
55//! the first register of each, since a class holds its own back and nothing on the instruction is
56//! in the other's.
57//!
58//! # What happens when two is not enough after all
59//!
60//! Two runs out on an instruction that reads three registers and writes none, because then there is
61//! no answer to fold back into a register an operand arrived in and the arithmetic above has nothing
62//! to work on. The instruction that does this on x86-64 is the indexed store, whose base, index and
63//! value are three registers it only reads, and at `-O0` all three of them can be stack slots. That
64//! is tamnd/rucc#913, and it stopped brotli and cmocka on the first file that held one.
65//!
66//! What answers it is borrowing: a register of the class the instruction has not named is
67//! taken, whatever is in it is put in a slot of the frame in front of the instruction, and it is
68//! brought back behind it. That asks nothing at all of the register, so it does not matter whether
69//! the value in it is wanted afterwards, whether the callee owes it back, or whether an argument
70//! travels in it, which are the three things that make a register held back hard to find. A third
71//! register held back would cost every function in the program one, and on x86-64 the only one
72//! available is `rax`, which is the return value, so the bill would be a move at every return. This
73//! costs two memory accesses at the one instruction that wanted it and a slot most functions never
74//! take.
75//!
76//! # What a fixed register turns into
77//!
78//! A move each way. The assignment deliberately gave the value some other register, so a division
79//! whose dividend has to be in `rax` gets a move into `rax` in front of it and a move out of `rax`
80//! behind it. That is the cost of the rule the assignment follows, and it is the rule that keeps
81//! the `-O0` allocator one pass.
82//!
83//! # What an edge turns into
84//!
85//! The moves that write the block's parameters, in an order they can be made in one at a time,
86//! which is what [`crate::moves`] is for. Where they go depends on the shape of the edge. A block
87//! with one successor puts them at its own end, in front of the branch it finishes with, and a
88//! block with several puts them at the start of the block the edge goes to, which is safe exactly
89//! because that block has no other predecessor. An edge that is critical has neither place to put
90//! them and has to have been split before allocation ran, which this checks rather than assumes.
91//!
92//! An edge is also the one place a value can be asked to go from one stack slot to another, which
93//! happens when a spilled value is passed to a parameter that was itself spilled. No machine here
94//! has that instruction, so the move goes through a register, and the register is a second scratch
95//! rather than the one the ordering may be holding a value in for the length of a cycle. Expanding
96//! it here rather than leaving it to the target is the same decision as everything else in this
97//! file: a move through a temporary is a fact about places, and which register is free to be the
98//! temporary is a fact only this crate has.
99
100use rucc_mir::{Block, Constraint, Func, Inst, Operand, Param, Reg, Role};
101use rucc_target::{PhysReg, RegClass};
102
103use crate::assign::{Assignment, Env, Place};
104use crate::moves::{self, Move};
105
106/// One move the places did not already make true.
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub struct Edit {
109 /// Where in the function it goes.
110 pub at: At,
111 /// What it moves, and where to.
112 pub mov: Move<Place>,
113 /// The class both places are in, which is what says how wide the move is.
114 pub class: RegClass,
115}
116
117/// Where an edit goes.
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
119pub enum At {
120 /// In front of an instruction, which is where a value it reads is put where it wants it.
121 Before(Inst),
122 /// Behind an instruction, which is where a value it wrote somewhere it insisted on is taken
123 /// away to where it lives.
124 After(Inst),
125 /// At the start of a block, in front of everything in it.
126 StartOf(Block),
127 /// At the end of a block, behind everything in it. Only ever a block with one edge out of
128 /// it, since a block with two puts an edge's moves at the start of the block it goes to.
129 EndOf(Block),
130}
131
132/// Rewrites a function to the places it was given, and says what moves are still wanted.
133///
134/// # Panics
135///
136/// Panics if the entry block has parameters, since there is no edge into it for their moves to go
137/// on and what arrives in a function is the ABI lowering's to say. Panics on a critical edge, on
138/// an edge carrying the wrong number of arguments, and if a class has fewer than two registers on
139/// an edge that moves a spilled value into a spilled parameter, all of which are the caller handing
140/// it something it was told not to.
141///
142/// The assignment is taken by reference and may gain a slot, which is the one the register borrowed
143/// at an instruction with more spilled operands than the class holds registers back for waits in.
144/// The section above says what the borrowing is, and the slot is asked for here rather than planned
145/// before allocation because most functions never want one.
146#[must_use]
147pub fn rewrite(func: &mut Func, assignment: &mut Assignment, env: &Env) -> Vec<Edit> {
148 let blocks: Vec<Block> = func.blocks().collect();
149 assert!(
150 func.entry().is_none_or(|entry| func[entry].params.is_empty()),
151 "what arrives in a function is not a block parameter"
152 );
153
154 let mut edits = Vec::new();
155 let mut spare = Spare::default();
156 for &block in &blocks {
157 let insts: Vec<Inst> = func.insts(block).collect();
158 for inst in insts {
159 instruction(func, assignment, env, &mut spare, inst, &mut edits);
160 }
161 }
162
163 let preds = preds(func, &blocks);
164 for &block in &blocks {
165 edges(func, assignment, env, block, &preds, &mut edits);
166 }
167 for &block in &blocks {
168 func.params_mut(block).clear();
169 for call in func.succs_mut(block) {
170 call.args.clear();
171 }
172 }
173 edits
174}
175
176/// Rewrites one instruction's operands, and says what has to happen either side of it.
177fn instruction(
178 func: &mut Func,
179 assignment: &mut Assignment,
180 env: &Env,
181 spare: &mut Spare,
182 inst: Inst,
183 edits: &mut Vec<Edit>,
184) {
185 let list = func[inst].operands;
186 let mut operands: Vec<Operand> = func[list].to_vec();
187 let mut before = Moves::new();
188 let mut after = Moves::new();
189 let mut taken = Taken::new();
190
191 // Where the assignment put each operand's value, taken before anything is rewritten, since
192 // rewriting an operand is what loses that. The second pass below reads it.
193 let places: Vec<Place> =
194 operands.iter().map(|operand| place(assignment, operand.reg)).collect();
195
196 // A spilled operand that reuses another is left for the second pass, because where it goes
197 // depends on where the operand it reuses went and that is not known until every operand ahead
198 // of it has been placed.
199 let mut reusing: Vec<usize> = Vec::new();
200
201 // Every register the instruction has named for itself, which is one an operand's value is
202 // already in and one a fixed constraint asked for. Taken before anything is rewritten, for the
203 // same reason the places above are: rewriting is what turns an operand's register into a
204 // physical one and loses which of the two it was.
205 let mut claimed = Claimed::default();
206 for (operand, place) in operands.iter().zip(&places) {
207 if let Place::Reg(at) = *place {
208 claimed.named(operand, at);
209 }
210 if let Constraint::Fixed(at) = operand.constraint {
211 claimed.named(operand, at);
212 }
213 }
214 let mut scratch = Scratch::new(env, assignment, spare, claimed);
215
216 for (index, operand) in operands.iter_mut().enumerate() {
217 let fixed = match operand.constraint {
218 Constraint::Fixed(at) => Some(at),
219 _ => None,
220 };
221 let at = match (place(scratch.assignment, operand.reg), fixed) {
222 (Place::Reg(at), None) => at,
223 (Place::Reg(at), Some(fixed)) => {
224 if at != fixed {
225 let (there, here) = (Place::Reg(fixed), Place::Reg(at));
226 push(&mut before, &mut after, operand, Move::new(there, here));
227 }
228 fixed
229 }
230 (Place::Slot(_), None) if matches!(operand.constraint, Constraint::Reuse(_)) => {
231 reusing.push(index);
232 continue;
233 }
234 (Place::Slot(slot), fixed) => {
235 // Which of the two jobs this register is for. An operand the instruction only
236 // writes wants one from the instruction onwards, and an operand it reads wants one
237 // from before the instruction until it reads it, so the same register does both
238 // and the two are counted apart.
239 let at = match fixed {
240 Some(fixed) => fixed,
241 None if operand.role.is_def() => {
242 taken.written_into(operand.class, &mut scratch)
243 }
244 None => taken.read_into(operand.class, &mut scratch),
245 };
246 push(
247 &mut before,
248 &mut after,
249 operand,
250 Move::new(Place::Reg(at), Place::Slot(slot)),
251 );
252 at
253 }
254 };
255 operand.reg = Reg::physical(at);
256 }
257
258 for index in reusing {
259 let Constraint::Reuse(other) = operands[index].constraint else {
260 unreachable!("only an operand that reuses another was left for this pass")
261 };
262 let Place::Slot(slot) = places[index] else {
263 unreachable!("only a spilled operand was left for this pass")
264 };
265 // Where the operand it reuses was read into, if it was read into anywhere. A scratch
266 // register holds a copy of a value that lives on the stack, so writing over it destroys
267 // nothing and the instruction can have it. A register the assignment gave out is a
268 // different matter: the value in it may be wanted after the instruction, and the
269 // assignment only lets one be written over when it is not, which it says by giving the
270 // answer that register. So a fresh scratch register there, and the copy below fills it.
271 //
272 // Either way this shape wants two of the class and no more. If the operand it reuses is on
273 // the stack then it is holding one of them already, and if it is not then it is not
274 // holding one at all.
275 //
276 // This one is asked for as a read even though the instruction writes it, because the copy
277 // that fills it goes in front of the instruction. It is live from there, which is the same
278 // span a value read in off the stack is live for, so it cannot share with one.
279 let other = usize::from(other);
280 let at = match places[other] {
281 Place::Slot(_) => phys(operands[other].reg),
282 Place::Reg(_) => taken.read_into(operands[index].class, &mut scratch),
283 };
284 push(
285 &mut before,
286 &mut after,
287 &operands[index],
288 Move::new(Place::Reg(at), Place::Slot(slot)),
289 );
290 operands[index].reg = Reg::physical(at);
291 }
292
293 // A two address instruction writes one of the registers it reads, and the copy that makes that
294 // true goes after everything else in front of the instruction, since what it reads may be a
295 // value that was itself only just read in from the stack.
296 for index in 0..operands.len() {
297 let Constraint::Reuse(other) = operands[index].constraint else { continue };
298 let (to, from) = (operands[index], operands[usize::from(other)]);
299 if to.reg != from.reg {
300 let mov = Move::new(Place::Reg(phys(to.reg)), Place::Reg(phys(from.reg)));
301 before.push((mov, to.class));
302 }
303 }
304
305 // A borrowed register is put away in front of everything else and brought back behind
306 // everything else, since what happens in between is the instruction using it and the moves
307 // that carry its operands in and out. Nothing borrowed at one instruction is still borrowed at
308 // the next, which is what lets the slot be shared.
309 let (saves, restores) = scratch.finish();
310
311 func[list].copy_from_slice(&operands);
312 edits.extend(saves.into_iter().map(|(mov, class)| Edit { at: At::Before(inst), mov, class }));
313 edits.extend(before.into_iter().map(|(mov, class)| Edit { at: At::Before(inst), mov, class }));
314 edits.extend(after.into_iter().map(|(mov, class)| Edit { at: At::After(inst), mov, class }));
315 edits.extend(restores.into_iter().map(|(mov, class)| Edit { at: At::After(inst), mov, class }));
316}
317
318/// How many scratch registers of each class one instruction has been handed, in each of the two
319/// jobs they do.
320///
321/// Counted per class rather than in one running number, because the classes hold their own back
322/// and an instruction reading a spilled value out of each of two files would otherwise skip the
323/// first register of the second file for no reason.
324///
325/// Counted per job as well, and that is the part that keeps the count down. A register a spilled
326/// value is read into is live from in front of the instruction until the instruction reads it. A
327/// register the instruction writes its answer into is live from the instruction until the store
328/// behind it. Those two spans do not meet, so one register does both jobs and the counting starts
329/// again rather than carrying on. What that rests on is the machine reading its operands before it
330/// writes its answer, which is true of every instruction the backends here emit and is the same
331/// thing that makes `addq %rax, %rax` mean what it looks like.
332///
333/// Where the count runs out is an instruction that reads three registers and writes none, because
334/// then there is no answer to fold back into a register an operand arrived in and the trick above
335/// has nothing to work on. On x86-64 that instruction is the indexed store, whose base, index and
336/// value are three registers it only reads, and at `-O0` all three of them can be stack slots. That
337/// is tamnd/rucc#913, and what answers it is [`Scratch::borrow`] rather than a third register held
338/// back, since holding a third back costs every function a register and this costs only the
339/// instruction that wanted one.
340#[derive(Debug, Default)]
341struct Taken {
342 /// How many of each class hold a value read in ahead of the instruction.
343 read: Vec<usize>,
344 /// How many of each class hold an answer the instruction writes.
345 written: Vec<usize>,
346}
347
348impl Taken {
349 /// Nothing handed out yet.
350 fn new() -> Self {
351 Self::default()
352 }
353
354 /// A register of a class for a value read in ahead of the instruction.
355 fn read_into(&mut self, class: RegClass, scratch: &mut Scratch<'_>) -> PhysReg {
356 Self::take(&mut self.read, class, scratch, Role::Use)
357 }
358
359 /// A register of a class for an answer the instruction writes.
360 fn written_into(&mut self, class: RegClass, scratch: &mut Scratch<'_>) -> PhysReg {
361 Self::take(&mut self.written, class, scratch, Role::Def)
362 }
363
364 /// The next register of a class out of one of the two counts, passing over any the instruction
365 /// has already named itself for a value travelling the same way and borrowing one when the held
366 /// back ones run out.
367 ///
368 /// An operand with a fixed constraint names a register the instruction has to have its value
369 /// in, and the move that puts it there is in the same list as the move that would fill a
370 /// scratch register. So handing the same register out for both would lose one of the two
371 /// values, quietly and at run time. It is passed over instead.
372 ///
373 /// Which way the value travels is what decides whether there is a clash at all, and [`Claimed`]
374 /// says why. A register the instruction only writes is free to carry a value in, which is what a
375 /// call wants: a call names every caller saved register as one it writes, and those are the very
376 /// registers held back for scratch.
377 ///
378 /// A clash comes up on a machine where a register held back is one an instruction can also
379 /// insist on, and on x86-64 the way in is inline assembly naming `r10` or `r11`.
380 fn take(
381 counts: &mut Vec<usize>,
382 class: RegClass,
383 scratch: &mut Scratch<'_>,
384 role: Role,
385 ) -> PhysReg {
386 let index = usize::from(class.number());
387 if counts.len() <= index {
388 counts.resize(index + 1, 0);
389 }
390 let held: &[PhysReg] = scratch.env.scratch(class);
391 while held.get(counts[index]).is_some_and(|®| scratch.claimed.clashes(role, class, reg))
392 {
393 counts[index] += 1;
394 }
395 if let Some(&at) = held.get(counts[index]) {
396 counts[index] += 1;
397 return at;
398 }
399 scratch.borrow(class)
400 }
401}
402
403/// The registers the instruction has named for itself, which scratch has to work around.
404///
405/// A register is kept with the class it was named in, because a register number is only a number
406/// into one file and the same one means a different register in another: a call names sixteen vector
407/// registers numbered nought to fifteen and sixteen general purpose ones numbered the same, and
408/// reading the two lists as one leaves the general purpose file looking entirely spoken for.
409///
410/// Reading and writing are kept apart because they clash with different things. A register a value
411/// arrives in is one no move in front of the instruction may write, and a register an answer leaves
412/// in is one no move behind it may write. A call is the case that makes the difference matter: it
413/// names every caller saved register as one it writes, `r10` and `r11` among them, and an indirect
414/// call through a pointer on the stack has to read that pointer into one of exactly those two.
415#[derive(Debug, Default)]
416struct Claimed {
417 /// The registers a value arrives in, with the class each was named in.
418 reads: Vec<(RegClass, PhysReg)>,
419 /// The registers an answer leaves in, with the class each was named in.
420 writes: Vec<(RegClass, PhysReg)>,
421}
422
423impl Claimed {
424 /// Records a register an operand named, on the side its value travels.
425 fn named(&mut self, operand: &Operand, at: PhysReg) {
426 self.side_mut(operand.role).push((operand.class, at));
427 }
428
429 /// Records a register nothing may be handed for the rest of the instruction, which is one
430 /// [`Scratch::borrow`] has just taken.
431 fn taken(&mut self, class: RegClass, at: PhysReg) {
432 self.reads.push((class, at));
433 self.writes.push((class, at));
434 }
435
436 /// Whether handing that register out for a value travelling that way would lose a value.
437 fn clashes(&self, role: Role, class: RegClass, at: PhysReg) -> bool {
438 self.side(role).contains(&(class, at))
439 }
440
441 /// Whether the instruction names that register at all, which is what borrowing has to keep off:
442 /// what is borrowed is put back behind the instruction, over anything left there.
443 fn names(&self, class: RegClass, at: PhysReg) -> bool {
444 self.reads.contains(&(class, at)) || self.writes.contains(&(class, at))
445 }
446
447 /// The list for values travelling that way. The lists are one instruction's long, so a scan
448 /// beats a set.
449 fn side(&self, role: Role) -> &Vec<(RegClass, PhysReg)> {
450 if role.is_def() { &self.writes } else { &self.reads }
451 }
452
453 /// The same, to write to.
454 fn side_mut(&mut self, role: Role) -> &mut Vec<(RegClass, PhysReg)> {
455 if role.is_def() { &mut self.writes } else { &mut self.reads }
456 }
457}
458
459/// Moves waiting to be filed, each with the class of the value it moves.
460///
461/// The class travels with the move because an [`Edit`] carries one and the consumer needs it to pick
462/// the instruction that does the move, and by the time a move is filed the operand it came from is
463/// out of reach.
464type Moves = Vec<(Move<Place>, RegClass)>;
465
466/// The frame slots a borrowed register's value waits in, one list per class.
467///
468/// They belong to the function rather than to an instruction, because a borrowed register is given
469/// back before the next instruction starts and the slot is dead in between, so one slot serves
470/// every instruction in the function that borrows. Most functions never take one at all.
471type Spare = Vec<Vec<u32>>;
472
473/// What it takes to hand a register to one instruction.
474///
475/// It is a struct rather than four arguments because [`Scratch::borrow`] writes to all of them at
476/// once: it reads the environment, takes a slot off the assignment, remembers the register so a
477/// second borrow at the same instruction does not land on it, and files the two moves that make it
478/// safe.
479struct Scratch<'a> {
480 env: &'a Env,
481 /// Where every value went, and where a slot for a borrowed register comes from.
482 assignment: &'a mut Assignment,
483 /// The function's slots for borrowed registers, reused at every instruction.
484 spare: &'a mut Spare,
485 /// Every register the instruction has named, and then every one borrowed here as it is borrowed.
486 claimed: Claimed,
487 /// How many of each class have been borrowed at this instruction, which says which slot the
488 /// next one uses.
489 borrowed: Vec<usize>,
490 /// The moves that put a borrowed register's value away, which go in front of everything else.
491 saves: Moves,
492 /// The moves that bring it back, which go behind everything else.
493 restores: Moves,
494}
495
496impl<'a> Scratch<'a> {
497 /// Nothing borrowed yet at an instruction claiming those registers.
498 fn new(
499 env: &'a Env,
500 assignment: &'a mut Assignment,
501 spare: &'a mut Spare,
502 claimed: Claimed,
503 ) -> Self {
504 Self {
505 env,
506 assignment,
507 spare,
508 claimed,
509 borrowed: Vec::new(),
510 saves: Vec::new(),
511 restores: Vec::new(),
512 }
513 }
514
515 /// A register of the class the instruction is not using, with whatever is in it put away in
516 /// front of the instruction and brought back behind it.
517 ///
518 /// This is what a class runs out to, and it works on any machine because it asks nothing at all
519 /// of the register it takes. Whatever was in it is somewhere else for the length of one
520 /// instruction, so it does not matter whether that value is wanted afterwards, whether the
521 /// callee owes the register back, or whether an argument travels in it, which are the three
522 /// things that make a register held back hard to find. What it costs is two memory accesses at
523 /// the one instruction that wanted it and one slot of the frame, against a register taken off
524 /// every function in the program, and `rucc_codegen::pipeline` says why that trade goes this
525 /// way round on x86-64.
526 ///
527 /// The register is any of the class the instruction has not claimed for itself. A register the
528 /// allocator gave a value that is live right across the instruction is as good as an idle one,
529 /// which is the whole point of putting the contents away first.
530 ///
531 /// # Panics
532 ///
533 /// Panics if the class has no register the instruction has not already claimed, which is an
534 /// instruction naming every register of a file at once.
535 fn borrow(&mut self, class: RegClass) -> PhysReg {
536 let index = usize::from(class.number());
537 let at = *self
538 .env
539 .order(class)
540 .iter()
541 .find(|&®| !self.claimed.names(class, reg))
542 .expect("an instruction naming every register of its class at once");
543
544 if self.borrowed.len() <= index {
545 self.borrowed.resize(index + 1, 0);
546 }
547 if self.spare.len() <= index {
548 self.spare.resize(index + 1, Vec::new());
549 }
550 let nth = self.borrowed[index];
551 if self.spare[index].len() <= nth {
552 let slot = self.assignment.take_slot(class);
553 self.spare[index].push(slot);
554 }
555 let slot = self.spare[index][nth];
556
557 self.borrowed[index] = nth + 1;
558 self.claimed.taken(class, at);
559 self.saves.push((Move::new(Place::Slot(slot), Place::Reg(at)), class));
560 self.restores.push((Move::new(Place::Reg(at), Place::Slot(slot)), class));
561 at
562 }
563
564 /// The moves either side of the instruction, once every register has been handed out.
565 fn finish(self) -> (Moves, Moves) {
566 (self.saves, self.restores)
567 }
568}
569
570/// Files a move in front of the instruction or behind it, and turns it round for a value the
571/// instruction writes, since that one travels the other way.
572fn push(before: &mut Moves, after: &mut Moves, operand: &Operand, mov: Move<Place>) {
573 if operand.role.is_def() {
574 after.push((Move::new(mov.from, mov.to), operand.class));
575 } else {
576 before.push((mov, operand.class));
577 }
578}
579
580/// The moves the edges out of a block turn into.
581fn edges(
582 func: &mut Func,
583 assignment: &Assignment,
584 env: &Env,
585 block: Block,
586 preds: &[usize],
587 edits: &mut Vec<Edit>,
588) {
589 let succs = func[block].succs.clone();
590 let single = succs.len() == 1;
591 for call in &succs {
592 let params = func[call.block].params.clone();
593 assert_eq!(
594 params.len(),
595 call.args.len(),
596 "an edge carries what the block it goes to asks for"
597 );
598 if params.is_empty() {
599 continue;
600 }
601 assert!(
602 single || preds[call.block.index()] == 1,
603 "a critical edge has nowhere to put its moves and has to be split before allocation"
604 );
605 let at = if single { At::EndOf(block) } else { At::StartOf(call.block) };
606 edits.extend(edge(assignment, env, ¶ms, &call.args, at));
607 }
608}
609
610/// The moves one edge turns into, in the order they can be made in.
611fn edge(assignment: &Assignment, env: &Env, params: &[Param], args: &[Reg], at: At) -> Vec<Edit> {
612 let mut classes: Vec<RegClass> = params.iter().map(|param| param.class).collect();
613 classes.sort_unstable();
614 classes.dedup();
615
616 let mut edits = Vec::new();
617 for class in classes {
618 // One class at a time, because a scratch register is per class and a value never crosses
619 // from one to another on an edge.
620 let parallel: Vec<Move<Place>> = params
621 .iter()
622 .zip(args)
623 .filter(|(param, _)| param.class == class)
624 .map(|(param, &arg)| Move::new(place(assignment, param.reg), place(assignment, arg)))
625 .collect();
626 let scratch = env.scratch(class);
627 let cycle = *scratch
628 .first()
629 .expect("a class whose values are passed on an edge and which has no scratch register");
630 for mov in moves::sequence(¶llel, Place::Reg(cycle)) {
631 match (mov.to, mov.from) {
632 // No machine here moves one piece of memory into another, so the value goes
633 // through a register, and it is a second scratch rather than the one the ordering
634 // above may be holding a value in for the length of a cycle.
635 (Place::Slot(_), Place::Slot(_)) => {
636 let through = Place::Reg(*scratch.get(1).expect(
637 "a class passing a spilled value to a spilled parameter and having only \
638 one scratch register",
639 ));
640 edits.push(Edit { at, mov: Move::new(through, mov.from), class });
641 edits.push(Edit { at, mov: Move::new(mov.to, through), class });
642 }
643 _ => edits.push(Edit { at, mov, class }),
644 }
645 }
646 }
647 edits
648}
649
650/// How many edges arrive in each block.
651fn preds(func: &Func, blocks: &[Block]) -> Vec<usize> {
652 let mut preds = vec![0; func.block_count()];
653 for &block in blocks {
654 for call in &func[block].succs {
655 preds[call.block.index()] += 1;
656 }
657 }
658 preds
659}
660
661/// Where a register is, whether the allocator put it there or it was already somewhere.
662fn place(assignment: &Assignment, reg: Reg) -> Place {
663 assignment.place(reg).unwrap_or_else(|| Place::Reg(phys(reg)))
664}
665
666/// The physical register a register is, once it has to be one.
667fn phys(reg: Reg) -> PhysReg {
668 reg.phys().expect("a register the assignment says nothing about and that is not a register")
669}
670
671#[cfg(test)]
672mod tests {
673 use rucc_base::Interner;
674 use rucc_mir::{BlockCall, Opcode};
675 use rucc_target::x86_64::{GPR, RAX, RCX, RDX, REGS, RSI, SYSV, XMM};
676
677 use super::*;
678 use crate::assign::assign;
679 use crate::live::Live;
680 use crate::order::Order;
681
682 /// The x86-64 environment, with the last three of the allocation order held back as scratch.
683 fn env() -> Env {
684 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
685 Env::new().with(GPR, order, scratch)
686 }
687
688 /// An environment with that many general purpose registers and two scratch after them.
689 fn narrow(count: usize) -> Env {
690 Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 2])
691 }
692
693 /// What a place is called, which is what an assertion reads.
694 ///
695 /// The class comes in because a register is a number within its class and the two files here
696 /// number from zero, so nothing but the class tells `rcx` from `xmm1`.
697 fn named(class: RegClass, place: Place) -> String {
698 match place {
699 Place::Reg(reg) => REGS.name(class, reg).expect("a register").to_string(),
700 Place::Slot(slot) => format!("slot{slot}"),
701 }
702 }
703
704 /// Runs both halves and reports the edits as lines an assertion can read.
705 fn run(func: &mut Func, env: &Env) -> Vec<String> {
706 let order = Order::of(func);
707 let live = Live::of(func, &order);
708 let mut assignment = assign(func, &order, &live, env);
709 rewrite(func, &mut assignment, env)
710 .into_iter()
711 .map(|edit| {
712 let at = match edit.at {
713 At::Before(inst) => format!("before {}", inst.index()),
714 At::After(inst) => format!("after {}", inst.index()),
715 At::StartOf(block) => format!("start of {}", block.index()),
716 At::EndOf(block) => format!("end of {}", block.index()),
717 };
718 format!(
719 "{at}: {} = {}",
720 named(edit.class, edit.mov.to),
721 named(edit.class, edit.mov.from)
722 )
723 })
724 .collect()
725 }
726
727 /// The registers an instruction's operands ended up naming.
728 fn operands(func: &Func, inst: Inst) -> Vec<String> {
729 func[func[inst].operands]
730 .iter()
731 .map(|operand| named(operand.class, Place::Reg(phys(operand.reg))))
732 .collect()
733 }
734
735 #[test]
736 fn every_operand_ends_up_naming_the_register_its_value_was_given() {
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 first = func.new_vreg(GPR);
742 let second = func.new_vreg(GPR);
743 func.build(block, opcode).def(first, GPR).finish();
744 func.build(block, opcode).def(second, GPR).finish();
745 let read = func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
746
747 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
748 assert_eq!(operands(&func, read), ["rax", "rcx"]);
749 }
750
751 #[test]
752 fn a_register_an_instruction_insists_on_costs_nothing_when_the_values_can_have_it() {
753 let mut names = Interner::new();
754 let mut func = Func::new(names.intern("f"));
755 let opcode = Opcode::new(names.intern("x64.nop"));
756 let block = func.create_block();
757 let dividend = func.new_vreg(GPR);
758 let quotient = func.new_vreg(GPR);
759 func.build(block, opcode).def(dividend, GPR).finish();
760 let divide = func
761 .build(block, opcode)
762 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
763 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
764 .finish();
765 func.build(block, opcode).uses(quotient, GPR).finish();
766
767 // Nothing either side of the division. The dividend is read out of `rax` for the last
768 // time and the quotient is written into it afterwards, so both of them live there and the
769 // moves that used to carry the value in and the answer out are not written.
770 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
771 assert_eq!(operands(&func, divide), ["rax", "rax"]);
772 }
773
774 #[test]
775 fn a_register_an_instruction_insists_on_is_moved_into_when_the_value_cannot_have_it() {
776 let mut names = Interner::new();
777 let mut func = Func::new(names.intern("f"));
778 let opcode = Opcode::new(names.intern("x64.nop"));
779 let block = func.create_block();
780 let dividend = func.new_vreg(GPR);
781 let quotient = func.new_vreg(GPR);
782 func.build(block, opcode).def(dividend, GPR).finish();
783 let divide = func
784 .build(block, opcode)
785 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
786 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
787 .finish();
788 func.build(block, opcode).uses(quotient, GPR).finish();
789 func.build(block, opcode).uses(dividend, GPR).finish();
790
791 // This time the dividend is wanted after the division, so it cannot be in the register the
792 // division writes and the value is moved in. The answer still comes out of `rax` without
793 // a move, which is the half of it the hint bought.
794 assert_eq!(run(&mut func, &env()), ["before 1: rax = rcx"]);
795 assert_eq!(operands(&func, divide), ["rax", "rax"]);
796 }
797
798 #[test]
799 fn a_two_address_instruction_that_did_not_get_its_register_copies_first() {
800 let mut names = Interner::new();
801 let mut func = Func::new(names.intern("f"));
802 let opcode = Opcode::new(names.intern("x64.nop"));
803 let block = func.create_block();
804 let left = func.new_vreg(GPR);
805 let right = func.new_vreg(GPR);
806 let sum = func.new_vreg(GPR);
807 func.build(block, opcode).def(left, GPR).finish();
808 func.build(block, opcode).def(right, GPR).finish();
809 let add = func
810 .build(block, opcode)
811 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
812 .uses(left, GPR)
813 .uses(right, GPR)
814 .finish();
815 func.build(block, opcode).uses(left, GPR).finish();
816
817 // The left value is wanted afterwards, so the answer could not have its register and the
818 // copy in front of the addition is what makes the instruction two address.
819 assert_eq!(run(&mut func, &env()), ["before 2: rdx = rax"]);
820 assert_eq!(operands(&func, add), ["rdx", "rax", "rcx"]);
821 }
822
823 #[test]
824 fn a_two_address_instruction_that_did_get_its_register_copies_nothing() {
825 let mut names = Interner::new();
826 let mut func = Func::new(names.intern("f"));
827 let opcode = Opcode::new(names.intern("x64.nop"));
828 let block = func.create_block();
829 let left = func.new_vreg(GPR);
830 let right = func.new_vreg(GPR);
831 let sum = func.new_vreg(GPR);
832 func.build(block, opcode).def(left, GPR).finish();
833 func.build(block, opcode).def(right, GPR).finish();
834 let add = func
835 .build(block, opcode)
836 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
837 .uses(left, GPR)
838 .uses(right, GPR)
839 .finish();
840 func.build(block, opcode).uses(right, GPR).finish();
841
842 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
843 assert_eq!(operands(&func, add), ["rax", "rax", "rcx"]);
844 }
845
846 #[test]
847 fn a_spilled_value_is_read_into_a_scratch_register_at_each_instruction_that_wants_it() {
848 let mut names = Interner::new();
849 let mut func = Func::new(names.intern("f"));
850 let opcode = Opcode::new(names.intern("x64.nop"));
851 let block = func.create_block();
852 let first = func.new_vreg(GPR);
853 let second = func.new_vreg(GPR);
854 func.build(block, opcode).def(first, GPR).finish();
855 func.build(block, opcode).def(second, GPR).finish();
856 let read = func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
857
858 // One register between two values, so one of them goes to the stack. It is written there
859 // where it is computed and read back where it is wanted, and both ends of that go through
860 // the scratch register that is held out of the allocation order for exactly this.
861 assert_eq!(run(&mut func, &narrow(1)), ["after 1: slot0 = rcx", "before 2: rcx = slot0"]);
862 assert_eq!(operands(&func, read), ["rax", "rcx"]);
863 }
864
865 /// A two address instruction with nothing in a register is two scratch registers and not three.
866 ///
867 /// The answer has no register of its own to be in, so what it is written into is whichever one
868 /// the operand it reuses was read into, and it is stored away from there afterwards. Handing it
869 /// a scratch register of its own would want a third, and a class holds two back, which is issue
870 /// #350: a program with enough live values around a call reached it and the compiler aborted.
871 #[test]
872 fn a_two_address_instruction_whose_answer_and_operands_are_all_spilled_wants_two_registers() {
873 let mut names = Interner::new();
874 let mut func = Func::new(names.intern("f"));
875 let opcode = Opcode::new(names.intern("x64.nop"));
876 let block = func.create_block();
877 let keeper = func.new_vreg(GPR);
878 let left = func.new_vreg(GPR);
879 let right = func.new_vreg(GPR);
880 let sum = func.new_vreg(GPR);
881 func.build(block, opcode).def(keeper, GPR).finish();
882 func.build(block, opcode)
883 .operand(Operand::write(left, GPR).with(Constraint::Stack))
884 .finish();
885 func.build(block, opcode)
886 .operand(Operand::write(right, GPR).with(Constraint::Stack))
887 .finish();
888 let add = func
889 .build(block, opcode)
890 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
891 .uses(left, GPR)
892 .uses(right, GPR)
893 .finish();
894 func.build(block, opcode).uses(keeper, GPR).finish();
895 func.build(block, opcode).uses(sum, GPR).finish();
896
897 // Both operands are read in, the answer is written into the register the operand it
898 // reuses arrived in, and it is stored away from there. Two scratch registers, which is
899 // what the class holds back. Asking for one of its own would be a third and would abort.
900 assert_eq!(
901 run(&mut func, &narrow(1)),
902 [
903 "after 1: slot0 = rcx",
904 "after 2: slot1 = rcx",
905 "before 3: rcx = slot0",
906 "before 3: rdx = slot1",
907 "after 3: slot2 = rcx",
908 "before 5: rcx = slot2",
909 ]
910 );
911 assert_eq!(operands(&func, add), ["rcx", "rcx", "rdx"]);
912 }
913
914 /// A three address instruction with nothing in a register is two scratch registers, not three.
915 ///
916 /// The case #726 aborted on. `x64.lea_64` and the `x64.cmp_set_*` family read two values and
917 /// write a third that is neither of them, and when all three ends are on the stack there are
918 /// three operands wanting a register at one instruction. Counting them in one running number
919 /// asks for a third scratch register and the class holds two back.
920 ///
921 /// Two is enough because the answer's register is not wanted until the instruction writes it,
922 /// by which time the registers the operands were read into have been read. So the answer goes
923 /// back into the first of them and is stored away from there.
924 #[test]
925 fn a_three_address_instruction_whose_answer_and_operands_are_all_spilled_wants_two_registers() {
926 let mut names = Interner::new();
927 let mut func = Func::new(names.intern("f"));
928 let opcode = Opcode::new(names.intern("x64.nop"));
929 let block = func.create_block();
930 let keeper = func.new_vreg(GPR);
931 let base = func.new_vreg(GPR);
932 let index = func.new_vreg(GPR);
933 let address = func.new_vreg(GPR);
934 func.build(block, opcode).def(keeper, GPR).finish();
935 func.build(block, opcode)
936 .operand(Operand::write(base, GPR).with(Constraint::Stack))
937 .finish();
938 func.build(block, opcode)
939 .operand(Operand::write(index, GPR).with(Constraint::Stack))
940 .finish();
941 let lea =
942 func.build(block, opcode).def(address, GPR).uses(base, GPR).uses(index, GPR).finish();
943 func.build(block, opcode).uses(keeper, GPR).finish();
944 func.build(block, opcode).uses(address, GPR).finish();
945
946 // Both operands are read in, the answer is written into the first of the two registers
947 // they arrived in, and it is stored away from there. Two, which is what the class holds.
948 assert_eq!(
949 run(&mut func, &narrow(1)),
950 [
951 "after 1: slot0 = rcx",
952 "after 2: slot1 = rcx",
953 "before 3: rcx = slot0",
954 "before 3: rdx = slot1",
955 "after 3: slot2 = rcx",
956 "before 5: rcx = slot2",
957 ]
958 );
959 assert_eq!(operands(&func, lea), ["rcx", "rcx", "rdx"]);
960 }
961
962 /// A spilled answer takes a scratch register where the operand it reuses is in a real one.
963 ///
964 /// The value in that register may be wanted after the instruction, and the assignment is the
965 /// only thing that knows whether it is. It says so by giving the answer that register, and here
966 /// it did not, so writing over it would destroy a value. The count still comes to two, because
967 /// an operand that is in a register is not holding a scratch register.
968 #[test]
969 fn a_spilled_answer_does_not_write_over_a_register_the_assignment_gave_to_something_else() {
970 let mut names = Interner::new();
971 let mut func = Func::new(names.intern("f"));
972 let opcode = Opcode::new(names.intern("x64.nop"));
973 let block = func.create_block();
974 let left = func.new_vreg(GPR);
975 let right = func.new_vreg(GPR);
976 let sum = func.new_vreg(GPR);
977 func.build(block, opcode).def(left, GPR).finish();
978 func.build(block, opcode)
979 .operand(Operand::write(right, GPR).with(Constraint::Stack))
980 .finish();
981 let add = func
982 .build(block, opcode)
983 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
984 .uses(left, GPR)
985 .uses(right, GPR)
986 .finish();
987 func.build(block, opcode).uses(left, GPR).finish();
988 func.build(block, opcode).uses(sum, GPR).finish();
989
990 // The left value is in `rax` and is read again afterwards, so the answer is copied into a
991 // scratch register and written there instead.
992 assert_eq!(
993 run(&mut func, &narrow(1)),
994 [
995 "after 1: slot0 = rcx",
996 "before 2: rcx = slot0",
997 "before 2: rdx = rax",
998 "after 2: slot1 = rdx",
999 "before 4: rcx = slot1",
1000 ]
1001 );
1002 assert_eq!(operands(&func, add), ["rdx", "rax", "rcx"]);
1003 }
1004
1005 /// The count of scratch registers handed out is per class and not one number for all of them.
1006 ///
1007 /// An instruction reading a spilled value out of each of two files wants the first register of
1008 /// each, since the files hold their own back and nothing on the instruction is in the other's.
1009 #[test]
1010 fn an_instruction_reading_out_of_two_files_takes_the_first_scratch_register_of_each() {
1011 let mut names = Interner::new();
1012 let mut func = Func::new(names.intern("f"));
1013 let opcode = Opcode::new(names.intern("x64.nop"));
1014 let block = func.create_block();
1015 let integer = func.new_vreg(GPR);
1016 let number = func.new_vreg(XMM);
1017 let spare = func.new_vreg(GPR);
1018 let other = func.new_vreg(XMM);
1019 func.build(block, opcode).def(integer, GPR).finish();
1020 func.build(block, opcode).def(number, XMM).finish();
1021 func.build(block, opcode).def(spare, GPR).finish();
1022 func.build(block, opcode).def(other, XMM).finish();
1023 func.build(block, opcode).uses(integer, GPR).uses(number, XMM).finish();
1024 let read = func.build(block, opcode).uses(spare, GPR).uses(other, XMM).finish();
1025
1026 // One register in each file, so the value of each that is wanted later goes to the stack
1027 // and is read back at the instruction that wants it.
1028 let env = Env::new().with(GPR, &SYSV.int_order[..1], &SYSV.int_order[1..3]).with(
1029 XMM,
1030 &SYSV.sse_order[..1],
1031 &SYSV.sse_order[1..3],
1032 );
1033 assert_eq!(
1034 run(&mut func, &env),
1035 [
1036 "after 2: slot0 = rcx",
1037 "after 3: slot1 = xmm1",
1038 "before 5: rcx = slot0",
1039 "before 5: xmm1 = slot1",
1040 ]
1041 );
1042 assert_eq!(operands(&func, read), ["rcx", "xmm1"]);
1043 }
1044
1045 #[test]
1046 fn an_edge_out_of_a_block_with_one_way_to_go_moves_at_the_end_of_it() {
1047 let mut names = Interner::new();
1048 let mut func = Func::new(names.intern("f"));
1049 let opcode = Opcode::new(names.intern("x64.nop"));
1050 let head = func.create_block();
1051 let tail = func.create_block();
1052 let held = func.new_vreg(GPR);
1053 let carried = func.new_vreg(GPR);
1054 func.build(head, opcode).def(held, GPR).finish();
1055 func.build(head, opcode).def(carried, GPR).finish();
1056 func.build(head, opcode).uses(held, GPR).finish();
1057 let param = func.append_param(tail, GPR);
1058 *func.succs_mut(head) = vec![BlockCall::with(tail, vec![carried])];
1059 let read = func.build(tail, opcode).uses(param, GPR).finish();
1060
1061 // The value the edge carries is in the second register, because the first was busy where
1062 // the value was written, and the parameter it arrives as is in the first, because by then
1063 // it is not. So the edge is a move, and it goes at the end of the block it leaves.
1064 assert_eq!(run(&mut func, &env()), ["end of 0: rax = rcx"]);
1065 assert_eq!(operands(&func, read), ["rax"]);
1066 // Nothing arrives in a block any more and no edge carries anything, which is where SSA
1067 // form stops.
1068 assert!(func[tail].params.is_empty());
1069 assert!(func[head].succs[0].args.is_empty());
1070 }
1071
1072 #[test]
1073 fn an_edge_out_of_a_block_with_a_choice_moves_at_the_start_of_where_it_goes() {
1074 let mut names = Interner::new();
1075 let mut func = Func::new(names.intern("f"));
1076 let opcode = Opcode::new(names.intern("x64.nop"));
1077 let head = func.create_block();
1078 let left = func.create_block();
1079 let right = func.create_block();
1080 let held = func.new_vreg(GPR);
1081 let carried = func.new_vreg(GPR);
1082 func.build(head, opcode).def(held, GPR).finish();
1083 func.build(head, opcode).def(carried, GPR).finish();
1084 func.build(head, opcode).uses(held, GPR).finish();
1085 let taken = func.append_param(left, GPR);
1086 *func.succs_mut(head) = vec![BlockCall::with(left, vec![carried]), BlockCall::to(right)];
1087 func.build(left, opcode).uses(taken, GPR).finish();
1088
1089 // The move cannot go at the end of the block it leaves, because the other way out of that
1090 // block does not want it. It goes at the start of the block it arrives in, which is safe
1091 // because nothing else arrives there.
1092 assert_eq!(run(&mut func, &env()), ["start of 1: rax = rcx"]);
1093 }
1094
1095 #[test]
1096 fn two_values_that_swap_on_an_edge_get_an_order_and_a_scratch_register() {
1097 let mut names = Interner::new();
1098 let mut func = Func::new(names.intern("f"));
1099 let opcode = Opcode::new(names.intern("x64.nop"));
1100 let head = func.create_block();
1101 let body = func.create_block();
1102 let first = func.new_vreg(GPR);
1103 let second = func.new_vreg(GPR);
1104 func.build(head, opcode).def(first, GPR).finish();
1105 func.build(head, opcode).def(second, GPR).finish();
1106 let left = func.append_param(body, GPR);
1107 let right = func.append_param(body, GPR);
1108 *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
1109 func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
1110 *func.succs_mut(body) = vec![BlockCall::with(body, vec![right, left])];
1111
1112 // The loop hands each value back the other way round, which is the case no order of two
1113 // moves answers, so one of them goes through the scratch register. The edge into the loop
1114 // moves nothing, because each value is already where the parameter it feeds lives.
1115 assert_eq!(
1116 run(&mut func, &env()),
1117 ["end of 1: r13 = rcx", "end of 1: rcx = rax", "end of 1: rax = r13"]
1118 );
1119 }
1120
1121 #[test]
1122 fn a_spilled_value_handed_to_a_spilled_parameter_goes_through_a_register() {
1123 let mut names = Interner::new();
1124 let mut func = Func::new(names.intern("f"));
1125 let opcode = Opcode::new(names.intern("x64.nop"));
1126 let head = func.create_block();
1127 let body = func.create_block();
1128 let first = func.new_vreg(GPR);
1129 let second = func.new_vreg(GPR);
1130 func.build(head, opcode).def(first, GPR).finish();
1131 func.build(head, opcode).def(second, GPR).finish();
1132 let left = func.append_param(body, GPR);
1133 let right = func.append_param(body, GPR);
1134 *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
1135 func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
1136
1137 // One register between the values and the parameters, so a value on the stack is handed to
1138 // a parameter on the stack, and no machine here has that instruction. It goes through the
1139 // second scratch register rather than the first, which is the one the ordering above is
1140 // entitled to be holding a value in.
1141 assert_eq!(
1142 run(&mut func, &narrow(1)),
1143 [
1144 "after 1: slot0 = rcx",
1145 "before 2: rcx = slot1",
1146 "end of 0: rdx = slot0",
1147 "end of 0: slot1 = rdx",
1148 ]
1149 );
1150 }
1151
1152 /// Three values read and none written wants a third register, which is tamnd/rucc#913.
1153 ///
1154 /// There is no answer here to fold back into the register an operand arrived in, so the trick
1155 /// that keeps a two address instruction down to two has nothing to work on and each of the
1156 /// three wants a register of its own. The instruction is the indexed store: `a[i] = v` reads a
1157 /// base, an index and a value, and at `-O0`, where nothing is coalesced, all three of them are
1158 /// stack slots. The rewriter aborted on it, which stopped brotli and cmocka on the first file
1159 /// that held one and sqlite3 on `fts5Init`.
1160 ///
1161 /// The third register is borrowed rather than held back, and the borrowing is what this is
1162 /// really about: it takes a register the allocator gave to a value that is live right across
1163 /// the instruction, which is safe because that value is put in a slot in front of the
1164 /// instruction and brought back behind it.
1165 #[test]
1166 fn an_instruction_reading_three_spilled_values_borrows_a_register_for_the_third() {
1167 let mut names = Interner::new();
1168 let mut func = Func::new(names.intern("f"));
1169 let opcode = Opcode::new(names.intern("x64.nop"));
1170 let block = func.create_block();
1171 let keeper = func.new_vreg(GPR);
1172 let base = func.new_vreg(GPR);
1173 let index = func.new_vreg(GPR);
1174 let value = func.new_vreg(GPR);
1175 func.build(block, opcode).def(keeper, GPR).finish();
1176 for reg in [base, index, value] {
1177 func.build(block, opcode)
1178 .operand(Operand::write(reg, GPR).with(Constraint::Stack))
1179 .finish();
1180 }
1181 let store =
1182 func.build(block, opcode).uses(base, GPR).uses(index, GPR).uses(value, GPR).finish();
1183 func.build(block, opcode).uses(keeper, GPR).finish();
1184
1185 assert_eq!(
1186 run(&mut func, &narrow(2)),
1187 [
1188 "after 1: slot0 = rdx",
1189 "after 2: slot1 = rdx",
1190 "after 3: slot2 = rdx",
1191 "before 4: slot3 = rax",
1192 "before 4: rdx = slot0",
1193 "before 4: rsi = slot1",
1194 "before 4: rax = slot2",
1195 "after 4: rax = slot3",
1196 ]
1197 );
1198 assert_eq!(operands(&func, store), ["rdx", "rsi", "rax"]);
1199 }
1200
1201 /// A register the instruction only writes still carries a value in.
1202 ///
1203 /// A call names every caller saved register as one it writes, and on x86-64 the two held back for
1204 /// scratch are both caller saved, so an indirect call through a pointer on the stack has nowhere
1205 /// to read the pointer into unless a register named only on the way out is still free on the way
1206 /// in. Reading them as spoken for stopped cmocka on its first file.
1207 #[test]
1208 fn a_register_the_instruction_only_writes_still_carries_a_value_in() {
1209 let mut names = Interner::new();
1210 let mut func = Func::new(names.intern("f"));
1211 let opcode = Opcode::new(names.intern("x64.nop"));
1212 let block = func.create_block();
1213 let target = func.new_vreg(GPR);
1214 func.build(block, opcode)
1215 .operand(Operand::write(target, GPR).with(Constraint::Stack))
1216 .finish();
1217 let call = func
1218 .build(block, opcode)
1219 .def(Reg::physical(RDX), GPR)
1220 .def(Reg::physical(RSI), GPR)
1221 .uses(target, GPR)
1222 .finish();
1223
1224 assert_eq!(run(&mut func, &narrow(2)), ["after 0: slot0 = rdx", "before 1: rdx = slot0"]);
1225 assert_eq!(operands(&func, call), ["rdx", "rsi", "rdx"]);
1226 }
1227
1228 /// A scratch register the instruction has already named for itself is passed over.
1229 ///
1230 /// The move that carries a value into a register a fixed constraint asks for and the move that
1231 /// fills a scratch register both go in front of the instruction, so handing the same register
1232 /// out twice would lose one of the two values without anything saying so. On x86-64 the way
1233 /// into this is inline assembly naming `r10` or `r11`, which are the two the file holds back.
1234 #[test]
1235 fn a_register_the_instruction_already_named_is_not_handed_out_as_scratch() {
1236 let mut names = Interner::new();
1237 let mut func = Func::new(names.intern("f"));
1238 let opcode = Opcode::new(names.intern("x64.nop"));
1239 let block = func.create_block();
1240 let wanted = func.new_vreg(GPR);
1241 let other = func.new_vreg(GPR);
1242 for reg in [wanted, other] {
1243 func.build(block, opcode)
1244 .operand(Operand::write(reg, GPR).with(Constraint::Stack))
1245 .finish();
1246 }
1247 let read = func
1248 .build(block, opcode)
1249 .operand(Operand::read(wanted, GPR).with(Constraint::Fixed(RCX)))
1250 .uses(other, GPR)
1251 .finish();
1252
1253 // `rcx` is both the first scratch register here and the one the instruction insists on, so
1254 // the value it did not ask for by name starts at the second one instead.
1255 assert_eq!(
1256 run(&mut func, &narrow(1)),
1257 [
1258 "after 0: slot0 = rcx",
1259 "after 1: slot1 = rcx",
1260 "before 2: rcx = slot0",
1261 "before 2: rdx = slot1"
1262 ]
1263 );
1264 assert_eq!(operands(&func, read), ["rcx", "rdx"]);
1265 }
1266
1267 #[test]
1268 #[should_panic(expected = "a critical edge has nowhere to put its moves")]
1269 fn a_critical_edge_is_refused() {
1270 let mut names = Interner::new();
1271 let mut func = Func::new(names.intern("f"));
1272 let opcode = Opcode::new(names.intern("x64.nop"));
1273 let head = func.create_block();
1274 let other = func.create_block();
1275 let join = func.create_block();
1276 let value = func.new_vreg(GPR);
1277 func.build(head, opcode).def(value, GPR).finish();
1278 let param = func.append_param(join, GPR);
1279 *func.succs_mut(head) = vec![BlockCall::with(join, vec![value]), BlockCall::to(other)];
1280 *func.succs_mut(other) = vec![BlockCall::with(join, vec![value])];
1281 func.build(join, opcode).uses(param, GPR).finish();
1282
1283 let _ = run(&mut func, &env());
1284 }
1285
1286 #[test]
1287 #[should_panic(expected = "what arrives in a function is not a block parameter")]
1288 fn a_parameter_on_the_entry_block_is_refused() {
1289 let mut names = Interner::new();
1290 let mut func = Func::new(names.intern("f"));
1291 let block = func.create_block();
1292 let param = func.append_param(block, GPR);
1293 let opcode = Opcode::new(names.intern("x64.nop"));
1294 func.build(block, opcode).uses(param, GPR).finish();
1295
1296 let _ = run(&mut func, &env());
1297 }
1298
1299 #[test]
1300 fn a_value_already_in_a_register_is_left_where_it_is() {
1301 let mut names = Interner::new();
1302 let mut func = Func::new(names.intern("f"));
1303 let opcode = Opcode::new(names.intern("x64.nop"));
1304 let block = func.create_block();
1305 let inst = func.build(block, opcode).uses(Reg::physical(RDX), GPR).finish();
1306
1307 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
1308 assert_eq!(operands(&func, inst), ["rdx"]);
1309 }
1310}