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/imm.rs.html | 44 +++++- src/juicebox_asm/insn.rs.html | 6 +- src/juicebox_asm/label.rs.html | 28 +++- src/juicebox_asm/lib.rs.html | 286 ++++++++++++++++++++++++++++++++++----- src/juicebox_asm/prelude.rs.html | 6 +- src/juicebox_asm/reg.rs.html | 46 +++++-- src/juicebox_asm/rt.rs.html | 6 +- 7 files changed, 359 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/juicebox_asm/imm.rs.html b/src/juicebox_asm/imm.rs.html index 0990c92..0a9933e 100644 --- a/src/juicebox_asm/imm.rs.html +++ b/src/juicebox_asm/imm.rs.html @@ -33,15 +33,33 @@ 33 34 35 -
/// Trait to interact with immediate operands.
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+
//! Definition of different immediate types which are used as input operands for various
+//! instructions.
+
+/// Trait to interact with immediate operands.
 pub(crate) trait Imm {
     /// Get immediate operand as slice of bytes.
     fn bytes(&self) -> &[u8];
 }
 
 macro_rules! impl_imm {
-    ($name:ident, $size:expr, from: $( $from:ty ),* $(,)?) => {
-        /// Immediate operand.
+    (#[$doc:meta] $name:ident, $size:expr, from: { $( $from:ty ),* $(,)? }) => {
+        #[$doc]
         pub struct $name([u8; $size]);
 
         impl Imm for $name {
@@ -64,9 +82,21 @@
     }
 }
 
-impl_imm!(Imm8, 1, from: u8, i8);
-impl_imm!(Imm16, 2, from: u16, i16, u8, i8);
-impl_imm!(Imm32, 4, from: u32, i32, u16, i16, u8, i8);
-impl_imm!(Imm64, 8, from: u64, i64, u32, i32, u16, i16, u8, i8);
+impl_imm!(
+    /// Type representing an 8 bit immediate.
+    Imm8, 1, from: { u8, i8 }
+);
+impl_imm!(
+    /// Type representing a 16 bit immediate.
+    Imm16, 2, from: { u16, i16, u8, i8 }
+);
+impl_imm!(
+    /// Type representing a 32 bit immediate.
+    Imm32, 4, from: { u32, i32, u16, i16, u8, i8 }
+);
+impl_imm!(
+    /// Type representing a 64 bit immediate.
+    Imm64, 8, from: { u64, i64, u32, i32, u16, i16, u8, i8 }
+);
 
\ No newline at end of file diff --git a/src/juicebox_asm/insn.rs.html b/src/juicebox_asm/insn.rs.html index 93134d8..2affb2d 100644 --- a/src/juicebox_asm/insn.rs.html +++ b/src/juicebox_asm/insn.rs.html @@ -44,7 +44,11 @@ 44 45 46 -
mod add;
+47
+48
+
//! Trait definitions of various instructions.
+
+mod add;
 mod dec;
 mod jmp;
 mod jnz;
diff --git a/src/juicebox_asm/label.rs.html b/src/juicebox_asm/label.rs.html
index cf7e7ae..aff9f8f 100644
--- a/src/juicebox_asm/label.rs.html
+++ b/src/juicebox_asm/label.rs.html
@@ -70,7 +70,20 @@
 70
 71
 72
-
use std::collections::HashSet;
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+
//! Definition of the lable type which can be used as jump target and can be bound to a location in
+//! the emitted code.
+
+use std::collections::HashSet;
 
 /// A label which is used as target for jump instructions.
 ///
@@ -107,7 +120,11 @@
         }
     }
 
-    /// Bind the label to the `location`.
+    /// Bind the label to the `location`, can only be bound once.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the lable is already bound.
     pub(crate) fn bind(&mut self, loc: usize) {
         // A label can only be bound once!
         assert!(!self.is_bound());
@@ -120,11 +137,14 @@
         self.offsets.insert(off);
     }
 
