From 9ebb4b9d82decbd9e8ea901e6d240e1af9f190b3 Mon Sep 17 00:00:00 2001 From: johannst Date: Wed, 11 Dec 2024 21:52:27 +0000 Subject: deploy: aedbcbf6c8e45367364a852d5b10d45b27b61c44 --- src/bf/bf.rs.html | 144 ++++++++++++++++++++++++++++++-------- src/gen/gen.rs.html | 91 ++++++++++++++++++++++++ src/juicebox_asm/insn.rs.html | 24 +++++-- src/juicebox_asm/insn/add.rs.html | 16 ++++- src/juicebox_asm/insn/sub.rs.html | 29 ++++++++ 5 files changed, 268 insertions(+), 36 deletions(-) create mode 100644 src/gen/gen.rs.html create mode 100644 src/juicebox_asm/insn/sub.rs.html (limited to 'src') diff --git a/src/bf/bf.rs.html b/src/bf/bf.rs.html index 25b814e..9bd6202 100644 --- a/src/bf/bf.rs.html +++ b/src/bf/bf.rs.html @@ -290,7 +290,50 @@ 289 290 291 -292
//! Brainfuck VM.
+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
//! Brainfuck VM.
 //!
 //! This example implements a simple
 //! [brainfuck](https://en.wikipedia.org/wiki/Brainfuck) interpreter
@@ -313,7 +356,9 @@
 use juicebox_asm::Runtime;
 use juicebox_asm::{Asm, Imm64, Imm8, Label, MemOp, MemOp8, Reg64, Reg8};
 
