aboutsummaryrefslogblamecommitdiffhomepage
path: root/src/lib.rs
blob: 6bbfcbfd11338227220883b0452de2eb9f07df3c (plain) (tree)
1
2
3
4
5
6



                                                                                        

                                                 

























































                                                                         

                                                                                        








                                                 
        
        
          
        

       

             
                 



                                         
 
                                       
                      
                
                                                        
                    

                                                                                            
                             


                                                                                       


            
                                                            



                                                   














                                                                             


         





































                                                                         
//! A simple `x64` jit assembler with a minimal runtime to execute emitted code for fun.
//!
//! The following is an fibonacci example implementation.
//! ```rust
//! use juicebox_asm::{Asm, Reg64, Imm64, Label};
//! use juicebox_asm::insn::*;
//! use juicebox_asm::Runtime;
//!
//! const fn fib_rs(n: u64) -> u64 {
//!     match n {
//!         0 => 0,
//!         1 => 1,
//!         _ => fib_rs(n - 2) + fib_rs(n - 1),
//!     }
//! }
//!
//! fn main() {
//!     let mut asm = Asm::new();
//!
//!     let mut lp = Label::new();
//!     let mut end = Label::new();
//!
//!     // Reference implementation:
//!     //
//!     // int fib(int n) {
//!     //   int tmp = 0;
//!     //   int prv = 1;
//!     //   int sum = 0;
//!     // loop:
//!     //   if (n == 0) goto end;
//!     //   tmp = sum;
//!     //   sum += prv;
//!     //   prv = tmp;
//!     //   --n;
//!     //   goto loop;
//!     // end:
//!     //   return sum;
//!     // }
//!
//!     // SystemV abi:
//!     //   rdi -> first argument
//!     //   rax -> return value
//!     let n = Reg64::rdi;
//!     let sum = Reg64::rax;
//!
//!     let tmp = Reg64::rcx;
//!     let prv = Reg64::rbx;
//!
//!     asm.mov(tmp, Imm64::from(0));
//!     asm.mov(prv, Imm64::from(1));
//!     asm.mov(sum, Imm64::from(0));
//!
//!     asm.bind(&mut lp);
//!     asm.test(n, n);
//!     asm.jz(&mut end);
//!     asm.mov(tmp, sum);
//!     asm.add(sum, prv);
//!     asm.mov(prv, tmp);
//!     asm.dec(n);
//!     asm.jmp(&mut lp);
//!     asm.bind(&mut end);
//!     asm.ret();
//!
//!     // Move code into executable page and get function pointer to it.
//!     let mut rt = Runtime::new();
//!     let fib = unsafe { rt.add_code::<extern "C" fn(u64) -> u64>(&asm.into_code()) };
//!
//!     for n in 0..15 {
//!         let fib_jit = fib(n);
//!         println!("fib({}) = {}", n, fib_jit);
//!         assert_eq!(fib_jit, fib_rs(n));
//!     }
//! }
//! ```

mod asm;
mod imm;
mod label;
mod reg;
mod rt;

pub mod insn;

pub use asm::Asm;
pub use imm::{Imm16, Imm32, Imm64, Imm8};
pub use label::Label;
pub use reg::{Reg16, Reg32, Reg64, Reg8};
pub use rt::Runtime;

/// Type representing a memory operand.
#[derive(Clone, Copy)]
pub enum MemOp {
    /// An indirect memory operand, eg `mov [rax], rcx`.
    Indirect(Reg64),

    /// An indirect memory operand with additional displacement, eg `mov [rax + 0x10], rcx`.
    IndirectDisp(Reg64, i32),

    /// An indirect memory operand in the form base + index, eg `mov [rax + rcx], rdx`.
    IndirectBaseIndex(Reg64, Reg64),
}

impl MemOp {
    /// Get the base address register of the memory operand.
    const fn base(&self) -> Reg64 {
        match self {
            MemOp::Indirect(base) => *base,
            MemOp::IndirectDisp(base, ..) => *base,
            MemOp::IndirectBaseIndex(base, ..) => *base,
        }
    }

    /// Get the index register of the memory operand.
    fn index(&self) -> Reg64 {
        // Return zero index register for memory operands w/o index register.
        let zero_index = Reg64::rax;
        use reg::Reg;
        assert_eq!(zero_index.idx(), 0);

        match self {
            MemOp::Indirect(..) => zero_index,
            MemOp::IndirectDisp(..) => zero_index,
            MemOp::IndirectBaseIndex(.., index) => *index,
        }
    }
}

/// Trait to give size hints for memory operands.
trait MemOpSized {
    fn mem_op(&self) -> MemOp;
}

macro_rules! impl_memop_sized {
    ($(#[$doc:meta] $name:ident)+) => {
        $(
        #[$doc]
        pub struct $name(MemOp);

        impl $name {
            /// Create a memory with size hint from a raw memory operand.
            pub fn from(op: MemOp) -> Self {
                Self(op)
            }
        }

        impl MemOpSized for $name {
            fn mem_op(&self) -> MemOp {
                self.0
            }
        }
        )+
    };
}

impl_memop_sized!(
    /// A memory operand with a word (8 bit) size hint.
    MemOp8
    /// A memory operand with a word (16 bit) size hint.
    MemOp16
    /// A memory operand with a dword (32 bit) size hint.
    MemOp32
    /// A memory operand with a qword (64 bit) size hint.
    MemOp64
);