From 865c1d3231fd57c648121d961be91793d0bfe690 Mon Sep 17 00:00:00 2001 From: johannst Date: Sun, 5 Mar 2023 20:47:28 +0000 Subject: deploy: 1ea7de2ba46b58b4afe3e65b95d8a45160218a5c --- src/juicebox_asm/lib.rs.html | 286 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 249 insertions(+), 37 deletions(-) (limited to 'src/juicebox_asm/lib.rs.html') diff --git a/src/juicebox_asm/lib.rs.html b/src/juicebox_asm/lib.rs.html index 7b17af1..47e6bf6 100644 --- a/src/juicebox_asm/lib.rs.html +++ b/src/juicebox_asm/lib.rs.html @@ -302,26 +302,214 @@ 302 303 304 -
pub mod prelude;
-pub mod rt;
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+
//! 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::prelude::*;
+//! 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 rt = Runtime::new(&asm.into_code());
+//!     let fib = unsafe { rt.as_fn::<extern "C" fn(u64) -> u64>() };
+//!
+//!     for n in 0..15 {
+//!         let fib_jit = fib(n);
+//!         println!("fib({}) = {}", n, fib_jit);
+//!         assert_eq!(fib_jit, fib_rs(n));
+//!     }
+//! }
+//! ```
+
+pub mod prelude;
 
 mod imm;
 mod insn;
 mod label;
 mod reg;
+mod rt;
+
+pub use imm::{Imm16, Imm32, Imm64, Imm8};
+pub use label::Label;
+pub use reg::{Reg16, Reg32, Reg64, Reg8};
+pub use rt::Runtime;
 
 use imm::Imm;
-use label::Label;
 use reg::Reg;
-use reg::{Reg16, Reg32, Reg64, Reg8};
 
