Last active
August 7, 2025 03:57
-
-
Save recmo/56ccd445210e26b6e92f2574f93cb0b3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env rust-script | |
| // cargo-deps: thiserror = "2.0" | |
| //! | |
| //! Implements a tail-calling interpreter in Rust. | |
| //! | |
| #![feature(explicit_tail_calls)] | |
| use std::mem::transmute; | |
| use thiserror::Error; | |
| #[derive(Error, Debug)] | |
| pub enum InterpreterError { | |
| #[error("Stack underflow")] | |
| StackUnderflow, | |
| } | |
| /// This should compile to the minimal inlined asm sequence | |
| /// | |
| /// x86_64: | |
| /// ```asm | |
| /// mov rax, qword ptr [rdi + 8] | |
| /// add rdi, 8 | |
| /// jmp rax | |
| /// ``` | |
| /// | |
| /// aarch64: | |
| /// ```asm | |
| /// ldr x2, [x0, #8]! | |
| /// br x2 | |
| /// ``` | |
| #[inline(always)] | |
| pub fn dispatch(mut pc: *const u64, stack: &mut Vec<u64>) -> Result<(), InterpreterError> { | |
| let handler: fn(*const u64, &mut Vec<u64>) -> Result<(), InterpreterError> = | |
| unsafe { transmute(*pc) }; | |
| pc = unsafe { pc.add(1) }; | |
| become handler(pc, stack) | |
| } | |
| macro_rules! imm { | |
| ($pc:ident) => { | |
| unsafe { | |
| let value = $pc.read(); | |
| $pc = $pc.add(1); | |
| value | |
| } | |
| }; | |
| } | |
| pub fn stop(pc: *const u64, stack: &mut Vec<u64>) -> Result<(), InterpreterError> { | |
| Ok(()) | |
| } | |
| pub fn nop(mut pc: *const u64, stack: &mut Vec<u64>) -> Result<(), InterpreterError> { | |
| become dispatch(pc, stack) | |
| } | |
| pub fn immediate(mut pc: *const u64, stack: &mut Vec<u64>) -> Result<(), InterpreterError> { | |
| let a = imm!(pc); | |
| stack.push(a); | |
| become dispatch(pc, stack) | |
| } | |
| pub fn add(mut pc: *const u64, stack: &mut Vec<u64>) -> Result<(), InterpreterError> { | |
| let a = stack.pop().ok_or(InterpreterError::StackUnderflow)?; | |
| let b = stack.pop().ok_or(InterpreterError::StackUnderflow)?; | |
| stack.push(a + b); | |
| become dispatch(pc, stack) | |
| } | |
| pub fn print(mut pc: *const u64, stack: &mut Vec<u64>) -> Result<(), InterpreterError> { | |
| let a = stack.pop().ok_or(InterpreterError::StackUnderflow)?; | |
| eprintln!("Value: {}", a); | |
| become dispatch(pc, stack) | |
| } | |
| pub fn main() { | |
| let code = [ | |
| immediate as u64, | |
| 42, | |
| immediate as u64, | |
| 1337, | |
| add as u64, | |
| print as u64, | |
| stop as u64, | |
| ]; | |
| let mut stack = Vec::new(); | |
| dispatch(code.as_ptr(), &mut stack).unwrap_or_else(|e| eprintln!("Interpreter error: {}", e)); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment