use hashbrown::{HashMap, HashSet}; use inkwell::execution_engine::JitFunction; use std::cell::{Cell, OnceCell, RefCell}; use std::rc::Rc; use crate::builtins::env; use crate::bytecode::{BinOp, Func as F, OpCode, OpCodes, Program, UnOp}; use crate::error::*; use crate::jit::{JITContext, JITFunc}; use crate::stack::Stack; use crate::ty::common::Const; use crate::ty::internal::*; use crate::ty::public::{self as p, Symbol}; use derive_more::Constructor; use ecow::EcoString; pub use env::VmEnv; mod env; #[cfg(test)] mod test; const STACK_SIZE: usize = 8 * 1024 / size_of::(); pub fn run(prog: Program, jit: JITContext<'_>) -> Result { let vm = VM { thunks: prog.thunks, funcs: prog.funcs, symbols: RefCell::new(prog.symbols), symmap: RefCell::new(prog.symmap), consts: prog.consts, jit, }; let env = env(&vm); let mut seen = HashSet::new(); let value = vm .eval(prog.top_level.into_iter(), env.into())? .to_public(&vm, &mut seen); Ok(value) } #[derive(Constructor)] pub struct VM<'jit> { thunks: Box<[OpCodes]>, funcs: Box<[F]>, symbols: RefCell>, symmap: RefCell>, consts: Box<[Const]>, jit: JITContext<'jit>, } impl<'vm, 'jit: 'vm> VM<'jit> { pub fn get_thunk(&self, idx: usize) -> &OpCodes { &self.thunks[idx] } pub fn get_func(&self, idx: usize) -> &F { &self.funcs[idx] } pub fn get_sym(&self, idx: usize) -> Symbol { self.symbols.borrow()[idx].clone().into() } pub fn new_sym(&self, sym: impl Into) -> usize { let sym = sym.into(); if let Some(&idx) = self.symmap.borrow().get(&sym) { idx } else { self.symmap .borrow_mut() .insert(sym.clone(), self.symbols.borrow().len()); self.symbols.borrow_mut().push(sym); self.symbols.borrow().len() - 1 } } pub fn get_const(&self, idx: usize) -> Const { self.consts[idx].clone() } pub fn eval( &'vm self, opcodes: impl Iterator, mut env: Rc>, ) -> Result> { let mut stack = Stack::<_, STACK_SIZE>::new(); let mut iter = opcodes.into_iter(); while let Some(opcode) = iter.next() { let jmp = self.single_op(opcode, &mut stack, &mut env)?; for _ in 0..jmp { iter.next().unwrap(); } } assert_eq!(stack.len(), 1); let mut ret = stack.pop(); ret.force(self)?; Ok(ret) } #[inline(always)] fn single_op<'s, const CAP: usize>( &'vm self, opcode: OpCode, stack: &'s mut Stack, CAP>, env: &mut Rc>, ) -> Result { match opcode { OpCode::Illegal => panic!("illegal opcode"), OpCode::Const { idx } => stack.push(Value::Const(self.get_const(idx)))?, OpCode::LoadThunk { idx } => { stack.push(Value::Thunk(Thunk::new(self.get_thunk(idx)).into()))? } OpCode::CaptureEnv => stack.tos().as_ref().unwrap_thunk().capture(env.clone()), OpCode::ForceValue => { stack.tos_mut().force(self)?; } OpCode::Jmp { step } => return Ok(step), OpCode::JmpIfFalse { step } => { if let Value::Const(Const::Bool(false)) = stack.pop() { return Ok(step); } } OpCode::Call => { let arg = stack.pop(); let func = stack.tos_mut(); func.force(self)?; func.call(self, arg)?; } OpCode::Func { idx } => { let func = self.get_func(idx); stack.push(Value::Func( Func::new(func, env.clone(), OnceCell::new(), Cell::new(0)).into(), ))?; } OpCode::UnOp { op } => { use UnOp::*; let value = stack.tos_mut(); value.force(self)?; match op { Neg => value.neg(), Not => value.not(), } } OpCode::BinOp { op } => { use BinOp::*; let mut rhs = stack.pop(); let lhs = stack.tos_mut(); lhs.force(self)?; rhs.force(self)?; match op { Add => lhs.add(rhs), Sub => { rhs.neg(); lhs.add(rhs); }, Mul => lhs.mul(rhs), Div => lhs.div(rhs)?, And => lhs.and(rhs), Or => lhs.or(rhs), Eq => Value::eq(lhs, rhs), Lt => lhs.lt(rhs), Con => lhs.concat(rhs), Upd => lhs.update(rhs), } } OpCode::ConcatString => { let mut rhs = stack.pop(); rhs.force(self)?; stack.tos_mut().concat_string(rhs); } OpCode::Path => { todo!() } OpCode::List => { stack.push(Value::List(List::empty().into()))?; } OpCode::PushElem => { let elem = stack.pop(); stack.tos_mut().push(elem); } OpCode::AttrSet { cap } => { stack.push(Value::AttrSet(AttrSet::with_capacity(cap).into()))?; } OpCode::FinalizeRec => { env.enter_let(stack.tos().clone().unwrap_attr_set().into_inner()); stack.tos_mut().as_mut().unwrap_attr_set().capture(env); } OpCode::PushStaticAttr { name } => { let val = stack.pop(); stack.tos_mut().push_attr(name, val); } OpCode::PushDynamicAttr => { let val = stack.pop(); let mut sym = stack.pop(); sym.force(self)?.coerce_to_string(); let sym = self.new_sym(sym.unwrap_const().unwrap_string()); stack.tos_mut().push_attr(sym, val); } OpCode::Select { sym } => { stack.tos_mut().force(self)?.select(sym, self)?; } OpCode::SelectOrDefault { sym } => { let default = stack.pop(); stack .tos_mut() .force(self)? .select_with_default(sym, default)?; } OpCode::SelectDynamic => { let mut val = stack.pop(); val.force(self)?; val.coerce_to_string(); let sym = self.new_sym(val.unwrap_const().unwrap_string()); stack.tos_mut().force(self)?.select(sym, self)?; } OpCode::SelectDynamicOrDefault => { let default = stack.pop(); let mut val = stack.pop(); val.force(self)?; val.coerce_to_string(); let sym = self.new_sym(val.unwrap_const().unwrap_string()); stack .tos_mut() .force(self)? .select_with_default(sym, default)?; } OpCode::HasAttr { sym } => { stack.tos_mut().force(self)?.has_attr(sym); } OpCode::HasDynamicAttr => { let mut val = stack.pop(); val.coerce_to_string(); let sym = self.new_sym(val.unwrap_const().unwrap_string()); stack.tos_mut().force(self)?.has_attr(sym); } OpCode::LookUp { sym } => { stack.push( env.lookup(&sym) .ok_or_else(|| { Error::EvalError(format!("{} not found", self.get_sym(sym))) })? .clone(), )?; } OpCode::EnterLetEnv => env.enter_let(stack.pop().unwrap_attr_set().into_inner()), OpCode::LeaveLetEnv => env.leave_let(), OpCode::EnterWithEnv => env.enter_with(stack.pop().unwrap_attr_set().into_inner()), OpCode::LeaveWithEnv => env.leave_with(), OpCode::Assert => { if !stack.pop().unwrap_const().unwrap_bool() { todo!() } } } Ok(0) } pub fn compile_func(&'vm self, func: &'vm F) -> JitFunction<'jit, JITFunc<'jit, 'vm>> { self.jit.compile_function(func, self).unwrap() } }