-pub enum MemOp {
-    Indirect(Reg64),
-    IndirectDisp(Reg64, i32),
+/// Type representing a memory operand.
+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),
 }
 
 impl MemOp {
-    const fn base(&self) -> Reg64 {
+    /// Get the base address register of the memory operand.
+    const fn base(&self) -> Reg64 {
         match self {
             MemOp::Indirect(base) => *base,
             MemOp::IndirectDisp(base, ..) => *base,
@@ -343,31 +531,42 @@
     ((mod_ & 0b11) << 6) | ((reg & 0b111) << 3) | (rm & 0b111)
 }
 
-pub struct Asm {
+/// `x64` jit assembler.
+pub struct Asm {
     buf: Vec<u8>,
 }
 
 impl Asm {
-    pub fn new() -> Asm {
-        let buf = Vec::with_capacity(1024);
+    /// Create a new `x64` jit assembler.
+    pub fn new() -> Asm {
+        // Some random default capacity.
+        let buf = Vec::with_capacity(1024);
         Asm { buf }
     }
 
-    pub fn into_code(self) -> Vec<u8> {
+    /// Consume the assembler and get the emitted code.
+    pub fn into_code(self) -> Vec<u8> {
         self.buf
     }
 
-    fn emit(&mut self, bytes: &[u8]) {
+    /// Emit a slice of bytes.
+    fn emit(&mut self, bytes: &[u8]) {
         self.buf.extend_from_slice(bytes);
     }
 
-    fn emit_optional(&mut self, bytes: &[Option<u8>]) {
+    /// Emit a slice of optional bytes.
+    fn emit_optional(&mut self, bytes: &[Option<u8>]) {
         for byte in bytes.iter().filter_map(|&b| b) {
             self.buf.push(byte);
         }
     }
 
-    fn emit_at(&mut self, pos: usize, bytes: &[u8]) {
+    /// Emit a slice of bytes at `pos`.
+    ///
+    /// # Panics
+    ///
+    /// Panics if [pos..pos+len] indexes out of bound of the underlying code buffer.
+    fn emit_at(&mut self, pos: usize, bytes: &[u8]) {
         if let Some(buf) = self.buf.get_mut(pos..pos + bytes.len()) {
             buf.copy_from_slice(bytes);
         } else {
@@ -387,7 +586,8 @@
     /// If the [Label] is bound, patch any pending relocation.
     pub fn resolve(&mut self, label: &mut Label) {
         if let Some(loc) = label.location() {
-            let loc = i32::try_from(loc).expect("Label location did not fit into i32.");
+            // For now we only support disp32 as label location.
+            let loc = i32::try_from(loc).expect("Label location did not fit into i32.");
 
             // Resolve any pending relocations for the label.
             for off in label.offsets_mut().drain() {
@@ -404,6 +604,7 @@
 
     // -- Encode utilities.
 
+    /// Encode an register-register instruction.
     fn encode_rr<T: Reg>(&mut self, opc: u8, op1: T, op2: T)
     where
         Self: EncodeRR<T>,
@@ -424,22 +625,25 @@
         self.emit(&[opc, modrm]);
     }
 
-    fn encode_oi<T: Reg, U: Imm>(&mut self, opc: u8, op1: T, op2: U)
+    /// Encode an offset-immediate instruction.
+    /// Register idx is encoded in the opcode.
+    fn encode_oi<T: Reg, U: Imm>(&mut self, opc: u8, op1: T, op2: U)
     where
-        Self: EncodeRI<T>,
+        Self: EncodeR<T>,
     {
         let opc = opc + (op1.idx() & 0b111);
-        let prefix = <Self as EncodeRI<T>>::legacy_prefix();
-        let rex = <Self as EncodeRI<T>>::rex(op1);
+        let prefix = <Self as EncodeR<T>>::legacy_prefix();
+        let rex = <Self as EncodeR<T>>::rex(op1);
 
         self.emit_optional(&[prefix, rex]);
         self.emit(&[opc]);
         self.emit(op2.bytes());
     }
 
-    fn encode_ri<T: Reg, U: Imm>(&mut self, opc: u8, opc_ext: u8, op1: T, op2: U)
+    /// Encode a register-immediate instruction.
+    fn encode_ri<T: Reg, U: Imm>(&mut self, opc: u8, opc_ext: u8, op1: T, op2: U)
     where
-        Self: EncodeRI<T>,
+        Self: EncodeR<T>,
     {
         // MI operand encoding.
         //   op1           -> modrm.rm
@@ -450,17 +654,18 @@
             op1.idx(), /* rm */
         );
 
-        let prefix = <Self as EncodeRI<T>>::legacy_prefix();
-        let rex = <Self as EncodeRI<T>>::rex(op1);
+        let prefix = <Self as EncodeR<T>>::legacy_prefix();
+        let rex = <Self as EncodeR<T>>::rex(op1);
 
         self.emit_optional(&[prefix, rex]);
         self.emit(&[opc, modrm]);
         self.emit(op2.bytes());
     }
 
-    fn encode_r<T: Reg>(&mut self, opc: u8, opc_ext: u8, op1: T)
+    /// Encode a register instruction.
+    fn encode_r<T: Reg>(&mut self, opc: u8, opc_ext: u8, op1: T)
     where
-        Self: EncodeRI<T>,
+        Self: EncodeR<T>,
     {
         // M operand encoding.
         //   op1           -> modrm.rm
@@ -471,14 +676,15 @@
             op1.idx(), /* rm */
         );
 
-        let prefix = <Self as EncodeRI<T>>::legacy_prefix();
-        let rex = <Self as EncodeRI<T>>::rex(op1);
+        let prefix = <Self as EncodeR<T>>::legacy_prefix();
+        let rex = <Self as EncodeR<T>>::rex(op1);
 
         self.emit_optional(&[prefix, rex]);
         self.emit(&[opc, modrm]);
     }
 
-    fn encode_mr<T: Reg>(&mut self, opc: u8, op1: MemOp, op2: T)
+    /// Encode a memory-register instruction.
+    fn encode_mr<T: Reg>(&mut self, opc: u8, op1: MemOp, op2: T)
     where
         Self: EncodeMR<T>,
     {
@@ -511,7 +717,8 @@
         }
     }
 
-    fn encode_rm<T: Reg>(&mut self, opc: u8, op1: T, op2: MemOp)
+    /// Encode a register-memory instruction.
+    fn encode_rm<T: Reg>(&mut self, opc: u8, op1: T, op2: MemOp)
     where
         Self: EncodeMR<T>,
     {
@@ -521,7 +728,8 @@
         self.encode_mr(opc, op2, op1);
     }
 
-    fn encode_jmp_label(&mut self, opc: &[u8], op1: &mut Label) {
+    /// Encode a jump to label instruction.
+    fn encode_jmp_label(&mut self, opc: &[u8], op1: &mut Label) {
         // Emit the opcode.
         self.emit(opc);
 
@@ -529,6 +737,7 @@
         op1.record_offset(self.buf.len());
 
         // Emit a zeroed disp32, which serves as placeholder for the relocation.
+        // We currently only support disp32 jump targets.
         self.emit(&[0u8; 4]);
 
         // Resolve any pending relocations for the label.
@@ -538,6 +747,7 @@
 
 // -- Encoder helper.
 
+/// Encode helper for register-register instructions.
 trait EncodeRR<T: Reg> {
     fn legacy_prefix() -> Option<u8> {
         None
@@ -561,7 +771,8 @@
 }
 impl EncodeRR<Reg64> for Asm {}
 
-trait EncodeRI<T: Reg> {
+/// Encode helper for register instructions.
+trait EncodeR<T: Reg> {
     fn legacy_prefix() -> Option<u8> {
         None
     }
@@ -575,16 +786,17 @@
     }
 }
 
-impl EncodeRI<Reg8> for Asm {}
-impl EncodeRI<Reg32> for Asm {}
-impl EncodeRI<Reg16> for Asm {
+impl EncodeR<Reg8> for Asm {}
+impl EncodeR<Reg32> for Asm {}
+impl EncodeR<Reg16> for Asm {
     fn legacy_prefix() -> Option<u8> {
         Some(0x66)
     }
 }
-impl EncodeRI<Reg64> for Asm {}
+impl EncodeR<Reg64> for Asm {}
 
-trait EncodeMR<T: Reg> {
+/// Encode helper for memory-register instructions.
+trait EncodeMR<T: Reg> {
     fn legacy_prefix() -> Option<u8> {
         None
     }
-- 
cgit v1.2.3