use std::collections::HashMap;
use std::fmt::Write;
use super::x86_64_enc::{Cond, Mem, Reg64, Reg8, X86_64Encoder};
use super::{Backend, CodegenResult, Target};
use crate::codegen::debug::DwarfGenerator;
use crate::codegen::ir::*;
use crate::codegen::{GeneratedCode, OutputFormat};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86_64OutputMode {
Assembly,
MachineCode,
}
pub struct X86_64Backend {
output: String,
label_counter: u32,
mode: X86_64OutputMode,
encoder: Option<X86_64Encoder>,
generate_debug: bool,
dwarf: Option<DwarfGenerator>,
local_offsets: HashMap<LocalId, i32>,
func_ranges: Vec<(String, u64, u64)>,
}
impl X86_64Backend {
pub fn new() -> Self {
Self {
output: String::new(),
label_counter: 0,
mode: X86_64OutputMode::Assembly,
encoder: None,
generate_debug: false,
dwarf: None,
local_offsets: HashMap::new(),
func_ranges: Vec::new(),
}
}
pub fn with_machine_code(mut self) -> Self {
self.mode = X86_64OutputMode::MachineCode;
self.encoder = Some(X86_64Encoder::new());
self
}
pub fn with_debug_info(mut self) -> Self {
self.generate_debug = true;
self.dwarf = Some(DwarfGenerator::new());
self
}
fn fresh_label(&mut self, prefix: &str) -> String {
let label = format!(".L{}_{}", prefix, self.label_counter);
self.label_counter += 1;
label
}
fn enc(&mut self) -> &mut X86_64Encoder {
self.encoder
.as_mut()
.expect("Machine code mode not enabled")
}
fn generate_module(&mut self, module: &MirModule) -> CodegenResult<()> {
self.output.push_str("# Generated by QuantaLang Compiler\n");
self.output.push_str("# Target: x86-64\n\n");
self.output.push_str(".intel_syntax noprefix\n\n");
self.output.push_str(".section .data\n");
self.generate_data_section(module)?;
self.output.push_str("\n.section .rodata\n");
self.generate_rodata_section(module)?;
self.output.push_str("\n.section .text\n");
for func in &module.functions {
if !func.is_declaration() {
self.generate_function(func)?;
}
}
Ok(())
}
fn generate_data_section(&mut self, module: &MirModule) -> CodegenResult<()> {
for global in &module.globals {
if global.is_mut {
write!(self.output, ".globl {}\n", global.name).unwrap();
write!(self.output, "{}:\n", global.name).unwrap();
if let Some(init) = &global.init {
self.emit_const_data(init, &global.ty)?;
} else {
let size = self.type_size(&global.ty);
write!(self.output, " .zero {}\n", size).unwrap();
}
}
}
Ok(())
}
fn generate_rodata_section(&mut self, module: &MirModule) -> CodegenResult<()> {
for (i, s) in module.strings.iter().enumerate() {
write!(self.output, "__str{}:\n", i).unwrap();
write!(self.output, " .asciz \"{}\"\n", self.escape_string(s)).unwrap();
}
for global in &module.globals {
if !global.is_mut {
write!(self.output, ".globl {}\n", global.name).unwrap();
write!(self.output, "{}:\n", global.name).unwrap();
if let Some(init) = &global.init {
self.emit_const_data(init, &global.ty)?;
} else {
let size = self.type_size(&global.ty);
write!(self.output, " .zero {}\n", size).unwrap();
}
}
}
Ok(())
}
fn generate_function(&mut self, func: &MirFunction) -> CodegenResult<()> {
if func.is_public {
write!(self.output, ".globl {}\n", func.name).unwrap();
}
write!(self.output, "{}:\n", func.name).unwrap();
self.output.push_str(" push rbp\n");
self.output.push_str(" mov rbp, rsp\n");
let stack_size = self.calculate_stack_size(func);
if stack_size > 0 {
write!(self.output, " sub rsp, {}\n", stack_size).unwrap();
}
if let Some(blocks) = &func.blocks {
for block in blocks {
self.generate_block(block, func)?;
}
}
self.output.push('\n');
Ok(())
}
fn generate_block(&mut self, block: &MirBlock, func: &MirFunction) -> CodegenResult<()> {
let label = block
.label
.as_ref()
.map(|l| l.to_string())
.unwrap_or_else(|| format!(".Lbb{}_{}", func.name, block.id.0));
write!(self.output, "{}:\n", label).unwrap();
for stmt in &block.stmts {
self.generate_statement(stmt, func)?;
}
if let Some(term) = &block.terminator {
self.generate_terminator(term, func)?;
}
Ok(())
}
fn generate_statement(&mut self, stmt: &MirStmt, func: &MirFunction) -> CodegenResult<()> {
match &stmt.kind {
MirStmtKind::Assign { dest, value } => {
self.generate_assign(*dest, value, func)?;
}
MirStmtKind::DerefAssign { ptr, .. } => {
let offset = self.local_stack_offset(*ptr, func);
self.output.push_str(&format!(
" ; deref store through local at rbp-{}\n",
offset
));
self.output
.push_str(&format!(" mov rbx, [rbp-{}]\n", offset));
self.output.push_str(" mov qword [rbx], rax\n");
}
MirStmtKind::FieldDerefAssign {
ptr, field_name, ..
} => {
let offset = self.local_stack_offset(*ptr, func);
self.output.push_str(&format!(
" ; field deref store .{} through local at rbp-{}\n",
field_name, offset
));
self.output
.push_str(&format!(" mov rbx, [rbp-{}]\n", offset));
self.output.push_str(" mov qword [rbx], rax\n");
}
MirStmtKind::FieldAssign {
base, field_name, ..
} => {
let offset = self.local_stack_offset(*base, func);
self.output.push_str(&format!(
" ; field store .{} on local at rbp-{}\n",
field_name, offset
));
self.output.push_str(" mov qword [rbx], rax\n");
}
MirStmtKind::StorageLive(_) | MirStmtKind::StorageDead(_) => {
}
MirStmtKind::Nop => {
self.output.push_str(" nop\n");
}
}
Ok(())
}
fn generate_assign(
&mut self,
dest: LocalId,
value: &MirRValue,
func: &MirFunction,
) -> CodegenResult<()> {
match value {
MirRValue::Use(val) => {
self.load_value_to_rax(val, func)?;
self.store_rax_to_local(dest, func)?;
}
MirRValue::BinaryOp { op, left, right } => {
self.load_value_to_rax(left, func)?;
self.output.push_str(" push rax\n");
self.load_value_to_rax(right, func)?;
self.output.push_str(" mov rcx, rax\n");
self.output.push_str(" pop rax\n");
self.emit_binary_op(*op)?;
self.store_rax_to_local(dest, func)?;
}
MirRValue::UnaryOp { op, operand } => {
self.load_value_to_rax(operand, func)?;
match op {
UnaryOp::Neg => self.output.push_str(" neg rax\n"),
UnaryOp::Not => self.output.push_str(" not rax\n"),
}
self.store_rax_to_local(dest, func)?;
}
_ => {
write!(self.output, " # TODO: rvalue {:?}\n", value).unwrap();
}
}
Ok(())
}
fn generate_terminator(
&mut self,
term: &MirTerminator,
func: &MirFunction,
) -> CodegenResult<()> {
match term {
MirTerminator::Goto(target) => {
write!(self.output, " jmp .Lbb{}_{}\n", func.name, target.0).unwrap();
}
MirTerminator::If {
cond,
then_block,
else_block,
} => {
self.load_value_to_rax(cond, func)?;
self.output.push_str(" test rax, rax\n");
write!(self.output, " jnz .Lbb{}_{}\n", func.name, then_block.0).unwrap();
write!(self.output, " jmp .Lbb{}_{}\n", func.name, else_block.0).unwrap();
}
MirTerminator::Return(value) => {
if let Some(val) = value {
self.load_value_to_rax(val, func)?;
}
self.output.push_str(" mov rsp, rbp\n");
self.output.push_str(" pop rbp\n");
self.output.push_str(" ret\n");
}
MirTerminator::Call {
func: callee,
args,
dest,
target,
..
} => {
let arg_regs = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
for (i, arg) in args.iter().enumerate() {
if i < arg_regs.len() {
self.load_value_to_rax(arg, func)?;
write!(self.output, " mov {}, rax\n", arg_regs[i]).unwrap();
} else {
self.load_value_to_rax(arg, func)?;
self.output.push_str(" push rax\n");
}
}
let callee_name = match callee {
MirValue::Function(name) => name.to_string(),
_ => "unknown".to_string(),
};
write!(self.output, " call {}\n", callee_name).unwrap();
if let Some(d) = dest {
self.store_rax_to_local(*d, func)?;
}
if let Some(target_block) = target {
write!(
self.output,
" jmp .Lbb{}_{}\n",
func.name, target_block.0
)
.unwrap();
}
}
MirTerminator::Unreachable => {
self.output.push_str(" ud2\n");
}
MirTerminator::Abort => {
self.output.push_str(" call abort\n");
}
_ => {
write!(self.output, " # TODO: terminator {:?}\n", term).unwrap();
}
}
Ok(())
}
fn load_value_to_rax(&mut self, value: &MirValue, func: &MirFunction) -> CodegenResult<()> {
match value {
MirValue::Local(id) => {
let offset = self.local_stack_offset(*id, func);
write!(self.output, " mov rax, [rbp - {}]\n", offset).unwrap();
}
MirValue::Const(c) => match c {
MirConst::Int(v, _) => {
write!(self.output, " mov rax, {}\n", v).unwrap();
}
MirConst::Uint(v, _) => {
write!(self.output, " mov rax, {}\n", v).unwrap();
}
MirConst::Bool(b) => {
write!(self.output, " mov rax, {}\n", if *b { 1 } else { 0 }).unwrap();
}
_ => {
self.output.push_str(" xor rax, rax\n");
}
},
MirValue::Global(name) => {
write!(self.output, " lea rax, [rip + {}]\n", name).unwrap();
}
MirValue::Function(name) => {
write!(self.output, " lea rax, [rip + {}]\n", name).unwrap();
}
}
Ok(())
}
fn store_rax_to_local(&mut self, id: LocalId, func: &MirFunction) -> CodegenResult<()> {
let offset = self.local_stack_offset(id, func);
write!(self.output, " mov [rbp - {}], rax\n", offset).unwrap();
Ok(())
}
fn emit_binary_op(&mut self, op: BinOp) -> CodegenResult<()> {
match op {
BinOp::Add | BinOp::AddChecked | BinOp::AddWrapping | BinOp::AddSaturating => {
self.output.push_str(" add rax, rcx\n");
}
BinOp::Sub | BinOp::SubChecked | BinOp::SubWrapping | BinOp::SubSaturating => {
self.output.push_str(" sub rax, rcx\n");
}
BinOp::Mul | BinOp::MulChecked | BinOp::MulWrapping => {
self.output.push_str(" imul rax, rcx\n");
}
BinOp::Div => {
self.output.push_str(" cqo\n");
self.output.push_str(" idiv rcx\n");
}
BinOp::Rem => {
self.output.push_str(" cqo\n");
self.output.push_str(" idiv rcx\n");
self.output.push_str(" mov rax, rdx\n");
}
BinOp::BitAnd => {
self.output.push_str(" and rax, rcx\n");
}
BinOp::BitOr => {
self.output.push_str(" or rax, rcx\n");
}
BinOp::BitXor => {
self.output.push_str(" xor rax, rcx\n");
}
BinOp::Shl => {
self.output.push_str(" shl rax, cl\n");
}
BinOp::Shr => {
self.output.push_str(" sar rax, cl\n");
}
BinOp::Eq => {
self.output.push_str(" cmp rax, rcx\n");
self.output.push_str(" sete al\n");
self.output.push_str(" movzx rax, al\n");
}
BinOp::Ne => {
self.output.push_str(" cmp rax, rcx\n");
self.output.push_str(" setne al\n");
self.output.push_str(" movzx rax, al\n");
}
BinOp::Lt => {
self.output.push_str(" cmp rax, rcx\n");
self.output.push_str(" setl al\n");
self.output.push_str(" movzx rax, al\n");
}
BinOp::Le => {
self.output.push_str(" cmp rax, rcx\n");
self.output.push_str(" setle al\n");
self.output.push_str(" movzx rax, al\n");
}
BinOp::Gt => {
self.output.push_str(" cmp rax, rcx\n");
self.output.push_str(" setg al\n");
self.output.push_str(" movzx rax, al\n");
}
BinOp::Ge => {
self.output.push_str(" cmp rax, rcx\n");
self.output.push_str(" setge al\n");
self.output.push_str(" movzx rax, al\n");
}
BinOp::Pow => {
let label = self.label_counter;
self.label_counter += 1;
self.output.push_str(" mov r8, 1\n"); write!(self.output, ".Lpow_loop_{}:\n", label).unwrap();
self.output.push_str(" test rcx, rcx\n"); write!(self.output, " jz .Lpow_done_{}\n", label).unwrap();
self.output.push_str(" test rcx, 1\n"); write!(self.output, " jz .Lpow_skip_{}\n", label).unwrap();
self.output.push_str(" imul r8, rax\n"); write!(self.output, ".Lpow_skip_{}:\n", label).unwrap();
self.output.push_str(" imul rax, rax\n"); self.output.push_str(" shr rcx, 1\n"); write!(self.output, " jmp .Lpow_loop_{}\n", label).unwrap();
write!(self.output, ".Lpow_done_{}:\n", label).unwrap();
self.output.push_str(" mov rax, r8\n"); }
}
Ok(())
}
fn emit_const_data(&mut self, c: &MirConst, ty: &MirType) -> CodegenResult<()> {
match c {
MirConst::Int(v, _) => {
let size = self.type_size(ty);
match size {
1 => write!(self.output, " .byte {}\n", v).unwrap(),
2 => write!(self.output, " .word {}\n", v).unwrap(),
4 => write!(self.output, " .long {}\n", v).unwrap(),
8 => write!(self.output, " .quad {}\n", v).unwrap(),
_ => write!(self.output, " .quad {}\n", v).unwrap(),
}
}
MirConst::Uint(v, _) => {
let size = self.type_size(ty);
match size {
1 => write!(self.output, " .byte {}\n", v).unwrap(),
2 => write!(self.output, " .word {}\n", v).unwrap(),
4 => write!(self.output, " .long {}\n", v).unwrap(),
8 => write!(self.output, " .quad {}\n", v).unwrap(),
_ => write!(self.output, " .quad {}\n", v).unwrap(),
}
}
MirConst::Float(v, ty) => match ty {
MirType::Float(FloatSize::F32) => {
write!(self.output, " .float {}\n", v).unwrap();
}
_ => {
write!(self.output, " .double {}\n", v).unwrap();
}
},
MirConst::Bool(b) => {
write!(self.output, " .byte {}\n", if *b { 1 } else { 0 }).unwrap();
}
_ => {
let size = self.type_size(ty);
write!(self.output, " .zero {}\n", size).unwrap();
}
}
Ok(())
}
fn calculate_stack_size(&self, func: &MirFunction) -> u32 {
let mut size = 0u32;
for local in &func.locals {
if !local.is_param {
size += self.type_size(&local.ty) as u32;
}
}
(size + 15) & !15
}
fn local_stack_offset(&self, id: LocalId, func: &MirFunction) -> u32 {
let mut offset = 8; for local in &func.locals {
if local.id == id {
return offset;
}
if !local.is_param {
offset += self.type_size(&local.ty) as u32;
}
}
offset
}
fn type_size(&self, ty: &MirType) -> usize {
match ty {
MirType::Void => 0,
MirType::Bool => 1,
MirType::Int(size, _) => match size {
IntSize::I8 => 1,
IntSize::I16 => 2,
IntSize::I32 => 4,
IntSize::I64 | IntSize::ISize => 8,
IntSize::I128 => 16,
},
MirType::Float(size) => match size {
FloatSize::F32 => 4,
FloatSize::F64 => 8,
},
MirType::Ptr(_) => 8,
MirType::Array(elem, len) => self.type_size(elem) * (*len as usize),
MirType::Slice(_) => 16, MirType::Struct(_) => 8, MirType::FnPtr(_) => 8,
MirType::Never => 0,
MirType::Vector(elem, lanes) => self.type_size(elem) * (*lanes as usize),
MirType::Texture2D(_) | MirType::Sampler | MirType::SampledImage(_) => 8,
MirType::TraitObject(_) => 16, MirType::Vec(_) => 8, MirType::Tuple(elems) => elems.iter().map(|e| self.type_size(e)).sum(),
MirType::Map(_, _) => 8, }
}
fn escape_string(&self, s: &str) -> String {
let mut result = String::new();
for c in s.chars() {
match c {
'\n' => result.push_str("\\n"),
'\r' => result.push_str("\\r"),
'\t' => result.push_str("\\t"),
'\\' => result.push_str("\\\\"),
'"' => result.push_str("\\\""),
c if c.is_ascii_control() => {
result.push_str(&format!("\\{:03o}", c as u8));
}
c => result.push(c),
}
}
result
}
}
impl Default for X86_64Backend {
fn default() -> Self {
Self::new()
}
}
impl Backend for X86_64Backend {
fn generate(&mut self, mir: &MirModule) -> CodegenResult<GeneratedCode> {
self.output.clear();
self.label_counter = 0;
self.local_offsets.clear();
self.func_ranges.clear();
match self.mode {
X86_64OutputMode::Assembly => {
self.generate_module(mir)?;
Ok(GeneratedCode::new(
OutputFormat::Assembly,
self.output.as_bytes().to_vec(),
))
}
X86_64OutputMode::MachineCode => {
self.generate_machine_code(mir)?;
let code = self.encoder.take().unwrap().finish();
Ok(GeneratedCode::new(OutputFormat::Object, code))
}
}
}
fn target(&self) -> Target {
Target::X86_64
}
}
impl X86_64Backend {
fn generate_machine_code(&mut self, module: &MirModule) -> CodegenResult<()> {
self.encoder = Some(X86_64Encoder::new());
for func in &module.functions {
if !func.is_declaration() {
self.generate_function_machine_code(func)?;
}
}
Ok(())
}
fn generate_function_machine_code(&mut self, func: &MirFunction) -> CodegenResult<()> {
let start_pos = self.enc().position() as u64;
self.build_local_offsets(func);
self.enc().push(Reg64::RBP);
self.enc().mov_rr(Reg64::RBP, Reg64::RSP);
let stack_size = self.calculate_stack_size(func);
if stack_size > 0 {
self.enc().sub_ri32(Reg64::RSP, stack_size as i32);
}
let param_regs = [
Reg64::RDI,
Reg64::RSI,
Reg64::RDX,
Reg64::RCX,
Reg64::R8,
Reg64::R9,
];
let mut param_idx = 0;
for local in &func.locals {
if local.is_param && param_idx < param_regs.len() {
let offset = self.local_offsets.get(&local.id).copied().unwrap_or(8);
let mem = Mem::base_disp(Reg64::RBP, -offset);
self.enc().mov_mr(&mem, param_regs[param_idx]);
param_idx += 1;
}
}
let mut block_labels: HashMap<BlockId, u32> = HashMap::new();
if let Some(blocks) = &func.blocks {
for block in blocks {
let label = self.enc().new_label();
block_labels.insert(block.id, label);
}
}
if let Some(blocks) = &func.blocks {
for block in blocks {
let label = block_labels[&block.id];
self.enc().define_label(label);
self.generate_block_machine_code(block, func, &block_labels)?;
}
}
let end_pos = self.enc().position() as u64;
self.func_ranges
.push((func.name.to_string(), start_pos, end_pos - start_pos));
Ok(())
}
fn build_local_offsets(&mut self, func: &MirFunction) {
self.local_offsets.clear();
let mut offset = 8i32;
for local in &func.locals {
let size = self.type_size(&local.ty) as i32;
offset += size;
let align = size.min(8);
offset = (offset + align - 1) & !(align - 1);
self.local_offsets.insert(local.id, offset);
}
}
fn generate_block_machine_code(
&mut self,
block: &MirBlock,
func: &MirFunction,
block_labels: &HashMap<BlockId, u32>,
) -> CodegenResult<()> {
for stmt in &block.stmts {
self.generate_stmt_machine_code(stmt, func)?;
}
if let Some(term) = &block.terminator {
self.generate_terminator_machine_code(term, func, block_labels)?;
}
Ok(())
}
fn generate_stmt_machine_code(
&mut self,
stmt: &MirStmt,
func: &MirFunction,
) -> CodegenResult<()> {
match &stmt.kind {
MirStmtKind::Assign { dest, value } => {
self.generate_assign_machine_code(*dest, value, func)?;
}
MirStmtKind::DerefAssign { .. }
| MirStmtKind::FieldDerefAssign { .. }
| MirStmtKind::FieldAssign { .. } => {
self.enc().nop();
}
MirStmtKind::StorageLive(_) | MirStmtKind::StorageDead(_) => {
}
MirStmtKind::Nop => {
self.enc().nop();
}
}
Ok(())
}
fn generate_assign_machine_code(
&mut self,
dest: LocalId,
value: &MirRValue,
func: &MirFunction,
) -> CodegenResult<()> {
match value {
MirRValue::Use(val) => {
self.load_value_to_reg(val, Reg64::RAX, func)?;
self.store_reg_to_local(Reg64::RAX, dest)?;
}
MirRValue::BinaryOp { op, left, right } => {
self.load_value_to_reg(left, Reg64::RAX, func)?;
self.enc().push(Reg64::RAX);
self.load_value_to_reg(right, Reg64::RCX, func)?;
self.enc().pop(Reg64::RAX);
self.emit_binary_op_machine_code(*op)?;
self.store_reg_to_local(Reg64::RAX, dest)?;
}
MirRValue::UnaryOp { op, operand } => {
self.load_value_to_reg(operand, Reg64::RAX, func)?;
match op {
UnaryOp::Neg => self.enc().neg(Reg64::RAX),
UnaryOp::Not => self.enc().not(Reg64::RAX),
}
self.store_reg_to_local(Reg64::RAX, dest)?;
}
MirRValue::Ref { place, .. } => {
if place.projections.is_empty() {
let offset = self.local_offsets.get(&place.local).copied().unwrap_or(8);
let mem = Mem::base_disp(Reg64::RBP, -offset);
self.enc().lea(Reg64::RAX, &mem);
self.store_reg_to_local(Reg64::RAX, dest)?;
}
}
MirRValue::Cast { value, ty: _, .. } => {
self.load_value_to_reg(value, Reg64::RAX, func)?;
self.store_reg_to_local(Reg64::RAX, dest)?;
}
_ => {
self.enc().xor_rr(Reg64::RAX, Reg64::RAX);
self.store_reg_to_local(Reg64::RAX, dest)?;
}
}
Ok(())
}
fn load_value_to_reg(
&mut self,
value: &MirValue,
reg: Reg64,
_func: &MirFunction,
) -> CodegenResult<()> {
match value {
MirValue::Local(id) => {
let offset = self.local_offsets.get(id).copied().unwrap_or(8);
let mem = Mem::base_disp(Reg64::RBP, -offset);
self.enc().mov_rm(reg, &mem);
}
MirValue::Const(c) => {
match c {
MirConst::Int(v, _) => {
let v = *v as i64;
if v >= i32::MIN as i64 && v <= i32::MAX as i64 {
self.enc().mov_ri32(reg, v as i32);
} else {
self.enc().mov_ri64(reg, v);
}
}
MirConst::Uint(v, _) => {
let v = *v as i64;
if v >= i32::MIN as i64 && v <= i32::MAX as i64 {
self.enc().mov_ri32(reg, v as i32);
} else {
self.enc().mov_ri64(reg, v);
}
}
MirConst::Bool(b) => {
if *b {
self.enc().mov_ri32(reg, 1);
} else {
self.enc().xor_rr(reg, reg);
}
}
MirConst::Float(v, ty) => {
match ty {
MirType::Float(FloatSize::F32) => {
let bits = (*v as f32).to_bits();
self.enc().mov_ri32(reg, bits as i32);
}
_ => {
let bits = v.to_bits();
self.enc().mov_ri64(reg, bits as i64);
}
}
}
_ => {
self.enc().xor_rr(reg, reg);
}
}
}
MirValue::Global(_name) => {
let mem = Mem::rip_relative(0);
self.enc().lea(reg, &mem);
}
MirValue::Function(_name) => {
let mem = Mem::rip_relative(0);
self.enc().lea(reg, &mem);
}
}
Ok(())
}
fn store_reg_to_local(&mut self, reg: Reg64, id: LocalId) -> CodegenResult<()> {
let offset = self.local_offsets.get(&id).copied().unwrap_or(8);
let mem = Mem::base_disp(Reg64::RBP, -offset);
self.enc().mov_mr(&mem, reg);
Ok(())
}
fn emit_binary_op_machine_code(&mut self, op: BinOp) -> CodegenResult<()> {
match op {
BinOp::Add | BinOp::AddChecked | BinOp::AddWrapping | BinOp::AddSaturating => {
self.enc().add_rr(Reg64::RAX, Reg64::RCX);
}
BinOp::Sub | BinOp::SubChecked | BinOp::SubWrapping | BinOp::SubSaturating => {
self.enc().sub_rr(Reg64::RAX, Reg64::RCX);
}
BinOp::Mul | BinOp::MulChecked | BinOp::MulWrapping => {
self.enc().imul_rr(Reg64::RAX, Reg64::RCX);
}
BinOp::Div => {
self.enc().cqo();
self.enc().idiv(Reg64::RCX);
}
BinOp::Rem => {
self.enc().cqo();
self.enc().idiv(Reg64::RCX);
self.enc().mov_rr(Reg64::RAX, Reg64::RDX);
}
BinOp::BitAnd => {
self.enc().and_rr(Reg64::RAX, Reg64::RCX);
}
BinOp::BitOr => {
self.enc().or_rr(Reg64::RAX, Reg64::RCX);
}
BinOp::BitXor => {
self.enc().xor_rr(Reg64::RAX, Reg64::RCX);
}
BinOp::Shl => {
self.enc().shl_cl(Reg64::RAX);
}
BinOp::Shr => {
self.enc().sar_cl(Reg64::RAX);
}
BinOp::Eq => {
self.enc().cmp_rr(Reg64::RAX, Reg64::RCX);
self.enc().setcc(Cond::E, Reg8::AL);
self.enc().movzx_r64_r8(Reg64::RAX, Reg8::AL);
}
BinOp::Ne => {
self.enc().cmp_rr(Reg64::RAX, Reg64::RCX);
self.enc().setcc(Cond::NE, Reg8::AL);
self.enc().movzx_r64_r8(Reg64::RAX, Reg8::AL);
}
BinOp::Lt => {
self.enc().cmp_rr(Reg64::RAX, Reg64::RCX);
self.enc().setcc(Cond::L, Reg8::AL);
self.enc().movzx_r64_r8(Reg64::RAX, Reg8::AL);
}
BinOp::Le => {
self.enc().cmp_rr(Reg64::RAX, Reg64::RCX);
self.enc().setcc(Cond::LE, Reg8::AL);
self.enc().movzx_r64_r8(Reg64::RAX, Reg8::AL);
}
BinOp::Gt => {
self.enc().cmp_rr(Reg64::RAX, Reg64::RCX);
self.enc().setcc(Cond::G, Reg8::AL);
self.enc().movzx_r64_r8(Reg64::RAX, Reg8::AL);
}
BinOp::Ge => {
self.enc().cmp_rr(Reg64::RAX, Reg64::RCX);
self.enc().setcc(Cond::GE, Reg8::AL);
self.enc().movzx_r64_r8(Reg64::RAX, Reg8::AL);
}
BinOp::Pow => {
let loop_label = self.enc().create_label();
let done_label = self.enc().create_label();
let skip_label = self.enc().create_label();
self.enc().mov_ri(Reg64::R8, 1); self.enc().bind_label(loop_label);
self.enc().test_rr(Reg64::RCX, Reg64::RCX);
self.enc().jcc_label(Cond::E, done_label); self.enc().test_ri(Reg64::RCX, 1);
self.enc().jcc_label(Cond::E, skip_label); self.enc().imul_rr(Reg64::R8, Reg64::RAX); self.enc().bind_label(skip_label);
self.enc().imul_rr(Reg64::RAX, Reg64::RAX); self.enc().shr_ri(Reg64::RCX, 1); self.enc().jmp_label(loop_label);
self.enc().bind_label(done_label);
self.enc().mov_rr(Reg64::RAX, Reg64::R8); }
}
Ok(())
}
fn generate_terminator_machine_code(
&mut self,
term: &MirTerminator,
func: &MirFunction,
block_labels: &HashMap<BlockId, u32>,
) -> CodegenResult<()> {
match term {
MirTerminator::Goto(target) => {
let label = block_labels[target];
self.enc().jmp_label(label);
}
MirTerminator::If {
cond,
then_block,
else_block,
} => {
self.load_value_to_reg(cond, Reg64::RAX, func)?;
self.enc().test_rr(Reg64::RAX, Reg64::RAX);
let then_label = block_labels[then_block];
let else_label = block_labels[else_block];
self.enc().jcc_label(Cond::NE, then_label);
self.enc().jmp_label(else_label);
}
MirTerminator::Return(value) => {
if let Some(val) = value {
self.load_value_to_reg(val, Reg64::RAX, func)?;
}
self.enc().mov_rr(Reg64::RSP, Reg64::RBP);
self.enc().pop(Reg64::RBP);
self.enc().ret();
}
MirTerminator::Call {
func: callee,
args,
dest,
target,
..
} => {
let arg_regs = [
Reg64::RDI,
Reg64::RSI,
Reg64::RDX,
Reg64::RCX,
Reg64::R8,
Reg64::R9,
];
for (i, arg) in args.iter().enumerate() {
if i < arg_regs.len() {
self.load_value_to_reg(arg, arg_regs[i], func)?;
} else {
self.load_value_to_reg(arg, Reg64::RAX, func)?;
self.enc().push(Reg64::RAX);
}
}
if let MirValue::Function(name) = callee {
self.enc().call_symbol(name);
} else {
self.load_value_to_reg(callee, Reg64::RAX, func)?;
self.enc().call_reg(Reg64::RAX);
}
if let Some(d) = dest {
self.store_reg_to_local(Reg64::RAX, *d)?;
}
if let Some(target_block) = target {
let label = block_labels[target_block];
self.enc().jmp_label(label);
}
}
MirTerminator::Switch {
value,
targets,
default,
} => {
self.load_value_to_reg(value, Reg64::RAX, func)?;
for (case_val, case_block) in targets {
let v_i32 = match case_val {
MirConst::Int(v, _) => *v as i32,
MirConst::Uint(v, _) => *v as i32,
_ => continue,
};
self.enc().cmp_ri32(Reg64::RAX, v_i32);
let label = block_labels[case_block];
self.enc().jcc_label(Cond::E, label);
}
let default_label = block_labels[default];
self.enc().jmp_label(default_label);
}
MirTerminator::Unreachable => {
self.enc().ud2();
}
MirTerminator::Abort => {
self.enc().call_symbol("abort");
}
MirTerminator::Drop { target, .. } => {
let label = block_labels[target];
self.enc().jmp_label(label);
}
MirTerminator::Assert {
cond,
expected,
target,
..
} => {
self.load_value_to_reg(cond, Reg64::RAX, func)?;
self.enc().test_rr(Reg64::RAX, Reg64::RAX);
let label = block_labels[target];
if *expected {
self.enc().jcc_label(Cond::NE, label);
} else {
self.enc().jcc_label(Cond::E, label);
}
self.enc().call_symbol("abort");
}
MirTerminator::Resume => {
self.enc().call_symbol("_Unwind_Resume");
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codegen::builder::{values, MirBuilder, MirModuleBuilder};
#[test]
fn test_x86_64_backend_simple() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![MirType::i32(), MirType::i32()], MirType::i32());
let mut builder = MirBuilder::new("add", sig);
let a = builder.param_local(0);
let b = builder.param_local(1);
let result = builder.create_local(MirType::i32());
builder.binary_op(result, BinOp::Add, values::local(a), values::local(b));
builder.ret(Some(values::local(result)));
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new();
let output = backend.generate(&module).unwrap();
let code = output.as_string().unwrap();
assert!(code.contains(".intel_syntax noprefix"));
assert!(code.contains("add:"));
assert!(code.contains("ret"));
}
#[test]
fn test_x86_64_backend_void_function() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![], MirType::Void);
let mut builder = MirBuilder::new("noop", sig);
builder.ret_void();
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new();
let output = backend.generate(&module).unwrap();
let code = output.as_string().unwrap();
assert!(code.contains("noop:"));
assert!(code.contains("push rbp"));
assert!(code.contains("mov rbp, rsp"));
assert!(code.contains("ret"));
}
#[test]
fn test_x86_64_backend_prologue_epilogue() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![], MirType::i32());
let mut builder = MirBuilder::new("test_func", sig);
builder.ret(Some(values::i32(42)));
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new();
let output = backend.generate(&module).unwrap();
let code = output.as_string().unwrap();
assert!(code.contains("push rbp"));
assert!(code.contains("mov rbp, rsp"));
assert!(code.contains("mov rsp, rbp"));
assert!(code.contains("pop rbp"));
}
#[test]
fn test_x86_64_backend_branching() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![MirType::Bool], MirType::i32());
let mut builder = MirBuilder::new("branch_test", sig);
let cond = builder.param_local(0);
let then_block = builder.create_block();
let else_block = builder.create_block();
builder.branch(values::local(cond), then_block, else_block);
builder.switch_to_block(then_block);
builder.ret(Some(values::i32(1)));
builder.switch_to_block(else_block);
builder.ret(Some(values::i32(0)));
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new();
let output = backend.generate(&module).unwrap();
let code = output.as_string().unwrap();
assert!(code.contains("test rax, rax"));
assert!(code.contains("jnz"));
assert!(code.contains("jmp"));
}
#[test]
fn test_x86_64_backend_binary_ops() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![MirType::i32(), MirType::i32()], MirType::i32());
let mut builder = MirBuilder::new("sub_test", sig);
let a = builder.param_local(0);
let b = builder.param_local(1);
let result = builder.create_local(MirType::i32());
builder.binary_op(result, BinOp::Sub, values::local(a), values::local(b));
builder.ret(Some(values::local(result)));
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new();
let output = backend.generate(&module).unwrap();
let code = output.as_string().unwrap();
assert!(code.contains("sub rax, rcx"));
}
#[test]
fn test_x86_64_backend_mul() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![MirType::i32(), MirType::i32()], MirType::i32());
let mut builder = MirBuilder::new("mul_test", sig);
let a = builder.param_local(0);
let b = builder.param_local(1);
let result = builder.create_local(MirType::i32());
builder.binary_op(result, BinOp::Mul, values::local(a), values::local(b));
builder.ret(Some(values::local(result)));
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new();
let output = backend.generate(&module).unwrap();
let code = output.as_string().unwrap();
assert!(code.contains("imul rax, rcx"));
}
#[test]
fn test_x86_64_backend_comparison() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![MirType::i32(), MirType::i32()], MirType::Bool);
let mut builder = MirBuilder::new("eq_test", sig);
let a = builder.param_local(0);
let b = builder.param_local(1);
let result = builder.create_local(MirType::Bool);
builder.binary_op(result, BinOp::Eq, values::local(a), values::local(b));
builder.ret(Some(values::local(result)));
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new();
let output = backend.generate(&module).unwrap();
let code = output.as_string().unwrap();
assert!(code.contains("cmp rax, rcx"));
assert!(code.contains("sete al"));
}
#[test]
fn test_x86_64_backend_global_variable() {
let mut module_builder = MirModuleBuilder::new("test");
let mut global = MirGlobal::new("MY_VAR", MirType::i64());
global.is_mut = true;
module_builder.add_global(global);
let sig = MirFnSig::new(vec![], MirType::Void);
let mut builder = MirBuilder::new("main", sig);
builder.ret_void();
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new();
let output = backend.generate(&module).unwrap();
let code = output.as_string().unwrap();
assert!(code.contains(".section .data"));
assert!(code.contains("MY_VAR:"));
}
#[test]
fn test_x86_64_backend_string_table() {
let mut module_builder = MirModuleBuilder::new("test");
module_builder.intern_string("hello world");
let sig = MirFnSig::new(vec![], MirType::Void);
let mut builder = MirBuilder::new("main", sig);
builder.ret_void();
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new();
let output = backend.generate(&module).unwrap();
let code = output.as_string().unwrap();
assert!(code.contains(".section .rodata"));
assert!(code.contains("__str0:"));
assert!(code.contains(".asciz"));
}
#[test]
fn test_x86_64_backend_target() {
let backend = X86_64Backend::new();
assert_eq!(backend.target(), Target::X86_64);
}
#[test]
fn test_x86_64_backend_type_size() {
let backend = X86_64Backend::new();
assert_eq!(backend.type_size(&MirType::Void), 0);
assert_eq!(backend.type_size(&MirType::Bool), 1);
assert_eq!(backend.type_size(&MirType::i8()), 1);
assert_eq!(backend.type_size(&MirType::i16()), 2);
assert_eq!(backend.type_size(&MirType::i32()), 4);
assert_eq!(backend.type_size(&MirType::i64()), 8);
assert_eq!(backend.type_size(&MirType::Int(IntSize::I128, true)), 16);
assert_eq!(backend.type_size(&MirType::f32()), 4);
assert_eq!(backend.type_size(&MirType::f64()), 8);
assert_eq!(
backend.type_size(&MirType::Ptr(Box::new(MirType::i32()))),
8
);
}
#[test]
fn test_x86_64_backend_unary_op() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![MirType::i32()], MirType::i32());
let mut builder = MirBuilder::new("negate", sig);
let a = builder.param_local(0);
let result = builder.create_local(MirType::i32());
builder.unary_op(result, UnaryOp::Neg, values::local(a));
builder.ret(Some(values::local(result)));
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new();
let output = backend.generate(&module).unwrap();
let code = output.as_string().unwrap();
assert!(code.contains("neg rax"));
}
#[test]
fn test_x86_64_backend_public_function() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![], MirType::Void);
let mut func = MirFunction::new("public_fn", sig);
func.is_public = true;
func.add_block(MirBlock::new(BlockId::ENTRY));
func.block_mut(BlockId::ENTRY)
.unwrap()
.set_terminator(MirTerminator::Return(None));
module_builder.add_function(func);
let module = module_builder.build();
let mut backend = X86_64Backend::new();
let output = backend.generate(&module).unwrap();
let code = output.as_string().unwrap();
assert!(code.contains(".globl public_fn"));
}
#[test]
fn test_x86_64_machine_code_simple() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![], MirType::i32());
let mut builder = MirBuilder::new("ret42", sig);
builder.ret(Some(values::i32(42)));
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new().with_machine_code();
let output = backend.generate(&module).unwrap();
assert_eq!(output.format, OutputFormat::Object);
assert!(!output.data.is_empty());
assert!(output.data.contains(&0x55));
}
#[test]
fn test_x86_64_machine_code_add() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![MirType::i64(), MirType::i64()], MirType::i64());
let mut builder = MirBuilder::new("add", sig);
let a = builder.param_local(0);
let b = builder.param_local(1);
let result = builder.create_local(MirType::i64());
builder.binary_op(result, BinOp::Add, values::local(a), values::local(b));
builder.ret(Some(values::local(result)));
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new().with_machine_code();
let output = backend.generate(&module).unwrap();
assert_eq!(output.format, OutputFormat::Object);
assert!(!output.data.is_empty());
}
#[test]
fn test_x86_64_machine_code_with_branch() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![MirType::Bool], MirType::i32());
let mut builder = MirBuilder::new("branch", sig);
let cond = builder.param_local(0);
let then_block = builder.create_block();
let else_block = builder.create_block();
builder.branch(values::local(cond), then_block, else_block);
builder.switch_to_block(then_block);
builder.ret(Some(values::i32(1)));
builder.switch_to_block(else_block);
builder.ret(Some(values::i32(0)));
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new().with_machine_code();
let output = backend.generate(&module).unwrap();
assert_eq!(output.format, OutputFormat::Object);
assert!(!output.data.is_empty());
let contains_jmp = output.data.iter().any(|&b| b == 0xE9);
let contains_jcc = output
.data
.windows(2)
.any(|w| w[0] == 0x0F && (w[1] & 0xF0) == 0x80);
assert!(contains_jmp || contains_jcc);
}
#[test]
fn test_x86_64_machine_code_prologue_epilogue() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![], MirType::Void);
let mut builder = MirBuilder::new("noop", sig);
builder.ret_void();
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new().with_machine_code();
let output = backend.generate(&module).unwrap();
assert!(output.data.starts_with(&[0x55]));
assert_eq!(*output.data.last().unwrap(), 0xC3);
}
#[test]
fn test_x86_64_machine_code_binary_ops() {
let ops = [
(BinOp::Add, "add"),
(BinOp::Sub, "sub"),
(BinOp::Mul, "mul"),
(BinOp::BitAnd, "and"),
(BinOp::BitOr, "or"),
(BinOp::BitXor, "xor"),
];
for (op, name) in ops {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![MirType::i64(), MirType::i64()], MirType::i64());
let mut builder = MirBuilder::new(name, sig);
let a = builder.param_local(0);
let b = builder.param_local(1);
let result = builder.create_local(MirType::i64());
builder.binary_op(result, op, values::local(a), values::local(b));
builder.ret(Some(values::local(result)));
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new().with_machine_code();
let output = backend.generate(&module).unwrap();
assert_eq!(output.format, OutputFormat::Object);
assert!(!output.data.is_empty(), "Failed for op: {}", name);
}
}
#[test]
fn test_x86_64_machine_code_comparison() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![MirType::i64(), MirType::i64()], MirType::Bool);
let mut builder = MirBuilder::new("eq", sig);
let a = builder.param_local(0);
let b = builder.param_local(1);
let result = builder.create_local(MirType::Bool);
builder.binary_op(result, BinOp::Eq, values::local(a), values::local(b));
builder.ret(Some(values::local(result)));
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new().with_machine_code();
let output = backend.generate(&module).unwrap();
let has_setcc = output
.data
.windows(2)
.any(|w| w[0] == 0x0F && (w[1] & 0xF0) == 0x90);
assert!(has_setcc);
}
#[test]
fn test_x86_64_output_mode() {
let backend = X86_64Backend::new();
assert_eq!(backend.mode, X86_64OutputMode::Assembly);
let backend = X86_64Backend::new().with_machine_code();
assert_eq!(backend.mode, X86_64OutputMode::MachineCode);
assert!(backend.encoder.is_some());
}
#[test]
fn test_x86_64_machine_code_unary_neg() {
let mut module_builder = MirModuleBuilder::new("test");
let sig = MirFnSig::new(vec![MirType::i64()], MirType::i64());
let mut builder = MirBuilder::new("negate", sig);
let a = builder.param_local(0);
let result = builder.create_local(MirType::i64());
builder.unary_op(result, UnaryOp::Neg, values::local(a));
builder.ret(Some(values::local(result)));
module_builder.add_function(builder.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new().with_machine_code();
let output = backend.generate(&module).unwrap();
assert_eq!(output.format, OutputFormat::Object);
assert!(output.data.contains(&0xF7));
}
#[test]
fn test_x86_64_machine_code_multiple_functions() {
let mut module_builder = MirModuleBuilder::new("test");
let sig1 = MirFnSig::new(vec![], MirType::i32());
let mut builder1 = MirBuilder::new("func1", sig1);
builder1.ret(Some(values::i32(1)));
module_builder.add_function(builder1.build());
let sig2 = MirFnSig::new(vec![], MirType::i32());
let mut builder2 = MirBuilder::new("func2", sig2);
builder2.ret(Some(values::i32(2)));
module_builder.add_function(builder2.build());
let module = module_builder.build();
let mut backend = X86_64Backend::new().with_machine_code();
let output = backend.generate(&module).unwrap();
assert_eq!(output.format, OutputFormat::Object);
let push_count = output.data.iter().filter(|&&b| b == 0x55).count();
assert_eq!(push_count, 2);
}
}