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