-struct BrainfuckInterp {
+// -- BRAINFUCK INTERPRETER ----------------------------------------------------
+
+struct BrainfuckInterp {
     pc: usize,
     imem: Vec<char>,
     dptr: usize,
@@ -323,8 +368,9 @@
 
 impl BrainfuckInterp {
     fn new(prog: &str) -> Result<Self, String> {
-        // Do a first pass over the bf program to filter whitespace and detect invalid tokens.
-        // Additionally validate all conditional branches, and compute their branch target.
+        // Do a first pass over the bf program to filter whitespace and detect
+        // invalid tokens. Additionally validate all conditional branches, and
+        // compute their branch target.
         let (imem, branches) = {
             // Instruction memory holding the final bf program.
             let mut imem = Vec::new();
@@ -422,7 +468,7 @@
     }
 }
 
-// -- BRAINFUCK JIT --------------------------------------------------------------
+// -- BRAINFUCK JIT ------------------------------------------------------------
 
 #[cfg(not(any(target_arch = "x86_64", target_os = "linux")))]
 compile_error!("Only supported on x86_64 with SystemV abi");
@@ -434,7 +480,8 @@
 
 impl BrainfuckJit {
     fn new(prog: &str) -> Result<Self, String> {
-        // Do a first pass over the bf program to filter whitespace and detect invalid tokens.
+        // Do a first pass over the bf program to filter whitespace and detect
+        // invalid tokens.
         let imem = prog
             .chars()
             .filter(|c| !c.is_whitespace())
@@ -460,25 +507,25 @@
 fn run_jit(prog: &str) {
     let mut vm = BrainfuckJit::new(prog).unwrap();
 
-    // Use callee saved registers to hold vm state, such that we don't
-    // need to save any state before calling out to putchar.
+    // Use callee saved registers to hold vm state, such that we don't need to
+    // save any state before calling out to putchar.
     let dmem_base = Reg64::rbx;
     let dmem_idx = Reg64::r12;
 
     let mut asm = Asm::new();
-    // Move data memory pointer (argument on jit entry) into correct
-    // register.
+    // Move data memory pointer (argument on jit entry) into correct register.
     asm.mov(dmem_base, Reg64::rdi);
     // Clear data memory index.
     asm.xor(dmem_idx, dmem_idx);
 
-    // A stack of label pairs, used to link up forward and backward
-    // jumps for a given '[]' pair.
+    // A stack of label pairs, used to link up forward and backward jumps for a
+    // given '[]' pair.
     let mut label_stack = Vec::new();
 
     // Generate code for each instruction in the bf program.
-    for insn in vm.imem {
-        match insn {
+    let mut pc = 0;
+    while pc < vm.imem.len() {
+        match vm.imem[pc] {
             '>' => {
                 // TODO: generate runtime bounds check.
                 asm.inc(dmem_idx);
@@ -488,18 +535,53 @@
                 asm.dec(dmem_idx);
             }
             '+' => {
-                asm.inc(MemOp8::from(MemOp::IndirectBaseIndex(dmem_base, dmem_idx)));
+                // Apply optimization to fold consecutive '+' instructions to a
+                // single add instruction during compile time.
+
+                match vm.imem[pc..].iter().take_while(|&&i| i.eq(&'+')).count() {
+                    1 => asm.inc(MemOp8::from(MemOp::IndirectBaseIndex(dmem_base, dmem_idx))),
+                    cnt if cnt <= i8::MAX as usize => {
+                        // For add m64, imm8, the immediate is sign-extend and
+                        // hence treated as signed.
+                        asm.add(
+                            MemOp::IndirectBaseIndex(dmem_base, dmem_idx),
+                            Imm8::from(cnt as u8),
+                        );
+
+                        // Advance pc, but account for pc increment at the end
+                        // of the loop.
+                        pc += cnt - 1;
+                    }
+                    cnt @ _ => unimplemented!("cnt={cnt} oob, add with larger imm"),
+                }
             }
             '-' => {
-                asm.dec(MemOp8::from(MemOp::IndirectBaseIndex(dmem_base, dmem_idx)));
+                // Apply optimization to fold consecutive '-' instructions to a
+                // single sub instruction during compile time.
+
+                match vm.imem[pc..].iter().take_while(|&&i| i.eq(&'-')).count() {
+                    1 => asm.dec(MemOp8::from(MemOp::IndirectBaseIndex(dmem_base, dmem_idx))),
+                    cnt if cnt <= i8::MAX as usize => {
+                        // For sub m64, imm8, the immediate is sign-extend and
+                        // hence treated as signed.
+                        asm.sub(
+                            MemOp::IndirectBaseIndex(dmem_base, dmem_idx),
+                            Imm8::from(cnt as u8),
+                        );
+
+                        // Advance pc, but account for pc increment at the end
+                        // of the loop.
+                        pc += cnt - 1;
+                    }
+                    cnt @ _ => unimplemented!("cnt={cnt} oob, sub with larger imm"),
+                }
             }
             '.' => {
-                // Load data memory from active cell into di register,
-                // which is the first argument register according to
-                // the SystemV abi, then call into putchar. Since we
-                // stored all out vm state in callee saved registers
-                // we don't need to save any registers before the
-                // call.
+                // Load data memory from active cell into di register, which is
+                // the first argument register according to the SystemV abi,
+                // then call into putchar. Since we stored all out vm state in
+                // callee saved registers we don't need to save any registers
+                // before the call.
                 asm.mov(Reg8::dil, MemOp::IndirectBaseIndex(dmem_base, dmem_idx));
                 asm.mov(Reg64::rax, Imm64::from(putchar as usize));
                 asm.call(Reg64::rax);
@@ -521,8 +603,8 @@
                 );
                 asm.jz(&mut label_pair.0);
 
-                // Bind label_pair.1 after the jump instruction, which
-                // will be the branch target for the matching ']'.
+                // Bind label_pair.1 after the jump instruction, which will be
+                // the branch target for the matching ']'.
                 asm.bind(&mut label_pair.1);
             }
             ']' => {
@@ -530,8 +612,7 @@
                     .pop()
                     .expect("encountered un-balanced brackets, found ']' without matching '['");
 
-                // Goto label_pair.1 if data memory at active cell is
-                // not 0.
+                // Goto label_pair.1 if data memory at active cell is not 0.
                 //   if vm.dmem[vm.dptr] != 0 goto label_pair.1
                 asm.cmp(
                     MemOp::IndirectBaseIndex(dmem_base, dmem_idx),
@@ -539,12 +620,15 @@
                 );
                 asm.jnz(&mut label_pair.1);
 
-                // Bind label_pair.0 after the jump instruction, which
-                // is the branch target for the matching '['.
+                // Bind label_pair.0 after the jump instruction, which is the
+                // branch target for the matching '['.
                 asm.bind(&mut label_pair.0);
             }
             _ => unreachable!(),
         }
+
+        // Increment pc to next instruction.
+        pc += 1;
     }
 
     // Return from bf program.
@@ -560,7 +644,9 @@
     bf_entry(&mut vm.dmem as *mut u8);
 }
 
-fn main() {
+// -- MAIN ---------------------------------------------------------------------
+
+fn main() {
     // https://en.wikipedia.org/wiki/Brainfuck#Adding_two_values
     //let inp = "++>+++++ [<+>-] ++++++++[<++++++>-]<.";
     //println!("add-print-7 (wikipedia.org) - interp");
diff --git a/src/gen/gen.rs.html b/src/gen/gen.rs.html
new file mode 100644
index 0000000..1df336a
--- /dev/null
+++ b/src/gen/gen.rs.html
@@ -0,0 +1,91 @@
+gen.rs - source

gen/
gen.rs

+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
//! Add example.
+//!
+//! Jit compile a function at runtime (generate native host code) which calls a function defined in
+//! the example based on the SystemV abi to demonstrate the [`juicebox_asm`] crate.
+
+#[cfg(not(any(target_arch = "x86_64", target_os = "linux")))]
+compile_error!("Only supported on x86_64 with SystemV abi");
+
+use juicebox_asm::insn::*;
+use juicebox_asm::{Asm, Imm16, Imm64, Imm8, Label, Reg16, Reg64::*, Runtime};
+use juicebox_asm::{MemOp, MemOp16, MemOp32, MemOp64, MemOp8};
+
+extern "C" fn add(a: u32, b: u32) -> u32 {
+    a + b
+}
+
+fn main() {
+    let mut asm1 = Asm::new();
+    asm1.xor(r11, r11);
+
+    assert!(0x7fu8 <= i8::MAX as u8);
+
+    asm1.add(MemOp::IndirectBaseIndex(rdi, r11), Imm8::from(0x10u8));
+    asm1.ret();
+
+    let code = asm1.into_code();
+    std::fs::write("jit.asm", &code).unwrap();
+
+    let mut asm2 = Asm::new();
+    asm2.mov(Reg16::cx, Imm16::from(1u16));
+    asm2.mov(MemOp::Indirect(rdi), Reg16::cx);
+    asm2.ret();
+
+    let mut rt = Runtime::new();
+    let fn1 = unsafe { rt.add_code::<extern "C" fn(*mut u64)>(code) };
+    let fn2 = unsafe { rt.add_code::<extern "C" fn(*mut u64)>(asm2.into_code()) };
+
+    let mut data = 0xffff_ffff_ffff_ff00u64;
+    fn1(&mut data as *mut u64);
+    println!("data={:x}", data);
+
+    let mut data = 0xffff_ffff_ffff_ffffu64;
+    fn2(&mut data as *mut u64);
+    println!("data={:x}", data);
+}
+
\ No newline at end of file diff --git a/src/juicebox_asm/insn.rs.html b/src/juicebox_asm/insn.rs.html index 99a3dd6..432d3da 100644 --- a/src/juicebox_asm/insn.rs.html +++ b/src/juicebox_asm/insn.rs.html @@ -117,7 +117,14 @@ 116 117 118 -119
//! Trait definitions of various instructions.
+119
+120
+121
+122
+123
+124
+125
+126
//! Trait definitions of various instructions.
 
 mod add;
 mod call;
@@ -134,6 +141,7 @@
 mod pop;
 mod push;
 mod ret;
+mod sub;
 mod test;
 mod xor;
 
@@ -210,16 +218,22 @@
     fn mov(&mut self, op1: T, op2: U);
 }
 
+/// Trait for [`pop`](https://www.felixcloutier.com/x86/pop) instruction kinds.
+pub trait Pop<T> {
+    /// Emit a pop instruction.
+    fn pop(&mut self, op1: T);
+}
+
 /// Trait for [`push`](https://www.felixcloutier.com/x86/push) instruction kinds.
 pub trait Push<T> {
     /// Emit a push instruction.
     fn push(&mut self, op1: T);
 }
 
-/// Trait for [`pop`](https://www.felixcloutier.com/x86/pop) instruction kinds.
-pub trait Pop<T> {
-    /// Emit a pop instruction.
-    fn pop(&mut self, op1: T);
+/// Trait for [`sub`](https://www.felixcloutier.com/x86/sub) instruction kinds.
+pub trait Sub<T, U> {
+    /// Emit an sub instruction.
+    fn sub(&mut self, op1: T, op2: U);
 }
 
 /// Trait for [`test`](https://www.felixcloutier.com/x86/test) instruction kinds.
diff --git a/src/juicebox_asm/insn/add.rs.html b/src/juicebox_asm/insn/add.rs.html
index e62bc4a..f91aa25 100644
--- a/src/juicebox_asm/insn/add.rs.html
+++ b/src/juicebox_asm/insn/add.rs.html
@@ -36,8 +36,14 @@
 35
 36
 37
-38
use super::Add;
-use crate::{Asm, Imm16, MemOp, Reg16, Reg32, Reg64};
+38
+39
+40
+41
+42
+43
+44
use super::Add;
+use crate::{Asm, Imm16, Imm8, MemOp, Reg16, Reg32, Reg64};
 
 impl Add<Reg64, Reg64> for Asm {
     fn add(&mut self, op1: Reg64, op2: Reg64) {
@@ -63,6 +69,12 @@
     }
 }
 
+impl Add<MemOp, Imm8> for Asm {
+    fn add(&mut self, op1: MemOp, op2: Imm8) {
+        self.encode_mi(0x83, 0, op1, op2);
+    }
+}
+
 impl Add<MemOp, Imm16> for Asm {
     fn add(&mut self, op1: MemOp, op2: Imm16) {
         self.encode_mi(0x81, 0, op1, op2);
diff --git a/src/juicebox_asm/insn/sub.rs.html b/src/juicebox_asm/insn/sub.rs.html
new file mode 100644
index 0000000..f350d05
--- /dev/null
+++ b/src/juicebox_asm/insn/sub.rs.html
@@ -0,0 +1,29 @@
+sub.rs - source

juicebox_asm/insn/
sub.rs

+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
use super::Sub;
+use crate::{Asm, Imm8, MemOp, Reg64};
+
+impl Sub<Reg64, Reg64> for Asm {
+    fn sub(&mut self, op1: Reg64, op2: Reg64) {
+        self.encode_rr(&[0x29], op1, op2);
+    }
+}
+
+impl Sub<MemOp, Imm8> for Asm {
+    fn sub(&mut self, op1: MemOp, op2: Imm8) {
+        self.encode_mi(0x83, 5, op1, op2);
+    }
+}
+
\ No newline at end of file -- cgit v1.2.3