Skip to content

Instantly share code, notes, and snippets.

@Robbepop
Last active January 7, 2022 14:40
Show Gist options
  • Save Robbepop/756eedb75466e09e0262ef917818c553 to your computer and use it in GitHub Desktop.
Save Robbepop/756eedb75466e09e0262ef917818c553 to your computer and use it in GitHub Desktop.
Simple interpreter architecture using ControlFlow.
#[derive(Copy, Clone)]
pub enum Instruction {
I32Const(i32),
I32Add,
Br(usize),
BrEqz(usize),
Ret,
}
pub type ControlFlow = core::ops::ControlFlow<BreakTarget, ()>;
#[derive(Copy, Clone)]
pub enum BreakTarget {
Goto(usize),
Ret,
}
pub struct Context<'func, 'engine> {
pc: usize,
insts: &'func [Instruction],
stack: &'engine mut Vec<i32>,
}
impl<'func, 'engine> Context<'func, 'engine> {
#[no_mangle]
fn execute_until_done(&mut self) -> Result<(), Error> {
'outer: loop {
let next_inst = self.insts[self.pc];
match self.execute_instruction(next_inst)? {
ControlFlow::Continue(()) => {
self.pc += 1;
}
ControlFlow::Break(BreakTarget::Goto(new_pc)) => {
self.pc = new_pc;
}
ControlFlow::Break(BreakTarget::Ret) => {
break 'outer
}
}
}
Ok(())
}
#[inline(always)]
fn execute_instruction(&mut self, inst: Instruction) -> Result<ControlFlow, Error> {
match inst {
Instruction::I32Const(value) => {
let sp = self.stack.len();
self.stack.get_mut(sp - 1)
.map(|cell| *cell = value)
.ok_or(Error::BugInInterpreter)?;
Ok(ControlFlow::Continue(()))
}
Instruction::I32Add => {
let rhs = self.stack.pop().ok_or(Error::BugInInterpreter)?;
let lhs = self.stack.pop().ok_or(Error::BugInInterpreter)?;
let result = lhs.wrapping_add(rhs);
let sp = self.stack.len();
self.stack.get_mut(sp - 1)
.map(|cell| *cell = result)
.ok_or(Error::BugInInterpreter)?;
Ok(ControlFlow::Continue(()))
}
Instruction::Br(target) => {
Ok(ControlFlow::Break(BreakTarget::Goto(target)))
}
Instruction::BrEqz(target) => {
let value = self.stack.pop().ok_or(Error::BugInInterpreter)?;
let targets = [self.pc + 1, target];
let new_pc = targets[(value == 0) as usize];
Ok(ControlFlow::Break(BreakTarget::Goto(new_pc)))
}
Instruction::Ret => {
Ok(ControlFlow::Break(BreakTarget::Ret))
}
}
}
}
pub struct Function {
insts: Vec<Instruction>,
}
#[derive(Debug)]
pub enum Error {
BugInInterpreter,
}
pub struct Interpreter {
stack: Vec<i32>,
}
impl Default for Interpreter {
fn default() -> Self {
Self { stack: vec![0x00; 1024] }
}
}
impl Interpreter {
fn execute(&mut self, func: &Function) -> Result<(), Error> {
Context {
pc: 0,
insts: &func.insts[..],
stack: &mut self.stack,
}.execute_until_done()
}
}
#[no_mangle]
fn execute_program() {
let mut engine = Interpreter::default();
let func = Function {
insts: vec![
Instruction::I32Const(1),
Instruction::I32Const(2),
Instruction::I32Add,
Instruction::Ret,
],
};
engine.execute(&func).unwrap();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment