use crate::{
compiler::{
emit::{eval, mask},
state::State,
},
core::*,
immediate, strings,
x86::{self, Reference::*, Register::*, *},
};
pub fn call(s: &mut State, fname: &str, args: &[Expr]) -> Option<ASM> {
match (fname, args) {
("%", [x, y]) => Some(remainder(s, x, y)),
("*", [x, y]) => Some(mul(s, x, y)),
("+", [x, y]) => Some(plus(s, x, y)),
("-", [x, y]) => Some(minus(s, x, y)),
("/", [x, y]) => Some(quotient(s, x, y)),
("<", [x, y]) => Some(lt(s, x, y)),
("<=", [x, y]) => Some(lte(s, x, y)),
("=", [x, y]) => Some(eq(s, x, y)),
(">", [x, y]) => Some(gt(s, x, y)),
(">=", [x, y]) => Some(gte(s, x, y)),
("boolean?", [arg]) => Some(booleanp(s, arg)),
("car", [arg]) => Some(car(s, arg)),
("cdr", [arg]) => Some(cdr(s, arg)),
("char?", [arg]) => Some(charp(s, arg)),
("cons", [x, y]) => Some(cons(s, x, y)),
("dec", [arg]) => Some(dec(s, arg)),
("fixnum?", [arg]) => Some(fixnump(s, arg)),
("inc", [arg]) => Some(inc(s, arg)),
("make-string", [Expr::Number(n)]) => Some(strings::make(s, *n)),
("not", [arg]) => Some(not(s, arg)),
("null?", [arg]) => Some(nullp(s, arg)),
("pair?", [arg]) => Some(pairp(s, arg)),
("string?", [arg]) => Some(stringp(s, arg)),
("symbol?", [arg]) => Some(symbolp(s, arg)),
("zero?", [arg]) => Some(zerop(s, arg)),
("vector", args) => Some(vector(s, args)),
_ => None,
}
}
fn inc(s: &mut State, x: &Expr) -> ASM {
eval(s, x) + x86::add(RAX.into(), immediate::n(1).into())
}
fn dec(s: &mut State, x: &Expr) -> ASM {
eval(s, x) + x86::sub(RAX.into(), immediate::n(1).into())
}
fn fixnump(s: &mut State, expr: &Expr) -> ASM {
eval(s, expr) + mask() + compare(RAX.into(), immediate::NUM.into(), "sete")
}
fn booleanp(s: &mut State, expr: &Expr) -> ASM {
eval(s, expr) + mask() + compare(RAX.into(), immediate::BOOL.into(), "sete")
}
fn charp(s: &mut State, expr: &Expr) -> ASM {
eval(s, expr) + mask() + compare(RAX.into(), immediate::CHAR.into(), "sete")
}
fn nullp(s: &mut State, expr: &Expr) -> ASM {
eval(s, expr) + compare(RAX.into(), immediate::NIL.into(), "sete")
}
fn pairp(s: &mut State, expr: &Expr) -> ASM {
eval(s, expr) + mask() + compare(RAX.into(), immediate::PAIR.into(), "sete")
}
fn stringp(s: &mut State, expr: &Expr) -> ASM {
eval(s, expr) + mask() + compare(RAX.into(), immediate::STR.into(), "sete")
}
fn symbolp(s: &mut State, expr: &Expr) -> ASM {
eval(s, expr) + mask() + compare(RAX.into(), immediate::SYM.into(), "sete")
}
fn zerop(s: &mut State, expr: &Expr) -> ASM {
eval(s, expr) + compare(RAX.into(), immediate::NUM.into(), "sete")
}
fn not(s: &mut State, expr: &Expr) -> ASM {
eval(s, expr) + compare(RAX.into(), immediate::FALSE.into(), "sete")
}
fn binop(s: &mut State, x: &Expr, y: &Expr) -> ASM {
let t = s.alloc();
let ctx = eval(s, x) + x86::save(RAX.into(), t) + eval(s, y);
s.dealloc(1);
ctx
}
fn plus(s: &mut State, x: &Expr, y: &Expr) -> ASM {
binop(s, &x, &y) + x86::add(RAX.into(), Reference::from(RBP + s.si))
}
fn minus(s: &mut State, x: &Expr, y: &Expr) -> ASM {
binop(s, &x, &y)
+ x86::mov(RDI.into(), RAX.into())
+ x86::mov(RAX.into(), Reference::from(RBP + s.si))
+ x86::sub(RAX.into(), RDI.into())
}
fn mul(s: &mut State, x: &Expr, y: &Expr) -> ASM {
binop(s, &x, &y)
+ x86::sar(RAX.into(), immediate::SHIFT.into())
+ x86::mul(Reference::from(RBP + s.si))
}
fn div(s: &mut State, x: &Expr, y: &Expr) -> ASM {
eval(s, y)
+ x86::sar(RAX.into(), immediate::SHIFT.into())
+ x86::mov(RCX.into(), RAX.into())
+ eval(s, x)
+ x86::sar(RAX.into(), immediate::SHIFT.into())
+ x86::mov(RDX.into(), 0.into())
+ Ins::from("cqo")
+ Ins::from("idiv rcx")
}
fn quotient(s: &mut State, x: &Expr, y: &Expr) -> ASM {
div(s, x, y) + x86::sal(RAX.into(), immediate::SHIFT.into())
}
fn remainder(s: &mut State, x: &Expr, y: &Expr) -> ASM {
div(s, x, y) + x86::mov(RAX.into(), RDX.into()) + x86::sal(RAX.into(), immediate::SHIFT.into())
}
fn compare(a: Reference, b: Reference, setcc: &str) -> ASM {
x86::cmp(a, b)
+ Ins(format!("{} al", setcc))
+ Ins::from("movzx rax, al")
+ Ins(format!("sal al, {}", immediate::SHIFT))
+ Ins(format!("or al, {}", immediate::BOOL))
}
fn eq(s: &mut State, x: &Expr, y: &Expr) -> ASM {
binop(s, x, y) + compare(Reference::from(RBP + s.si), RAX.into(), "sete")
}
fn lt(s: &mut State, x: &Expr, y: &Expr) -> ASM {
binop(s, x, y) + compare(Reference::from(RBP + s.si), RAX.into(), "setl")
}
fn gt(s: &mut State, x: &Expr, y: &Expr) -> ASM {
binop(s, x, y) + compare(Reference::from(RBP + s.si), RAX.into(), "setg")
}
fn lte(s: &mut State, x: &Expr, y: &Expr) -> ASM {
binop(s, x, y) + compare(Reference::from(RBP + s.si), RAX.into(), "setle")
}
fn gte(s: &mut State, x: &Expr, y: &Expr) -> ASM {
binop(s, x, y) + compare(Reference::from(RBP + s.si), RAX.into(), "setge")
}
#[allow(clippy::identity_op)]
fn cons(s: &mut State, x: &Expr, y: &Expr) -> ASM {
let bp = s.si;
let scratch = s.alloc();
let ctx = eval(s, x)
+ x86::save(RAX.into(), scratch)
+ eval(s, y)
+ x86::mov(Reference::from(R12 + 8), RAX.into())
+ x86::mov(RAX.into(), Reference::from(RBP + scratch))
+ x86::mov(Reference::from(R12 + 0), RAX.into())
+ x86::mov(RAX.into(), R12.into())
+ x86::add(R12.into(), Reference::from(WORDSIZE * 2))
+ x86::or(RAX.into(), immediate::PAIR.into());
s.dealloc(1);
assert!(s.si == bp, "Stack deallocated; expected {}, found {} ", bp, s.si);
ctx
}
fn car(s: &mut State, pair: &Expr) -> ASM {
eval(s, pair) + Ins(format!("mov rax, [rax - {}] # (car ..)", immediate::PAIR))
}
fn cdr(s: &mut State, pair: &Expr) -> ASM {
eval(s, pair) + Ins(format!("mov rax, [rax + {}] # (cdr ...)", 5))
}
#[allow(clippy::identity_op)]
fn vector(s: &mut State, exprs: &[Expr]) -> ASM {
let mut asm: ASM = x86::mov(Relative(R12 + 0), Const(exprs.len() as i64)).into();
for (index, expr) in exprs.iter().enumerate() {
let dest = Relative(R12 + (WORDSIZE * (index + 1) as i64));
match immediate::to(expr) {
Some(c) => asm += x86::mov(dest, Reference::Const(c)),
None => asm += eval(s, expr) + x86::mov(dest, RAX.into()),
}
}
asm = asm
+ x86::mov(RAX.into(), R12.into())
+ x86::add(R12.into(), Const(WORDSIZE * exprs.len() as i64))
+ x86::or(RAX.into(), immediate::VEC.into());
asm
}