From d3e1eff9593501ff8677b9399e1f0625f415ec78 Mon Sep 17 00:00:00 2001 From: johannst Date: Thu, 7 Dec 2023 23:53:44 +0000 Subject: deploy: b5aea3fb5fcce31599e3d7397d5413a934132231 --- src/juicebox_asm/asm.rs.html | 689 ++++++++++++++++++++++++++++++++++ src/juicebox_asm/imm.rs.html | 28 +- src/juicebox_asm/insn.rs.html | 44 ++- src/juicebox_asm/insn/add.rs.html | 4 +- src/juicebox_asm/insn/call.rs.html | 4 +- src/juicebox_asm/insn/cmp.rs.html | 4 +- src/juicebox_asm/insn/dec.rs.html | 4 +- src/juicebox_asm/insn/jmp.rs.html | 4 +- src/juicebox_asm/insn/jnz.rs.html | 4 +- src/juicebox_asm/insn/jz.rs.html | 4 +- src/juicebox_asm/insn/mov.rs.html | 4 +- src/juicebox_asm/insn/nop.rs.html | 4 +- src/juicebox_asm/insn/ret.rs.html | 4 +- src/juicebox_asm/insn/test.rs.html | 4 +- src/juicebox_asm/label.rs.html | 4 +- src/juicebox_asm/lib.rs.html | 736 +------------------------------------ src/juicebox_asm/prelude.rs.html | 21 -- src/juicebox_asm/reg.rs.html | 98 +---- src/juicebox_asm/rt.rs.html | 290 +++++++++++++-- 19 files changed, 1070 insertions(+), 884 deletions(-) create mode 100644 src/juicebox_asm/asm.rs.html delete mode 100644 src/juicebox_asm/prelude.rs.html (limited to 'src/juicebox_asm') diff --git a/src/juicebox_asm/asm.rs.html b/src/juicebox_asm/asm.rs.html new file mode 100644 index 0000000..fd5f0d5 --- /dev/null +++ b/src/juicebox_asm/asm.rs.html @@ -0,0 +1,689 @@ +asm.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+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
+
//! The `x64` jit assembler.
+
+use crate::*;
+use imm::Imm;
+use reg::Reg;
+
+/// Encode the `REX` byte.
+const fn rex(w: bool, r: u8, x: u8, b: u8) -> u8 {
+    let w = if w { 1 } else { 0 };
+    let r = (r >> 3) & 1;
+    let x = (x >> 3) & 1;
+    let b = (b >> 3) & 1;
+    0b0100_0000 | ((w & 1) << 3) | (r << 2) | (x << 1) | b
+}
+
+/// Encode the `ModR/M` byte.
+const fn modrm(mod_: u8, reg: u8, rm: u8) -> u8 {
+    ((mod_ & 0b11) << 6) | ((reg & 0b111) << 3) | (rm & 0b111)
+}
+
+/// `x64` jit assembler.
+pub struct Asm {
+    buf: Vec<u8>,
+}
+
+impl Asm {
+    /// Create a new `x64` jit assembler.
+    pub fn new() -> Asm {
+        // Some random default capacity.
+        let buf = Vec::with_capacity(1024);
+        Asm { buf }
+    }
+
+    /// Consume the assembler and get the emitted code.
+    pub fn into_code(self) -> Vec<u8> {
+        self.buf
+    }
+
+    /// Emit a slice of bytes.
+    pub(crate) fn emit(&mut self, bytes: &[u8]) {
+        self.buf.extend_from_slice(bytes);
+    }
+
+    /// 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);
+        }
+    }
+
+    /// 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 {
+            unimplemented!();
+        }
+    }
+
+    /// Bind the [Label] to the current location.
+    pub fn bind(&mut self, label: &mut Label) {
+        // Bind the label to the current offset.
+        label.bind(self.buf.len());
+
+        // Resolve any pending relocations for the label.
+        self.resolve(label);
+    }
+
+    /// If the [Label] is bound, patch any pending relocation.
+    fn resolve(&mut self, label: &mut Label) {
+        if let Some(loc) = label.location() {
+            // 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() {
+                // Displacement is relative to the next instruction following the jump.
+                // We record the offset to patch at the first byte of the disp32 therefore we need
+                // to account for that in the disp computation.
+                let disp32 = loc - i32::try_from(off).expect("Label offset did not fit into i32") - 4 /* account for the disp32 */;
+
+                // Patch the relocation with the disp32.
+                self.emit_at(off, &disp32.to_ne_bytes());
+            }
+        }
+    }
+
+    // -- Encode utilities.
+
+    /// Encode an register-register instruction.
+    pub(crate) fn encode_rr<T: Reg>(&mut self, opc: u8, op1: T, op2: T)
+    where
+        Self: EncodeRR<T>,
+    {
+        // MR operand encoding.
+        //   op1 -> modrm.rm
+        //   op2 -> modrm.reg
+        let modrm = modrm(
+            0b11,      /* mod */
+            op2.idx(), /* reg */
+            op1.idx(), /* rm */
+        );
+
+        let prefix = <Self as EncodeRR<T>>::legacy_prefix();
+        let rex = <Self as EncodeRR<T>>::rex(op1, op2);
+
+        self.emit_optional(&[prefix, rex]);
+        self.emit(&[opc, modrm]);
+    }
+
+    /// Encode an offset-immediate instruction.
+    /// Register idx is encoded in the opcode.
+    pub(crate) fn encode_oi<T: Reg, U: Imm>(&mut self, opc: u8, op1: T, op2: U)
+    where
+        Self: EncodeR<T>,
+    {
+        let opc = opc + (op1.idx() & 0b111);
+        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());
+    }
+
+    /// Encode a register instruction.
+    pub(crate) fn encode_r<T: Reg>(&mut self, opc: u8, opc_ext: u8, op1: T)
+    where
+        Self: EncodeR<T>,
+    {
+        // M operand encoding.
+        //   op1           -> modrm.rm
+        //   opc extension -> modrm.reg
+        let modrm = modrm(
+            0b11,      /* mod */
+            opc_ext,   /* reg */
+            op1.idx(), /* rm */
+        );
+
+        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]);
+    }
+
+    /// Encode a memory-immediate instruction.
+    pub(crate) fn encode_mi<T: Imm>(&mut self, opc: u8, opc_ext: u8, op1: MemOp, op2: T)
+    where
+        Self: EncodeMI<T>,
+    {
+        // MI operand encoding.
+        //   op1 -> modrm.rm
+        //   op2 -> imm
+        let mode = match op1 {
+            MemOp::Indirect(..) => {
+                assert!(!op1.base().need_sib() && !op1.base().is_pc_rel());
+                0b00
+            }
+            MemOp::IndirectDisp(..) => {
+                assert!(!op1.base().need_sib());
+                0b10
+            }
+        };
+
+        let modrm = modrm(
+            mode,             /* mode */
+            opc_ext,          /* reg */
+            op1.base().idx(), /* rm */
+        );
+
+        let prefix = <Self as EncodeMI<T>>::legacy_prefix();
+        let rex = <Self as EncodeMI<T>>::rex(&op1);
+
+        self.emit_optional(&[prefix, rex]);
+        self.emit(&[opc, modrm]);
+        if let MemOp::IndirectDisp(_, disp) = op1 {
+            self.emit(&disp.to_ne_bytes());
+        }
+        self.emit(op2.bytes());
+    }
+
+    /// Encode a memory-register instruction.
+    pub(crate) fn encode_mr<T: Reg>(&mut self, opc: u8, op1: MemOp, op2: T)
+    where
+        Self: EncodeMR<T>,
+    {
+        // MR operand encoding.
+        //   op1 -> modrm.rm
+        //   op2 -> modrm.reg
+        let mode = match op1 {
+            MemOp::Indirect(..) => {
+                assert!(!op1.base().need_sib() && !op1.base().is_pc_rel());
+                0b00
+            }
+            MemOp::IndirectDisp(..) => {
+                assert!(!op1.base().need_sib());
+                0b10
+            }
+        };
+
+        let modrm = modrm(
+            mode,             /* mode */
+            op2.idx(),        /* reg */
+            op1.base().idx(), /* rm */
+        );
+        let prefix = <Self as EncodeMR<T>>::legacy_prefix();
+        let rex = <Self as EncodeMR<T>>::rex(&op1, op2);
+
+        self.emit_optional(&[prefix, rex]);
+        self.emit(&[opc, modrm]);
+        if let MemOp::IndirectDisp(_, disp) = op1 {
+            self.emit(&disp.to_ne_bytes());
+        }
+    }
+
+    /// Encode a register-memory instruction.
+    pub(crate) fn encode_rm<T: Reg>(&mut self, opc: u8, op1: T, op2: MemOp)
+    where
+        Self: EncodeMR<T>,
+    {
+        // RM operand encoding.
+        //   op1 -> modrm.reg
+        //   op2 -> modrm.rm
+        self.encode_mr(opc, op2, op1);
+    }
+
+    /// Encode a jump to label instruction.
+    pub(crate) fn encode_jmp_label(&mut self, opc: &[u8], op1: &mut Label) {
+        // Emit the opcode.
+        self.emit(opc);
+
+        // Record relocation offset starting at the first byte of the disp32.
+        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.
+        self.resolve(op1);
+    }
+}
+
+// -- Encoder helper.
+
+/// Encode helper for register-register instructions.
+pub(crate) trait EncodeRR<T: Reg> {
+    fn legacy_prefix() -> Option<u8> {
+        None
+    }
+
+    fn rex(op1: T, op2: T) -> Option<u8> {
+        if op1.need_rex() || op2.need_rex() {
+            Some(rex(op1.rexw(), op2.idx(), 0, op1.idx()))
+        } else {
+            None
+        }
+    }
+}
+
+impl EncodeRR<Reg8> for Asm {}
+impl EncodeRR<Reg32> for Asm {}
+impl EncodeRR<Reg16> for Asm {
+    fn legacy_prefix() -> Option<u8> {
+        Some(0x66)
+    }
+}
+impl EncodeRR<Reg64> for Asm {}
+
+/// Encode helper for register instructions.
+pub(crate) trait EncodeR<T: Reg> {
+    fn legacy_prefix() -> Option<u8> {
+        None
+    }
+
+    fn rex(op1: T) -> Option<u8> {
+        if op1.need_rex() {
+            Some(rex(op1.rexw(), 0, 0, op1.idx()))
+        } else {
+            None
+        }
+    }
+}
+
+impl EncodeR<Reg8> for Asm {}
+impl EncodeR<Reg32> for Asm {}
+impl EncodeR<Reg16> for Asm {
+    fn legacy_prefix() -> Option<u8> {
+        Some(0x66)
+    }
+}
+impl EncodeR<Reg64> for Asm {}
+
+/// Encode helper for memory-register instructions.
+pub(crate) trait EncodeMR<T: Reg> {
+    fn legacy_prefix() -> Option<u8> {
+        None
+    }
+
+    fn rex(op1: &MemOp, op2: T) -> Option<u8> {
+        if op2.need_rex() || (op1.base().is_ext()) {
+            Some(rex(op2.rexw(), op2.idx(), 0, op1.base().idx()))
+        } else {
+            None
+        }
+    }
+}
+
+impl EncodeMR<Reg8> for Asm {}
+impl EncodeMR<Reg16> for Asm {
+    fn legacy_prefix() -> Option<u8> {
+        Some(0x66)
+    }
+}
+impl EncodeMR<Reg32> for Asm {}
+impl EncodeMR<Reg64> for Asm {}
+
+/// Encode helper for memory-immediate instructions.
+pub(crate) trait EncodeMI<T: Imm> {
+    fn legacy_prefix() -> Option<u8> {
+        None
+    }
+
+    fn rex(op1: &MemOp) -> Option<u8> {
+        if op1.base().is_ext() {
+            Some(rex(false, 0, 0, op1.base().idx()))
+        } else {
+            None
+        }
+    }
+}
+
+impl EncodeMI<Imm8> for Asm {}
+impl EncodeMI<Imm16> for Asm {
+    fn legacy_prefix() -> Option<u8> {
+        Some(0x66)
+    }
+}
+impl EncodeMI<Imm32> for Asm {}
+
\ No newline at end of file diff --git a/src/juicebox_asm/imm.rs.html b/src/juicebox_asm/imm.rs.html index 78f4a8b..e0b6592 100644 --- a/src/juicebox_asm/imm.rs.html +++ b/src/juicebox_asm/imm.rs.html @@ -48,6 +48,19 @@ 48 49 50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63
//! Definition of different immediate types which are used as input operands for various
 //! instructions.
 
