1use rucc_mir::{Block, Constraint, Func, Inst, Operand, Param, Reg};
50use rucc_target::{PhysReg, RegClass};
51
52use crate::assign::{Assignment, Env, Place};
53use crate::moves::{self, Move};
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub struct Edit {
58 pub at: At,
60 pub mov: Move<Place>,
62 pub class: RegClass,
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum At {
69 Before(Inst),
71 After(Inst),
74 StartOf(Block),
76 EndOf(Block),
79}
80
81#[must_use]
91pub fn rewrite(func: &mut Func, assignment: &Assignment, env: &Env) -> Vec<Edit> {
92 let blocks: Vec<Block> = func.blocks().collect();
93 assert!(
94 func.entry().is_none_or(|entry| func[entry].params.is_empty()),
95 "what arrives in a function is not a block parameter"
96 );
97
98 let mut edits = Vec::new();
99 for &block in &blocks {
100 let insts: Vec<Inst> = func.insts(block).collect();
101 for inst in insts {
102 instruction(func, assignment, env, inst, &mut edits);
103 }
104 }
105
106 let preds = preds(func, &blocks);
107 for &block in &blocks {
108 edges(func, assignment, env, block, &preds, &mut edits);
109 }
110 for &block in &blocks {
111 func.params_mut(block).clear();
112 for call in func.succs_mut(block) {
113 call.args.clear();
114 }
115 }
116 edits
117}
118
119fn instruction(
121 func: &mut Func,
122 assignment: &Assignment,
123 env: &Env,
124 inst: Inst,
125 edits: &mut Vec<Edit>,
126) {
127 let list = func[inst].operands;
128 let mut operands: Vec<Operand> = func[list].to_vec();
129 let mut before: Vec<(Move<Place>, RegClass)> = Vec::new();
130 let mut after: Vec<(Move<Place>, RegClass)> = Vec::new();
131 let mut taken = 0;
132
133 for operand in &mut operands {
134 let fixed = match operand.constraint {
135 Constraint::Fixed(at) => Some(at),
136 _ => None,
137 };
138 let at = match (place(assignment, operand.reg), fixed) {
139 (Place::Reg(at), None) => at,
140 (Place::Reg(at), Some(fixed)) => {
141 if at != fixed {
142 let (there, here) = (Place::Reg(fixed), Place::Reg(at));
143 push(&mut before, &mut after, operand, Move::new(there, here));
144 }
145 fixed
146 }
147 (Place::Slot(slot), fixed) => {
148 let at = fixed.unwrap_or_else(|| {
149 let scratch = *env
150 .scratch(operand.class)
151 .get(taken)
152 .expect("an instruction wanting more scratch registers than the class has");
153 taken += 1;
154 scratch
155 });
156 push(
157 &mut before,
158 &mut after,
159 operand,
160 Move::new(Place::Reg(at), Place::Slot(slot)),
161 );
162 at
163 }
164 };
165 operand.reg = Reg::physical(at);
166 }
167
168 for index in 0..operands.len() {
172 let Constraint::Reuse(other) = operands[index].constraint else { continue };
173 let (to, from) = (operands[index], operands[usize::from(other)]);
174 if to.reg != from.reg {
175 let mov = Move::new(Place::Reg(phys(to.reg)), Place::Reg(phys(from.reg)));
176 before.push((mov, to.class));
177 }
178 }
179
180 func[list].copy_from_slice(&operands);
181 edits.extend(before.into_iter().map(|(mov, class)| Edit { at: At::Before(inst), mov, class }));
182 edits.extend(after.into_iter().map(|(mov, class)| Edit { at: At::After(inst), mov, class }));
183}
184
185fn push(
188 before: &mut Vec<(Move<Place>, RegClass)>,
189 after: &mut Vec<(Move<Place>, RegClass)>,
190 operand: &Operand,
191 mov: Move<Place>,
192) {
193 if operand.role.is_def() {
194 after.push((Move::new(mov.from, mov.to), operand.class));
195 } else {
196 before.push((mov, operand.class));
197 }
198}
199
200fn edges(
202 func: &mut Func,
203 assignment: &Assignment,
204 env: &Env,
205 block: Block,
206 preds: &[usize],
207 edits: &mut Vec<Edit>,
208) {
209 let succs = func[block].succs.clone();
210 let single = succs.len() == 1;
211 for call in &succs {
212 let params = func[call.block].params.clone();
213 assert_eq!(
214 params.len(),
215 call.args.len(),
216 "an edge carries what the block it goes to asks for"
217 );
218 if params.is_empty() {
219 continue;
220 }
221 assert!(
222 single || preds[call.block.index()] == 1,
223 "a critical edge has nowhere to put its moves and has to be split before allocation"
224 );
225 let at = if single { At::EndOf(block) } else { At::StartOf(call.block) };
226 edits.extend(edge(assignment, env, ¶ms, &call.args, at));
227 }
228}
229
230fn edge(assignment: &Assignment, env: &Env, params: &[Param], args: &[Reg], at: At) -> Vec<Edit> {
232 let mut classes: Vec<RegClass> = params.iter().map(|param| param.class).collect();
233 classes.sort_unstable();
234 classes.dedup();
235
236 let mut edits = Vec::new();
237 for class in classes {
238 let parallel: Vec<Move<Place>> = params
241 .iter()
242 .zip(args)
243 .filter(|(param, _)| param.class == class)
244 .map(|(param, &arg)| Move::new(place(assignment, param.reg), place(assignment, arg)))
245 .collect();
246 let scratch = env.scratch(class);
247 let cycle = *scratch
248 .first()
249 .expect("a class whose values are passed on an edge and which has no scratch register");
250 for mov in moves::sequence(¶llel, Place::Reg(cycle)) {
251 match (mov.to, mov.from) {
252 (Place::Slot(_), Place::Slot(_)) => {
256 let through = Place::Reg(*scratch.get(1).expect(
257 "a class passing a spilled value to a spilled parameter and having only \
258 one scratch register",
259 ));
260 edits.push(Edit { at, mov: Move::new(through, mov.from), class });
261 edits.push(Edit { at, mov: Move::new(mov.to, through), class });
262 }
263 _ => edits.push(Edit { at, mov, class }),
264 }
265 }
266 }
267 edits
268}
269
270fn preds(func: &Func, blocks: &[Block]) -> Vec<usize> {
272 let mut preds = vec![0; func.block_count()];
273 for &block in blocks {
274 for call in &func[block].succs {
275 preds[call.block.index()] += 1;
276 }
277 }
278 preds
279}
280
281fn place(assignment: &Assignment, reg: Reg) -> Place {
283 assignment.place(reg).unwrap_or_else(|| Place::Reg(phys(reg)))
284}
285
286fn phys(reg: Reg) -> PhysReg {
288 reg.phys().expect("a register the assignment says nothing about and that is not a register")
289}
290
291#[cfg(test)]
292mod tests {
293 use rucc_base::Interner;
294 use rucc_mir::{BlockCall, Opcode};
295 use rucc_target::x86_64::{GPR, RAX, RDX, REGS, SYSV};
296
297 use super::*;
298 use crate::assign::assign;
299 use crate::live::Live;
300 use crate::order::Order;
301
302 fn env() -> Env {
304 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
305 Env::new().with(GPR, order, scratch)
306 }
307
308 fn narrow(count: usize) -> Env {
310 Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 2])
311 }
312
313 fn named(place: Place) -> String {
315 match place {
316 Place::Reg(reg) => REGS.name(GPR, reg).expect("a register").to_string(),
317 Place::Slot(slot) => format!("slot{slot}"),
318 }
319 }
320
321 fn run(func: &mut Func, env: &Env) -> Vec<String> {
323 let order = Order::of(func);
324 let live = Live::of(func, &order);
325 let assignment = assign(func, &order, &live, env);
326 rewrite(func, &assignment, env)
327 .into_iter()
328 .map(|edit| {
329 let at = match edit.at {
330 At::Before(inst) => format!("before {}", inst.index()),
331 At::After(inst) => format!("after {}", inst.index()),
332 At::StartOf(block) => format!("start of {}", block.index()),
333 At::EndOf(block) => format!("end of {}", block.index()),
334 };
335 format!("{at}: {} = {}", named(edit.mov.to), named(edit.mov.from))
336 })
337 .collect()
338 }
339
340 fn operands(func: &Func, inst: Inst) -> Vec<String> {
342 func[func[inst].operands]
343 .iter()
344 .map(|operand| named(Place::Reg(phys(operand.reg))))
345 .collect()
346 }
347
348 #[test]
349 fn every_operand_ends_up_naming_the_register_its_value_was_given() {
350 let mut names = Interner::new();
351 let mut func = Func::new(names.intern("f"));
352 let opcode = Opcode::new(names.intern("x64.nop"));
353 let block = func.create_block();
354 let first = func.new_vreg(GPR);
355 let second = func.new_vreg(GPR);
356 func.build(block, opcode).def(first, GPR).finish();
357 func.build(block, opcode).def(second, GPR).finish();
358 let read = func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
359
360 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
361 assert_eq!(operands(&func, read), ["rax", "rcx"]);
362 }
363
364 #[test]
365 fn a_register_an_instruction_insists_on_is_moved_into_and_out_of() {
366 let mut names = Interner::new();
367 let mut func = Func::new(names.intern("f"));
368 let opcode = Opcode::new(names.intern("x64.nop"));
369 let block = func.create_block();
370 let dividend = func.new_vreg(GPR);
371 let quotient = func.new_vreg(GPR);
372 func.build(block, opcode).def(dividend, GPR).finish();
373 let divide = func
374 .build(block, opcode)
375 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
376 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
377 .finish();
378 func.build(block, opcode).uses(quotient, GPR).finish();
379
380 assert_eq!(run(&mut func, &env()), ["before 1: rax = rcx", "after 1: rcx = rax"]);
384 assert_eq!(operands(&func, divide), ["rax", "rax"]);
385 }
386
387 #[test]
388 fn a_two_address_instruction_that_did_not_get_its_register_copies_first() {
389 let mut names = Interner::new();
390 let mut func = Func::new(names.intern("f"));
391 let opcode = Opcode::new(names.intern("x64.nop"));
392 let block = func.create_block();
393 let left = func.new_vreg(GPR);
394 let right = func.new_vreg(GPR);
395 let sum = func.new_vreg(GPR);
396 func.build(block, opcode).def(left, GPR).finish();
397 func.build(block, opcode).def(right, GPR).finish();
398 let add = func
399 .build(block, opcode)
400 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
401 .uses(left, GPR)
402 .uses(right, GPR)
403 .finish();
404 func.build(block, opcode).uses(left, GPR).finish();
405
406 assert_eq!(run(&mut func, &env()), ["before 2: rdx = rax"]);
409 assert_eq!(operands(&func, add), ["rdx", "rax", "rcx"]);
410 }
411
412 #[test]
413 fn a_two_address_instruction_that_did_get_its_register_copies_nothing() {
414 let mut names = Interner::new();
415 let mut func = Func::new(names.intern("f"));
416 let opcode = Opcode::new(names.intern("x64.nop"));
417 let block = func.create_block();
418 let left = func.new_vreg(GPR);
419 let right = func.new_vreg(GPR);
420 let sum = func.new_vreg(GPR);
421 func.build(block, opcode).def(left, GPR).finish();
422 func.build(block, opcode).def(right, GPR).finish();
423 let add = func
424 .build(block, opcode)
425 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
426 .uses(left, GPR)
427 .uses(right, GPR)
428 .finish();
429 func.build(block, opcode).uses(right, GPR).finish();
430
431 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
432 assert_eq!(operands(&func, add), ["rax", "rax", "rcx"]);
433 }
434
435 #[test]
436 fn a_spilled_value_is_read_into_a_scratch_register_at_each_instruction_that_wants_it() {
437 let mut names = Interner::new();
438 let mut func = Func::new(names.intern("f"));
439 let opcode = Opcode::new(names.intern("x64.nop"));
440 let block = func.create_block();
441 let first = func.new_vreg(GPR);
442 let second = func.new_vreg(GPR);
443 func.build(block, opcode).def(first, GPR).finish();
444 func.build(block, opcode).def(second, GPR).finish();
445 let read = func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
446
447 assert_eq!(run(&mut func, &narrow(1)), ["after 1: slot0 = rcx", "before 2: rcx = slot0"]);
451 assert_eq!(operands(&func, read), ["rax", "rcx"]);
452 }
453
454 #[test]
455 fn an_edge_out_of_a_block_with_one_way_to_go_moves_at_the_end_of_it() {
456 let mut names = Interner::new();
457 let mut func = Func::new(names.intern("f"));
458 let opcode = Opcode::new(names.intern("x64.nop"));
459 let head = func.create_block();
460 let tail = func.create_block();
461 let held = func.new_vreg(GPR);
462 let carried = func.new_vreg(GPR);
463 func.build(head, opcode).def(held, GPR).finish();
464 func.build(head, opcode).def(carried, GPR).finish();
465 func.build(head, opcode).uses(held, GPR).finish();
466 let param = func.append_param(tail, GPR);
467 *func.succs_mut(head) = vec![BlockCall::with(tail, vec![carried])];
468 let read = func.build(tail, opcode).uses(param, GPR).finish();
469
470 assert_eq!(run(&mut func, &env()), ["end of 0: rax = rcx"]);
474 assert_eq!(operands(&func, read), ["rax"]);
475 assert!(func[tail].params.is_empty());
478 assert!(func[head].succs[0].args.is_empty());
479 }
480
481 #[test]
482 fn an_edge_out_of_a_block_with_a_choice_moves_at_the_start_of_where_it_goes() {
483 let mut names = Interner::new();
484 let mut func = Func::new(names.intern("f"));
485 let opcode = Opcode::new(names.intern("x64.nop"));
486 let head = func.create_block();
487 let left = func.create_block();
488 let right = func.create_block();
489 let held = func.new_vreg(GPR);
490 let carried = func.new_vreg(GPR);
491 func.build(head, opcode).def(held, GPR).finish();
492 func.build(head, opcode).def(carried, GPR).finish();
493 func.build(head, opcode).uses(held, GPR).finish();
494 let taken = func.append_param(left, GPR);
495 *func.succs_mut(head) = vec![BlockCall::with(left, vec![carried]), BlockCall::to(right)];
496 func.build(left, opcode).uses(taken, GPR).finish();
497
498 assert_eq!(run(&mut func, &env()), ["start of 1: rax = rcx"]);
502 }
503
504 #[test]
505 fn two_values_that_swap_on_an_edge_get_an_order_and_a_scratch_register() {
506 let mut names = Interner::new();
507 let mut func = Func::new(names.intern("f"));
508 let opcode = Opcode::new(names.intern("x64.nop"));
509 let head = func.create_block();
510 let body = func.create_block();
511 let first = func.new_vreg(GPR);
512 let second = func.new_vreg(GPR);
513 func.build(head, opcode).def(first, GPR).finish();
514 func.build(head, opcode).def(second, GPR).finish();
515 let left = func.append_param(body, GPR);
516 let right = func.append_param(body, GPR);
517 *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
518 func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
519 *func.succs_mut(body) = vec![BlockCall::with(body, vec![right, left])];
520
521 assert_eq!(
525 run(&mut func, &env()),
526 ["end of 1: r13 = rcx", "end of 1: rcx = rax", "end of 1: rax = r13"]
527 );
528 }
529
530 #[test]
531 fn a_spilled_value_handed_to_a_spilled_parameter_goes_through_a_register() {
532 let mut names = Interner::new();
533 let mut func = Func::new(names.intern("f"));
534 let opcode = Opcode::new(names.intern("x64.nop"));
535 let head = func.create_block();
536 let body = func.create_block();
537 let first = func.new_vreg(GPR);
538 let second = func.new_vreg(GPR);
539 func.build(head, opcode).def(first, GPR).finish();
540 func.build(head, opcode).def(second, GPR).finish();
541 let left = func.append_param(body, GPR);
542 let right = func.append_param(body, GPR);
543 *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
544 func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
545
546 assert_eq!(
551 run(&mut func, &narrow(1)),
552 [
553 "after 1: slot0 = rcx",
554 "before 2: rcx = slot1",
555 "end of 0: rdx = slot0",
556 "end of 0: slot1 = rdx",
557 ]
558 );
559 }
560
561 #[test]
562 #[should_panic(expected = "a critical edge has nowhere to put its moves")]
563 fn a_critical_edge_is_refused() {
564 let mut names = Interner::new();
565 let mut func = Func::new(names.intern("f"));
566 let opcode = Opcode::new(names.intern("x64.nop"));
567 let head = func.create_block();
568 let other = func.create_block();
569 let join = func.create_block();
570 let value = func.new_vreg(GPR);
571 func.build(head, opcode).def(value, GPR).finish();
572 let param = func.append_param(join, GPR);
573 *func.succs_mut(head) = vec![BlockCall::with(join, vec![value]), BlockCall::to(other)];
574 *func.succs_mut(other) = vec![BlockCall::with(join, vec![value])];
575 func.build(join, opcode).uses(param, GPR).finish();
576
577 let _ = run(&mut func, &env());
578 }
579
580 #[test]
581 #[should_panic(expected = "what arrives in a function is not a block parameter")]
582 fn a_parameter_on_the_entry_block_is_refused() {
583 let mut names = Interner::new();
584 let mut func = Func::new(names.intern("f"));
585 let block = func.create_block();
586 let param = func.append_param(block, GPR);
587 let opcode = Opcode::new(names.intern("x64.nop"));
588 func.build(block, opcode).uses(param, GPR).finish();
589
590 let _ = run(&mut func, &env());
591 }
592
593 #[test]
594 fn a_value_already_in_a_register_is_left_where_it_is() {
595 let mut names = Interner::new();
596 let mut func = Func::new(names.intern("f"));
597 let opcode = Opcode::new(names.intern("x64.nop"));
598 let block = func.create_block();
599 let inst = func.build(block, opcode).uses(Reg::physical(RDX), GPR).finish();
600
601 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
602 assert_eq!(operands(&func, inst), ["rdx"]);
603 }
604}