use std::fmt;
use std::ops::{Add, AddAssign, Sub};
pub const WORDSIZE: i64 = 8;
#[derive(Debug, PartialEq, Clone)]
pub struct Ins(pub String);
#[derive(Default, Clone)]
pub struct ASM(pub Vec<Ins>);
#[derive(PartialEq, Debug, Clone)]
pub enum Reference {
Register(Register),
Relative(Relative),
Const(i64),
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Register {
RAX,
RBX,
RCX,
RDX,
RSP,
RBP,
RSI,
RDI,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
}
pub const SYS_V: [Register; 6] =
[Register::RDI, Register::RSI, Register::RDX, Register::RCX, Register::R8, Register::R9];
#[derive(PartialEq, Debug, Clone)]
pub struct Relative {
pub register: Register,
pub offset: i64,
}
pub fn add(r: Reference, v: Reference) -> Ins {
Ins(format!("add {}, {}", r, v))
}
pub fn and(r: Reference, v: Reference) -> Ins {
Ins(format!("and {}, {}", r, v))
}
pub fn call(f: &str) -> Ins {
Ins(format!("call \"{}\"", f))
}
pub fn cmp(a: Reference, b: Reference) -> Ins {
Ins(format!("cmp {}, {}", a, b))
}
pub fn enter() -> ASM {
Ins::from("push rbp") + Ins::from("mov rbp, rsp")
}
pub fn je(l: &str) -> Ins {
Ins(format!("je {}", l))
}
pub fn jmp(l: &str) -> Ins {
Ins(format!("jmp {}", l))
}
pub fn label(l: &str) -> Ins {
Ins(format!("\"{}\":", l))
}
pub fn leave() -> ASM {
Ins::from("pop rbp") + Ins::from("ret")
}
pub fn lea(r: Register, of: &str, offset: i64) -> Ins {
Ins(format!("lea {}, [rip + {} + {}]", r, offset, of))
}
pub fn load(r: Register, si: i64) -> Ins {
Ins(format!("mov {}, {}", r, Register::RBP + si))
}
pub fn mov(to: Reference, from: Reference) -> Ins {
match (&to, &from) {
(Reference::Register(_), _) => Ins(format!("mov {}, {}", to, from)),
_ => Ins(format!("mov qword ptr {}, {}", to, from)),
}
}
pub fn mul(v: Reference) -> Ins {
Ins(format!("mul qword ptr {}", v))
}
pub fn or(r: Reference, v: Reference) -> Ins {
Ins(format!("or {}, {}", r, v))
}
pub fn pop(r: Reference) -> Ins {
Ins(format!("pop {}", r))
}
pub fn push(r: Reference) -> Ins {
Ins(format!("push {}", r))
}
pub fn ret() -> Ins {
Ins::from("ret")
}
pub fn save(r: Reference, si: i64) -> Ins {
mov((Register::RBP + si).into(), r)
}
pub fn sal(r: Reference, v: Reference) -> Ins {
Ins(format!("sal {}, {}", r, v))
}
pub fn sar(r: Reference, v: Reference) -> Ins {
Ins(format!("sar {}, {}", r, v))
}
pub fn sub(r: Reference, v: Reference) -> Ins {
Ins(format!("sub {}, {}", r, v))
}
pub fn init_heap() -> Ins {
Ins::from("mov r12, rdi # Store heap index to R12")
}
#[cfg(target_os = "macos")]
pub fn init() -> String {
String::from("_init")
}
#[cfg(target_os = "linux")]
pub fn init() -> String {
String::from("init")
}
#[cfg(target_os = "macos")]
pub fn func(name: &str) -> ASM {
Ins::from("") + Ins(format!(".globl \"{}\"", &name)) + label(name)
}
#[cfg(target_os = "linux")]
pub fn func(name: &str) -> ASM {
Ins::from("")
+ Ins(format!(".globl \"{}\"", &name))
+ Ins(format!(".type \"{}\", @function", &name))
+ label(name)
}
#[cfg(target_os = "macos")]
pub fn prelude() -> ASM {
Ins::from(".section __TEXT,__text") + Ins::from(".intel_syntax noprefix")
}
#[cfg(target_os = "linux")]
pub fn prelude() -> ASM {
Ins::from(".text") + Ins::from(".intel_syntax noprefix")
}
impl Add<i64> for Register {
type Output = Relative;
fn add(self, offset: i64) -> Relative {
Relative { register: self, offset }
}
}
impl Sub<i64> for Register {
type Output = Relative;
fn sub(self, offset: i64) -> Relative {
Relative { register: self, offset: -offset }
}
}
impl From<&str> for Ins {
fn from(s: &str) -> Self {
Ins(s.to_string())
}
}
impl Add<Ins> for Ins {
type Output = ASM;
fn add(self, op: Ins) -> ASM {
ASM { 0: vec![self, op] }
}
}
impl AddAssign<Ins> for ASM {
fn add_assign(&mut self, op: Ins) {
self.0.push(op)
}
}
impl AddAssign<ASM> for ASM {
fn add_assign(&mut self, asm: ASM) {
self.0.extend(asm.0)
}
}
impl Add<Ins> for ASM {
type Output = Self;
fn add(mut self, op: Ins) -> Self {
self.0.push(op);
self
}
}
impl Add<ASM> for ASM {
type Output = Self;
fn add(mut self, mut asm: ASM) -> Self {
self.0.append(&mut asm.0);
self
}
}
impl From<Ins> for ASM {
fn from(op: Ins) -> Self {
ASM { 0: vec![op] }
}
}
impl From<Register> for Reference {
fn from(r: Register) -> Self {
Reference::Register(r)
}
}
impl From<Relative> for Reference {
fn from(r: Relative) -> Self {
Reference::Relative(r)
}
}
impl From<i64> for Reference {
fn from(i: i64) -> Self {
Reference::Const(i)
}
}
impl fmt::Display for Register {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", format!("{:?}", self).to_lowercase())
}
}
impl fmt::Display for Relative {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.offset < 0 {
write!(f, "{}", format!("[{} - {}]", self.register, -self.offset))
} else if self.offset > 0 {
write!(f, "{}", format!("[{} + {}]", self.register, self.offset))
} else {
write!(f, "{}", format!("[{}]", self.register))
}
}
}
impl fmt::Display for Reference {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Reference::Register(r) => write!(f, "{}", r),
Reference::Relative(r) => write!(f, "{}", r),
Reference::Const(i) => write!(f, "{}", i),
}
}
}
impl fmt::Display for ASM {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut ctx = String::new();
for op in &self.0 {
if op.0.ends_with(':') {
ctx.push_str(&format!("{}\n", &op.0));
} else {
ctx.push_str(&format!(" {}\n", &op.0));
}
}
write!(f, "{}", ctx)
}
}
#[cfg(test)]
mod tests {
use super::{Ins, Reference, Register::*};
use pretty_assertions::assert_eq;
#[test]
fn mov() {
assert_eq!(Ins::from("mov rax, 16"), super::mov(RAX.into(), 16.into()));
assert_eq!(
Ins::from("mov qword ptr [rbp + 8], 16"),
super::mov(Reference::from(RBP + 8), 16.into())
)
}
}