@@ -96,6 +109,19 @@
 );
 impl_imm!(
     /// Type representing a 64 bit immediate.
-    Imm64, 8, from: { u64, i64, u32, i32, u16, i16, u8, i8 }
+    Imm64, 8, from: { u64, i64, u32, i32, u16, i16, u8, i8, usize, isize }
 );
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use std::mem::size_of;
+
+    #[test]
+    fn test_usize_isize() {
+        // Imm64 should not implementd from usize/isize if this fails.
+        assert_eq!(size_of::<usize>(), size_of::<Imm64>());
+        assert_eq!(size_of::<isize>(), size_of::<Imm64>());
+    }
+}
 
\ No newline at end of file diff --git a/src/juicebox_asm/insn.rs.html b/src/juicebox_asm/insn.rs.html index 550a4ab..a367feb 100644 --- a/src/juicebox_asm/insn.rs.html +++ b/src/juicebox_asm/insn.rs.html @@ -59,6 +59,18 @@ 59 60 61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73
//! Trait definitions of various instructions.
 
 mod add;
@@ -73,47 +85,59 @@
 mod ret;
 mod test;
 
-pub trait Add<T, U> {
+/// Trait for [`add`](https://www.felixcloutier.com/x86/add) instruction kinds.
+pub trait Add<T, U> {
     /// Emit an add instruction.
     fn add(&mut self, op1: T, op2: U);
 }
 
-pub trait Call<T> {
+/// Trait for [`call`](https://www.felixcloutier.com/x86/call) instruction kinds.
+pub trait Call<T> {
     /// Emit a call instruction.
     fn call(&mut self, op1: T);
 }
 
-pub trait Cmp<T, U> {
-    /// Emit a compare call instruction.
+/// Trait for [`cmp`](https://www.felixcloutier.com/x86/cmp) instruction kinds.
+pub trait Cmp<T, U> {
+    /// Emit a compare instruction.
+    ///
+    /// Computes `op2 - op1` and sets the status flags in the same way as the `sub` instruction,
+    /// the result is discarded.
     fn cmp(&mut self, op1: T, op2: U);
 }
 
-pub trait Dec<T> {
+/// Trait for [`dec`](https://www.felixcloutier.com/x86/dec) instruction kinds.
+pub trait Dec<T> {
     /// Emit a decrement instruction.
     fn dec(&mut self, op1: T);
 }
 
-pub trait Jmp<T> {
+/// Trait for [`jmp`](https://www.felixcloutier.com/x86/jmp) instruction kinds.
+pub trait Jmp<T> {
     /// Emit an unconditional jump instruction.
     fn jmp(&mut self, op1: T);
 }
 
-pub trait Jnz<T> {
+/// Trait for [`jnz`](https://www.felixcloutier.com/x86/jcc) instruction kinds.
+pub trait Jnz<T> {
     /// Emit a conditional jump if not zero instruction (`ZF = 0`).
     fn jnz(&mut self, op1: T);
 }
 
-pub trait Jz<T> {
+/// Trait for [`jz`](https://www.felixcloutier.com/x86/jcc) instruction kinds.
+pub trait Jz<T> {
     /// Emit a conditional jump if zero instruction (`ZF = 1`).
     fn jz(&mut self, op1: T);
 }
 
-pub trait Mov<T, U> {
+/// Trait for [`mov`](https://www.felixcloutier.com/x86/mov) instruction kinds.
+pub trait Mov<T, U> {
     /// Emit an move instruction.
     fn mov(&mut self, op1: T, op2: U);
 }
 
-pub trait Test<T, U> {
+/// Trait for [`test`](https://www.felixcloutier.com/x86/test) instruction kinds.
+pub trait Test<T, U> {
     /// Emit a logical compare instruction.
     ///
     /// Computes the bit-wise logical AND of first operand and the second operand and sets the
diff --git a/src/juicebox_asm/insn/add.rs.html b/src/juicebox_asm/insn/add.rs.html
index d7f968a..d7d8c39 100644
--- a/src/juicebox_asm/insn/add.rs.html
+++ b/src/juicebox_asm/insn/add.rs.html
@@ -23,7 +23,9 @@
 23
 24
 25
-
use crate::prelude::*;
+26
+
use super::Add;
+use crate::{Asm, Imm16, MemOp, Reg16, Reg32, Reg64};
 
 impl Add<Reg64, Reg64> for Asm {
     fn add(&mut self, op1: Reg64, op2: Reg64) {
diff --git a/src/juicebox_asm/insn/call.rs.html b/src/juicebox_asm/insn/call.rs.html
index f9649ed..e88996d 100644
--- a/src/juicebox_asm/insn/call.rs.html
+++ b/src/juicebox_asm/insn/call.rs.html
@@ -5,7 +5,9 @@
 5
 6
 7
-
use crate::prelude::*;
+8
+
use super::Call;
+use crate::{Asm, Reg64};
 
 impl Call<Reg64> for Asm {
     fn call(&mut self, op1: Reg64) {
diff --git a/src/juicebox_asm/insn/cmp.rs.html b/src/juicebox_asm/insn/cmp.rs.html
index b50d90d..4e28818 100644
--- a/src/juicebox_asm/insn/cmp.rs.html
+++ b/src/juicebox_asm/insn/cmp.rs.html
@@ -5,7 +5,9 @@
 5
 6
 7
-
use crate::prelude::*;
+8
+
use super::Cmp;
+use crate::{Asm, Imm16, MemOp};
 
 impl Cmp<MemOp, Imm16> for Asm {
     fn cmp(&mut self, op1: MemOp, op2: Imm16) {
diff --git a/src/juicebox_asm/insn/dec.rs.html b/src/juicebox_asm/insn/dec.rs.html
index 2afecd2..5a10063 100644
--- a/src/juicebox_asm/insn/dec.rs.html
+++ b/src/juicebox_asm/insn/dec.rs.html
@@ -11,7 +11,9 @@
 11
 12
 13
-
use crate::prelude::*;
+14
+
use super::Dec;
+use crate::{Asm, Reg32, Reg64};
 
 impl Dec<Reg64> for Asm {
     fn dec(&mut self, op1: Reg64) {
diff --git a/src/juicebox_asm/insn/jmp.rs.html b/src/juicebox_asm/insn/jmp.rs.html
index 55da0e3..e0167e3 100644
--- a/src/juicebox_asm/insn/jmp.rs.html
+++ b/src/juicebox_asm/insn/jmp.rs.html
@@ -5,7 +5,9 @@
 5
 6
 7
-
use crate::prelude::*;
+8
+
use super::Jmp;
+use crate::{Asm, Label};
 
 impl Jmp<&mut Label> for Asm {
     fn jmp(&mut self, op1: &mut Label) {
diff --git a/src/juicebox_asm/insn/jnz.rs.html b/src/juicebox_asm/insn/jnz.rs.html
index 45fb92d..be0062d 100644
--- a/src/juicebox_asm/insn/jnz.rs.html
+++ b/src/juicebox_asm/insn/jnz.rs.html
@@ -5,7 +5,9 @@
 5
 6
 7
-
use crate::prelude::*;
+8
+
use super::Jnz;
+use crate::{Asm, Label};
 
 impl Jnz<&mut Label> for Asm {
     fn jnz(&mut self, op1: &mut Label) {
diff --git a/src/juicebox_asm/insn/jz.rs.html b/src/juicebox_asm/insn/jz.rs.html
index 4b17a71..e4197af 100644
--- a/src/juicebox_asm/insn/jz.rs.html
+++ b/src/juicebox_asm/insn/jz.rs.html
@@ -5,7 +5,9 @@
 5
 6
 7
-
use crate::prelude::*;
+8
+
use super::Jz;
+use crate::{Asm, Label};
 
 impl Jz<&mut Label> for Asm {
     fn jz(&mut self, op1: &mut Label) {
diff --git a/src/juicebox_asm/insn/mov.rs.html b/src/juicebox_asm/insn/mov.rs.html
index 7be5d09..1e672a1 100644
--- a/src/juicebox_asm/insn/mov.rs.html
+++ b/src/juicebox_asm/insn/mov.rs.html
@@ -111,7 +111,9 @@
 111
 112
 113
-
use crate::prelude::*;
+114
+
use super::Mov;
+use crate::{Asm, Imm16, Imm32, Imm64, Imm8, MemOp, Reg16, Reg32, Reg64, Reg8};
 
 // -- MOV : reg reg
 
diff --git a/src/juicebox_asm/insn/nop.rs.html b/src/juicebox_asm/insn/nop.rs.html
index accc13a..c3c264b 100644
--- a/src/juicebox_asm/insn/nop.rs.html
+++ b/src/juicebox_asm/insn/nop.rs.html
@@ -5,10 +5,12 @@
 5
 6
 7
+8
 
use crate::Asm;
 
 impl Asm {
-    pub fn nop(&mut self) {
+    /// Emit a [`nop`](https://www.felixcloutier.com/x86/nop) instruction.
+    pub fn nop(&mut self) {
         self.emit(&[0x90]);
     }
 }
diff --git a/src/juicebox_asm/insn/ret.rs.html b/src/juicebox_asm/insn/ret.rs.html
index 97070e8..26e3a47 100644
--- a/src/juicebox_asm/insn/ret.rs.html
+++ b/src/juicebox_asm/insn/ret.rs.html
@@ -5,10 +5,12 @@
 5
 6
 7
+8
 
use crate::Asm;
 
 impl Asm {
-    pub fn ret(&mut self) {
+    /// Emit a [`ret`](https://www.felixcloutier.com/x86/ret) instruction.
+    pub fn ret(&mut self) {
         self.emit(&[0xc3]);
     }
 }
diff --git a/src/juicebox_asm/insn/test.rs.html b/src/juicebox_asm/insn/test.rs.html
index d636920..0f1d8da 100644
--- a/src/juicebox_asm/insn/test.rs.html
+++ b/src/juicebox_asm/insn/test.rs.html
@@ -17,7 +17,9 @@
 17
 18
 19
-
use crate::prelude::*;
+20
+
use super::Test;
+use crate::{Asm, Imm16, MemOp, Reg32, Reg64};
 
 impl Test<Reg64, Reg64> for Asm {
     fn test(&mut self, op1: Reg64, op2: Reg64) {
diff --git a/src/juicebox_asm/label.rs.html b/src/juicebox_asm/label.rs.html
index e209289..29d236e 100644
--- a/src/juicebox_asm/label.rs.html
+++ b/src/juicebox_asm/label.rs.html
@@ -80,6 +80,7 @@
 80
 81
 82
+83
 
//! Definition of the lable type which can be used as jump target and can be bound to a location in
 //! the emitted code.
 
@@ -88,7 +89,8 @@
 /// A label which is used as target for jump instructions.
 ///
 /// ```rust
-/// use juicebox_asm::prelude::*;
+/// use juicebox_asm::{Asm, Label, Reg64};
+/// use juicebox_asm::insn::{Mov, Jmp};
 ///
 /// let mut lbl = Label::new();
 /// let mut asm = Asm::new();
diff --git a/src/juicebox_asm/lib.rs.html b/src/juicebox_asm/lib.rs.html
index 83c8f81..7d1bc0c 100644
--- a/src/juicebox_asm/lib.rs.html
+++ b/src/juicebox_asm/lib.rs.html
@@ -105,373 +105,12 @@
 105
 106
 107
-108
-109
-110
-111
-112
-113
-114
-115
-116
-117
-118
-119
-120
-121
-122
-123
-124
-125
-126
-127
-128
-129
-130
-131
-132
-133
-134
-135
-136
-137
-138
-139
-140
-141
-142
-143
-144
-145
-146
-147
-148
-149
-150
-151
-152
-153
-154
-155
-156
-157
-158
-159
-160
-161
-162
-163
-164
-165
-166
-167
-168
-169
-170
-171
-172
-173
-174
-175
-176
-177
-178
-179
-180
-181
-182
-183
-184
-185
-186
-187
-188
-189
-190
-191
-192
-193
-194
-195
-196
-197
-198
-199
-200
-201
-202
-203
-204
-205
-206
-207
-208
-209
-210
-211
-212
-213
-214
-215
-216
-217
-218
-219
-220
-221
-222
-223
-224
-225
-226
-227
-228
-229
-230
-231
-232
-233
-234
-235
-236
-237
-238
-239
-240
-241
-242
-243
-244
-245
-246
-247
-248
-249
-250
-251
-252
-253
-254
-255
-256
-257
-258
-259
-260
-261
-262
-263
-264
-265
-266
-267
-268
-269
-270
-271
-272
-273
-274
-275
-276
-277
-278
-279
-280
-281
-282
-283
-284
-285
-286
-287
-288
-289
-290
-291
-292
-293
-294
-295
-296
-297
-298
-299
-300
-301
-302
-303
-304
-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
-411
-412
-413
-414
-415
-416
-417
-418
-419
-420
-421
-422
-423
-424
-425
-426
-427
-428
-429
-430
-431
-432
-433
-434
-435
-436
-437
-438
-439
-440
-441
-442
-443
-444
-445
-446
-447
-448
-449
-450
-451
-452
-453
-454
-455
-456
-457
-458
-459
-460
-461
-462
-463
-464
-465
-466
-467
-468
-469
 
//! 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::{Asm, Reg64, Imm64, Label};
+//! use juicebox_asm::insn::*;
 //! use juicebox_asm::Runtime;
 //!
 //! const fn fib_rs(n: u64) -> u64 {
@@ -541,22 +180,20 @@
 //! }
 //! ```
 
-pub mod prelude;
-
+mod asm;
 mod imm;
-mod insn;
 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;
 
-use imm::Imm;
-use reg::Reg;
-
 /// Type representing a memory operand.
 pub enum MemOp {
     /// An indirect memory operand, eg `mov [rax], rcx`.
@@ -575,365 +212,4 @@
         }
     }
 }
-
-/// Encode the `REX` byte.
-const fn rex(w: bool, r: u8, x: u8, b: u8) -> u8 {
-    let w = if w { 1 } else { 0 };
-    let r = (r >> 3) & 1;
-    let x = (x >> 3) & 1;
-    let b = (b >> 3) & 1;
-    0b0100_0000 | ((w & 1) << 3) | (r << 2) | (x << 1) | b
-}
-
-/// Encode the `ModR/M` byte.
-const fn modrm(mod_: u8, reg: u8, rm: u8) -> u8 {
-    ((mod_ & 0b11) << 6) | ((reg & 0b111) << 3) | (rm & 0b111)
-}
-
-/// `x64` jit assembler.
-pub struct Asm {
-    buf: Vec<u8>,
-}
-
-impl Asm {
-    /// Create a new `x64` jit assembler.
-    pub fn new() -> Asm {
-        // Some random default capacity.
-        let buf = Vec::with_capacity(1024);
-        Asm { buf }
-    }
-
-    /// Consume the assembler and get the emitted code.
-    pub fn into_code(self) -> Vec<u8> {
-        self.buf
-    }
-
-    /// Emit a slice of bytes.
-    fn emit(&mut self, bytes: &[u8]) {
-        self.buf.extend_from_slice(bytes);
-    }
-
-    /// 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);
-        }
-    }
-
-    /// 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 {
-            unimplemented!();
-        }
-    }
-
-    /// Bind the [Label] to the current location.
-    pub fn bind(&mut self, label: &mut Label) {
-        // Bind the label to the current offset.
-        label.bind(self.buf.len());
-
-        // Resolve any pending relocations for the label.
-        self.resolve(label);
-    }
-
-    /// If the [Label] is bound, patch any pending relocation.
-    pub fn resolve(&mut self, label: &mut Label) {
-        if let Some(loc) = label.location() {
-            // 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() {
-                // Displacement is relative to the next instruction following the jump.
-                // We record the offset to patch at the first byte of the disp32 therefore we need
-                // to account for that in the disp computation.
-                let disp32 = loc - i32::try_from(off).expect("Label offset did not fit into i32") - 4 /* account for the disp32 */;
-
-                // Patch the relocation with the disp32.
-                self.emit_at(off, &disp32.to_ne_bytes());
-            }
-        }
-    }
-
-    // -- Encode utilities.
-
-    /// Encode an register-register instruction.
-    fn encode_rr<T: Reg>(&mut self, opc: u8, op1: T, op2: T)
-    where
-        Self: EncodeRR<T>,
-    {
-        // MR operand encoding.
-        //   op1 -> modrm.rm
-        //   op2 -> modrm.reg
-        let modrm = modrm(
-            0b11,      /* mod */
-            op2.idx(), /* reg */
-            op1.idx(), /* rm */
-        );
-
-        let prefix = <Self as EncodeRR<T>>::legacy_prefix();
-        let rex = <Self as EncodeRR<T>>::rex(op1, op2);
-
-        self.emit_optional(&[prefix, rex]);
-        self.emit(&[opc, modrm]);
-    }
-
-    /// 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: EncodeR<T>,
-    {
-        let opc = opc + (op1.idx() & 0b111);
-        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());
-    }
-
-    /// 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: EncodeR<T>,
-    {
-        // MI operand encoding.
-        //   op1           -> modrm.rm
-        //   opc extension -> modrm.reg
-        let modrm = modrm(
-            0b11,      /* mod */
-            opc_ext,   /* reg */
-            op1.idx(), /* rm */
-        );
-
-        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());
-    }
-
-    /// Encode a register instruction.
-    fn encode_r<T: Reg>(&mut self, opc: u8, opc_ext: u8, op1: T)
-    where
-        Self: EncodeR<T>,
-    {
-        // M operand encoding.
-        //   op1           -> modrm.rm
-        //   opc extension -> modrm.reg
-        let modrm = modrm(
-            0b11,      /* mod */
-            opc_ext,   /* reg */
-            op1.idx(), /* rm */
-        );
-
-        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]);
-    }
-
-    /// Encode a memory-immediate instruction.
-    fn encode_mi<T: Imm>(&mut self, opc: u8, opc_ext: u8, op1: MemOp, op2: T)
-    where
-        Self: EncodeMI<T>,
-    {
-        // MI operand encoding.
-        //   op1 -> modrm.rm
-        //   op2 -> imm
-        let mode = match op1 {
-            MemOp::Indirect(..) => {
-                assert!(!op1.base().need_sib() && !op1.base().is_pc_rel());
-                0b00
-            }
-            MemOp::IndirectDisp(..) => {
-                assert!(!op1.base().need_sib());
-                0b10
-            }
-        };
-
-        let modrm = modrm(
-            mode,             /* mode */
-            opc_ext,          /* reg */
-            op1.base().idx(), /* rm */
-        );
-
-        let prefix = <Self as EncodeMI<T>>::legacy_prefix();
-        let rex = <Self as EncodeMI<T>>::rex(&op1);
-
-        self.emit_optional(&[prefix, rex]);
-        self.emit(&[opc, modrm]);
-        if let MemOp::IndirectDisp(_, disp) = op1 {
-            self.emit(&disp.to_ne_bytes());
-        }
-        self.emit(op2.bytes());
-    }
-
-    /// Encode a memory-register instruction.
-    fn encode_mr<T: Reg>(&mut self, opc: u8, op1: MemOp, op2: T)
-    where
-        Self: EncodeMR<T>,
-    {
-        // MR operand encoding.
-        //   op1 -> modrm.rm
-        //   op2 -> modrm.reg
-        let mode = match op1 {
-            MemOp::Indirect(..) => {
-                assert!(!op1.base().need_sib() && !op1.base().is_pc_rel());
-                0b00
-            }
-            MemOp::IndirectDisp(..) => {
-                assert!(!op1.base().need_sib());
-                0b10
-            }
-        };
-
-        let modrm = modrm(
-            mode,             /* mode */
-            op2.idx(),        /* reg */
-            op1.base().idx(), /* rm */
-        );
-        let prefix = <Self as EncodeMR<T>>::legacy_prefix();
-        let rex = <Self as EncodeMR<T>>::rex(&op1, op2);
-
-        self.emit_optional(&[prefix, rex]);
-        self.emit(&[opc, modrm]);
-        if let MemOp::IndirectDisp(_, disp) = op1 {
-            self.emit(&disp.to_ne_bytes());
-        }
-    }
-
-    /// Encode a register-memory instruction.
-    fn encode_rm<T: Reg>(&mut self, opc: u8, op1: T, op2: MemOp)
-    where
-        Self: EncodeMR<T>,
-    {
-        // RM operand encoding.
-        //   op1 -> modrm.reg
-        //   op2 -> modrm.rm
-        self.encode_mr(opc, op2, op1);
-    }
-
-    /// Encode a jump to label instruction.
-    fn encode_jmp_label(&mut self, opc: &[u8], op1: &mut Label) {
-        // Emit the opcode.
-        self.emit(opc);
-
-        // Record relocation offset starting at the first byte of the disp32.
-        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.
-        self.resolve(op1);
-    }
-}
-
-// -- Encoder helper.
-
-/// Encode helper for register-register instructions.
-trait EncodeRR<T: Reg> {
-    fn legacy_prefix() -> Option<u8> {
-        None
-    }
-
-    fn rex(op1: T, op2: T) -> Option<u8> {
-        if op1.need_rex() || op2.need_rex() {
-            Some(rex(op1.rexw(), op2.idx(), 0, op1.idx()))
-        } else {
-            None
-        }
-    }
-}
-
-impl EncodeRR<Reg8> for Asm {}
-impl EncodeRR<Reg32> for Asm {}
-impl EncodeRR<Reg16> for Asm {
-    fn legacy_prefix() -> Option<u8> {
-        Some(0x66)
-    }
-}
-impl EncodeRR<Reg64> for Asm {}
-
-/// Encode helper for register instructions.
-trait EncodeR<T: Reg> {
-    fn legacy_prefix() -> Option<u8> {
-        None
-    }
-
-    fn rex(op1: T) -> Option<u8> {
-        if op1.need_rex() {
-            Some(rex(op1.rexw(), 0, 0, op1.idx()))
-        } else {
-            None
-        }
-    }
-}
-
-impl EncodeR<Reg8> for Asm {}
-impl EncodeR<Reg32> for Asm {}
-impl EncodeR<Reg16> for Asm {
-    fn legacy_prefix() -> Option<u8> {
-        Some(0x66)
-    }
-}
-impl EncodeR<Reg64> for Asm {}
-
-/// Encode helper for memory-register instructions.
-trait EncodeMR<T: Reg> {
-    fn legacy_prefix() -> Option<u8> {
-        None
-    }
-
-    fn rex(op1: &MemOp, op2: T) -> Option<u8> {
-        if op2.need_rex() || (op1.base().is_ext()) {
-            Some(rex(op2.rexw(), op2.idx(), 0, op1.base().idx()))
-        } else {
-            None
-        }
-    }
-}
-
-impl EncodeMR<Reg8> for Asm {}
-impl EncodeMR<Reg16> for Asm {
-    fn legacy_prefix() -> Option<u8> {
-        Some(0x66)
-    }
-}
-impl EncodeMR<Reg32> for Asm {}
-impl EncodeMR<Reg64> for Asm {}
-
-/// Encode helper for memory-immediate instructions.
-trait EncodeMI<T: Imm> {
-    fn legacy_prefix() -> Option<u8> {
-        None
-    }
-
-    fn rex(op1: &MemOp) -> Option<u8> {
-        if op1.base().is_ext() {
-            Some(rex(false, 0, 0, op1.base().idx()))
-        } else {
-            None
-        }
-    }
-}
-
-impl EncodeMI<Imm8> for Asm {}
-impl EncodeMI<Imm16> for Asm {
-    fn legacy_prefix() -> Option<u8> {
-        Some(0x66)
-    }
-}
-impl EncodeMI<Imm32> for Asm {}
 
\ No newline at end of file diff --git a/src/juicebox_asm/prelude.rs.html b/src/juicebox_asm/prelude.rs.html deleted file mode 100644 index e877286..0000000 --- a/src/juicebox_asm/prelude.rs.html +++ /dev/null @@ -1,21 +0,0 @@ -prelude.rs - source
1
-2
-3
-4
-5
-6
-7
-8
-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};
-pub use crate::label::Label;
-pub use crate::reg::{Reg16, Reg32, Reg64, Reg8};
-
-pub use crate::insn::{Add, Call, Cmp, Dec, Jmp, Jnz, Jz, Mov, Test};
-
\ No newline at end of file diff --git a/src/juicebox_asm/reg.rs.html b/src/juicebox_asm/reg.rs.html index a511005..fe4b50c 100644 --- a/src/juicebox_asm/reg.rs.html +++ b/src/juicebox_asm/reg.rs.html @@ -302,36 +302,6 @@ 302 303 304 -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
//! Definition of registers which are used as input operands for various instructions.
 
 /// Trait to interact with register operands.
@@ -489,27 +459,21 @@
             assert_eq!(r.idx(), idx);
 
             // Check REX.W bit.
-            assert_eq!(r.rexw(), false);
+            assert!(!r.rexw());
 
             // Check need REX byte.
-            let rex = match r {
-                r8l | r9l | r10l | r11l | r12l | r13l | r14l | r15l | spl | bpl | sil | dil => true,
-                _ => false,
-            };
+            let rex = matches!(
+                r,
+                r8l | r9l | r10l | r11l | r12l | r13l | r14l | r15l | spl | bpl | sil | dil
+            );
             assert_eq!(r.need_rex(), rex);
 
             // Check need SIB byte.
-            let sib = match r {
-                spl | r12l | ah => true,
-                _ => false,
-            };
+            let sib = matches!(r, spl | r12l | ah);
             assert_eq!(r.need_sib(), sib);
 
             // Check if is PC relative addressing.
-            let rel = match r {
-                bpl | r13l | ch => true,
-                _ => false,
-            };
+            let rel = matches!(r, bpl | r13l | ch);
             assert_eq!(r.is_pc_rel(), rel);
         }
     }
@@ -541,27 +505,18 @@
             assert_eq!(r.idx(), idx);
 
             // Check REX.W bit.
-            assert_eq!(r.rexw(), false);
+            assert!(!r.rexw());
 
             // Check need REX byte.
-            let rex = match r {
-                r8w | r9w | r10w | r11w | r12w | r13w | r14w | r15w => true,
-                _ => false,
-            };
+            let rex = matches!(r, r8w | r9w | r10w | r11w | r12w | r13w | r14w | r15w);
             assert_eq!(r.need_rex(), rex);
 
             // Check need SIB byte.
-            let sib = match r {
-                sp | r12w => true,
-                _ => false,
-            };
+            let sib = matches!(r, sp | r12w);
             assert_eq!(r.need_sib(), sib);
 
             // Check if is PC relative addressing.
-            let rel = match r {
-                bp | r13w => true,
-                _ => false,
-            };
+            let rel = matches!(r, bp | r13w);
             assert_eq!(r.is_pc_rel(), rel);
         }
     }
@@ -593,27 +548,18 @@
             assert_eq!(r.idx(), idx);
 
             // Check REX.W bit.
-            assert_eq!(r.rexw(), false);
+            assert!(!r.rexw());
 
             // Check need REX byte.
-            let rex = match r {
-                r8d | r9d | r10d | r11d | r12d | r13d | r14d | r15d => true,
-                _ => false,
-            };
+            let rex = matches!(r, r8d | r9d | r10d | r11d | r12d | r13d | r14d | r15d);
             assert_eq!(r.need_rex(), rex);
 
             // Check need SIB byte.
-            let sib = match r {
-                esp | r12d => true,
-                _ => false,
-            };
+            let sib = matches!(r, esp | r12d);
             assert_eq!(r.need_sib(), sib);
 
             // Check if is PC relative addressing.
-            let rel = match r {
-                ebp | r13d => true,
-                _ => false,
-            };
+            let rel = matches!(r, ebp | r13d);
             assert_eq!(r.is_pc_rel(), rel);
         }
     }
@@ -645,23 +591,17 @@
             assert_eq!(r.idx(), idx);
 
             // Check REX.W bit.
-            assert_eq!(r.rexw(), true);
+            assert!(r.rexw());
 
             // Check need REX byte.
-            assert_eq!(r.need_rex(), true);
+            assert!(r.need_rex());
 
             // Check need SIB byte.
-            let sib = match r {
-                rsp | r12 => true,
-                _ => false,
-            };
+            let sib = matches!(r, rsp | r12);
             assert_eq!(r.need_sib(), sib);
 
             // Check if is PC relative addressing.
-            let rel = match r {
-                rbp | r13 => true,
-                _ => false,
-            };
+            let rel = matches!(r, rbp | r13);
             assert_eq!(r.is_pc_rel(), rel);
         }
     }
diff --git a/src/juicebox_asm/rt.rs.html b/src/juicebox_asm/rt.rs.html
index 244be8b..efc55d3 100644
--- a/src/juicebox_asm/rt.rs.html
+++ b/src/juicebox_asm/rt.rs.html
@@ -86,10 +86,129 @@
 86
 87
 88
-
//! A simple runtime which can be used to execute emitted instructions.
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+
//! Simple `mmap`ed runtime.
+//!
+//! This runtime supports adding code to executable pages and turn the added code into user
+//! specified function pointer.
 
-use core::slice;
-use nix::sys::mman::{mmap, mprotect, munmap, MapFlags, ProtFlags};
+use nix::sys::mman::{mmap, mprotect, munmap, MapFlags, ProtFlags};
+
+#[cfg(not(target_os = "linux"))]
+compile_error!("This runtime is only supported on linux");
 
 /// A simple `mmap`ed runtime with executable pages.
 pub struct Runtime {
@@ -100,19 +219,23 @@
 
 impl Runtime {
     /// Create a new [Runtime].
+    ///
+    /// # Panics
+    ///
+    /// Panics if the `mmap` call fails.
     pub fn new() -> Runtime {
         // Allocate a single page.
-        let len = core::num::NonZeroUsize::new(4096).unwrap();
+        let len = core::num::NonZeroUsize::new(4096).expect("Value is non zero");
         let buf = unsafe {
             mmap(
                 None,
                 len,
-                ProtFlags::PROT_WRITE | ProtFlags::PROT_READ | ProtFlags::PROT_EXEC,
+                ProtFlags::PROT_NONE,
                 MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS,
                 0, /* fd */
                 0, /* off */
             )
-            .unwrap() as *mut u8
+            .expect("Failed to mmap runtime code page") as *mut u8
         };
 
         Runtime {
@@ -122,55 +245,160 @@
         }
     }
 
-    /// Write protect the underlying code page(s).
-    pub fn protect(&mut self) {
-        unsafe {
-            // Remove write permissions from code buffer and allow to read-execute from it.
-            mprotect(
-                self.buf.cast(),
-                self.len,
-                ProtFlags::PROT_READ | ProtFlags::PROT_EXEC,
-            )
-            .expect("Failed to RX mprotect Runtime code buffer");
-        }
-    }
-
-    /// Add block of code to the runtime and get function pointer back.
+    /// Add the block of `code` to the runtime and a get function pointer of type `F`.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the `code` does not fit on the `mmap`ed pages or is empty.
+    ///
+    /// # Safety
+    ///
+    /// The code added must fulfill the ABI of the specified function `F` and the returned function
+    /// pointer is only valid until the [`Runtime`] is dropped.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// let mut rt = juicebox_asm::Runtime::new();
+    ///
+    /// let code = [ 0x90 /* nop */, 0xc3 /* ret */ ];
+    /// let nop = unsafe { rt.add_code::<extern "C" fn()>(&code) };
+    ///
+    /// nop();
+    /// ```
     pub unsafe fn add_code<F>(&mut self, code: impl AsRef<[u8]>) -> F {
         // Get pointer to start of next free byte.
-        assert!(self.idx < self.len);
+        assert!(self.idx < self.len, "Runtime code page full");
         let fn_start = self.buf.add(self.idx);
 
         // Copy over code.
         let code = code.as_ref();
-        assert!(code.len() < (self.len - self.idx));
+        assert!(!code.is_empty(), "Adding empty code not supported");
+        assert!(
+            code.len() <= (self.len - self.idx),
+            "Code does not fit on the runtime code page"
+        );
+        self.unprotect();
         unsafe { std::ptr::copy_nonoverlapping(code.as_ptr(), fn_start, code.len()) };
+        self.protect();
 
         // Increment index to next free byte.
         self.idx += code.len();
 
         // Return function to newly added code.
-        Self::as_fn::<F>(fn_start)
+        unsafe { Self::as_fn::<F>(fn_start) }
     }
 
-    /// Reinterpret the block of code as `F`.
+    /// Dump the code added so far to the runtime into a file called `jit.asm` in the processes
+    /// current working directory.
+    ///
+    /// The code can be inspected with a disassembler as for example `ndiasm` from
+    /// [nasm.us](https://nasm.us/index.php).
+    /// ```sh
+    /// ndisasm -b 64 jit.asm
+    /// ```
+    ///
+    /// # Panics
+    ///
+    /// Panics if writing the file failed.
+    pub fn dump(&self) {
+        assert!(self.idx <= self.len);
+        let code = unsafe { core::slice::from_raw_parts(self.buf, self.idx) };
+        std::fs::write("jit.asm", code).expect("Failed to write file");
+    }
+
+    /// Reinterpret the block of code pointed to by `fn_start` as `F`.
     #[inline]
     unsafe fn as_fn<F>(fn_start: *mut u8) -> F {
         unsafe { std::mem::transmute_copy(&fn_start) }
     }
 
-    /// Dump the currently added code to a file called `jit.asm`. The disassembly can be inspected
-    /// as `ndisasm -b 64 jit.asm`.
-    pub fn dump(&self) {
-        let code = unsafe { slice::from_raw_parts(self.buf, self.idx) };
-        std::fs::write("jit.asm", code).unwrap();
+    /// Add write protection the underlying code page(s).
+    ///
+    /// # Panics
+    ///
+    /// Panics if the `mprotect` call fails.
+    fn protect(&mut self) {
+        unsafe {
+            // Remove write permissions from code page and allow to read-execute from it.
+            mprotect(
+                self.buf.cast(),
+                self.len,
+                ProtFlags::PROT_READ | ProtFlags::PROT_EXEC,
+            )
+            .expect("Failed to RX mprotect runtime code page");
+        }
+    }
+
+    /// Remove write protection the underlying code page(s).
+    ///
+    /// # Panics
+    ///
+    /// Panics if the `mprotect` call fails.
+    fn unprotect(&mut self) {
+        unsafe {
+            // Add write permissions to code page.
+            mprotect(self.buf.cast(), self.len, ProtFlags::PROT_WRITE)
+                .expect("Failed to W mprotect runtime code page");
+        }
     }
 }
 
 impl Drop for Runtime {
-    fn drop(&mut self) {
+    /// Unmaps the code page. This invalidates all the function pointer returned by
+    /// [`Runtime::add_code`].
+    fn drop(&mut self) {
+        unsafe {
+            munmap(self.buf.cast(), self.len).expect("Failed to munmap runtime");
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_code_max_size() {
+        let mut rt = Runtime::new();
+        let code = [0u8; 4096];
+        unsafe {
+            rt.add_code::<extern "C" fn()>(code);
+        }
+    }
+
+    #[test]
+    #[should_panic]
+    fn test_code_max_size_plus_1() {
+        let mut rt = Runtime::new();
+        let code = [0u8; 4097];
+        unsafe {
+            rt.add_code::<extern "C" fn()>(code);
+        }
+    }
+
+    #[test]
+    #[should_panic]
+    fn test_code_max_size_plus_1_2() {
+        let mut rt = Runtime::new();
+        let code = [0u8; 4096];
+        unsafe {
+            rt.add_code::<extern "C" fn()>(code);
+        }
+
+        let code = [0u8; 1];
+        unsafe {
+            rt.add_code::<extern "C" fn()>(code);
+        }
+    }
+
+    #[test]
+    #[should_panic]
+    fn test_empty_code() {
+        let mut rt = Runtime::new();
+        let code = [0u8; 0];
         unsafe {
-            munmap(self.buf.cast(), self.len).expect("Failed to munmap Runtime");
+            rt.add_code::<extern "C" fn()>(code);
         }
     }
 }
-- 
cgit v1.2.3