From efd6fd88d2c73ae48ff74ba9e772e2347009fe9e Mon Sep 17 00:00:00 2001 From: johannst Date: Fri, 13 Dec 2024 21:12:22 +0000 Subject: deploy: e6095b086f6e2429fb952ae75a193dc89b4b9082 --- src/bf/bf.rs.html | 184 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 157 insertions(+), 27 deletions(-) (limited to 'src/bf') diff --git a/src/bf/bf.rs.html b/src/bf/bf.rs.html index b29403b..556b131 100644 --- a/src/bf/bf.rs.html +++ b/src/bf/bf.rs.html @@ -337,7 +337,72 @@ 336 337 338 -339
//! Brainfuck VM.
+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
//! Brainfuck VM.
 //!
 //! This example implements a simple
 //! [brainfuck](https://en.wikipedia.org/wiki/Brainfuck) interpreter
@@ -514,11 +579,20 @@
     // 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 dmem_size = Reg64::r12;
+    let dmem_idx = Reg64::r13;
 
     let mut asm = Asm::new();
+
+    // Save callee saved registers before we tamper them.
+    asm.push(dmem_base);
+    asm.push(dmem_size);
+    asm.push(dmem_idx);
+
     // Move data memory pointer (argument on jit entry) into correct register.
     asm.mov(dmem_base, Reg64::rdi);
+    // Move data memory size into correct register.
+    asm.mov(dmem_size, Reg64::rsi);
     // Clear data memory index.
     asm.xor(dmem_idx, dmem_idx);
 
@@ -526,17 +600,28 @@
     // given '[]' pair.
     let mut label_stack = Vec::new();
 
+    // Label to jump to when a data pointer overflow is detected.
+    let mut oob_ov = Label::new();
+    // Label to jump to when a data pointer underflow is detected.
+    let mut oob_uv = Label::new();
+
     // Generate code for each instruction in the bf program.
     let mut pc = 0;
     while pc < vm.imem.len() {
         match vm.imem[pc] {
             '>' => {
-                // TODO: generate runtime bounds check.
-                asm.inc(dmem_idx);
+                asm.inc(dmem_idx);
+
+                // Check for data pointer overflow and jump to error handler if needed.
+                asm.cmp(dmem_idx, dmem_size);
+                asm.jz(&mut oob_ov);
             }
             '<' => {
-                // TODO: generate runtime bounds check.
-                asm.dec(dmem_idx);
+                // Check for data pointer underflow and jump to error handler if needed.
+                asm.test(dmem_idx, dmem_idx);
+                asm.jz(&mut oob_uv);
+
+                asm.dec(dmem_idx);
             }
             '+' => {
                 // Apply optimization to fold consecutive '+' instructions to a
@@ -546,10 +631,8 @@
                     1 => {
                         asm.inc(Mem8::indirect_base_index(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(
+                    cnt if cnt <= u8::MAX as usize => {
+                        asm.add(
                             Mem8::indirect_base_index(dmem_base, dmem_idx),
                             Imm8::from(cnt as u8),
                         );
@@ -569,10 +652,8 @@
                     1 => {
                         asm.dec(Mem8::indirect_base_index(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(
+                    cnt if cnt <= u8::MAX as usize => {
+                        asm.sub(
                             Mem8::indirect_base_index(dmem_base, dmem_idx),
                             Imm8::from(cnt as u8),
                         );
@@ -639,30 +720,48 @@
         pc += 1;
     }
 
-    // Return from bf program.
-    asm.ret();
+    let mut ret_epilogue = Label::new();
+
+    // Successful return from bf program.
+    asm.xor(Reg64::rax, Reg64::rax);
+    asm.bind(&mut ret_epilogue);
+    // Restore callee saved registers before returning from jit.
+    asm.pop(dmem_idx);
+    asm.pop(dmem_size);
+    asm.pop(dmem_base);
+    asm.ret();
+
+    // Return because of data pointer overflow.
+    asm.bind(&mut oob_ov);
+    asm.mov(Reg64::rax, Imm64::from(1));
+    asm.jmp(&mut ret_epilogue);
+
+    // Return because of data pointer underflow.
+    asm.bind(&mut oob_uv);
+    asm.mov(Reg64::rax, Imm64::from(2));
+    asm.jmp(&mut ret_epilogue);
 
     if !label_stack.is_empty() {
         panic!("encountered un-balanced brackets, left-over '[' after jitting bf program")
     }
 
-    // Execute jitted bf program.
+    // Get function pointer to jitted bf program.
     let mut rt = Runtime::new();
-    let bf_entry = unsafe { rt.add_code::<extern "C" fn(*mut u8)>(asm.into_code()) };
-    bf_entry(&mut vm.dmem as *mut u8);
+    let bf_entry = unsafe { rt.add_code::<extern "C" fn(*mut u8, usize) -> u64>(asm.into_code()) };
+
+    // Execute jitted bf program.
+    match bf_entry(&mut vm.dmem as *mut u8, vm.dmem.len()) {
+        0 => {}
+        1 => panic!("oob: data pointer overflow"),
+        2 => panic!("oob: data pointer underflow"),
+        _ => unreachable!(),
+    }
 }
 
 // -- MAIN ---------------------------------------------------------------------
 
 fn main() {
-    // https://en.wikipedia.org/wiki/Brainfuck#Adding_two_values
-    //let inp = "++>+++++ [<+>-] ++++++++[<++++++>-]<.";
-    //println!("add-print-7 (wikipedia.org) - interp");
-    //run_interp(inp);
-    //println!("add-print-7 (wikipedia.org) - jit");
-    //run_jit(inp);
-
-    // https://en.wikipedia.org/wiki/Brainfuck#Hello_World!
+    // https://en.wikipedia.org/wiki/Brainfuck#Hello_World!
     let inp = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";
     println!("hello-world (wikipedia.org) - interp");
     run_interp(inp);
@@ -676,4 +775,35 @@
     println!("hello-world (programmingwiki.de) - jit");
     run_jit(inp);
 }
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn data_ptr_no_overflow() {
+        let inp = std::iter::repeat('>').take(255).collect::<String>();
+        run_jit(&inp);
+    }
+
+    #[test]
+    #[should_panic]
+    fn data_ptr_overflow() {
+        let inp = std::iter::repeat('>').take(255 + 1).collect::<String>();
+        run_jit(&inp);
+    }
+
+    #[test]
+    fn data_ptr_no_underflow() {
+        let inp = ">><< ><";
+        run_jit(inp);
+    }
+
+    #[test]
+    #[should_panic]
+    fn data_ptr_underflow() {
+        let inp = ">><< >< <";
+        run_jit(&inp);
+    }
+}
 
\ No newline at end of file -- cgit v1.2.3