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