]>
Witch of Git - ivy/blob - src/trans/x64.rs
2 use crate::trans
::{Code
, FnName
, Func
, Program
, SsaName
, Var
};
10 #[cfg(target_os = "macos")]
11 macro_rules
! extern_label
{
13 Label
::Extern(concat
!("_", $l
))
16 #[cfg(not(target_os = "macos"))]
17 macro_rules
! extern_label
{
23 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28 Builtin(&'
static str),
31 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
58 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
63 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
68 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
85 CLoad(Cond
, Reg
, Addr
),
90 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
91 pub struct Definition
{
93 body
: Cow
<'
static, [Inst
]>,
101 pub fn write_compile(
102 builtins
: &HashMap
<String
, ast
::Name
>,
103 out
: &mut impl Write
,
105 ) -> io
::Result
<()> {
106 let (entry
, globals
, defns
) = compile(&prog
);
112 let builtins
= builtins
115 let number
= id
.global_number().unwrap
();
116 let (params
, name
, code
) = builtin(&name
);
120 name
: Label
::Builtin(name
),
121 body
: Cow
::Borrowed(code
),
126 .collect
::<Vec
<_
>>();
127 writeln
!(out
, ".data")?
;
128 for global
in globals
{
129 writeln
!(out
, "{}", global
)?
;
141 for builtin
in &builtins
{
149 mov [rip + {}], rax"#,
152 extern_label
!("ivy_make_lam"),
153 Label
::Global(builtin
.number
),
165 extern_label
!("ivy_exit"),
167 for builtin
in &builtins
{
168 writeln
!(out
, "{}", builtin
.defn
)?
;
172 writeln
!(out
, "{}", defn
)?
;
178 pub fn compile(prog
: &Program
) -> (Label
, Vec
<Global
>, Vec
<Definition
>) {
182 .map(|&global
| Global
{
183 name
: Label
::Global(global
),
190 .map(|func
| compile_func(func
))
192 (Label
::Function(prog
.top
), globals
, defns
)
195 fn even_up(x
: usize) -> usize {
199 fn compile_func(func
: &Func
) -> Definition
{
200 fn stack(x
: &SsaName
) -> Addr
{
201 Addr
::Off(Reg
::Rsp
, x
.0 as i32 * 8)
204 const HEADER_SIZE
: i32 = 0x18;
206 fn param(x
: u16) -> Addr
{
207 Addr
::Off(Reg
::Rbx
, HEADER_SIZE
+ x
as i32 * 8)
210 let upvar
= move |x
: u16| {
211 let params
= func
.params
as i32 * 8;
212 Addr
::Off(Reg
::Rbx
, HEADER_SIZE
+ params
+ x
as i32 * 8)
215 let mut body
= Vec
::new();
216 let stack_slots
= even_up(func
.order
.len());
217 body
.push(Inst
::Push(Reg
::Rbx
));
218 body
.push(Inst
::Cfi(Cfi
::Def(Reg
::Rsp
, 16)));
219 body
.push(Inst
::Mov(Reg
::Rbx
, Reg
::Rdi
));
220 body
.push(Inst
::SubI(Reg
::Rsp
, stack_slots
as u32 * 8));
221 body
.push(Inst
::Cfi(Cfi
::Def(Reg
::Rsp
, stack_slots
as i32 * 8 + 16)));
222 let address
= |v
| match v
{
223 Var
::Global(x
) => Addr
::Rip(Label
::Global(x
)),
224 Var
::Param(x
) => param(x
),
225 Var
::Upvar(x
) => upvar(x
),
226 Var
::Ssa(s
) => stack(&s
),
228 let get
= |r
, s
| match func
.block
[s
] {
229 Code
::Load(v
) => Inst
::Load(r
, address(v
)),
230 Code
::Num(v
) => Inst
::Imm(r
, (v
<< 1) | 1),
231 _
=> Inst
::Load(r
, stack(s
)),
233 for ssa
in &func
.order
{
234 match &func
.block
[ssa
] {
235 // We don't need to generate code for loads or nums themselves, they're consulted by other accesses
238 Code
::StoreGlobal(x
, s
) => {
239 body
.push(get(Reg
::Rax
, s
));
240 body
.push(Inst
::Store(Addr
::Rip(Label
::Global(*x
)), Reg
::Rax
));
247 body
.push(Inst
::Lea(Reg
::Rdi
, Addr
::Rip(Label
::Function(*name
))));
248 body
.push(Inst
::Imm(Reg
::Rsi
, *params
as i64));
249 body
.push(Inst
::Imm(Reg
::Rdx
, upvars
.len() as i64));
250 body
.push(Inst
::Call(extern_label
!("ivy_make_lam")));
251 body
.push(Inst
::Store(stack(ssa
), Reg
::Rax
));
252 for (i
, s
) in upvars
.iter
().enumerate() {
253 body
.push(get(Reg
::Rdi
, s
));
254 body
.push(Inst
::Store(
255 Addr
::Off(Reg
::Rax
, HEADER_SIZE
+ (*params
as i32) * 8 + i
as i32 * 8),
258 body
.push(Inst
::Call(extern_label
!("ivy_incref")));
259 body
.push(Inst
::Load(Reg
::Rax
, stack(ssa
)));
262 Code
::App(terms
) => {
263 let mut terms
= terms
.iter
();
264 body
.push(get(Reg
::Rdi
, terms
.next().expect("a function to apply")));
266 Some(s
) => body
.push(get(Reg
::Rsi
, s
)),
267 None
=> body
.push(Inst
::Imm(Reg
::Rsi
, 0)),
269 body
.push(Inst
::Call(extern_label
!("ivy_app")));
270 while let Some(term
) = terms
.next() {
271 body
.push(Inst
::Mov(Reg
::Rdi
, Reg
::Rax
));
272 body
.push(get(Reg
::Rsi
, term
));
273 body
.push(Inst
::Call(extern_label
!("ivy_app")));
275 body
.push(Inst
::Store(stack(ssa
), Reg
::Rax
));
279 match func
.block
[&func
.result
] {
280 Code
::Load(v
) => body
.push(Inst
::Load(Reg
::Rax
, address(v
))),
281 Code
::Num(n
) => body
.push(Inst
::Imm(Reg
::Rax
, (n
<< 1) | 1i64)),
284 body
.push(Inst
::AddI(Reg
::Rsp
, stack_slots
as u32 * 8));
285 body
.push(Inst
::Cfi(Cfi
::Def(Reg
::Rsp
, 16)));
286 body
.push(Inst
::Pop(Reg
::Rbx
));
287 body
.push(Inst
::Cfi(Cfi
::Def(Reg
::Rsp
, 8)));
288 body
.push(Inst
::Ret
);
290 name
: Label
::Function(func
.name
),
291 body
: Cow
::Owned(body
),
295 impl fmt
::Display
for Definition
{
296 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
297 writeln
!(fmt
, " .globl {}", self.name
)?
;
298 match self.body
.len() {
299 0 => write
!(fmt
, "{}:", self.name
),
300 1 => write
!(fmt
, "{}: {}", self.name
, self.body
[0]),
302 writeln
!(fmt
, " .cfi_startproc")?
;
303 writeln
!(fmt
, "{}:", self.name
)?
;
304 for inst
in self.body
.as_ref() {
305 writeln
!(fmt
, " {}", inst
)?
;
307 write
!(fmt
, " .cfi_endproc")
313 impl fmt
::Display
for Global
{
314 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
315 write
!(fmt
, "{}: .quad {}", self.name
, self.value
)
319 impl fmt
::Display
for Label
{
320 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
322 Label
::Global(g
) => write
!(fmt
, "IVY_GLOBAL${}", g
),
323 Label
::Function(f
) => write
!(fmt
, "ivy_fn${}", f
.0),
324 Label
::Extern(l
) => write
!(fmt
, "{}", l
),
325 Label
::Builtin(l
) => write
!(fmt
, "ivy_builtin${}", l
),
330 impl fmt
::Display
for Reg
{
331 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
333 Reg
::Rax
=> write
!(fmt
, "rax"),
334 Reg
::Rbx
=> write
!(fmt
, "rbx"),
335 Reg
::Rcx
=> write
!(fmt
, "rcx"),
336 Reg
::Rdx
=> write
!(fmt
, "rdx"),
337 Reg
::Rsi
=> write
!(fmt
, "rsi"),
338 Reg
::Rdi
=> write
!(fmt
, "rdi"),
339 Reg
::Rbp
=> write
!(fmt
, "rbp"),
340 Reg
::Rsp
=> write
!(fmt
, "rsp"),
341 Reg
::R8
=> write
!(fmt
, "r8"),
342 Reg
::R9
=> write
!(fmt
, "r9"),
343 Reg
::R10
=> write
!(fmt
, "r10"),
344 Reg
::R11
=> write
!(fmt
, "r11"),
345 Reg
::R12
=> write
!(fmt
, "r12"),
346 Reg
::R13
=> write
!(fmt
, "r13"),
347 Reg
::R14
=> write
!(fmt
, "r14"),
348 Reg
::R15
=> write
!(fmt
, "r15"),
353 impl fmt
::Display
for Addr
{
354 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
356 Addr
::Rip(l
) => write
!(fmt
, "[rip + {}]", l
),
357 Addr
::Off(r
, o
) => write
!(fmt
, "[{} + 0x{:02x}]", r
, o
),
362 impl fmt
::Display
for Cond
{
363 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
365 Cond
::Le
=> write
!(fmt
, "le"),
370 impl fmt
::Display
for Cfi
{
371 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
373 Cfi
::Def(r
, i
) => write
!(fmt
, ".cfi_def_cfa {}, {}", r
, i
),
378 impl fmt
::Display
for Inst
{
379 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
381 Inst
::Push(r
) => write
!(fmt
, "push {}", r
),
382 Inst
::Pop(r
) => write
!(fmt
, "pop {}", r
),
383 Inst
::Cfi(c
) => write
!(fmt
, "{}", c
),
384 Inst
::Mov(d
, s
) => write
!(fmt
, "mov {}, {}", d
, s
),
385 Inst
::Load(r
, a
) => write
!(fmt
, "mov {}, {}", r
, a
),
386 Inst
::Store(a
, r
) => write
!(fmt
, "mov {}, {}", a
, r
),
387 Inst
::Imm(r
, i
) => write
!(fmt
, "mov {}, {}", r
, i
),
388 Inst
::Lea(r
, a
) => write
!(fmt
, "lea {}, {}", r
, a
),
389 Inst
::Call(l
) => write
!(fmt
, "call {}", l
),
390 Inst
::Jmp(l
) => write
!(fmt
, "jmp {}", l
),
391 Inst
::Add(d
, s
) => write
!(fmt
, "add {}, {}", d
, s
),
392 Inst
::Sub(d
, s
) => write
!(fmt
, "sub {}, {}", d
, s
),
393 Inst
::AddI(r
, x
) => write
!(fmt
, "add {}, {}", r
, x
),
394 Inst
::SubI(r
, x
) => write
!(fmt
, "sub {}, {}", r
, x
),
395 Inst
::Cmp(a
, b
) => write
!(fmt
, "cmp {}, {}", a
, b
),
396 Inst
::CLoad(c
, r
, a
) => write
!(fmt
, "cmov{} {}, {}", c
, r
, a
),
397 Inst
::CMov(c
, d
, s
) => write
!(fmt
, "cmov{} {}, {}", c
, d
, s
),
398 Inst
::Ret
=> write
!(fmt
, "ret"),
403 fn builtin(name
: &str) -> (u16, &'
static str, &'
static [Inst
]) {
409 Inst
::Load(Reg
::Rdi
, Addr
::Off(Reg
::Rdi
, 0x18)),
410 Inst
::Jmp(extern_label
!("ivy_debug")),
417 Inst
::Load(Reg
::Rax
, Addr
::Off(Reg
::Rdi
, 0x18)),
418 Inst
::Load(Reg
::Rdi
, Addr
::Off(Reg
::Rdi
, 0x18 + 8)),
419 Inst
::Add(Reg
::Rax
, Reg
::Rdi
),
420 Inst
::SubI(Reg
::Rax
, 1),
428 Inst
::Load(Reg
::Rax
, Addr
::Off(Reg
::Rdi
, 0x18)),
429 Inst
::Load(Reg
::Rdi
, Addr
::Off(Reg
::Rdi
, 0x18 + 8)),
430 Inst
::Sub(Reg
::Rax
, Reg
::Rdi
),
431 Inst
::AddI(Reg
::Rax
, 1),
439 Inst
::Load(Reg
::Rax
, Addr
::Off(Reg
::Rdi
, 0x18)),
440 Inst
::Load(Reg
::Rdi
, Addr
::Off(Reg
::Rdi
, 0x18 + 8)),
441 Inst
::Cmp(Reg
::Rax
, Reg
::Rdi
),
442 Inst
::Load(Reg
::Rax
, Addr
::Rip(Label
::Global(2))), // false
443 Inst
::CLoad(Cond
::Le
, Reg
::Rax
, Addr
::Rip(Label
::Global(1))), // true
450 &[Inst
::Load(Reg
::Rax
, Addr
::Off(Reg
::Rdi
, 0x18)), Inst
::Ret
],
456 Inst
::Load(Reg
::Rax
, Addr
::Off(Reg
::Rdi
, 0x18 + 8)),
460 name
=> panic
!("Unsupported builtin '{}' for x64", name
),