-    pub(crate) fn location(&self) -> Option<usize> {
+    /// Get the location of the lable if already bound, `None` else.
+    pub(crate) fn location(&self) -> Option<usize> {
         self.location
     }
 
-    pub(crate) fn offsets_mut(&mut self) -> &mut HashSet<usize> {
+    /// Get the offsets which refer to the label. These are used to patch the jump instructions to
+    /// the label location.
+    pub(crate) fn offsets_mut(&mut self) -> &mut HashSet<usize> {
         &mut self.offsets
     }
 
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
     }
diff --git a/src/juicebox_asm/prelude.rs.html b/src/juicebox_asm/prelude.rs.html
index 51df87d..be1334b 100644
--- a/src/juicebox_asm/prelude.rs.html
+++ b/src/juicebox_asm/prelude.rs.html
@@ -6,7 +6,11 @@
 6
 7
 8
-
pub use crate::Asm;
+9
+10
+
//! Crate prelude, which can be used to import the most important types at once.
+
+pub use crate::Asm;
 pub use crate::MemOp;
 
 pub use crate::imm::{Imm16, Imm32, Imm64, Imm8};
diff --git a/src/juicebox_asm/reg.rs.html b/src/juicebox_asm/reg.rs.html
index 65ff29e..8791ddb 100644
--- a/src/juicebox_asm/reg.rs.html
+++ b/src/juicebox_asm/reg.rs.html
@@ -321,7 +321,20 @@
 321
 322
 323
-
/// Trait to interact with register operands.
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+
//! Definition of registers which are used as input operands for various instructions.
+
+/// Trait to interact with register operands.
 pub(crate) trait Reg {
     /// Get the raw x64 register code.
     fn idx(&self) -> u8;
@@ -358,10 +371,10 @@
     }
 }
 
-macro_rules! impl_reg {
-    (ENUM_ONLY, $name:ident, { $($reg:ident),+ $(,)? }) => {
-        /// General purpose register operands.
-        #[allow(non_camel_case_types)]
+macro_rules! enum_reg {
+    (#[$doc:meta]  $name:ident, { $($reg:ident),+ $(,)? }) => {
+        #[$doc]
+        #[allow(non_camel_case_types)]
         #[derive(Copy, Clone)]
         #[repr(u8)]
         pub enum $name {
@@ -376,9 +389,11 @@
             }
         }
     };
+}
 
-    ($name:ident, $rexw:expr, { $($reg:ident),+ $(,)? }) => {
-        impl_reg!(ENUM_ONLY, $name, { $( $reg, )+ });
+macro_rules! impl_reg {
+    (#[$doc:meta] $name:ident, $rexw:expr, { $($reg:ident),+ $(,)? }) => {
+        enum_reg!(#[$doc] $name, { $( $reg, )+ });
 
         impl Reg for $name {
             /// Get the raw x64 register code.
@@ -394,11 +409,18 @@
     }
 }
 
-impl_reg!(Reg64, true,  { rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8,  r9,  r10,  r11,  r12,  r13,  r14,  r15  });
-impl_reg!(Reg32, false, { eax, ecx, edx, ebx, esp, ebp, esi, edi, r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d });
-impl_reg!(Reg16, false, { ax,  cx,  dx,  bx,  sp,  bp,  si,  di,  r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w });
-impl_reg!(ENUM_ONLY,
-          Reg8,         { al,  cl,  dl,  bl,  spl, bpl, sil, dil, r8l, r9l, r10l, r11l, r12l, r13l, r14l, r15l,
+impl_reg!(
+    /// Definition of 64 bit registers.
+    Reg64, true,  { rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8,  r9,  r10,  r11,  r12,  r13,  r14,  r15  });
+impl_reg!(
+    /// Definition of 32 bit registers.
+    Reg32, false, { eax, ecx, edx, ebx, esp, ebp, esi, edi, r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d });
+impl_reg!(
+    /// Definition of 16 bit registers.
+    Reg16, false, { ax,  cx,  dx,  bx,  sp,  bp,  si,  di,  r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w });
+enum_reg!(
+    /// Definition of 8 bit registers.
+    Reg8,         { al,  cl,  dl,  bl,  spl, bpl, sil, dil, r8l, r9l, r10l, r11l, r12l, r13l, r14l, r15l,
                           ah,  ch,  dh,  bh });
 
 impl Reg for Reg8 {
diff --git a/src/juicebox_asm/rt.rs.html b/src/juicebox_asm/rt.rs.html
index a44e465..62ee9b7 100644
--- a/src/juicebox_asm/rt.rs.html
+++ b/src/juicebox_asm/rt.rs.html
@@ -49,7 +49,11 @@
 49
 50
 51
-
use core::ffi::c_void;
+52
+53
+
//! A simple runtime which can be used to execute emitted instructions.
+
+use core::ffi::c_void;
 use nix::sys::mman::{mmap, munmap, MapFlags, ProtFlags};
 
 /// A simple `mmap`ed runtime with executable pages.
-- 
cgit v1.2.3