6. 实现虚拟机
添加模块 src/bfjit.rs
导入必要的类型
use crate::bfir::{self, BfIR};
use crate::error::{Result, RuntimeError, VMError};
use std::io::{Read, Write};
use std::path::Path;
use std::ptr;
use dynasm::dynasm;
use dynasmrt::{DynasmApi, DynasmLabelApi};
虚拟机定义与 Brainfuck 机器模型一致。
机器码缓冲区,起始偏移,字节数组,输入输出流。
字节数组大小至少为 30000,这里设置为 4 MiB.
pub struct BfVM<'io> {
code: dynasmrt::ExecutableBuffer,
start: dynasmrt::AssemblyOffset,
memory: Box<[u8]>,
input: Box<dyn Read + 'io>,
output: Box<dyn Write + 'io>,
}
准备汇编可调用的函数,brainfuck 通过 unsafe 函数与虚拟机交互。
为了传出可能的错误,把错误移动到堆上,返回裸指针。您也可以选择其他方式来传出错误。
在 FFI 时完善地处理 panic 和 backtrace 是一个复杂的问题。为了不增加过多处理代码,这里选择忽略 unsafe 函数中的 panic 问题。在代码正确的情况下,panic 应该不会发生。
请注意:跨语言的栈展开 (stack unwind) 是未定义行为,有可能引发段错误,您需要仔细研究 ABI 才能解决它。
#[inline(always)]
fn vm_error(re: RuntimeError) -> *mut VMError {
let e = Box::new(VMError::from(re));
Box::into_raw(e)
}
impl<'io> BfVM<'io> {
unsafe extern "sysv64" fn getbyte(this: *mut Self, ptr: *mut u8) -> *mut VMError {
let mut buf = [0_u8];
let this = &mut *this;
match this.input.read(&mut buf) {
Ok(0) => {}
Ok(1) => *ptr = buf[0],
Err(e) => return vm_error(RuntimeError::IO(e)),
_ => unreachable!(),
}
ptr::null_mut()
}
unsafe extern "sysv64" fn putbyte(this: *mut Self, ptr: *const u8) -> *mut VMError {
let buf = std::slice::from_ref(&*ptr);
let this = &mut *this;
match this.output.write_all(buf) {
Ok(()) => ptr::null_mut(),
Err(e) => vm_error(RuntimeError::IO(e)),
}
}
unsafe extern "sysv64" fn overflow_error() -> *mut VMError {
vm_error(RuntimeError::PointerOverflow)
}
}
用输入流、输出流、源文件路径初始化虚拟机,优化选项也由外部提供。
compile 方法暂时留空。
impl<'io> BfVM<'io> {
fn compile(code: &[BfIR]) -> Result<(dynasmrt::ExecutableBuffer, dynasmrt::AssemblyOffset)> {
todo!()
}
impl<'io> BfVM<'io> {
pub fn new(
file_path: &Path,
input: Box<dyn Read + 'io>,
output: Box<dyn Write + 'io>,
optimize: bool,
) -> Result<Self> {
let src = std::fs::read_to_string(file_path)?;
let mut ir = bfir::compile(&src)?;
drop(src);
if optimize {
bfir::optimize(&mut ir);
}
let (code, start) = Self::compile(&ir)?;
drop(ir);
let memory = vec![0; MEMORY_SIZE].into_boxed_slice();
Ok(Self {
code,
start,
memory,
input,
output,
})
}
即时编译出的裸函数接收虚拟机指针和字节数组的边界指针,返回错误指针。
执行函数后检查错误指针,如果非空,就把错误从堆上移动到栈上再返回。
pub fn run(&mut self) -> Result<()> {
type RawFn = unsafe extern "sysv64" fn(
this: *mut BfVM<'_>,
memory_start: *mut u8,
memory_end: *const u8,
) -> *mut VMError;
let raw_fn: RawFn = unsafe { std::mem::transmute(self.code.ptr(self.start)) };
let this: *mut Self = self;
let memory_start = self.memory.as_mut_ptr();
let memory_end = unsafe { memory_start.add(MEMORY_SIZE) };
let ret: *mut VMError = unsafe { raw_fn(this, memory_start, memory_end) };
if ret.is_null() {
Ok(())
} else {
Err(*unsafe { Box::from_raw(ret) })
}
}