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#[derive(Debug, Clone)]
13pub enum RelocationKind {
14 RipRelative,
16 Relative32,
18 Absolute64,
20}
21
22#[derive(Debug, Clone)]
24pub struct Relocation {
25 pub instruction_index: usize,
27 pub target: String,
29 pub kind: RelocationKind,
31 pub addend: i32,
33}
34
35#[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 pub fn emit(&mut self) -> Result<()> {
59 self.collect_strings();
60
61 self.push_inst(Instruction::Sub { dst: Operand::Reg(Register::RSP), src: Operand::Imm { value: 40, size: 8 } });
63
64 self.emit_entry_stub()?;
66
67 for function in &self.program.functions {
69 self.emit_function(function)?;
70 }
71
72 self.push_inst(Instruction::Add { dst: Operand::Reg(Register::RSP), src: Operand::Imm { value: 40, size: 8 } });
74
75 Ok(())
76 }
77
78 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); 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 self.push_inst(Instruction::Xor { dst: Operand::Reg(Register::EAX), src: Operand::Reg(Register::EAX) });
111 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 self.push_inst(Instruction::Label(function.name.clone()));
121
122 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 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 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 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 }
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 self.emit_call_setup(*argc)?;
316
317 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 self.push_inst(Instruction::Mov {
327 dst: Operand::Reg(Register::RCX),
328 src: Operand::Imm { value: 64, size: 64 }, });
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 self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) });
339 let offset = 8; 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 self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RBX) }); self.push_inst(Instruction::Pop { dst: Operand::Reg(Register::RAX) }); 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 _ => { }
359 }
360 Ok(())
361 }
362
363 fn emit_managed_inst(&mut self, inst: &ManagedInstruction) -> Result<()> {
364 match inst {
365 ManagedInstruction::CallMethod { method, .. } => {
366 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 _ => { }
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 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}