From 7293cb9f757ce684db65d60dd363f8e9d5c77cce Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Sun, 8 Jun 2025 17:27:43 +0800 Subject: [PATCH] feat: initial parallel impl --- Cargo.lock | 71 ++------ Cargo.toml | 2 +- src/bin/eval.rs | 2 +- src/engine/mod.rs | 48 +++++- src/engine/test.rs | 9 +- src/env.rs | 39 ++--- src/eval/jit/helpers.rs | 19 +-- src/eval/jit/mod.rs | 27 ++- src/eval/mod.rs | 250 ++++++++++++++++++++++------ src/ir/mod.rs | 94 +++++++---- src/ty/internal/attrset.rs | 58 ++++--- src/ty/internal/func.rs | 4 +- src/ty/internal/list.rs | 40 +++-- src/ty/internal/mod.rs | 221 +++++++++++-------------- src/ty/internal/primop.rs | 16 +- src/ty/public.rs | 4 +- src/vm/mod.rs | 328 ------------------------------------- src/vm/test.rs | 231 -------------------------- 18 files changed, 529 insertions(+), 934 deletions(-) delete mode 100644 src/vm/mod.rs delete mode 100644 src/vm/test.rs diff --git a/Cargo.lock b/Cargo.lock index f118071..d8385d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,26 +80,6 @@ version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "dashmap" -version = "6.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "hashbrown 0.14.3", - "lock_api", - "once_cell", - "parking_lot_core", -] - [[package]] name = "derive_more" version = "2.0.1" @@ -205,6 +185,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown 0.15.3", +] + [[package]] name = "inkwell" version = "0.6.0" @@ -271,16 +261,6 @@ dependencies = [ "semver", ] -[[package]] -name = "lock_api" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.27" @@ -327,12 +307,12 @@ dependencies = [ name = "nixjit" version = "0.0.0" dependencies = [ - "dashmap", "derive_more", "ecow", "hashbrown 0.15.3", "inkwell", "itertools", + "priority-queue", "regex", "replace_with", "rnix", @@ -347,16 +327,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] -name = "parking_lot_core" -version = "0.9.11" +name = "priority-queue" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "5676d703dda103cbb035b653a9f11448c0a7216c7926bd35fcb5865475d0c970" dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", + "autocfg", + "equivalent", + "indexmap", ] [[package]] @@ -387,15 +365,6 @@ dependencies = [ "nibble_vec", ] -[[package]] -name = "redox_syscall" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" -dependencies = [ - "bitflags", -] - [[package]] name = "regex" version = "1.11.1" @@ -500,12 +469,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "semver" version = "1.0.26" diff --git a/Cargo.toml b/Cargo.toml index 25f89f0..cb24a38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ derive_more = { version = "2.0", features = ["full"] } ecow = "0.2" regex = "1.11" hashbrown = "0.15" -dashmap = "6.1" +priority-queue = "2.5" replace_with = "0.1" inkwell = { version = "0.6.0", features = ["llvm18-1"] } diff --git a/src/bin/eval.rs b/src/bin/eval.rs index 4bed9bd..dc6776c 100644 --- a/src/bin/eval.rs +++ b/src/bin/eval.rs @@ -5,7 +5,7 @@ use itertools::Itertools; use nixjit::error::Error; use nixjit::error::Result; use nixjit::ir::downgrade; -use nixjit::eval::eval; +use nixjit::engine::eval; fn main() -> Result<()> { let mut args = std::env::args(); diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 135c748..1a9cadb 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -1,29 +1,61 @@ -use hashbrown::HashSet; +use std::sync::RwLock; +use std::rc::Rc; +use hashbrown::HashSet; +use priority_queue::PriorityQueue; + +use crate::env::VmEnv; use crate::eval::Evaluate; use crate::ir::{Downgraded, Ir}; use crate::ty::public::Value; +use crate::ty::internal as i; use crate::error::Result; #[cfg(test)] mod test; pub struct Engine { - + thunks: Box<[RwLock]>, + func_offset: usize, + tasks: PriorityQueue } pub fn eval(downgraded: Downgraded) -> Result { - let mut engine = Engine::new(); - engine.eval(downgraded.top_level) + let mut engine = Engine::new(downgraded.thunks, downgraded.func_offset); + engine.eval(downgraded.top_level, &VmEnv::new(vec![])).map(|mut val| Ok(val.force(&engine)?.to_public(&engine, &mut HashSet::new())))? } impl Engine { - pub fn new() -> Self { - Self {} + pub fn new(thunks: Box<[Ir]>, func_offset: usize) -> Self { + Self { + thunks: thunks.into_iter().map(Thunk::new).map(RwLock::new).collect(), + func_offset, + tasks: PriorityQueue::new() + } } - pub fn eval(&mut self, expr: Ir) -> Result { - expr.eval(self).map(|val| val.to_public(self, &mut HashSet::new())) + pub fn eval(&mut self, expr: Ir, env: &VmEnv) -> Result { + expr.eval(self, env) + } + + pub fn eval_thunk(&self, idx: usize, env: &VmEnv) -> Result { + todo!() } } +enum Thunk { + Expr(Ir), + Compiling, + Compiled(fn(Rc) -> Value) +} + +impl Thunk { + fn new(ir: Ir) -> Thunk { + Thunk::Expr(ir) + } +} + +#[derive(Hash, PartialEq, Eq)] +struct CompileTask { + idx: usize +} diff --git a/src/engine/test.rs b/src/engine/test.rs index ca25022..bea9a0f 100644 --- a/src/engine/test.rs +++ b/src/engine/test.rs @@ -2,7 +2,7 @@ extern crate test; -use hashbrown::HashMap; +use std::collections::BTreeMap; use test::{Bencher, black_box}; @@ -23,12 +23,7 @@ fn test_expr(expr: &str, expected: Value) { macro_rules! map { ($($k:expr => $v:expr),*) => { { - #[allow(unused_mut)] - let mut m = HashMap::new(); - $( - m.insert($k, $v); - )* - m + BTreeMap::from([$(($k, $v),)*]) } }; } diff --git a/src/env.rs b/src/env.rs index 5e877c5..dd7f9cb 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1,15 +1,15 @@ use std::hash::Hash; use std::rc::Rc; +use ecow::EcoString; use hashbrown::HashMap; -use crate::{ir::Ir, ty::internal::Value}; +use crate::ty::internal::Value; pub struct Env { let_: Rc>, with: Rc>, args: Rc>, - last: Option>>, } pub struct LetEnv { @@ -17,8 +17,7 @@ pub struct LetEnv { last: Option>>, } -pub type VmEnv<'gc> = Env>; -pub type IrEnv<'gc> = Env; +pub type VmEnv = Env; #[derive(Default, Clone)] pub struct With { @@ -39,16 +38,15 @@ pub enum Type { } impl Env { - pub fn new(map: Vec) -> Rc { - Rc::new(Self { + pub fn new(map: Vec) -> Self { + Self { let_: LetEnv::new(map), with: With { map: None, last: None, }.into(), args: Vec::new().into(), - last: None, - }) + } } pub fn lookup_arg(&self, level: usize) -> &V { @@ -68,39 +66,32 @@ impl Env { } #[must_use] - pub fn enter_arg(self: Rc, val: V) -> Rc { + pub fn enter_arg(&self, val: V) -> Self { let mut args = self.args.clone(); Rc::make_mut(&mut args).push(val); - Rc::new(Self { + Self { let_: self.let_.clone(), with: self.with.clone(), - last: Some(self), args, - }) + } } #[must_use] - pub fn enter_let(self: Rc, map: Vec) -> Rc { - Rc::new(Self { + pub fn enter_let(&self, map: Vec) -> Self { + Self { let_: self.let_.clone().enter_let(map), with: self.with.clone(), args: self.args.clone(), - last: Some(self), - }) + } } #[must_use] - pub fn enter_with(self: Rc, map: Rc>) -> Rc { - Rc::new(Self { + pub fn enter_with(&self, map: Rc>) -> Self { + Self { let_: self.let_.clone(), with: self.with.clone().enter(map), args: self.args.clone(), - last: Some(self), - }) - } - - pub fn leave(&self) -> Rc { - self.last.clone().unwrap() + } } } diff --git a/src/eval/jit/helpers.rs b/src/eval/jit/helpers.rs index 73d87a2..7e69069 100644 --- a/src/eval/jit/helpers.rs +++ b/src/eval/jit/helpers.rs @@ -1,5 +1,4 @@ use std::ptr::NonNull; -use std::rc::Rc; use inkwell::AddressSpace; use inkwell::context::Context; @@ -10,7 +9,6 @@ use inkwell::values::{BasicValueEnum, FunctionValue}; use crate::env::VmEnv; use crate::eval::Engine; -use crate::ty::internal::{Thunk, Value}; use super::{JITContext, JITValue, ValueTag, JITValueData}; @@ -228,11 +226,8 @@ extern "C" fn helper_debug(value: JITValue) { dbg!(value.tag); } -extern "C" fn helper_capture_env<'gc>(thunk: JITValue, env: *const VmEnv<'gc>) { - let thunk = unsafe { (thunk.data.ptr as *const Thunk).as_ref().unwrap() }; - let env = unsafe { Rc::from_raw(env) }; - thunk.capture_env(env.clone()); - std::mem::forget(env); +extern "C" fn helper_capture_env(thunk: JITValue, env: *const VmEnv) { + todo!() } extern "C" fn helper_neg(rhs: JITValue, _env: *const VmEnv) -> JITValue { @@ -335,7 +330,7 @@ extern "C" fn helper_or(lhs: JITValue, rhs: JITValue) -> JITValue { } } -extern "C" fn helper_call<'gc>(func: JITValue, arg: JITValue, engine: *mut Engine) -> JITValue { +extern "C" fn helper_call(func: JITValue, arg: JITValue, engine: *mut Engine) -> JITValue { todo!() } @@ -352,15 +347,13 @@ extern "C" fn helper_lookup_let(level: usize, idx: usize, env: *const VmEnv) -> } extern "C" fn helper_lookup(sym: usize, env: *const VmEnv) -> JITValue { - let env = unsafe { env.as_ref() }.unwrap(); - let val: JITValue = env.lookup_with(&sym).unwrap().clone().into(); - val + todo!() } -extern "C" fn helper_force<'gc>( +extern "C" fn helper_force( thunk: JITValue, vm: NonNull, - jit: *const JITContext<'gc>, + jit: *const JITContext, ) -> JITValue { if !matches!(thunk.tag, ValueTag::Thunk) { return thunk; diff --git a/src/eval/jit/mod.rs b/src/eval/jit/mod.rs index fa77dce..f3ae1f0 100644 --- a/src/eval/jit/mod.rs +++ b/src/eval/jit/mod.rs @@ -1,5 +1,4 @@ use std::ops::Deref; -use std::rc::Rc; use std::marker::PhantomData; use inkwell::OptimizationLevel; @@ -9,7 +8,7 @@ use inkwell::execution_engine::ExecutionEngine; use inkwell::module::Module; use crate::env::VmEnv; -use crate::ty::internal::{Value, Thunk}; +use crate::ty::internal::Value; mod helpers; mod compile; @@ -51,29 +50,29 @@ pub union JITValueData { ptr: *const (), } -impl<'gc> From for Value<'gc> { +impl From for Value { fn from(value: JITValue) -> Self { use ValueTag::*; match value.tag { Int => Value::Int(unsafe { value.data.int }), Null => Value::Null, - Function => Value::Func(unsafe { Rc::from_raw(value.data.ptr as *const _) }), + /* Function => Value::Func(unsafe { Rc::from_raw(value.data.ptr as *const _) }), Thunk => Value::Thunk(self::Thunk { thunk: unsafe { Rc::from_raw(value.data.ptr as *const _) }, - }), + }), */ _ => todo!("not implemented for {:?}", value.tag), } } } -impl From> for JITValue { +impl From for JITValue { fn from(value: Value) -> Self { match value { Value::Int(int) => JITValue { tag: ValueTag::Int, data: JITValueData { int }, }, - Value::Func(func) => JITValue { + /* Value::Func(func) => JITValue { tag: ValueTag::Function, data: JITValueData { ptr: Rc::into_raw(func) as *const _, @@ -84,23 +83,23 @@ impl From> for JITValue { data: JITValueData { ptr: Rc::into_raw(thunk.thunk) as *const _, }, - }, + }, */ _ => todo!(), } } } -pub struct JITFunc<'gc>(F<'gc>, PhantomData<&'gc mut ()>); -type F<'gc> = unsafe extern "C" fn(*const VmEnv<'gc>) -> JITValue; +pub struct JITFunc<'ctx>(F, PhantomData<&'ctx mut ()>); +type F = unsafe extern "C" fn(*const VmEnv) -> JITValue; -impl<'gc> From> for JITFunc<'gc> { - fn from(value: F<'gc>) -> Self { +impl From for JITFunc<'_> { + fn from(value: F) -> Self { Self(value, PhantomData) } } -impl<'gc> Deref for JITFunc<'gc> { - type Target = F<'gc>; +impl Deref for JITFunc<'_> { + type Target = F; fn deref(&self) -> &Self::Target { &self.0 } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 71bdae3..cbbc236 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -1,141 +1,285 @@ -use crate::ty::common::Const; -use crate::ty::internal::Value; -use crate::ir::{self, Downgraded}; -use crate::ty::public as p; -use crate::error::Result; +use std::rc::Rc; + +use ecow::EcoVec; + use crate::engine::Engine; +use crate::env::VmEnv; +use crate::error::{Error, Result}; +use crate::ir::{self, DynamicAttrPair}; +use crate::ty::common::Const; +use crate::ty::internal::{AttrSet, List, Thunk, ThunkRef, Value}; +use crate::ty::public::Symbol; pub mod jit; pub trait Evaluate { - fn eval<'a>(self, engine: &'a Engine) -> Result>; + fn eval(self, engine: &Engine, env: &VmEnv) -> Result; } impl Evaluate for ir::Attrs { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + let mut attrs = AttrSet::new( + self.stcs + .into_iter() + .map(|(k, v)| Ok((k, v.eval(engine, env)?))) + .collect::>()?, + ); + for DynamicAttrPair(k, v) in self.dyns { + let mut k = k.eval(engine, env)?; + k.coerce_to_string(); + attrs.push_attr(k.unwrap_string(), v.eval(engine, env)?); + } + Value::AttrSet(attrs.into()).ok() } } -impl Evaluate for ir::List { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() +impl Evaluate for ir::List { + fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + Value::List(List::from( + self.items + .into_iter() + .map(|val| val.eval(engine, env)) + .collect::>>()?, + )) + .ok() } } impl Evaluate for ir::HasAttr { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + use ir::Attr::*; + let mut val = self.lhs.eval(engine, env)?; + val.has_attr(self.rhs.into_iter().map(|attr| { + Ok(match attr { + Str(ident) => ident, + Strs(expr) => expr.eval(engine, env)?.unwrap_string(), + Dynamic(expr) => { + let mut val = expr.eval(engine, env)?; + val.coerce_to_string(); + val.unwrap_string() + } + }) + }))?; + val.ok() } } impl Evaluate for ir::BinOp { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + use ir::BinOpKind::*; + let mut lhs = self.lhs.eval(engine, env)?; + let mut rhs = self.rhs.eval(engine, env)?; + match self.kind { + Add => lhs.add(rhs), + Sub => { + rhs.neg(); + lhs.add(rhs); + } + Mul => lhs.mul(rhs), + Div => lhs.div(rhs)?, + Eq => Value::eq(&mut lhs, rhs), + Neq => { + Value::eq(&mut lhs, rhs); + lhs.not(); + } + Lt => lhs.lt(rhs), + Gt => { + rhs.lt(lhs); + lhs = rhs; + } + Leq => { + rhs.lt(lhs); + rhs.not(); + lhs = rhs; + } + Geq => { + lhs.lt(rhs); + lhs.not(); + } + And => lhs.and(rhs), + Or => lhs.or(rhs), + Impl => { + lhs.not(); + lhs.or(rhs); + } + Con => lhs.concat(rhs), + Upd => lhs.update(rhs), + PipeL => lhs.call(rhs, engine)?, + PipeR => { + rhs.call(lhs, engine)?; + lhs = rhs; + } + } + Ok(lhs) } } -impl Evaluate for ir::UnOp { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() +impl Evaluate for ir::UnOp { + fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + use ir::UnOpKind::*; + let mut rhs = self.rhs.eval(engine, env)?; + match self.kind { + Neg => rhs.neg(), + Not => rhs.not(), + } + Ok(rhs) } } impl Evaluate for ir::Select { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + use ir::Attr::*; + let mut val = self.expr.eval(engine, env)?; + if let Some(default) = self.default { + val.select_with_default( + self.attrpath.into_iter().map(|attr| { + Ok(match attr { + Str(ident) => ident, + Strs(expr) => expr.eval(engine, env)?.unwrap_string(), + Dynamic(expr) => { + let mut val = expr.eval(engine, env)?; + val.coerce_to_string(); + val.unwrap_string() + } + }) + }), + default.eval(engine, env)?, + )?; + } else { + val.select(self.attrpath.into_iter().map(|attr| { + Ok(match attr { + Str(ident) => ident, + Strs(expr) => expr.eval(engine, env)?.unwrap_string(), + Dynamic(expr) => { + let mut val = expr.eval(engine, env)?; + val.coerce_to_string(); + val.unwrap_string() + } + }) + }))?; + } + val.ok() } } impl Evaluate for ir::If { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + // TODO: Error Handling + let cond = self.cond.eval(engine, env)?.unwrap_bool(); + if cond { + self.consq.eval(engine, env) + } else { + self.alter.eval(engine, env) + } } } impl Evaluate for ir::LoadFunc { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, _: &Engine, _: &VmEnv) -> Result { + Value::Func(Rc::new(ThunkRef::new(self.idx).into())).ok() } } impl Evaluate for ir::Call { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + let mut func = self.func.eval(engine, env)?; + // FIXME: Modify Value::call + for arg in self.args { + func.call(arg.eval(engine, env)?, engine)?; + } + func.ok() } } impl Evaluate for ir::Let { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + let bindings = self.bindings.into_iter().map(|(_, v)| Ok(v.eval(engine, env)?)).collect::>>()?; + self.expr.eval(engine, &env.enter_let(bindings)) } } impl Evaluate for ir::With { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + let namespace = self.namespace.eval(engine, env)?; + // TODO: Error Handling + self.expr.eval(engine, &env.enter_with(namespace.unwrap_attr_set().into_inner())) } } impl Evaluate for ir::Assert { - fn eval<'a>(self, engine: &'a Engine) -> Result> { + fn eval(self, engine: &Engine, env: &VmEnv) -> Result { todo!() } } impl Evaluate for ir::ConcatStrings { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + let mut parts = self + .parts + .into_iter() + .map(|part| { + let mut part = part.eval(engine, env)?; + part.coerce_to_string(); + part.ok() + }) + .collect::>>()? + .into_iter(); + let init = parts.next().unwrap(); + parts + .fold(init, |mut a, b| { + a.concat_string(b); + a + }) + .ok() } } impl Evaluate for ir::String { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, _: &Engine, _: &VmEnv) -> Result { + Value::String(self.val).ok() } } - + impl Evaluate for ir::Const { - fn eval<'a>(self, _: &'a Engine) -> Result> { + fn eval(self, _: &Engine, _: &VmEnv) -> Result { match self.val { Const::Null => Value::Null, Const::Int(x) => Value::Int(x), Const::Float(x) => Value::Float(x), Const::Bool(x) => Value::Bool(x), - }.ok() + } + .ok() } } - + impl Evaluate for ir::Var { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, _: &Engine, env: &VmEnv) -> Result { + env.lookup_with(&self.sym) + .cloned() + .ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(self.sym)))) } } impl Evaluate for ir::Arg { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, _: &Engine, env: &VmEnv) -> Result { + env.lookup_arg(self.level).clone().ok() } } impl Evaluate for ir::LetVar { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, _: &Engine, env: &VmEnv) -> Result { + env.lookup_let(self.level, self.idx).clone().ok() } } impl Evaluate for ir::Thunk { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, _: &Engine, _: &VmEnv) -> Result { + Value::Thunk(Rc::new(Thunk::new(self.idx).into())).ok() } } impl Evaluate for ir::Path { - fn eval<'a>(self, engine: &'a Engine) -> Result> { - todo!() + fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + todo!() } } - -pub fn eval(expr: Downgraded) -> Result { - todo!() -} diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 442a3fb..551fa17 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -7,13 +7,14 @@ use rnix::ast::HasEntry; use rnix::ast::{self, Expr}; use crate::builtins::ir_env; +use crate::engine::Engine; +use crate::env::VmEnv; use crate::error::*; -use crate::ty::public::Symbol; +use crate::eval::Evaluate; +use crate::eval::jit::{JITCompile, JITContext}; use crate::ty::common as c; use crate::ty::internal::Value; -use crate::eval::jit::{JITContext, JITCompile}; -use crate::eval::Evaluate; -use crate::engine::Engine; +use crate::ty::public::Symbol; mod utils; use utils::*; @@ -26,8 +27,12 @@ pub fn downgrade(expr: Expr) -> Result { let ir = ir.resolve(&mut ctx, &env)?; Ok(Downgraded { top_level: ir, - thunks: ctx.thunks.into(), - funcs: ctx.funcs.into(), + func_offset: ctx.thunks.len(), + thunks: ctx + .thunks + .into_iter() + .chain(ctx.funcs.into_iter().map(|Func { body, .. }| *body)) + .collect(), }) } @@ -42,7 +47,7 @@ macro_rules! ir { ,*$(,)? ) => { #[derive(Clone, Debug, IsVariant, Unwrap)] - pub enum Ir { + pub enum Ir { $( $ty($ty), )* @@ -63,16 +68,6 @@ macro_rules! ir { } impl Ir { - #[inline] - fn boxed(self) -> Box { - Box::new(self) - } - - #[inline] - fn ok(self) -> Result { - Ok(self) - } - #[inline] fn as_ref(&self) -> IrRef { match self { @@ -104,9 +99,9 @@ macro_rules! ir { } impl Evaluate for Ir { - fn eval<'a>(self, engine: &'a Engine) -> Result> { + fn eval(self, engine: &Engine, env: &VmEnv) -> Result { match self { - $(Ir::$ty(ir) => ir.eval(engine),)* + $(Ir::$ty(ir) => ir.eval(engine, env),)* } } } @@ -158,6 +153,18 @@ ir! { Path => { expr: Box }, } +impl Ir { + #[inline] + fn boxed(self) -> Box { + Box::new(self) + } + + #[inline] + fn ok(self) -> Result { + Ok(self) + } +} + impl> From for Const { fn from(value: T) -> Self { Const { val: value.into() } @@ -215,7 +222,11 @@ impl<'a, 'env> Env<'a, 'env> { } } - fn enter_multi_arg(&'env self, map: HashMap>, alias: Option) -> Self { + fn enter_multi_arg( + &'env self, + map: HashMap>, + alias: Option, + ) -> Self { Self { env: EnvNode::MultiArg(map, alias), prev: Some(self), @@ -301,7 +312,7 @@ impl DowngradeContext { pub struct Downgraded { pub top_level: Ir, pub thunks: Box<[Ir]>, - pub funcs: Box<[Func]>, + pub func_offset: usize, } impl DowngradeContext { @@ -362,10 +373,13 @@ impl Attrs { .get_mut(&ident) .unwrap() .as_mut() - .try_unwrap_attrs().map_err(|_| Error::DowngradeError(format!( + .try_unwrap_attrs() + .map_err(|_| { + Error::DowngradeError(format!( r#"attribute '{}' already defined"#, Symbol::from(ident) - ))) + )) + }) .and_then(|attrs: &mut Attrs| attrs._insert(path, name, value, ctx)) } else { let mut attrs = Attrs { @@ -641,7 +655,11 @@ impl Downgrade for ast::Path { let parts = self .parts() .map(|part| match part { - ast::InterpolPart::Literal(lit) => String { val: lit.to_string().into() }.ir().ok(), + ast::InterpolPart::Literal(lit) => String { + val: lit.to_string().into(), + } + .ir() + .ok(), ast::InterpolPart::Interpolation(interpol) => { interpol.expr().unwrap().downgrade(ctx) } @@ -706,11 +724,14 @@ impl ConcatStrings { } impl Downgrade for ast::Literal { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { + fn downgrade(self, _: &mut DowngradeContext) -> Result { match self.kind() { ast::LiteralKind::Integer(int) => Const::from(int.value().unwrap()).ir(), ast::LiteralKind::Float(float) => Const::from(float.value().unwrap()).ir(), - ast::LiteralKind::Uri(uri) => String { val: uri.to_string().into() }.ir() + ast::LiteralKind::Uri(uri) => String { + val: uri.to_string().into(), + } + .ir(), } .ok() } @@ -729,17 +750,20 @@ impl String { } impl Downgrade for ast::Ident { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { + fn downgrade(self, _: &mut DowngradeContext) -> Result { let sym = self.ident_token().unwrap().to_string().into(); Var { sym }.ir().ok() } } impl Var { - fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { + fn resolve<'a, 'env>(self, _: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { use LookupResult::*; let Ok(res) = env.lookup(&self.sym) else { - return Err(Error::DowngradeError(format!("{} not found", Symbol::from(self.sym)))) + return Err(Error::DowngradeError(format!( + "{} not found", + Symbol::from(self.sym) + ))); }; match res { Builtin(ir) => ir, @@ -1021,14 +1045,22 @@ impl Downgrade for ast::LetIn { } } } - let bindings = bindings.into_iter().sorted_by(|(a, _), (b, _)| a.cmp(b)).collect(); + let bindings = bindings + .into_iter() + .sorted_by(|(a, _), (b, _)| a.cmp(b)) + .collect(); Let { bindings, expr }.ir().ok() } } impl Let { fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { - let map = self.bindings.iter().map(|(sym, _)| sym.clone()).sorted().collect(); + let map = self + .bindings + .iter() + .map(|(sym, _)| sym.clone()) + .sorted() + .collect(); let env = env.enter_let(&map); let bindings = self .bindings diff --git a/src/ty/internal/attrset.rs b/src/ty/internal/attrset.rs index 3c18f3b..24ff79c 100644 --- a/src/ty/internal/attrset.rs +++ b/src/ty/internal/attrset.rs @@ -7,74 +7,94 @@ use hashbrown::{HashMap, HashSet}; use itertools::Itertools; use crate::engine::Engine; +use crate::error::{Error, Result}; +use crate::ty::public::Symbol; use super::super::public as p; use super::Value; #[repr(transparent)] #[derive(Constructor, Clone, PartialEq)] -pub struct AttrSet<'gc> { - data: HashMap>, +pub struct AttrSet { + data: HashMap, } -impl<'gc> From>> for AttrSet<'gc> { - fn from(data: HashMap>) -> Self { +impl From> for AttrSet { + fn from(data: HashMap) -> Self { Self { data } } } -impl<'gc> Deref for AttrSet<'gc> { - type Target = HashMap>; +impl Deref for AttrSet { + type Target = HashMap; fn deref(&self) -> &Self::Target { &self.data } } -impl<'gc> AttrSet<'gc> { +impl AttrSet { pub fn with_capacity(cap: usize) -> Self { AttrSet { data: HashMap::with_capacity(cap), } } - pub fn push_attr_force(&mut self, sym: EcoString, val: Value<'gc>) { + pub fn push_attr_force(&mut self, sym: EcoString, val: Value) { self.data.insert(sym, val); } - pub fn push_attr(&mut self, sym: EcoString, val: Value<'gc>) { + pub fn push_attr(&mut self, sym: EcoString, val: Value) { if self.data.get(&sym).is_some() { todo!() } self.data.insert(sym, val); } - pub fn select(&self, sym: &EcoString) -> Option> { - self.data.get(sym).cloned() + pub fn select(&self, mut path: impl DoubleEndedIterator>) -> Result { + // .ok_or_else(|| Error::EvalError())), + let mut data = &self.data; + let last = path.nth_back(0).unwrap(); + for item in path { + let item = item?; + let Some(Value::AttrSet(attrs)) = data.get(&item) else { + return Err(Error::EvalError(format!("{} not found", Symbol::from(item)))) + }; + data = attrs.as_inner(); + } + let last = last?; + data.get(&last).cloned().ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(last)))) } - pub fn has_attr(&self, sym: &EcoString) -> bool { - self.data.get(sym).is_some() + pub fn has_attr(&self, path: impl IntoIterator>) -> Result { + let mut data = &self.data; + for item in path { + let Some(Value::AttrSet(attrs)) = data.get(&item?) else { + return Ok(false) + }; + data = attrs.as_inner(); + } + Ok(true) } - pub fn update(&mut self, other: &AttrSet<'gc>) { + pub fn update(&mut self, other: &AttrSet) { for (k, v) in other.data.iter() { self.push_attr_force(k.clone(), v.clone()) } } - pub fn as_inner(&self) -> &HashMap> { + pub fn as_inner(&self) -> &HashMap { &self.data } - pub fn into_inner(self: Rc) -> Rc>> { + pub fn into_inner(self: Rc) -> Rc> { unsafe { std::mem::transmute(self) } } - pub fn from_inner(data: HashMap>) -> Self { + pub fn from_inner(data: HashMap) -> Self { Self { data } } - pub fn eq_impl(&self, other: &AttrSet<'gc>) -> bool { + pub fn eq_impl(&self, other: &AttrSet) -> bool { self.data.iter().len() == other.data.iter().len() && std::iter::zip( self.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)), @@ -83,7 +103,7 @@ impl<'gc> AttrSet<'gc> { .all(|((_, v1), (_, v2))| v1.eq_impl(v2)) } - pub fn to_public(&self, engine: &'gc Engine, seen: &mut HashSet>) -> p::Value { + pub fn to_public(&self, engine: &Engine, seen: &mut HashSet) -> p::Value { p::Value::AttrSet(p::AttrSet::new( self.data .iter() diff --git a/src/ty/internal/func.rs b/src/ty/internal/func.rs index 7240b9e..fa23524 100644 --- a/src/ty/internal/func.rs +++ b/src/ty/internal/func.rs @@ -5,11 +5,11 @@ use crate::env::VmEnv; use crate::ir; pub struct Func<'gc> { pub func: &'gc ir::Func, - pub env: Rc>, + pub env: Rc, } impl<'gc> Func<'gc> { - pub fn new(func: &'gc ir::Func, env: Rc>) -> Self { + pub fn new(func: &'gc ir::Func, env: Rc) -> Self { Self { func, env, diff --git a/src/ty/internal/list.rs b/src/ty/internal/list.rs index c611a8d..7f1a81f 100644 --- a/src/ty/internal/list.rs +++ b/src/ty/internal/list.rs @@ -1,5 +1,7 @@ +use std::ops::Deref; use std::rc::Weak; +use ecow::EcoVec; use hashbrown::HashSet; use crate::env::VmEnv; @@ -9,50 +11,64 @@ use crate::engine::Engine; use super::Value; #[derive(Clone, PartialEq)] -pub struct List<'gc> { - data: Vec>, +pub struct List { + data: EcoVec, } -impl<'gc> Default for List<'gc> { +impl Default for List { fn default() -> Self { Self::new() } } -impl<'gc> List<'gc> { +impl<'gc, T: Into>> From for List { + fn from(value: T) -> Self { + Self { data: value.into() } + } +} + +impl Deref for List { + type Target = [Value]; + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl List { pub fn new() -> Self { - List { data: Vec::new() } + List { data: EcoVec::new() } } pub fn with_capacity(cap: usize) -> Self { List { - data: Vec::with_capacity(cap), + data: EcoVec::with_capacity(cap), } } - pub fn push(&mut self, elem: Value<'gc>) { + pub fn push(&mut self, elem: Value) { self.data.push(elem); } - pub fn concat(&mut self, other: &List<'gc>) { + pub fn concat(&mut self, other: &List) { for elem in other.data.iter() { self.data.push(elem.clone()); } } - pub fn capture(&mut self, env: &Weak>) { + pub fn capture(&mut self, env: &Weak) { self.data.iter().for_each(|v| { if let Value::Thunk(ref thunk) = v.clone() { - thunk.capture_env_weak(env.clone()); + todo!() + // thunk.capture_env_weak(env.clone()); } }) } - pub fn into_inner(self) -> Vec> { + pub fn into_inner(self) -> EcoVec { self.data } - pub fn to_public(&self, engine: &'gc Engine, seen: &mut HashSet>) -> p::Value { + pub fn to_public(&self, engine: &Engine, seen: &mut HashSet) -> p::Value { p::Value::List(p::List::new( self.data .iter() diff --git a/src/ty/internal/mod.rs b/src/ty/internal/mod.rs index 783563e..55281c7 100644 --- a/src/ty/internal/mod.rs +++ b/src/ty/internal/mod.rs @@ -1,6 +1,7 @@ use std::cell::RefCell; use std::hash::Hash; use std::rc::{Rc, Weak}; +use std::sync::RwLock; use derive_more::{IsVariant, Unwrap}; use ecow::EcoString; @@ -21,39 +22,84 @@ mod primop; mod string; pub use attrset::*; -pub use func::*; pub use list::List; pub use primop::*; +#[derive(Clone)] +pub enum EnvRef { + Strong(Rc), + Weak(Weak) +} + +#[derive(Clone)] +pub struct ThunkRef { + pub idx: usize, + pub env: Option +} + +pub enum Thunk { + Expr(ThunkRef), + Suspended, + Value(Value) +} + +impl Thunk { + pub fn new(idx: usize) -> Self { + Thunk::Expr(ThunkRef::new(idx)) + } + + pub fn force(&mut self, engine: &Engine) -> Result { + todo!() + } +} + +impl ThunkRef { + pub fn new(idx: usize) -> Self { + ThunkRef { idx, env: None } + } + + pub fn capture(&mut self, env: EnvRef) { + let _ = self.env.insert(env); + } +} + +impl EnvRef { + pub fn upgrade(&mut self) { + if let EnvRef::Weak(weak) = &*self { + *self = EnvRef::Strong(weak.upgrade().unwrap()) + } + } +} + #[derive(IsVariant, Unwrap, Clone)] -pub enum Value<'gc> { +pub enum Value { Int(i64), Float(f64), Bool(bool), String(EcoString), Null, - Thunk(Thunk<'gc>), - AttrSet(Rc>), - List(Rc>), + Thunk(Rc>), + AttrSet(Rc), + List(List), Catchable(EcoString), PrimOp(Rc), - PartialPrimOp(Rc>), - Func(Rc>), + PartialPrimOp(Rc), + Func(Rc>), } -impl Hash for Value<'_> { +impl Hash for Value { fn hash(&self, state: &mut H) { use Value::*; std::mem::discriminant(self).hash(state); match self { AttrSet(x) => Rc::as_ptr(x).hash(state), - List(x) => Rc::as_ptr(x).hash(state), + List(x) => x.as_ptr().hash(state), _ => 0.hash(state), } } } -impl<'gc> Value<'gc> { +impl Value { fn eq_impl(&self, other: &Self) -> bool { use Value::*; match (self, other) { @@ -71,37 +117,37 @@ impl<'gc> Value<'gc> { } } -impl<'gc> PartialEq for Value<'gc> { +impl PartialEq for Value { fn eq(&self, other: &Self) -> bool { use Value::*; match (self, other) { (AttrSet(a), AttrSet(b)) => Rc::as_ptr(a).eq(&Rc::as_ptr(b)), - (List(a), List(b)) => Rc::as_ptr(a).eq(&Rc::as_ptr(b)), + (List(a), List(b)) => a.as_ptr().eq(&b.as_ptr()), _ => false, } } } -impl Eq for Value<'_> {} +impl Eq for Value {} #[derive(IsVariant, Unwrap, Clone)] -pub enum ValueAsRef<'v, 'gc> { +pub enum ValueAsRef<'v> { Int(i64), Float(f64), Bool(bool), - String(&'v str), + String(&'v EcoString), Null, - Thunk(&'v Thunk<'gc>), - AttrSet(&'v AttrSet<'gc>), - List(&'v List<'gc>), + Thunk(&'v RwLock), + AttrSet(&'v AttrSet), + List(&'v List), Catchable(&'v str), PrimOp(&'v PrimOp), - PartialPrimOp(&'v PartialPrimOp<'gc>), - Func(&'v Func<'gc>), + PartialPrimOp(&'v PartialPrimOp), + Func(&'v RwLock), } -impl<'gc, 'v> Value<'gc> { - pub fn as_ref(&'v self) -> ValueAsRef<'v, 'gc> { +impl Value { + pub fn as_ref(&self) -> ValueAsRef { use Value::*; use ValueAsRef as R; match self { @@ -120,7 +166,7 @@ impl<'gc, 'v> Value<'gc> { } } } -impl<'gc> Value<'gc> { +impl Value { pub fn ok(self) -> Result { Ok(self) } @@ -151,15 +197,15 @@ impl<'gc> Value<'gc> { } } - pub fn call(&mut self, arg: Self, vm: &Engine) -> Result<()> { + pub fn call(&mut self, arg: Self, engine: &Engine) -> Result<()> { use Value::*; if matches!(arg, Value::Catchable(_)) { *self = arg; return Ok(()); } *self = match self { - PrimOp(func) => func.call(arg, vm), - PartialPrimOp(func) => func.call(arg, vm), + PrimOp(func) => func.call(arg, engine), + PartialPrimOp(func) => func.call(arg, engine), Catchable(_) => return Ok(()), _ => todo!(), }?; @@ -293,7 +339,7 @@ impl<'gc> Value<'gc> { pub fn push(&mut self, elem: Self) -> &mut Self { if let Value::List(list) = self { - Rc::make_mut(list).push(elem); + list.push(elem); } else if let Value::Catchable(_) = self { } else if let Value::Catchable(_) = elem { *self = elem; @@ -310,7 +356,7 @@ impl<'gc> Value<'gc> { } match (self, other) { (Value::List(a), Value::List(b)) => { - Rc::make_mut(a).concat(&b); + a.concat(&b); } (Value::Catchable(_), _) => (), _ => todo!(), @@ -343,11 +389,10 @@ impl<'gc> Value<'gc> { } } - pub fn select(&mut self, sym: &EcoString) -> Result<&mut Self> { + pub fn select(&mut self, path: impl DoubleEndedIterator>) -> Result<&mut Self> { let val = match self { Value::AttrSet(attrs) => attrs - .select(sym) - .ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(sym.as_str())))), + .select(path), Value::Catchable(_) => return Ok(self), _ => Err(Error::EvalError(format!( "cannot select from {:?}", @@ -358,9 +403,9 @@ impl<'gc> Value<'gc> { Ok(self) } - pub fn select_with_default(&mut self, sym: &EcoString, default: Self) -> Result<&mut Self> { + pub fn select_with_default(&mut self, path: impl DoubleEndedIterator>, default: Self) -> Result<&mut Self> { let val = match self { - Value::AttrSet(attrs) => attrs.select(sym).unwrap_or(default), + Value::AttrSet(attrs) => attrs.select(path).unwrap_or(default), Value::Catchable(_) => return Ok(self), _ => { return Err(Error::EvalError(format!( @@ -373,15 +418,15 @@ impl<'gc> Value<'gc> { Ok(self) } - pub fn has_attr(&mut self, sym: &EcoString) -> &mut Self { + pub fn has_attr(&mut self, path: impl IntoIterator>) -> Result<()> { if let Value::AttrSet(attrs) = self { - let val = Value::Bool(attrs.has_attr(sym)); + let val = Value::Bool(attrs.has_attr(path)?); *self = val; } else if let Value::Catchable(_) = self { } else { *self = Value::Bool(false); } - self + Ok(()) } pub fn coerce_to_string(&mut self) -> &mut Self { @@ -393,7 +438,18 @@ impl<'gc> Value<'gc> { self } - pub fn to_public(&self, vm: &'gc Engine, seen: &mut HashSet>) -> p::Value { + pub fn force(&mut self, engine: &Engine) -> Result<&mut Self> { + if let Value::Thunk(thunk) = &*self { + let val = { + let mut thunk = thunk.write().unwrap(); + thunk.force(engine) + }?; + *self = val; + } + Ok(self) + } + + pub fn to_public(&self, engine: &Engine, seen: &mut HashSet) -> p::Value { use self::Value::*; use p::Value; if seen.contains(self) { @@ -402,11 +458,11 @@ impl<'gc> Value<'gc> { match self { AttrSet(attrs) => { seen.insert(self.clone()); - attrs.to_public(vm, seen) + attrs.to_public(engine, seen) } List(list) => { seen.insert(self.clone()); - list.to_public(vm, seen) + list.to_public(engine, seen) } Catchable(catchable) => Value::Catchable(catchable.clone().into()), Int(x) => Value::Const(Const::Int(*x)), @@ -422,90 +478,3 @@ impl<'gc> Value<'gc> { } } -#[repr(transparent)] -#[derive(Clone)] -pub struct Thunk<'gc> { - pub thunk: Rc>>, -} - -// TODO: impl -type OpCodes = (); - -#[derive(IsVariant, Unwrap)] -pub enum _Thunk<'gc> { - Code(&'gc OpCodes, Option>), - Suspended, - Value(Value<'gc>), -} - -#[derive(Unwrap)] -pub enum Env<'gc> { - Strong(Rc>), - Weak(Weak>), -} - -impl Env<'_> { - fn upgrade(self) -> Self { - if let Self::Weak(weak) = self { - Env::Strong(weak.upgrade().unwrap()) - } else { - self - } - } -} - -impl<'gc> Thunk<'gc> { - pub fn new(opcodes: &'gc OpCodes) -> Self { - Thunk { - thunk: RefCell::new(_Thunk::Code(opcodes, None)).into(), - } - } - - pub fn capture_env(&self, env: Rc>) { - if let _Thunk::Code(_, envcell) = &mut *self.thunk.borrow_mut() { - let _ = envcell.insert(Env::Strong(env)); - } - } - - pub fn capture_env_weak(&self, env: Weak>) { - if let _Thunk::Code(_, envcell) = &mut *self.thunk.borrow_mut() { - let _ = envcell.insert(Env::Weak(env)); - } - } - - pub fn upgrade(&self) { - replace_with_or_abort(&mut *self.thunk.borrow_mut(), |this| { - if let _Thunk::Code(opcodes, envcell) = this { - _Thunk::Code(opcodes, envcell.map(Env::upgrade)) - } else { - this - } - }); - } - - pub fn suspend(&self) -> Result<(&'gc OpCodes, Rc>)> { - use _Thunk::*; - match std::mem::replace(&mut *self.thunk.borrow_mut(), _Thunk::Suspended) { - Code(opcodes, env) => Ok((opcodes, env.unwrap().upgrade().unwrap_strong())), - _ => Err(Error::EvalError("infinite recursion encountered".into())), - } - } - - pub fn insert_value(&self, value: Value<'gc>) { - *self.thunk.borrow_mut() = _Thunk::Value(value); - } - - pub fn get_value(&self) -> Option<&Value<'gc>> { - if let _Thunk::Value(val) = unsafe { &*self.thunk.as_ptr() } { - Some(val) - } else { - None - } - } -} - -impl PartialEq for Thunk<'_> { - fn eq(&self, _: &Self) -> bool { - false - } -} diff --git a/src/ty/internal/primop.rs b/src/ty/internal/primop.rs index 483d678..8337d30 100644 --- a/src/ty/internal/primop.rs +++ b/src/ty/internal/primop.rs @@ -11,7 +11,7 @@ use super::Value; pub struct PrimOp { pub name: &'static str, arity: usize, - func: for<'gc> fn(Vec>, &Engine) -> Result>, + func: fn(Vec, &Engine) -> Result, } impl PartialEq for PrimOp { @@ -21,7 +21,7 @@ impl PartialEq for PrimOp { } impl PrimOp { - pub fn call<'gc>(&self, arg: Value<'gc>, ctx: &Engine) -> Result> { + pub fn call(&self, arg: Value, ctx: &Engine) -> Result { let mut args = Vec::with_capacity(self.arity); args.push(arg); if self.arity > 1 { @@ -39,21 +39,21 @@ impl PrimOp { } #[derive(Clone)] -pub struct PartialPrimOp<'gc> { +pub struct PartialPrimOp { pub name: &'static str, arity: usize, - args: Vec>, - func: fn(Vec>, &Engine) -> Result>, + args: Vec, + func: fn(Vec, &Engine) -> Result, } -impl PartialEq for PartialPrimOp<'_> { +impl PartialEq for PartialPrimOp { fn eq(&self, _: &Self) -> bool { false } } -impl<'gc> PartialPrimOp<'gc> { - pub fn call(self: &mut Rc, arg: Value<'gc>, ctx: &Engine) -> Result> { +impl PartialPrimOp { + pub fn call(self: &mut Rc, arg: Value, ctx: &Engine) -> Result { let func = self.func; let Some(ret) = ({ let self_mut = Rc::make_mut(self); diff --git a/src/ty/public.rs b/src/ty/public.rs index 2eb1aec..3111c0b 100644 --- a/src/ty/public.rs +++ b/src/ty/public.rs @@ -1,7 +1,7 @@ -use hashbrown::HashMap; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::ops::Deref; use std::sync::LazyLock; +use std::collections::BTreeMap; use derive_more::{Constructor, IsVariant, Unwrap}; use ecow::EcoString; @@ -55,7 +55,7 @@ impl Symbol { #[derive(Constructor, Clone, PartialEq)] pub struct AttrSet { - data: HashMap, + data: BTreeMap, } impl Debug for AttrSet { diff --git a/src/vm/mod.rs b/src/vm/mod.rs deleted file mode 100644 index 0aa0d17..0000000 --- a/src/vm/mod.rs +++ /dev/null @@ -1,328 +0,0 @@ -use std::cell::RefCell; -use std::rc::Rc; - -use hashbrown::{HashMap, HashSet}; -use inkwell::context::Context; - -use crate::bytecode::{BinOp, Func as F, OpCode, OpCodes, Program, UnOp}; -use crate::env::VmEnv; -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; - -#[cfg(test)] -mod test; - -const STACK_SIZE: usize = 8 * 1024 / size_of::(); -const COLLECTOR_GRANULARITY: f64 = 1024.0; - -struct ContextWrapper(Context); - -pub fn run(mut prog: Program) -> Result { - let jit = Context::create(); - prog.thunks.iter_mut().for_each(|code| code.reverse()); - prog.funcs - .iter_mut() - .for_each(|F { opcodes, .. }| opcodes.reverse()); - let vm = VM { - thunks: prog.thunks, - funcs: prog.funcs, - consts: prog.consts, - symbols: RefCell::new(prog.symbols), - symmap: RefCell::new(prog.symmap), - jit: JITContext::new(&jit), - }; - prog.top_level.reverse(); - Ok(eval(prog.top_level, &vm, VmEnv::new(vec![]))?.to_public(&vm, &mut HashSet::new())) -} - -pub fn eval<'gc>(opcodes: Box<[OpCode]>, vm: &VM<'gc>, env: Rc>) -> Result> { - let mut opcodes = opcodes.into_vec(); - let mut envs = vec![env]; - let mut stack = Stack::<_, STACK_SIZE>::new(); - while let Some(opcode) = opcodes.pop() { - let consq = single_op(vm, opcode, &mut stack, envs.last_mut().unwrap())?; - match consq { - Consq::NoOp => (), - Consq::Jmp(step) => opcodes.resize_with(opcodes.len() - step, || unreachable!()), - Consq::Force => { - let thunk = stack.tos().as_ref().unwrap_thunk(); - let (code, env) = thunk.suspend()?; - opcodes.push(OpCode::InsertValue); - opcodes.push(OpCode::PopEnv); - opcodes.extend(code); - envs.push(env); - } - Consq::PopEnv => { - let _ = envs.pop().unwrap(); - } - Consq::Call => { - let arg = stack.pop(); - let func = stack.pop().unwrap_func(); - let env = func.env.clone().enter_arg(arg); - let count = func.count.get(); - func.count.set(count + 1); - if count > 1 { - let compiled = func.compiled.get_or_init(|| vm.compile_func(func.func)); - let ret = unsafe { compiled(env.as_ref() as *const VmEnv) }; - stack.push(ret.into())?; - } else { - envs.push(env); - opcodes.push(OpCode::PopEnv); - opcodes.extend(&func.func.opcodes); - } - } - } - } - stack.pop().ok() -} - -enum Consq { - Jmp(usize), - Call, - Force, - PopEnv, - NoOp, -} - -#[inline(always)] -fn single_op<'gc, const CAP: usize>( - vm: &VM<'gc>, - opcode: OpCode, - stack: &mut Stack, CAP>, - env: &mut Rc>, -) -> Result { - match opcode { - OpCode::Illegal => panic!("illegal opcode"), - OpCode::Const { idx } => stack.push(match vm.get_const(idx) { - Const::Int(x) => Value::Int(x), - Const::Float(x) => Value::Float(x), - Const::Bool(x) => Value::Bool(x), - Const::String(x) => Value::String(Rc::new(x.into())), - Const::Null => Value::Null, - })?, - OpCode::LoadThunk { idx } => stack.push(Value::Thunk(Thunk::new(vm.get_thunk(idx))))?, - OpCode::LoadValue { idx } => { - stack.push(Value::Thunk(Thunk::new(vm.get_thunk(idx))))?; - stack.tos().as_ref().unwrap_thunk().capture_env(env.clone()); - return Ok(Consq::Force); - } - OpCode::CaptureEnv => stack.tos().as_ref().unwrap_thunk().capture_env(env.clone()), - OpCode::ForceValue => { - if !stack.tos().is_thunk() { - return Ok(Consq::NoOp); - } - let Some(val) = stack.tos().as_ref().unwrap_thunk().get_value() else { - return Ok(Consq::Force); - }; - *stack.tos_mut() = val.clone(); - } - OpCode::InsertValue => { - let val = stack.pop(); - stack - .tos() - .as_ref() - .unwrap_thunk() - .insert_value(val.clone()); - *stack.tos_mut() = val; - } - OpCode::Jmp { step } => return Ok(Consq::Jmp(step)), - OpCode::JmpIfFalse { step } => { - if let Value::Bool(false) = stack.pop() { - return Ok(Consq::Jmp(step)); - } - } - OpCode::Call => { - let arg = stack.pop(); - let func = stack.tos_mut(); - if func.is_func() { - let _ = stack.push(arg); - return Ok(Consq::Call); - } - func.call(arg, vm)?; - } - OpCode::Func { idx } => { - let func = vm.get_func(idx); - stack.push(Value::Func(Rc::new(Func::new(func, env.clone()))))?; - } - OpCode::Arg { level } => { - stack.push(env.lookup_arg(level).clone())?; - } - OpCode::UnOp { op } => { - use UnOp::*; - let value = stack.tos_mut(); - match op { - Neg => value.neg(), - Not => value.not(), - } - } - OpCode::BinOp { op } => { - use BinOp::*; - let mut rhs = stack.pop(); - let lhs = stack.tos_mut(); - 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 rhs = stack.pop(); - stack.tos_mut().concat_string(rhs); - } - OpCode::Path => { - todo!() - } - OpCode::List { cap } => { - stack.push(Value::List(Rc::new(List::with_capacity(cap))))?; - } - OpCode::PushElem => { - let elem = stack.pop(); - stack.tos_mut().push(elem); - } - OpCode::AttrSet { cap } => { - stack.push(Value::AttrSet(Rc::new(AttrSet::with_capacity(cap))))?; - } - OpCode::FinalizeLet => { - let mut list = stack.pop().unwrap_list(); - let map = list.as_ref().clone().into_inner(); - *env = env.clone().enter_let(map); - Rc::make_mut(&mut list).capture(&Rc::downgrade(env)); - } - OpCode::PushStaticAttr { name } => { - let val = stack.pop(); - stack.tos_mut().push_attr(name, val); - } - OpCode::PushDynamicAttr => { - let val = stack.pop(); - let sym = stack.pop(); - let sym = vm.new_sym::<&str>(&sym.unwrap_string()); - stack.tos_mut().push_attr(sym, val); - } - OpCode::Select { sym } => { - stack.tos_mut().select(sym, vm)?; - } - OpCode::SelectOrDefault { sym } => { - let default = stack.pop(); - stack.tos_mut().select_with_default(sym, default)?; - } - OpCode::SelectDynamic => { - let mut val = stack.pop(); - val.coerce_to_string(); - let sym = vm.new_sym::<&str>(&val.unwrap_string()); - stack.tos_mut().select(sym, vm)?; - } - OpCode::SelectDynamicOrDefault => { - let default = stack.pop(); - let mut val = stack.pop(); - val.coerce_to_string(); - let sym = vm.new_sym::<&str>(&val.unwrap_string()); - stack.tos_mut().select_with_default(sym, default)?; - } - OpCode::HasAttr { sym } => { - stack.tos_mut().has_attr(sym); - } - OpCode::HasDynamicAttr => { - let mut val = stack.pop(); - val.coerce_to_string(); - let sym = vm.new_sym::<&str>(&val.unwrap_string()); - stack.tos_mut().has_attr(sym); - } - OpCode::LookUp { sym } => { - stack.push( - env.lookup_with(&sym) - .ok_or_else(|| Error::EvalError(format!("{} not found", vm.get_sym(sym))))? - .clone(), - )?; - } - OpCode::LookUpLet { level, idx } => { - let val = env.lookup_let(level, idx); - if let Value::Thunk(thunk) = val { - thunk.upgrade(); - } - stack.push(val.clone())?; - } - OpCode::LeaveEnv => *env = env.leave(), - OpCode::EnterWithEnv => { - let mut new = HashMap::new(); - stack - .pop() - .unwrap_attr_set() - .as_inner() - .iter() - .map(|(&k, v)| (k, v.clone())) - .collect_into(&mut new); - *env = env.clone().enter_with(new.into()); - } - OpCode::PopEnv => return Ok(Consq::PopEnv), - OpCode::Assert => { - if !stack.pop().unwrap_bool() { - todo!() - } - } - } - Ok(Consq::NoOp) -} - -#[derive(Constructor)] -pub struct VM<'gc> { - thunks: Box<[OpCodes]>, - funcs: Box<[F]>, - symbols: RefCell>, - symmap: RefCell>, - consts: Box<[Const]>, - jit: JITContext<'gc>, -} - -impl<'gc> VM<'gc> { - pub fn get_thunk(&self, idx: usize) -> &'gc OpCodes { - unsafe { &*(&self.thunks[idx] as *const _) } - } - - pub fn get_func(&self, idx: usize) -> &'gc F { - unsafe { &*(&self.funcs[idx] as *const _) } - } - - pub fn get_sym(&self, idx: usize) -> Symbol { - self.symbols.borrow()[idx].clone().into() - } - - pub fn new_sym>(&self, sym: T) -> 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 compile_func(&self, func: &'gc F) -> JITFunc<'gc> { - self.jit - .compile_seq(func.opcodes.iter().copied().rev(), self) - .unwrap() - } -} diff --git a/src/vm/test.rs b/src/vm/test.rs deleted file mode 100644 index 22e854e..0000000 --- a/src/vm/test.rs +++ /dev/null @@ -1,231 +0,0 @@ -#![allow(unused_macros)] - -extern crate test; - -use hashbrown::HashMap; - -use test::{Bencher, black_box}; - -use ecow::EcoString; - -use crate::compile::compile; -use crate::ir::downgrade; -use crate::ty::common::Const; -use crate::ty::public::*; - -use super::run; - -#[inline] -fn test_expr(expr: &str, expected: Value) { - let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap(); - let prog = compile(downgraded); - dbg!(&prog); - assert_eq!(run(prog).unwrap(), expected); -} - -macro_rules! map { - ($($k:expr => $v:expr),*) => { - { - #[allow(unused_mut)] - let mut m = HashMap::new(); - $( - m.insert($k, $v); - )* - m - } - }; -} - -macro_rules! thunk { - () => { - Value::Thunk - }; -} - -macro_rules! int { - ($e:expr) => { - Value::Const(Const::Int($e)) - }; -} - -macro_rules! float { - ($e:expr) => { - Value::Const(Const::Float($e as f64)) - }; -} - -macro_rules! boolean { - ($e:expr) => { - Value::Const(Const::Bool($e)) - }; -} - -macro_rules! string { - ($e:expr) => { - Value::Const(Const::String(EcoString::from($e))) - }; -} - -macro_rules! symbol { - ($e:expr) => { - Symbol::from($e.to_string()) - }; -} - -macro_rules! list { - ($($x:tt)*) => ( - Value::List(List::new(vec![$($x)*])) - ); -} - -macro_rules! attrs { - ($($x:tt)*) => ( - Value::AttrSet(AttrSet::new(map!{$($x)*})) - ) -} - -#[test] -fn test_arith() { - test_expr("1", int!(1)); - test_expr("1.", float!(1)); - test_expr("-1", int!(-1)); - test_expr("-1.", float!(-1)); - test_expr("1 + 1", int!(2)); - test_expr("1 + 1.", float!(2)); - test_expr("1. + 1", float!(2)); - test_expr("1. + 1.", float!(2)); - test_expr("1 - 1", int!(0)); - test_expr("1 - 1.", float!(0)); - test_expr("1. - 1", float!(0)); - test_expr("1. - 1.", float!(0)); - test_expr("1 * 1", int!(1)); - test_expr("1 * 1.", float!(1)); - test_expr("1. * 1", float!(1)); - test_expr("1. * 1.", float!(1)); - test_expr("1 / 1", int!(1)); - test_expr("1 / 1.", float!(1)); - test_expr("1. / 1", float!(1)); - test_expr("1. / 1.", float!(1)); -} - -#[test] -fn test_cmp() { - test_expr("1 < 2", boolean!(true)); - test_expr("1 < 1", boolean!(false)); - test_expr("1 > 0", boolean!(true)); - test_expr("1 > 1", boolean!(false)); - test_expr("1 <= 1", boolean!(true)); - test_expr("1 <= 0", boolean!(false)); - test_expr("1 >= 1", boolean!(true)); - test_expr("1 >= 2", boolean!(false)); -} - -#[test] -fn test_string() { - test_expr(r#""test""#, string!("test")); - test_expr(r#""hello" + " world""#, string!("hello world")); -} - -#[test] -fn test_bool() { - test_expr("true", boolean!(true)); - test_expr("false", boolean!(false)); - test_expr("!false", boolean!(true)); - test_expr("true && false", boolean!(false)); - test_expr("true || false", boolean!(true)); - test_expr("true -> false", boolean!(false)); -} - -#[test] -fn test_list() { - test_expr( - "[ 1 2 3 true ]", - list![int!(1), int!(2), int!(3), boolean!(true)], - ); - test_expr( - "[ 1 2 ] ++ [ 3 4 ]", - list![int!(1), int!(2), int!(3), int!(4)], - ); -} - -#[test] -fn test_attrs() { - test_expr( - "{ a = 1; }", - attrs! { - symbol!("a") => int!(1) - }, - ); - test_expr("{ a = 1; }.a", int!(1)); - test_expr("{ a = 1; }.b or 1", int!(1)); - test_expr( - "{ a = { a = 1; }; }.a", - attrs! { - symbol!("a") => int!(1) - }, - ); - test_expr("{ a.b = 1; }.a.b", int!(1)); - test_expr( - "{ a.b = 1; a.c = 2; }", - attrs! { symbol!("a") => attrs!{ symbol!("b") => int!(1), symbol!("c") => int!(2) } }, - ); - test_expr("{ a.b = 1; } ? a.b", boolean!(true)); - test_expr( - "{ a.b = 1; } // { a.c = 2; }", - attrs! { symbol!("a") => attrs!{ symbol!("c") => int!(2) } }, - ); -} - -#[test] -fn test_if() { - test_expr("if true || false then 1 else 2", int!(1)); -} - -#[test] -fn test_with() { - test_expr(r#"with { a = 1; }; a"#, int!(1)); -} - -#[test] -fn test_let() { - test_expr(r#"let a = 1; in a"#, int!(1)); - test_expr(r#"let a = 1; b = a; in b"#, int!(1)); - test_expr(r#"let a = { a = 1; }; b = "a"; in a.${b}"#, int!(1)); - test_expr( - r#"let b = "c"; in { a.b = 1; } // { a."a${b}" = 2; }"#, - attrs! { symbol!("a") => attrs!{ symbol!("ac") => int!(2) } }, - ); -} - -#[test] -fn test_func() { - test_expr("(x: x) 1", int!(1)); - test_expr("(x: x) (x: x) 1", int!(1)); - test_expr("(x: y: x + y) 1 1", int!(2)); - test_expr("({ x, y }: x + y) { x = 1; y = 2; }", int!(3)); - test_expr("({ x, y, ... }: x + y) { x = 1; y = 2; z = 3; }", int!(3)); - test_expr( - "(inputs@{ x, y, ... }: x + inputs.y) { x = 1; y = 2; z = 3; }", - int!(3), - ); -} - -#[test] -#[ignore] -fn test_fib() { - test_expr( - "let fib = n: if n == 1 || n == 2 then 1 else (fib (n - 1)) + (fib (n - 2)); in fib 30", - int!(832040), - ) -} - -#[bench] -fn bench_fib(b: &mut Bencher) { - b.iter(|| { - test_expr( - "let fib = n: if n == 1 || n == 2 then 1 else (fib (n - 1)) + (fib (n - 2)); in fib 30", - int!(832040), - ); - black_box(()) - }) -}