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#[derive(Debug, Clone)]
12pub enum RelocationKind {
13 RipRelative,
15 Relative32,
17 Absolute64,
19}
20
21#[derive(Debug, Clone)]
23pub struct Relocation {
24 pub instruction_index: usize,
26 pub target: String,
28 pub kind: RelocationKind,
30 pub addend: i32,
32}
33
34pub 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 pub fn emit(&mut self) -> Result<()> {
56 self.collect_strings();
57
58 self.push_inst(Instruction::Sub { dst: Operand::Reg(Register::RSP), src: Operand::Imm { value: 40, size: 8 } });
60
61 self.emit_entry_stub()?;
63
64 for function in &self.program.functions {
66 self.emit_function(function)?;
67 }
68
69 self.push_inst(Instruction::Add { dst: Operand::Reg(Register::RSP), src: Operand::Imm { value: 40, size: 8 } });
71
72 Ok(())
73 }
74
75 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); 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 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 self.push_reloc("main", RelocationKind::Relative32, 0);
112 self.push_inst(Instruction::Call { target: Operand::Imm { value: 0, size: 32 } });
113
114 self.push_inst(Instruction::Mov { dst: Operand::Reg(Register::RCX), src: Operand::Reg(Register::RAX) });
116
117 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 self.push_inst(Instruction::Label(function.name.clone()));
127
128 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 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 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 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 }
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 self.emit_call_setup(*argc)?;
322
323 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 self.push_inst(Instruction::Mov {
333 dst: Operand::Reg(Register::RCX),
334 src: Operand::Imm { value: 64, size: 64 }, });
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 self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) });
345 let offset = 8; 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 self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RBX) }); self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) }); 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 _ => { }
365 }
366 Ok(())
367 }
368
369 fn emit_managed_inst(&mut self, inst: &ManagedInstruction) -> Result<()> {
370 match inst {
371 ManagedInstruction::CallMethod { method, .. } => {
372 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 _ => { }
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 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}