1use rucc_base::Interner;
64use rucc_mir::{Block, Func, Inst, Mem, Opcode, Operand, Reg};
65use rucc_regalloc::Allocation;
66use rucc_regalloc::assign::Place;
67use rucc_regalloc::rewrite::{At, Edit};
68use rucc_target::{CallRegs, FrameInsts, PhysReg, RegClass};
69
70use crate::frame::Frame;
71use crate::lower::Stack;
72
73pub fn finish(
82 func: &mut Func,
83 allocation: &Allocation,
84 frame: &Frame,
85 stack: &Stack,
86 conv: &CallRegs,
87 insts: &FrameInsts,
88 names: &mut Interner,
89) {
90 let entry = func.entry().expect("a function with a block in it");
91 let returns: Vec<Block> = func.blocks().filter(|&block| func[block].succs.is_empty()).collect();
92
93 for &(inst, local) in &stack.addresses {
97 let at = frame.local(local).expect("a local the frame was worked out from");
98 let mem = func[inst].mem.expect("the address of a local is an address");
99 func[mem].disp = at;
100 }
101
102 let incoming = frame.incoming();
106 for &(inst, up) in &stack.arguments {
107 let mem = func[inst].mem.expect("an argument read out of memory is read from an address");
108 func[mem].disp = incoming.at + offset(up);
109 if incoming.through_frame_pointer {
110 let at = func[mem].base.expect("an address the lowering wrote a base register into");
113 let operands = func[inst].operands;
114 func[operands][usize::from(at)].reg = Reg::physical(conv.frame_pointer);
115 }
116 }
117
118 let mut writer = Writer { func, conv, insts, names };
119
120 let mut cursors: Vec<(At, Inst)> = Vec::new();
121 for edit in &allocation.edits {
122 let inst = writer.mov(edit, frame);
123 writer.put(&mut cursors, edit.at, inst);
124 }
125
126 let prologue = writer.prologue(frame);
127 for &inst in prologue.iter().rev() {
128 writer.func.prepend_inst(entry, inst);
129 }
130 for block in returns {
131 let epilogue = writer.epilogue(frame);
132 for inst in epilogue {
133 writer.func.append_inst(block, inst);
134 }
135 }
136}
137
138struct Writer<'a> {
140 func: &'a mut Func,
141 conv: &'a CallRegs,
142 insts: &'a FrameInsts,
143 names: &'a mut Interner,
144}
145
146impl Writer<'_> {
147 fn prologue(&mut self, frame: &Frame) -> Vec<Inst> {
156 let sp = self.conv.stack_pointer;
157 let fp = self.conv.frame_pointer;
158 let mut out = Vec::new();
159 if frame.frame_pointer() {
160 out.push(self.push(fp));
161 let mov = self.opcode(self.insts.moves(self.conv.int_class).expect("a move").mov);
162 out.push(self.two(mov, fp, sp));
163 }
164 for ® in frame.saved_int() {
165 out.push(self.push(reg));
166 }
167 if let Some(to) = frame.realign() {
168 let and = self.opcode(self.insts.align);
169 out.push(self.arith(and, -i64::from(to)));
170 }
171 if frame.size() > 0 {
172 let sub = self.opcode(self.insts.sub);
173 out.push(self.arith(sub, i64::from(frame.size())));
174 }
175 for save in frame.saved_sse() {
176 out.push(self.store(self.conv.sse_class, save.reg, save.at));
177 }
178 out
179 }
180
181 fn epilogue(&mut self, frame: &Frame) -> Vec<Inst> {
188 let sp = self.conv.stack_pointer;
189 let fp = self.conv.frame_pointer;
190 let word = self.conv.word;
191 let mut out = Vec::new();
192 for save in frame.saved_sse() {
193 out.push(self.load(self.conv.sse_class, save.reg, save.at));
194 }
195 let pushed = u32::try_from(frame.saved_int().len()).expect("a frame");
196 if frame.frame_pointer() {
197 if pushed == 0 {
198 let mov = self.opcode(self.insts.moves(self.conv.int_class).expect("a move").mov);
199 out.push(self.two(mov, sp, fp));
200 } else {
201 let lea = self.opcode(self.insts.lea);
202 let back = -offset(word * pushed);
203 out.push(self.address(lea, sp, fp, back));
204 }
205 } else if frame.size() > 0 {
206 let add = self.opcode(self.insts.add);
207 out.push(self.arith(add, i64::from(frame.size())));
208 }
209 for ® in frame.saved_int().iter().rev() {
210 out.push(self.pop(reg));
211 }
212 if frame.frame_pointer() {
213 out.push(self.pop(fp));
214 }
215 let ret = self.opcode(self.insts.ret);
216 out.push(self.func.build_loose(ret).finish());
217 out
218 }
219
220 fn mov(&mut self, edit: &Edit, frame: &Frame) -> Inst {
222 let moves = self.insts.moves(edit.class).expect("a class the target says how to move");
223 match (edit.mov.to, edit.mov.from) {
224 (Place::Reg(to), Place::Reg(from)) => {
225 let mov = self.opcode(moves.mov);
226 self.func
227 .build_loose(mov)
228 .def(Reg::physical(to), edit.class)
229 .uses(Reg::physical(from), edit.class)
230 .finish()
231 }
232 (Place::Reg(to), Place::Slot(slot)) => {
233 let at = self.slot(frame, slot);
234 self.load(edit.class, to, at)
235 }
236 (Place::Slot(slot), Place::Reg(from)) => {
237 let at = self.slot(frame, slot);
238 self.store(edit.class, from, at)
239 }
240 (Place::Slot(_), Place::Slot(_)) => {
243 unreachable!("a move from one stack slot straight into another")
244 }
245 }
246 }
247
248 fn put(&mut self, cursors: &mut Vec<(At, Inst)>, at: At, inst: Inst) {
253 if let Some(cursor) = cursors.iter_mut().find(|(place, _)| *place == at) {
254 self.func.insert_after(cursor.1, inst);
255 cursor.1 = inst;
256 return;
257 }
258 match at {
259 At::Before(before) => self.func.insert_before(before, inst),
260 At::After(after) => self.func.insert_after(after, inst),
261 At::StartOf(block) => self.func.prepend_inst(block, inst),
262 At::EndOf(block) => self.func.append_inst(block, inst),
268 }
269 cursors.push((at, inst));
270 }
271
272 fn slot(&self, frame: &Frame, slot: u32) -> i32 {
274 frame.slot(slot).expect("a slot the frame was worked out from")
275 }
276
277 fn load(&mut self, class: RegClass, reg: PhysReg, at: i32) -> Inst {
279 let load = self.opcode(self.insts.moves(class).expect("a class to load").load);
280 let base = Operand::read(Reg::physical(self.conv.stack_pointer), self.conv.int_class);
281 self.func
282 .build_loose(load)
283 .def(Reg::physical(reg), class)
284 .mem(Mem::at(base).plus(at))
285 .finish()
286 }
287
288 fn store(&mut self, class: RegClass, reg: PhysReg, at: i32) -> Inst {
290 let store = self.opcode(self.insts.moves(class).expect("a class to store").store);
291 let base = Operand::read(Reg::physical(self.conv.stack_pointer), self.conv.int_class);
292 self.func
293 .build_loose(store)
294 .uses(Reg::physical(reg), class)
295 .mem(Mem::at(base).plus(at))
296 .finish()
297 }
298
299 fn push(&mut self, reg: PhysReg) -> Inst {
301 let push = self.opcode(self.insts.push);
302 self.func.build_loose(push).uses(Reg::physical(reg), self.conv.int_class).finish()
303 }
304
305 fn pop(&mut self, reg: PhysReg) -> Inst {
307 let pop = self.opcode(self.insts.pop);
308 self.func.build_loose(pop).def(Reg::physical(reg), self.conv.int_class).finish()
309 }
310
311 fn two(&mut self, opcode: Opcode, to: PhysReg, from: PhysReg) -> Inst {
313 let class = self.conv.int_class;
314 self.func
315 .build_loose(opcode)
316 .def(Reg::physical(to), class)
317 .uses(Reg::physical(from), class)
318 .finish()
319 }
320
321 fn arith(&mut self, opcode: Opcode, value: i64) -> Inst {
323 let class = self.conv.int_class;
324 let sp = Reg::physical(self.conv.stack_pointer);
325 self.func.build_loose(opcode).def(sp, class).uses(sp, class).imm(value).finish()
326 }
327
328 fn address(&mut self, opcode: Opcode, to: PhysReg, base: PhysReg, disp: i32) -> Inst {
330 let class = self.conv.int_class;
331 let base = Operand::read(Reg::physical(base), class);
332 self.func
333 .build_loose(opcode)
334 .def(Reg::physical(to), class)
335 .mem(Mem::at(base).plus(disp))
336 .finish()
337 }
338
339 fn opcode(&mut self, name: &str) -> Opcode {
342 Opcode::new(self.names.intern(&format!("{}{name}", self.insts.prefix)))
343 }
344}
345
346fn offset(bytes: u32) -> i32 {
348 i32::try_from(bytes).expect("a frame under two gigabytes")
349}
350
351#[cfg(test)]
352mod tests {
353 use rucc_base::Interner;
354 use rucc_mir::{BlockCall, print_func};
355 use rucc_regalloc::assign::Env;
356 use rucc_target::x86_64::{FRAME, GPR, REGS, SYSV, WIN64, XMM, xmm};
357
358 use super::*;
359 use crate::frame::{Layout, Local};
360
361 fn env(conv: &CallRegs, count: usize) -> Env {
364 Env::new().with(GPR, &conv.int_order[..count], &conv.int_order[count..])
365 }
366
367 fn pressure(conv: &CallRegs, values: usize, count: usize) -> (Func, Allocation, Interner) {
371 let mut names = Interner::new();
372 let mut func = Func::new(names.intern("f"));
373 let opcode = Opcode::new(names.intern("x64.nop"));
374 let block = func.create_block();
375 let regs: Vec<Reg> = (0..values).map(|_| func.new_vreg(GPR)).collect();
376 for ® in ®s {
377 func.build(block, opcode).def(reg, GPR).finish();
378 }
379 for ® in ®s {
380 func.build(block, opcode).uses(reg, GPR).finish();
381 }
382 let allocation = rucc_regalloc::run(&mut func, &env(conv, count));
383 (func, allocation, names)
384 }
385
386 fn written(
388 func: &mut Func,
389 allocation: &Allocation,
390 layout: &Layout<'_>,
391 names: &mut Interner,
392 ) -> Vec<String> {
393 let frame = Frame::of(func, allocation, layout);
394 finish(func, allocation, &frame, &Stack::default(), layout.conv, &FRAME, names);
395 print_func(func, names, ®S)
396 .lines()
397 .filter(|line| !line.is_empty())
398 .map(|line| line.trim().to_string())
399 .collect()
400 }
401
402 fn added(lines: &[String]) -> Vec<&str> {
405 lines
406 .iter()
407 .map(String::as_str)
408 .filter(|line| !line.contains("x64.nop"))
409 .filter(|line| !line.starts_with("mfunc") && !line.starts_with("block") && *line != "}")
410 .collect()
411 }
412
413 #[test]
414 fn a_function_that_needs_no_frame_is_given_a_return_and_nothing_else() {
415 let (mut func, allocation, mut names) = pressure(&SYSV, 2, 4);
416 let lines = written(&mut func, &allocation, &Layout::new(&SYSV, REGS), &mut names);
417
418 assert_eq!(added(&lines), ["x64.ret"]);
421 }
422
423 #[test]
424 fn a_spill_is_a_store_and_a_reload_is_a_load() {
425 let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
426 let lines = written(&mut func, &allocation, &Layout::new(&SYSV, REGS), &mut names);
427
428 assert_eq!(
433 lines,
434 [
435 "mfunc @f {",
436 "block0:",
437 "$rax = x64.nop",
438 "$rcx = x64.nop",
439 "$rdx = x64.nop",
440 "x64.mov_mr_64 $rdx, [$rsp - 16]",
441 "$rdx = x64.nop",
442 "x64.mov_mr_64 $rdx, [$rsp - 8]",
443 "x64.nop $rax",
444 "x64.nop $rcx",
445 "$rdx = x64.mov_rm_64 [$rsp - 16]",
446 "x64.nop $rdx",
447 "$rdx = x64.mov_rm_64 [$rsp - 8]",
448 "x64.nop $rdx",
449 "x64.ret",
450 "}",
451 ]
452 );
453 }
454
455 #[test]
456 fn the_frame_the_prologue_takes_is_the_frame_the_epilogue_gives_back() {
457 let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
458 let base = Layout::new(&SYSV, REGS);
459 let layout = Layout { red_zone: false, ..base };
460 let lines = written(&mut func, &allocation, &layout, &mut names);
461
462 assert_eq!(
465 added(&lines),
466 [
467 "$rsp = x64.sub_ri_64 $rsp, 16",
468 "x64.mov_mr_64 $rdx, [$rsp]",
469 "x64.mov_mr_64 $rdx, [$rsp + 8]",
470 "$rdx = x64.mov_rm_64 [$rsp]",
471 "$rdx = x64.mov_rm_64 [$rsp + 8]",
472 "$rsp = x64.add_ri_64 $rsp, 16",
473 "x64.ret",
474 ]
475 );
476 }
477
478 #[test]
479 fn the_registers_the_prologue_pushes_come_back_in_the_opposite_order() {
480 let (mut func, allocation, mut names) = pressure(&SYSV, 13, 13);
481 let lines = written(&mut func, &allocation, &Layout::new(&SYSV, REGS), &mut names);
482
483 assert_eq!(
486 added(&lines),
487 [
488 "x64.push_64 $rbx",
489 "x64.push_64 $r12",
490 "x64.push_64 $r13",
491 "x64.push_64 $r14",
492 "$r14 = x64.pop_64",
493 "$r13 = x64.pop_64",
494 "$r12 = x64.pop_64",
495 "$rbx = x64.pop_64",
496 "x64.ret",
497 ]
498 );
499 }
500
501 #[test]
502 fn a_function_that_keeps_a_frame_pointer_sets_it_up_and_leaves_by_it() {
503 let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
504 let base = Layout::new(&SYSV, REGS);
505 let layout = Layout { frame_pointer: true, red_zone: false, ..base };
506 let lines = written(&mut func, &allocation, &layout, &mut names);
507
508 assert_eq!(
511 added(&lines),
512 [
513 "x64.push_64 $rbp",
514 "$rbp = x64.mov_rr_64 $rsp",
515 "$rsp = x64.sub_ri_64 $rsp, 16",
516 "x64.mov_mr_64 $rdx, [$rsp]",
517 "x64.mov_mr_64 $rdx, [$rsp + 8]",
518 "$rdx = x64.mov_rm_64 [$rsp]",
519 "$rdx = x64.mov_rm_64 [$rsp + 8]",
520 "$rsp = x64.mov_rr_64 $rbp",
521 "$rbp = x64.pop_64",
522 "x64.ret",
523 ]
524 );
525 }
526
527 #[test]
528 fn a_realigned_frame_forces_the_alignment_after_it_has_pushed_what_it_saves() {
529 let (mut func, allocation, mut names) = pressure(&SYSV, 13, 13);
530 let locals = [Local { size: 64, align: 32 }];
531 let base = Layout::new(&SYSV, REGS);
532 let layout = Layout { locals: &locals, ..base };
533 let lines = written(&mut func, &allocation, &layout, &mut names);
534
535 assert_eq!(
539 added(&lines),
540 [
541 "x64.push_64 $rbp",
542 "$rbp = x64.mov_rr_64 $rsp",
543 "x64.push_64 $rbx",
544 "x64.push_64 $r12",
545 "x64.push_64 $r13",
546 "x64.push_64 $r14",
547 "$rsp = x64.and_ri_64 $rsp, -32",
548 "$rsp = x64.sub_ri_64 $rsp, 64",
549 "$rsp = x64.lea_64 [$rbp - 32]",
550 "$r14 = x64.pop_64",
551 "$r13 = x64.pop_64",
552 "$r12 = x64.pop_64",
553 "$rbx = x64.pop_64",
554 "$rbp = x64.pop_64",
555 "x64.ret",
556 ]
557 );
558 }
559
560 #[test]
561 fn every_block_the_function_returns_from_gets_an_epilogue() {
562 let mut names = Interner::new();
563 let mut func = Func::new(names.intern("f"));
564 let opcode = Opcode::new(names.intern("x64.nop"));
565 let head = func.create_block();
566 let left = func.create_block();
567 let right = func.create_block();
568 func.build(head, opcode).finish();
569 *func.succs_mut(head) = vec![BlockCall::to(left), BlockCall::to(right)];
570 func.build(left, opcode).finish();
571 func.build(right, opcode).finish();
572 let allocation = rucc_regalloc::run(&mut func, &env(&SYSV, 4));
573 let base = Layout::new(&SYSV, REGS);
574 let layout = Layout { leaf: false, ..base };
575 let lines = written(&mut func, &allocation, &layout, &mut names);
576
577 assert_eq!(
580 lines,
581 [
582 "mfunc @f {",
583 "block0:",
584 "$rsp = x64.sub_ri_64 $rsp, 8",
585 "x64.nop block1, block2",
586 "block1:",
587 "x64.nop",
588 "$rsp = x64.add_ri_64 $rsp, 8",
589 "x64.ret",
590 "block2:",
591 "x64.nop",
592 "$rsp = x64.add_ri_64 $rsp, 8",
593 "x64.ret",
594 "}",
595 ]
596 );
597 }
598
599 #[test]
600 fn a_vector_register_a_windows_call_preserves_is_stored_and_read_back() {
601 let mut names = Interner::new();
602 let mut func = Func::new(names.intern("f"));
603 let opcode = Opcode::new(names.intern("x64.nop"));
604 let block = func.create_block();
605 func.build(block, opcode).operand(Operand::write(Reg::physical(xmm(6)), XMM)).finish();
608 let allocation = rucc_regalloc::run(&mut func, &env(&WIN64, 4));
609 let lines = written(&mut func, &allocation, &Layout::new(&WIN64, REGS), &mut names);
610
611 assert_eq!(
614 added(&lines),
615 [
616 "$rsp = x64.sub_ri_64 $rsp, 24",
617 "x64.movaps_mr $xmm6, [$rsp]",
618 "$xmm6 = x64.movaps_rm [$rsp]",
619 "$rsp = x64.add_ri_64 $rsp, 24",
620 "x64.ret",
621 ]
622 );
623 }
624}