Skip to main content

gaia_assembler/backends/windows/
emitter.rs

1use crate::{
2    instruction::{CmpCondition, CoreInstruction, GaiaInstruction, ManagedInstruction},
3    program::{GaiaBlock, GaiaConstant, GaiaFunction, GaiaModule},
4    types::GaiaType,
5};
6use gaia_types::{GaiaError, Result};
7use std::collections::HashMap;
8use x86_64_assembler::instruction::{Instruction, Operand, Register};
9
10/// Relocation kind
11#[derive(Debug, Clone)]
12pub enum RelocationKind {
13    /// RIP-relative addressing (32-bit offset)
14    RipRelative,
15    /// Relative 32-bit jump/call (rel32)
16    Relative32,
17    /// Absolute 64-bit address (imm64)
18    Absolute64,
19}
20
21/// Relocation entry
22#[derive(Debug, Clone)]
23pub struct Relocation {
24    /// Offset position in the instruction stream (relative to the start of the sequence)
25    pub instruction_index: usize,
26    /// Target symbol name (e.g., function name, label name, or data section offset)
27    pub target: String,
28    /// Relocation kind
29    pub kind: RelocationKind,
30    /// Additional offset
31    pub addend: i32,
32}
33
34/// x86_64 instruction emitter, converts Gaia IR to abstract instruction sequences and relocation info
35pub struct X64Emitter<'a> {
36    program: &'a GaiaModule,
37    instructions: Vec<Instruction>,
38    relocations: Vec<Relocation>,
39    string_table: HashMap<String, usize>,
40    rdata_content: Vec<u8>,
41}
42
43impl<'a> X64Emitter<'a> {
44    pub fn new(program: &'a GaiaModule) -> Self {
45        Self {
46            program,
47            instructions: Vec::new(),
48            relocations: Vec::new(),
49            string_table: HashMap::new(),
50            rdata_content: Vec::new(),
51        }
52    }
53
54    /// Perform emission logic
55    pub fn emit(&mut self) -> Result<()> {
56        self.collect_strings();
57
58        // sub rsp, 40 (Entry stack alignment)
59        self.push_inst(Instruction::Sub { dst: Operand::Reg(Register::RSP), src: Operand::Imm { value: 40, size: 8 } });
60
61        // 1. Generate Entry Stub
62        self.emit_entry_stub()?;
63
64        // 2. Generate all function bodies
65        for function in &self.program.functions {
66            self.emit_function(function)?;
67        }
68
69        // add rsp, 40
70        self.push_inst(Instruction::Add { dst: Operand::Reg(Register::RSP), src: Operand::Imm { value: 40, size: 8 } });
71
72        Ok(())
73    }
74
75    /// Extract all string constants used in the module and store them in the data section
76    fn collect_strings(&mut self) {
77        let mut next_offset = 0;
78        for function in &self.program.functions {
79            for block in &function.blocks {
80                for inst in &block.instructions {
81                    if let Some(s) = self.get_string_constant(inst) {
82                        if !self.string_table.contains_key(s) {
83                            self.string_table.insert(s.clone(), next_offset);
84                            self.rdata_content.extend_from_slice(s.as_bytes());
85                            self.rdata_content.push(0); // Null terminator
86                            next_offset += s.len() + 1;
87                        }
88                    }
89                }
90            }
91        }
92    }
93
94    fn get_string_constant<'b>(&self, inst: &'b GaiaInstruction) -> Option<&'b String> {
95        match inst {
96            GaiaInstruction::Core(CoreInstruction::PushConstant(GaiaConstant::String(s)))
97            | GaiaInstruction::Core(CoreInstruction::New(s))
98            | GaiaInstruction::Core(CoreInstruction::StoreField(_, s))
99            | GaiaInstruction::Core(CoreInstruction::LoadField(_, s))
100            | GaiaInstruction::Managed(ManagedInstruction::CallMethod { method: s, .. }) => Some(s),
101            _ => None,
102        }
103    }
104
105    fn emit_entry_stub(&mut self) -> Result<()> {
106        // call nyar_init_runtime (External Import)
107        self.push_reloc("nyar_init_runtime", RelocationKind::RipRelative, 0);
108        self.push_inst(Instruction::Call { target: Operand::Mem { base: None, index: None, scale: 1, displacement: 0 } });
109
110        // call main (Internal/Entry)
111        self.push_reloc("main", RelocationKind::Relative32, 0);
112        self.push_inst(Instruction::Call { target: Operand::Imm { value: 0, size: 32 } });
113
114        // mov rcx, rax (Return code to exit code)
115        self.push_inst(Instruction::Mov { dst: Operand::Reg(Register::RCX), src: Operand::Reg(Register::RAX) });
116
117        // call ExitProcess (External Import)
118        self.push_reloc("ExitProcess", RelocationKind::RipRelative, 0);
119        self.push_inst(Instruction::Call { target: Operand::Mem { base: None, index: None, scale: 1, displacement: 0 } });
120
121        Ok(())
122    }
123
124    fn emit_function(&mut self, function: &GaiaFunction) -> Result<()> {
125        // Record function label
126        self.push_inst(Instruction::Label(function.name.clone()));
127
128        // --- Prologue ---
129        self.push_inst(Instruction::Push { op: Operand::Reg(Register::RBP) });
130        self.push_inst(Instruction::Mov { dst: Operand::Reg(Register::RBP), src: Operand::Reg(Register::RSP) });
131
132        // Calculate stack size (Locals + Shadow Space)
133        let locals_count = function
134            .blocks
135            .iter()
136            .flat_map(|b| &b.instructions)
137            .filter(|i| matches!(i, GaiaInstruction::Core(CoreInstruction::Alloca(_, _))))
138            .count();
139        let has_managed_calls =
140            function.blocks.iter().flat_map(|b| &b.instructions).any(|i| matches!(i, GaiaInstruction::Managed(_)));
141
142        let locals_size = locals_count * 8;
143        let shadow_space = if has_managed_calls { 64 } else { 32 };
144        let total_stack_size = (locals_size + shadow_space + 15) & !15;
145
146        if total_stack_size > 0 {
147            self.push_inst(Instruction::Sub {
148                dst: Operand::Reg(Register::RSP),
149                src: Operand::Imm { value: total_stack_size as i64, size: 32 },
150            });
151        }
152
153        // Save first 4 parameters into Shadow Space (Windows x64 calling convention)
154        self.push_inst(Instruction::Mov {
155            dst: Operand::Mem { base: Some(Register::RBP), index: None, scale: 1, displacement: 0x10 },
156            src: Operand::Reg(Register::RCX),
157        });
158        self.push_inst(Instruction::Mov {
159            dst: Operand::Mem { base: Some(Register::RBP), index: None, scale: 1, displacement: 0x18 },
160            src: Operand::Reg(Register::RDX),
161        });
162        self.push_inst(Instruction::Mov {
163            dst: Operand::Mem { base: Some(Register::RBP), index: None, scale: 1, displacement: 0x20 },
164            src: Operand::Reg(Register::R8),
165        });
166        self.push_inst(Instruction::Mov {
167            dst: Operand::Mem { base: Some(Register::RBP), index: None, scale: 1, displacement: 0x28 },
168            src: Operand::Reg(Register::R9),
169        });
170
171        // --- Function Body ---
172        for block in &function.blocks {
173            self.emit_block(block, total_stack_size)?;
174        }
175
176        Ok(())
177    }
178
179    fn emit_block(&mut self, block: &GaiaBlock, total_stack_size: usize) -> Result<()> {
180        self.push_inst(Instruction::Label(block.label.clone()));
181
182        for inst in &block.instructions {
183            match inst {
184                GaiaInstruction::Core(core_inst) => self.emit_core_inst(core_inst, total_stack_size)?,
185                GaiaInstruction::Managed(managed_inst) => self.emit_managed_inst(managed_inst)?,
186                _ => return Err(GaiaError::custom_error(format!("Unsupported: {:?}", inst))),
187            }
188        }
189        Ok(())
190    }
191
192    fn emit_core_inst(&mut self, inst: &CoreInstruction, total_stack_size: usize) -> Result<()> {
193        match inst {
194            CoreInstruction::PushConstant(constant) => match constant {
195                GaiaConstant::I64(v) => {
196                    self.push_inst(Instruction::Mov {
197                        dst: Operand::Reg(Register::RAX),
198                        src: Operand::Imm { value: *v, size: 64 },
199                    });
200                    self.push_inst(Instruction::Push { op: Operand::Reg(Register::RAX) });
201                }
202                GaiaConstant::I32(v) => {
203                    self.push_inst(Instruction::Push { op: Operand::Imm { value: *v as i64, size: 32 } });
204                }
205                GaiaConstant::String(s) => {
206                    let offset = *self.string_table.get(s).unwrap() as i32;
207                    self.push_reloc(".rdata", RelocationKind::RipRelative, offset);
208                    self.push_inst(Instruction::Lea { dst: Register::RAX, displacement: 0, rip_relative: true });
209                    self.push_inst(Instruction::Push { op: Operand::Reg(Register::RAX) });
210                }
211                _ => {
212                    // TODO: Other constant types
213                }
214            },
215            CoreInstruction::Pop => {
216                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) });
217            }
218            CoreInstruction::Add(_) => {
219                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RBX) });
220                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) });
221                self.push_inst(Instruction::Add { dst: Operand::Reg(Register::RAX), src: Operand::Reg(Register::RBX) });
222                self.push_inst(Instruction::Push { op: Operand::Reg(Register::RAX) });
223            }
224            CoreInstruction::Sub(_) => {
225                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RBX) });
226                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) });
227                self.push_inst(Instruction::Sub { dst: Operand::Reg(Register::RAX), src: Operand::Reg(Register::RBX) });
228                self.push_inst(Instruction::Push { op: Operand::Reg(Register::RAX) });
229            }
230            CoreInstruction::Mul(_) => {
231                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RBX) });
232                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) });
233                self.push_inst(Instruction::Imul { dst: Register::RAX, src: Operand::Reg(Register::RBX) });
234                self.push_inst(Instruction::Push { op: Operand::Reg(Register::RAX) });
235            }
236            CoreInstruction::Div(_) => {
237                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RBX) });
238                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) });
239                self.push_inst(Instruction::Cqo);
240                self.push_inst(Instruction::Idiv { src: Operand::Reg(Register::RBX) });
241                self.push_inst(Instruction::Push { op: Operand::Reg(Register::RAX) });
242            }
243            CoreInstruction::Cmp(cond, _) => {
244                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RBX) });
245                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) });
246                self.push_inst(Instruction::Cmp { dst: Operand::Reg(Register::RAX), src: Operand::Reg(Register::RBX) });
247                let cc = match cond {
248                    CmpCondition::Eq => x86_64_assembler::instruction::Condition::E,
249                    CmpCondition::Ne => x86_64_assembler::instruction::Condition::NE,
250                    CmpCondition::Lt => x86_64_assembler::instruction::Condition::L,
251                    CmpCondition::Le => x86_64_assembler::instruction::Condition::LE,
252                    CmpCondition::Gt => x86_64_assembler::instruction::Condition::G,
253                    CmpCondition::Ge => x86_64_assembler::instruction::Condition::GE,
254                };
255                self.push_inst(Instruction::Setcc { cond: cc, dst: Operand::Reg(Register::AL) });
256                self.push_inst(Instruction::Movzx { dst: Register::RAX, src: Operand::Reg(Register::AL), size: 8 });
257                self.push_inst(Instruction::Push { op: Operand::Reg(Register::RAX) });
258            }
259            CoreInstruction::LoadLocal(idx, _) => {
260                let offset = -((*idx as i32 + 1) * 8);
261                self.push_inst(Instruction::Mov {
262                    dst: Operand::Reg(Register::RAX),
263                    src: Operand::Mem { base: Some(Register::RBP), index: None, scale: 1, displacement: offset },
264                });
265                self.push_inst(Instruction::Push { op: Operand::Reg(Register::RAX) });
266            }
267            CoreInstruction::StoreLocal(idx, _) => {
268                let offset = -((*idx as i32 + 1) * 8);
269                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) });
270                self.push_inst(Instruction::Mov {
271                    dst: Operand::Mem { base: Some(Register::RBP), index: None, scale: 1, displacement: offset },
272                    src: Operand::Reg(Register::RAX),
273                });
274            }
275            CoreInstruction::LoadArg(idx, _) => {
276                let offset = (*idx as i32 + 2) * 8;
277                self.push_inst(Instruction::Mov {
278                    dst: Operand::Reg(Register::RAX),
279                    src: Operand::Mem { base: Some(Register::RBP), index: None, scale: 1, displacement: offset },
280                });
281                self.push_inst(Instruction::Push { op: Operand::Reg(Register::RAX) });
282            }
283            CoreInstruction::BrTrue(target) => {
284                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) });
285                self.push_inst(Instruction::Test { dst: Operand::Reg(Register::RAX), src: Operand::Reg(Register::RAX) });
286                self.push_reloc(target, RelocationKind::Relative32, 0);
287                self.push_inst(Instruction::Jcc {
288                    cond: x86_64_assembler::instruction::Condition::NE,
289                    target: Operand::Imm { value: 0, size: 32 },
290                });
291            }
292            CoreInstruction::BrFalse(target) => {
293                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) });
294                self.push_inst(Instruction::Test { dst: Operand::Reg(Register::RAX), src: Operand::Reg(Register::RAX) });
295                self.push_reloc(target, RelocationKind::Relative32, 0);
296                self.push_inst(Instruction::Jcc {
297                    cond: x86_64_assembler::instruction::Condition::E,
298                    target: Operand::Imm { value: 0, size: 32 },
299                });
300            }
301            CoreInstruction::Label(name) => {
302                self.push_inst(Instruction::Label(name.clone()));
303            }
304            CoreInstruction::Ret => {
305                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) });
306                if total_stack_size > 0 {
307                    self.push_inst(Instruction::Add {
308                        dst: Operand::Reg(Register::RSP),
309                        src: Operand::Imm { value: total_stack_size as i64, size: 32 },
310                    });
311                }
312                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RBP) });
313                self.push_inst(Instruction::Ret);
314            }
315            CoreInstruction::Br(target) => {
316                self.push_reloc(target, RelocationKind::Relative32, 0);
317                self.push_inst(Instruction::Jmp { target: Operand::Imm { value: 0, size: 32 } });
318            }
319            CoreInstruction::Call(name, argc) => {
320                // Setup arguments (first 4 in registers, rest on stack)
321                self.emit_call_setup(*argc)?;
322
323                // Determine if it's an internal or external call
324                // Handled by relocation processor for now, which distinguishes symbol source
325                self.push_reloc(name, RelocationKind::Relative32, 0);
326                self.push_inst(Instruction::Call { target: Operand::Imm { value: 0, size: 32 } });
327                self.push_inst(Instruction::Push { op: Operand::Reg(Register::RAX) });
328            }
329            CoreInstruction::New(type_name) => {
330                // Call gc_alloc from runtime
331                // 1. Get type size (hardcoded for now or from metadata)
332                self.push_inst(Instruction::Mov {
333                    dst: Operand::Reg(Register::RCX),
334                    src: Operand::Imm { value: 64, size: 64 }, // TODO: Dynamically calculate type size
335                });
336                self.push_reloc("gaia_gc_alloc", RelocationKind::RipRelative, 0);
337                self.push_inst(Instruction::Call {
338                    target: Operand::Mem { base: None, index: None, scale: 1, displacement: 0 },
339                });
340                self.push_inst(Instruction::Push { op: Operand::Reg(Register::RAX) });
341            }
342            CoreInstruction::LoadField(type_name, field_name) => {
343                // Stack: [..., object_ptr]
344                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) });
345                // TODO: Get offset based on type_name and field_name
346                let offset = 8; // Hardcoded for now
347                self.push_inst(Instruction::Mov {
348                    dst: Operand::Reg(Register::RAX),
349                    src: Operand::Mem { base: Some(Register::RAX), index: None, scale: 1, displacement: offset },
350                });
351                self.push_inst(Instruction::Push { op: Operand::Reg(Register::RAX) });
352            }
353            CoreInstruction::StoreField(type_name, field_name) => {
354                // Stack: [..., object_ptr, value]
355                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RBX) }); // value
356                self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) }); // object_ptr
357                                                                                       // TODO: Get offset
358                let offset = 8;
359                self.push_inst(Instruction::Mov {
360                    dst: Operand::Mem { base: Some(Register::RAX), index: None, scale: 1, displacement: offset },
361                    src: Operand::Reg(Register::RBX),
362                });
363            }
364            _ => { /* TODO: Complete other infrequent instructions */ }
365        }
366        Ok(())
367    }
368
369    fn emit_managed_inst(&mut self, inst: &ManagedInstruction) -> Result<()> {
370        match inst {
371            ManagedInstruction::CallMethod { method, .. } => {
372                // Simplified: treat as ordinary Call
373                self.push_reloc(method, RelocationKind::Relative32, 0);
374                self.push_inst(Instruction::Call { target: Operand::Imm { value: 0, size: 32 } });
375                self.push_inst(Instruction::Push { op: Operand::Reg(Register::RAX) });
376            }
377            ManagedInstruction::CallStatic { method, .. } => {
378                self.push_reloc(method, RelocationKind::Relative32, 0);
379                self.push_inst(Instruction::Call { target: Operand::Imm { value: 0, size: 32 } });
380                self.push_inst(Instruction::Push { op: Operand::Reg(Register::RAX) });
381            }
382            _ => { /* TODO: Complete Managed instructions */ }
383        }
384        Ok(())
385    }
386
387    fn emit_call_setup(&mut self, argc: usize) -> Result<()> {
388        if argc >= 4 {
389            self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::R9) });
390        }
391        if argc >= 3 {
392            self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::R8) });
393        }
394        if argc >= 2 {
395            self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RDX) });
396        }
397        if argc >= 1 {
398            self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RCX) });
399        }
400        Ok(())
401    }
402
403    // --- 辅助方法 ---
404
405    fn push_inst(&mut self, inst: Instruction) {
406        self.instructions.push(inst);
407    }
408
409    fn push_reloc(&mut self, target: &str, kind: RelocationKind, addend: i32) {
410        self.relocations.push(Relocation {
411            instruction_index: self.instructions.len(),
412            target: target.to_string(),
413            kind,
414            addend,
415        });
416    }
417
418    pub fn take_result(self) -> (Vec<Instruction>, Vec<Relocation>, Vec<u8>) {
419        (self.instructions, self.relocations, self.rdata_content)
420    }
421}