Skip to main content

lamina_codegen/x86_64/
util.rs

1//! Utility functions for x86_64 code generation.
2
3use lamina_mir::{Register, VirtualReg};
4
5/// Load a virtual register into RAX
6pub fn load_register_to_rax<
7    W: std::io::Write,
8    RA: crate::regalloc::RegisterAllocator<PhysReg = &'static str>,
9>(
10    reg: &VirtualReg,
11    writer: &mut W,
12    reg_alloc: &RA,
13    stack_slots: &std::collections::HashMap<VirtualReg, i32>,
14) -> Result<(), std::io::Error> {
15    if let Some(phys) = reg_alloc.get_mapping(reg) {
16        writeln!(writer, "    movq %{}, %rax", phys)?;
17    } else if let Some(offset) = stack_slots.get(reg) {
18        writeln!(writer, "    movq {}(%rbp), %rax", offset)?;
19    } else {
20        return Err(std::io::Error::new(
21            std::io::ErrorKind::InvalidData,
22            format!("Virtual register {:?} has no mapping or stack slot", reg),
23        ));
24    }
25    Ok(())
26}
27
28/// Store RAX to a virtual register
29pub fn store_rax_to_register<
30    W: std::io::Write,
31    RA: crate::regalloc::RegisterAllocator<PhysReg = &'static str>,
32>(
33    reg: &VirtualReg,
34    writer: &mut W,
35    reg_alloc: &RA,
36    stack_slots: &std::collections::HashMap<VirtualReg, i32>,
37) -> Result<(), std::io::Error> {
38    if let Some(phys) = reg_alloc.get_mapping(reg) {
39        writeln!(writer, "    movq %rax, %{}", phys)?;
40    } else if let Some(offset) = stack_slots.get(reg) {
41        writeln!(writer, "    movq %rax, {}(%rbp)", offset)?;
42    } else {
43        return Err(std::io::Error::new(
44            std::io::ErrorKind::InvalidData,
45            format!("Virtual register {:?} has no mapping or stack slot", reg),
46        ));
47    }
48    Ok(())
49}
50
51/// Load a register to another register
52pub fn load_register_to_register<
53    W: std::io::Write,
54    RA: crate::regalloc::RegisterAllocator<PhysReg = &'static str>,
55>(
56    src: &VirtualReg,
57    writer: &mut W,
58    reg_alloc: &RA,
59    stack_slots: &std::collections::HashMap<VirtualReg, i32>,
60    target_reg: &str,
61) -> Result<(), std::io::Error> {
62    if let Some(phys) = reg_alloc.get_mapping(src) {
63        writeln!(writer, "    movq %{}, %{}", phys, target_reg)?;
64    } else if let Some(offset) = stack_slots.get(src) {
65        writeln!(writer, "    movq {}(%rbp), %{}", offset, target_reg)?;
66    } else {
67        return Err(std::io::Error::new(
68            std::io::ErrorKind::InvalidData,
69            format!("Virtual register {:?} has no mapping or stack slot", src),
70        ));
71    }
72    Ok(())
73}
74
75/// Load an operand into RAX
76pub fn load_operand_to_rax<
77    W: std::io::Write,
78    RA: crate::regalloc::RegisterAllocator<PhysReg = &'static str>,
79>(
80    operand: &lamina_mir::Operand,
81    writer: &mut W,
82    reg_alloc: &RA,
83    stack_slots: &std::collections::HashMap<VirtualReg, i32>,
84) -> Result<(), std::io::Error> {
85    match operand {
86        lamina_mir::Operand::Register(reg) => match reg {
87            Register::Virtual(v) => load_register_to_rax(v, writer, reg_alloc, stack_slots),
88            Register::Physical(p) => {
89                writeln!(writer, "    movq %{}, %rax", p.name)?;
90                Ok(())
91            }
92        },
93        lamina_mir::Operand::Immediate(imm) => match imm {
94            lamina_mir::instruction::Immediate::I8(v) => {
95                writeln!(writer, "    movq ${}, %rax", *v as i64)
96            }
97            lamina_mir::instruction::Immediate::I16(v) => {
98                writeln!(writer, "    movq ${}, %rax", *v as i64)
99            }
100            lamina_mir::instruction::Immediate::I32(v) => {
101                writeln!(writer, "    movq ${}, %rax", *v as i64)
102            }
103            lamina_mir::instruction::Immediate::I64(v) => writeln!(writer, "    movq ${}, %rax", v),
104            lamina_mir::instruction::Immediate::F32(_)
105            | lamina_mir::instruction::Immediate::F64(_) => {
106                writeln!(
107                    writer,
108                    "    # Floating point immediates not yet implemented"
109                )?;
110                Ok(())
111            }
112        },
113    }
114}
115
116/// Load an operand into a specific register
117pub fn load_operand_to_register<
118    W: std::io::Write,
119    RA: crate::regalloc::RegisterAllocator<PhysReg = &'static str>,
120>(
121    operand: &lamina_mir::Operand,
122    writer: &mut W,
123    reg_alloc: &RA,
124    stack_slots: &std::collections::HashMap<VirtualReg, i32>,
125    target_reg: &str,
126) -> Result<(), std::io::Error> {
127    match operand {
128        lamina_mir::Operand::Register(reg) => match reg {
129            Register::Virtual(v) => {
130                load_register_to_register(v, writer, reg_alloc, stack_slots, target_reg)
131            }
132            Register::Physical(p) => {
133                writeln!(writer, "    movq %{}, %{}", p.name, target_reg)?;
134                Ok(())
135            }
136        },
137        lamina_mir::Operand::Immediate(imm) => match imm {
138            lamina_mir::instruction::Immediate::I8(v) => {
139                writeln!(writer, "    movq ${}, %{}", *v as i64, target_reg)
140            }
141            lamina_mir::instruction::Immediate::I16(v) => {
142                writeln!(writer, "    movq ${}, %{}", *v as i64, target_reg)
143            }
144            lamina_mir::instruction::Immediate::I32(v) => {
145                writeln!(writer, "    movq ${}, %{}", *v as i64, target_reg)
146            }
147            lamina_mir::instruction::Immediate::I64(v) => {
148                writeln!(writer, "    movq ${}, %{}", v, target_reg)
149            }
150            lamina_mir::instruction::Immediate::F32(_)
151            | lamina_mir::instruction::Immediate::F64(_) => {
152                writeln!(
153                    writer,
154                    "    # Floating point immediates not yet implemented"
155                )?;
156                Ok(())
157            }
158        },
159    }
160}