1use rucc_base::Interner;
54use rucc_mir::{Block, Func, Inst, Mem, Opcode, Operand, Reg};
55use rucc_regalloc::Allocation;
56use rucc_regalloc::assign::Place;
57use rucc_regalloc::rewrite::{At, Edit};
58use rucc_target::{CallRegs, FrameInsts, PhysReg, RegClass};
59
60use crate::frame::Frame;
61
62pub fn finish(
71 func: &mut Func,
72 allocation: &Allocation,
73 frame: &Frame,
74 locals: &[(Inst, usize)],
75 conv: &CallRegs,
76 insts: &FrameInsts,
77 names: &mut Interner,
78) {
79 let entry = func.entry().expect("a function with a block in it");
80 let returns: Vec<Block> = func.blocks().filter(|&block| func[block].succs.is_empty()).collect();
81
82 for &(inst, local) in locals {
86 let at = frame.local(local).expect("a local the frame was worked out from");
87 let mem = func[inst].mem.expect("the address of a local is an address");
88 func[mem].disp = at;
89 }
90
91 let mut writer = Writer { func, conv, insts, names };
92
93 let mut cursors: Vec<(At, Inst)> = Vec::new();
94 for edit in &allocation.edits {
95 let inst = writer.mov(edit, frame);
96 writer.put(&mut cursors, edit.at, inst);
97 }
98
99 let prologue = writer.prologue(frame);
100 for &inst in prologue.iter().rev() {
101 writer.func.prepend_inst(entry, inst);
102 }
103 for block in returns {
104 let epilogue = writer.epilogue(frame);
105 for inst in epilogue {
106 writer.func.append_inst(block, inst);
107 }
108 }
109}
110
111struct Writer<'a> {
113 func: &'a mut Func,
114 conv: &'a CallRegs,
115 insts: &'a FrameInsts,
116 names: &'a mut Interner,
117}
118
119impl Writer<'_> {
120 fn prologue(&mut self, frame: &Frame) -> Vec<Inst> {
129 let sp = self.conv.stack_pointer;
130 let fp = self.conv.frame_pointer;
131 let mut out = Vec::new();
132 if frame.frame_pointer() {
133 out.push(self.push(fp));
134 let mov = self.opcode(self.insts.moves(self.conv.int_class).expect("a move").mov);
135 out.push(self.two(mov, fp, sp));
136 }
137 for ® in frame.saved_int() {
138 out.push(self.push(reg));
139 }
140 if let Some(to) = frame.realign() {
141 let and = self.opcode(self.insts.align);
142 out.push(self.arith(and, -i64::from(to)));
143 }
144 if frame.size() > 0 {
145 let sub = self.opcode(self.insts.sub);
146 out.push(self.arith(sub, i64::from(frame.size())));
147 }
148 for save in frame.saved_sse() {
149 out.push(self.store(self.conv.sse_class, save.reg, save.at));
150 }
151 out
152 }
153
154 fn epilogue(&mut self, frame: &Frame) -> Vec<Inst> {
161 let sp = self.conv.stack_pointer;
162 let fp = self.conv.frame_pointer;
163 let word = self.conv.word;
164 let mut out = Vec::new();
165 for save in frame.saved_sse() {
166 out.push(self.load(self.conv.sse_class, save.reg, save.at));
167 }
168 let pushed = u32::try_from(frame.saved_int().len()).expect("a frame");
169 if frame.frame_pointer() {
170 if pushed == 0 {
171 let mov = self.opcode(self.insts.moves(self.conv.int_class).expect("a move").mov);
172 out.push(self.two(mov, sp, fp));
173 } else {
174 let lea = self.opcode(self.insts.lea);
175 let back = -offset(word * pushed);
176 out.push(self.address(lea, sp, fp, back));
177 }
178 } else if frame.size() > 0 {
179 let add = self.opcode(self.insts.add);
180 out.push(self.arith(add, i64::from(frame.size())));
181 }
182 for ® in frame.saved_int().iter().rev() {
183 out.push(self.pop(reg));
184 }
185 if frame.frame_pointer() {
186 out.push(self.pop(fp));
187 }
188 let ret = self.opcode(self.insts.ret);
189 out.push(self.func.build_loose(ret).finish());
190 out
191 }
192
193 fn mov(&mut self, edit: &Edit, frame: &Frame) -> Inst {
195 let moves = self.insts.moves(edit.class).expect("a class the target says how to move");
196 match (edit.mov.to, edit.mov.from) {
197 (Place::Reg(to), Place::Reg(from)) => {
198 let mov = self.opcode(moves.mov);
199 self.func
200 .build_loose(mov)
201 .def(Reg::physical(to), edit.class)
202 .uses(Reg::physical(from), edit.class)
203 .finish()
204 }
205 (Place::Reg(to), Place::Slot(slot)) => {
206 let at = self.slot(frame, slot);
207 self.load(edit.class, to, at)
208 }
209 (Place::Slot(slot), Place::Reg(from)) => {
210 let at = self.slot(frame, slot);
211 self.store(edit.class, from, at)
212 }
213 (Place::Slot(_), Place::Slot(_)) => {
216 unreachable!("a move from one stack slot straight into another")
217 }
218 }
219 }
220
221 fn put(&mut self, cursors: &mut Vec<(At, Inst)>, at: At, inst: Inst) {
226 if let Some(cursor) = cursors.iter_mut().find(|(place, _)| *place == at) {
227 self.func.insert_after(cursor.1, inst);
228 cursor.1 = inst;
229 return;
230 }
231 match at {
232 At::Before(before) => self.func.insert_before(before, inst),
233 At::After(after) => self.func.insert_after(after, inst),
234 At::StartOf(block) => self.func.prepend_inst(block, inst),
235 At::EndOf(block) => self.func.append_inst(block, inst),
241 }
242 cursors.push((at, inst));
243 }
244
245 fn slot(&self, frame: &Frame, slot: u32) -> i32 {
247 frame.slot(slot).expect("a slot the frame was worked out from")
248 }
249
250 fn load(&mut self, class: RegClass, reg: PhysReg, at: i32) -> Inst {
252 let load = self.opcode(self.insts.moves(class).expect("a class to load").load);
253 let base = Operand::read(Reg::physical(self.conv.stack_pointer), self.conv.int_class);
254 self.func
255 .build_loose(load)
256 .def(Reg::physical(reg), class)
257 .mem(Mem::at(base).plus(at))
258 .finish()
259 }
260
261 fn store(&mut self, class: RegClass, reg: PhysReg, at: i32) -> Inst {
263 let store = self.opcode(self.insts.moves(class).expect("a class to store").store);
264 let base = Operand::read(Reg::physical(self.conv.stack_pointer), self.conv.int_class);
265 self.func
266 .build_loose(store)
267 .uses(Reg::physical(reg), class)
268 .mem(Mem::at(base).plus(at))
269 .finish()
270 }
271
272 fn push(&mut self, reg: PhysReg) -> Inst {
274 let push = self.opcode(self.insts.push);
275 self.func.build_loose(push).uses(Reg::physical(reg), self.conv.int_class).finish()
276 }
277
278 fn pop(&mut self, reg: PhysReg) -> Inst {
280 let pop = self.opcode(self.insts.pop);
281 self.func.build_loose(pop).def(Reg::physical(reg), self.conv.int_class).finish()
282 }
283
284 fn two(&mut self, opcode: Opcode, to: PhysReg, from: PhysReg) -> Inst {
286 let class = self.conv.int_class;
287 self.func
288 .build_loose(opcode)
289 .def(Reg::physical(to), class)
290 .uses(Reg::physical(from), class)
291 .finish()
292 }
293
294 fn arith(&mut self, opcode: Opcode, value: i64) -> Inst {
296 let class = self.conv.int_class;
297 let sp = Reg::physical(self.conv.stack_pointer);
298 self.func.build_loose(opcode).def(sp, class).uses(sp, class).imm(value).finish()
299 }
300
301 fn address(&mut self, opcode: Opcode, to: PhysReg, base: PhysReg, disp: i32) -> Inst {
303 let class = self.conv.int_class;
304 let base = Operand::read(Reg::physical(base), class);
305 self.func
306 .build_loose(opcode)
307 .def(Reg::physical(to), class)
308 .mem(Mem::at(base).plus(disp))
309 .finish()
310 }
311
312 fn opcode(&mut self, name: &str) -> Opcode {
315 Opcode::new(self.names.intern(&format!("{}{name}", self.insts.prefix)))
316 }
317}
318
319fn offset(bytes: u32) -> i32 {
321 i32::try_from(bytes).expect("a frame under two gigabytes")
322}
323
324#[cfg(test)]
325mod tests {
326 use rucc_base::Interner;
327 use rucc_mir::{BlockCall, print_func};
328 use rucc_regalloc::assign::Env;
329 use rucc_target::x86_64::{FRAME, GPR, REGS, SYSV, WIN64, XMM, xmm};
330
331 use super::*;
332 use crate::frame::{Layout, Local};
333
334 fn env(conv: &CallRegs, count: usize) -> Env {
337 Env::new().with(GPR, &conv.int_order[..count], &conv.int_order[count..])
338 }
339
340 fn pressure(conv: &CallRegs, values: usize, count: usize) -> (Func, Allocation, Interner) {
344 let mut names = Interner::new();
345 let mut func = Func::new(names.intern("f"));
346 let opcode = Opcode::new(names.intern("x64.nop"));
347 let block = func.create_block();
348 let regs: Vec<Reg> = (0..values).map(|_| func.new_vreg(GPR)).collect();
349 for ® in ®s {
350 func.build(block, opcode).def(reg, GPR).finish();
351 }
352 for ® in ®s {
353 func.build(block, opcode).uses(reg, GPR).finish();
354 }
355 let allocation = rucc_regalloc::run(&mut func, &env(conv, count));
356 (func, allocation, names)
357 }
358
359 fn written(
361 func: &mut Func,
362 allocation: &Allocation,
363 layout: &Layout<'_>,
364 names: &mut Interner,
365 ) -> Vec<String> {
366 let frame = Frame::of(func, allocation, layout);
367 finish(func, allocation, &frame, &[], layout.conv, &FRAME, names);
368 print_func(func, names, ®S)
369 .lines()
370 .filter(|line| !line.is_empty())
371 .map(|line| line.trim().to_string())
372 .collect()
373 }
374
375 fn added(lines: &[String]) -> Vec<&str> {
378 lines
379 .iter()
380 .map(String::as_str)
381 .filter(|line| !line.contains("x64.nop"))
382 .filter(|line| !line.starts_with("mfunc") && !line.starts_with("block") && *line != "}")
383 .collect()
384 }
385
386 #[test]
387 fn a_function_that_needs_no_frame_is_given_a_return_and_nothing_else() {
388 let (mut func, allocation, mut names) = pressure(&SYSV, 2, 4);
389 let lines = written(&mut func, &allocation, &Layout::new(&SYSV, REGS), &mut names);
390
391 assert_eq!(added(&lines), ["x64.ret"]);
394 }
395
396 #[test]
397 fn a_spill_is_a_store_and_a_reload_is_a_load() {
398 let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
399 let lines = written(&mut func, &allocation, &Layout::new(&SYSV, REGS), &mut names);
400
401 assert_eq!(
406 lines,
407 [
408 "mfunc @f {",
409 "block0:",
410 "$rax = x64.nop",
411 "$rcx = x64.nop",
412 "$rdx = x64.nop",
413 "x64.mov_mr_64 $rdx, [$rsp - 16]",
414 "$rdx = x64.nop",
415 "x64.mov_mr_64 $rdx, [$rsp - 8]",
416 "x64.nop $rax",
417 "x64.nop $rcx",
418 "$rdx = x64.mov_rm_64 [$rsp - 16]",
419 "x64.nop $rdx",
420 "$rdx = x64.mov_rm_64 [$rsp - 8]",
421 "x64.nop $rdx",
422 "x64.ret",
423 "}",
424 ]
425 );
426 }
427
428 #[test]
429 fn the_frame_the_prologue_takes_is_the_frame_the_epilogue_gives_back() {
430 let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
431 let base = Layout::new(&SYSV, REGS);
432 let layout = Layout { red_zone: false, ..base };
433 let lines = written(&mut func, &allocation, &layout, &mut names);
434
435 assert_eq!(
438 added(&lines),
439 [
440 "$rsp = x64.sub_ri_64 $rsp, 16",
441 "x64.mov_mr_64 $rdx, [$rsp]",
442 "x64.mov_mr_64 $rdx, [$rsp + 8]",
443 "$rdx = x64.mov_rm_64 [$rsp]",
444 "$rdx = x64.mov_rm_64 [$rsp + 8]",
445 "$rsp = x64.add_ri_64 $rsp, 16",
446 "x64.ret",
447 ]
448 );
449 }
450
451 #[test]
452 fn the_registers_the_prologue_pushes_come_back_in_the_opposite_order() {
453 let (mut func, allocation, mut names) = pressure(&SYSV, 13, 13);
454 let lines = written(&mut func, &allocation, &Layout::new(&SYSV, REGS), &mut names);
455
456 assert_eq!(
459 added(&lines),
460 [
461 "x64.push_64 $rbx",
462 "x64.push_64 $r12",
463 "x64.push_64 $r13",
464 "x64.push_64 $r14",
465 "$r14 = x64.pop_64",
466 "$r13 = x64.pop_64",
467 "$r12 = x64.pop_64",
468 "$rbx = x64.pop_64",
469 "x64.ret",
470 ]
471 );
472 }
473
474 #[test]
475 fn a_function_that_keeps_a_frame_pointer_sets_it_up_and_leaves_by_it() {
476 let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
477 let base = Layout::new(&SYSV, REGS);
478 let layout = Layout { frame_pointer: true, red_zone: false, ..base };
479 let lines = written(&mut func, &allocation, &layout, &mut names);
480
481 assert_eq!(
484 added(&lines),
485 [
486 "x64.push_64 $rbp",
487 "$rbp = x64.mov_rr_64 $rsp",
488 "$rsp = x64.sub_ri_64 $rsp, 16",
489 "x64.mov_mr_64 $rdx, [$rsp]",
490 "x64.mov_mr_64 $rdx, [$rsp + 8]",
491 "$rdx = x64.mov_rm_64 [$rsp]",
492 "$rdx = x64.mov_rm_64 [$rsp + 8]",
493 "$rsp = x64.mov_rr_64 $rbp",
494 "$rbp = x64.pop_64",
495 "x64.ret",
496 ]
497 );
498 }
499
500 #[test]
501 fn a_realigned_frame_forces_the_alignment_after_it_has_pushed_what_it_saves() {
502 let (mut func, allocation, mut names) = pressure(&SYSV, 13, 13);
503 let locals = [Local { size: 64, align: 32 }];
504 let base = Layout::new(&SYSV, REGS);
505 let layout = Layout { locals: &locals, ..base };
506 let lines = written(&mut func, &allocation, &layout, &mut names);
507
508 assert_eq!(
512 added(&lines),
513 [
514 "x64.push_64 $rbp",
515 "$rbp = x64.mov_rr_64 $rsp",
516 "x64.push_64 $rbx",
517 "x64.push_64 $r12",
518 "x64.push_64 $r13",
519 "x64.push_64 $r14",
520 "$rsp = x64.and_ri_64 $rsp, -32",
521 "$rsp = x64.sub_ri_64 $rsp, 64",
522 "$rsp = x64.lea_64 [$rbp - 32]",
523 "$r14 = x64.pop_64",
524 "$r13 = x64.pop_64",
525 "$r12 = x64.pop_64",
526 "$rbx = x64.pop_64",
527 "$rbp = x64.pop_64",
528 "x64.ret",
529 ]
530 );
531 }
532
533 #[test]
534 fn every_block_the_function_returns_from_gets_an_epilogue() {
535 let mut names = Interner::new();
536 let mut func = Func::new(names.intern("f"));
537 let opcode = Opcode::new(names.intern("x64.nop"));
538 let head = func.create_block();
539 let left = func.create_block();
540 let right = func.create_block();
541 func.build(head, opcode).finish();
542 *func.succs_mut(head) = vec![BlockCall::to(left), BlockCall::to(right)];
543 func.build(left, opcode).finish();
544 func.build(right, opcode).finish();
545 let allocation = rucc_regalloc::run(&mut func, &env(&SYSV, 4));
546 let base = Layout::new(&SYSV, REGS);
547 let layout = Layout { leaf: false, ..base };
548 let lines = written(&mut func, &allocation, &layout, &mut names);
549
550 assert_eq!(
553 lines,
554 [
555 "mfunc @f {",
556 "block0:",
557 "$rsp = x64.sub_ri_64 $rsp, 8",
558 "x64.nop block1, block2",
559 "block1:",
560 "x64.nop",
561 "$rsp = x64.add_ri_64 $rsp, 8",
562 "x64.ret",
563 "block2:",
564 "x64.nop",
565 "$rsp = x64.add_ri_64 $rsp, 8",
566 "x64.ret",
567 "}",
568 ]
569 );
570 }
571
572 #[test]
573 fn a_vector_register_a_windows_call_preserves_is_stored_and_read_back() {
574 let mut names = Interner::new();
575 let mut func = Func::new(names.intern("f"));
576 let opcode = Opcode::new(names.intern("x64.nop"));
577 let block = func.create_block();
578 func.build(block, opcode).operand(Operand::write(Reg::physical(xmm(6)), XMM)).finish();
581 let allocation = rucc_regalloc::run(&mut func, &env(&WIN64, 4));
582 let lines = written(&mut func, &allocation, &Layout::new(&WIN64, REGS), &mut names);
583
584 assert_eq!(
587 added(&lines),
588 [
589 "$rsp = x64.sub_ri_64 $rsp, 24",
590 "x64.movaps_mr $xmm6, [$rsp]",
591 "$xmm6 = x64.movaps_rm [$rsp]",
592 "$rsp = x64.add_ri_64 $rsp, 24",
593 "x64.ret",
594 ]
595 );
596 }
597}