From 484cfa4610bedd25df0b94816919a2cba69d0da7 Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Thu, 5 Jun 2025 16:43:47 +0800 Subject: [PATCH] feat: get rid of gc and cyclic thunk --- Cargo.lock | 32 ------ Cargo.toml | 1 - src/builtins/mod.rs | 35 +++---- src/bytecode.rs | 13 +-- src/compile.rs | 15 ++- src/env.rs | 195 ++++++++++++---------------------- src/ir.rs | 45 ++++---- src/jit/helpers.rs | 80 +++++--------- src/jit/mod.rs | 70 ++++--------- src/stack.rs | 9 -- src/ty/common.rs | 9 +- src/ty/internal/attrset.rs | 7 -- src/ty/internal/func.rs | 42 ++------ src/ty/internal/list.rs | 21 ++-- src/ty/internal/mod.rs | 110 ++++++++++--------- src/ty/internal/primop.rs | 44 +++----- src/vm/mod.rs | 209 +++++++++++++------------------------ 17 files changed, 342 insertions(+), 595 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7868885..ae6f7e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -159,26 +159,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "gc-arena" -version = "0.5.3" -source = "git+https://github.com/kyren/gc-arena?rev=d651e3b4363d525a2d502c2305bc73e291835c84#d651e3b4363d525a2d502c2305bc73e291835c84" -dependencies = [ - "gc-arena-derive", - "hashbrown 0.15.3", -] - -[[package]] -name = "gc-arena-derive" -version = "0.5.3" -source = "git+https://github.com/kyren/gc-arena?rev=d651e3b4363d525a2d502c2305bc73e291835c84#d651e3b4363d525a2d502c2305bc73e291835c84" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "hashbrown" version = "0.14.3" @@ -319,7 +299,6 @@ version = "0.0.0" dependencies = [ "derive_more", "ecow", - "gc-arena", "hashbrown 0.15.3", "inkwell", "itertools", @@ -497,17 +476,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "text-size" version = "1.1.1" diff --git a/Cargo.toml b/Cargo.toml index 1bf29d6..0117ec0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,5 @@ regex = "1.11" hashbrown = "0.15" replace_with = "0.1" inkwell = { version = "0.6.0", features = ["llvm18-1"] } -gc-arena = { git = "https://github.com/kyren/gc-arena", rev = "d651e3b4363d525a2d502c2305bc73e291835c84", features= ["hashbrown"] } rustyline = { version = "15.0", optional = true } diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index e4b8e67..b4e5d28 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -1,12 +1,11 @@ use std::rc::Rc; -use gc_arena::{Gc, Mutation}; use hashbrown::HashMap; use crate::env::VmEnv; use crate::ir::{DowngradeContext, Ir}; use crate::ty::common::Const; -use crate::ty::internal::{AttrSet, PrimOp, Value}; +use crate::ty::internal::{PrimOp, Value}; use crate::vm::VM; pub fn ir_env(ctx: &mut DowngradeContext) -> HashMap { @@ -17,16 +16,16 @@ pub fn ir_env(ctx: &mut DowngradeContext) -> HashMap { map } -pub fn vm_env<'gc>(vm: &VM, mc: &Mutation<'gc>) -> Gc<'gc, VmEnv<'gc>> { +pub fn vm_env<'gc>(vm: &VM) -> Rc> { let primops = [ - PrimOp::new("add", 2, |args, _, mc| { + PrimOp::new("add", 2, |args, _| { let Ok([mut first, second]): Result<[Value; 2], _> = args.try_into() else { unreachable!() }; first.add(second); first.ok() }), - PrimOp::new("sub", 2, |args, _, mc| { + PrimOp::new("sub", 2, |args, _| { let Ok([mut first, mut second]): Result<[Value; 2], _> = args.try_into() else { unreachable!() }; @@ -34,39 +33,39 @@ pub fn vm_env<'gc>(vm: &VM, mc: &Mutation<'gc>) -> Gc<'gc, VmEnv<'gc>> { first.add(second); first.ok() }), - PrimOp::new("mul", 2, |args, _, _| { + PrimOp::new("mul", 2, |args, _| { let Ok([mut first, second]): Result<[Value; 2], _> = args.try_into() else { unreachable!() }; first.mul(second); first.ok() }), - PrimOp::new("div", 2, |args, _, _| { + PrimOp::new("div", 2, |args, _| { let Ok([mut first, second]): Result<[Value; 2], _> = args.try_into() else { unreachable!() }; first.div(second)?; first.ok() }), - PrimOp::new("lessThan", 2, |args, _, _| { + PrimOp::new("lessThan", 2, |args, _| { let Ok([mut first, second]): Result<[Value; 2], _> = args.try_into() else { unreachable!() }; first.lt(second); first.ok() }), - /* PrimOp::new("seq", 2, |args, vm, mc| { - let Ok([mut first, second]): Result<[_; 2], _> = args.try_into() else { + /* PrimOp::new("seq", 2, |args, vm| { + let Ok([mut first, second]): Result<[_; 2]> = args.try_into() else { unreachable!() }; - first.force(vm, mc).unwrap(); + first.force(vm).unwrap(); second.ok() }), - PrimOp::new("deepSeq", 2, |args, vm, mc| { - let Ok([mut first, second]): Result<[_; 2], _> = args.try_into() else { + PrimOp::new("deepSeq", 2, |args, vm| { + let Ok([mut first, second]): Result<[_; 2]> = args.try_into() else { unreachable!() }; - first.force_deep(vm, mc).unwrap(); + first.force_deep(vm).unwrap(); second.ok() }), */ ]; @@ -85,16 +84,16 @@ pub fn vm_env<'gc>(vm: &VM, mc: &Mutation<'gc>) -> Gc<'gc, VmEnv<'gc>> { map.insert(vm.new_sym(primop.name), Value::PrimOp(primop)); } let sym = vm.new_sym("builtins"); - /* let mut attrs = CoW::new(AttrSet::new(map), mc); + /* let mut attrs = CoW::new(AttrSet::new(map)); unsafe { attrs.make_cyclic(|attrs, this| { let _ = attrs.push_attr(sym, Value::AttrSet(this)); - }, mc); + }); } let builtins = Value::AttrSet(attrs); env_map.insert(sym, builtins); */ // TODO: - // VmEnv::new(Gc::new(mc, env_map), mc) - VmEnv::new(Vec::new(), mc) + // VmEnv::new(Gc::new(mc, env_map)) + VmEnv::new(Vec::new()) } diff --git a/src/bytecode.rs b/src/bytecode.rs index 504cb6f..7de7938 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -1,5 +1,4 @@ use ecow::EcoString; -use gc_arena::Collect; use hashbrown::HashMap; use crate::ty::common::Const; @@ -9,8 +8,7 @@ type Slice = Box<[T]>; pub type OpCodes = Slice; -#[derive(Debug, Clone, Copy, Collect)] -#[collect(no_drop)] +#[derive(Debug, Clone, Copy)] pub enum OpCode { /// load a constant onto stack Const { idx: usize }, @@ -90,8 +88,7 @@ pub enum OpCode { Illegal, } -#[derive(Debug, Clone, Copy, Collect)] -#[collect(no_drop)] +#[derive(Debug, Clone, Copy)] pub enum BinOp { Add, Sub, @@ -105,15 +102,13 @@ pub enum BinOp { Upd, } -#[derive(Debug, Clone, Copy, Collect)] -#[collect(no_drop)] +#[derive(Debug, Clone, Copy)] pub enum UnOp { Neg, Not, } -#[derive(Debug, Collect)] -#[collect(no_drop)] +#[derive(Debug)] pub struct Func { pub param: Param, pub opcodes: OpCodes, diff --git a/src/compile.rs b/src/compile.rs index 8495716..d93ea72 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -104,7 +104,10 @@ impl Compile for ir::Arg { impl Compile for ir::LetVar { fn compile(self, comp: &mut Compiler) { - comp.push(OpCode::LookUpLet { level: self.level, idx: self.idx }); + comp.push(OpCode::LookUpLet { + level: self.level, + idx: self.idx, + }); } } @@ -144,7 +147,9 @@ impl Compile for ir::Attrs { impl Compile for ir::List { fn compile(self, comp: &mut Compiler) { - comp.push(OpCode::List { cap: self.items.len() }); + comp.push(OpCode::List { + cap: self.items.len(), + }); for item in self.items { item.compile(comp); comp.push(OpCode::PushElem); @@ -385,14 +390,18 @@ impl Compile for ir::If { impl Compile for ir::Let { fn compile(self, comp: &mut Compiler) { comp.push(OpCode::List { - cap: self.bindings.len() + cap: self.bindings.len(), }); for (_, val) in self.bindings { val.compile(comp); comp.push(OpCode::PushElem); } comp.push(OpCode::FinalizeLet); + let thunk = self.expr.is_thunk(); self.expr.compile(comp); + if thunk { + comp.push(OpCode::CaptureEnv); + } comp.push(OpCode::LeaveEnv); } } diff --git a/src/env.rs b/src/env.rs index 2a30ac2..229ce66 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1,42 +1,34 @@ use std::hash::Hash; +use std::rc::Rc; -use gc_arena::{Collect, Gc, Mutation}; use hashbrown::HashMap; use crate::{ir::Ir, ty::internal::Value}; -#[derive(Collect)] -#[collect(no_drop)] -pub struct Env<'gc, K: Hash + Eq + Collect<'gc>, V: Collect<'gc>> { - let_: Gc<'gc, LetEnv<'gc, V>>, - with: Gc<'gc, With<'gc, K, V>>, +pub struct Env { + let_: Rc>, + with: Rc>, args: Vec, - jit_store: Vec>, - last: Option>>, + last: Option>>, } -#[derive(Collect)] -#[collect(no_drop)] -pub struct LetEnv<'gc, V: Collect<'gc>> { +pub struct LetEnv { map: Vec, - last: Option>>, + last: Option>>, } -pub type VmEnv<'gc> = Env<'gc, usize, Value<'gc>>; -pub type IrEnv<'gc> = Env<'gc, usize, Ir>; +pub type VmEnv<'gc> = Env>; +pub type IrEnv<'gc> = Env; -#[derive(Default, Clone, Collect)] -#[collect(no_drop)] -pub struct With<'gc, K: Hash + Eq + Collect<'gc>, V: Collect<'gc>> { - map: Option>>, - last: Option>>, +#[derive(Default, Clone)] +pub struct With { + map: Option>>, + last: Option>>, } -#[derive(Collect)] -#[collect(no_drop)] -enum LetNode<'gc, K: Hash + Eq + Collect<'gc>, V: Collect<'gc>> { - Let(Gc<'gc, HashMap>), - MultiArg(Gc<'gc, HashMap>), +enum LetNode { + Let(Rc>), + MultiArg(Rc>), } #[derive(Clone, Copy)] @@ -46,24 +38,17 @@ pub enum Type { With, } -impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> Env<'gc, K, V> { - pub fn new(map: Vec, mc: &Mutation<'gc>) -> Gc<'gc, Self> { - Gc::new( - mc, - Self { - let_: LetEnv::new(map, mc), - with: Gc::new( - mc, - With { - map: None, - last: None, - }, - ), - args: Vec::new(), - jit_store: Vec::new(), +impl Env { + pub fn new(map: Vec) -> Rc { + Rc::new(Self { + let_: LetEnv::new(map), + with: Rc::new(With { + map: None, last: None, - }, - ) + }), + args: Vec::new(), + last: None, + }) } pub fn lookup_arg(&self, level: usize) -> &V { @@ -83,109 +68,64 @@ impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> Env<'gc, } #[must_use] - pub fn enter_arg(self: Gc<'gc, Self>, val: V, mc: &Mutation<'gc>) -> Gc<'gc, Self> { + pub fn enter_arg(self: Rc, val: V) -> Rc { let mut args = self.args.clone(); args.push(val); - Gc::new( - mc, - Env { - let_: self.let_, - with: self.with, - last: Some(self), - jit_store: Vec::new(), - args - }, - ) + Rc::new(Self { + let_: self.let_.clone(), + with: self.with.clone(), + last: Some(self), + args, + }) } #[must_use] - pub fn enter_let( - self: Gc<'gc, Self>, - map: Vec, - mc: &Mutation<'gc>, - ) -> Gc<'gc, Self> { - Gc::new( - mc, - Self { - let_: self.let_.enter_let(map, mc), - with: self.with, - args: self.args.clone(), - jit_store: Vec::new(), - last: Some(self), - }, - ) + pub fn enter_let(self: Rc, map: Vec) -> Rc { + Rc::new(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: Gc<'gc, Self>, - map: Gc<'gc, HashMap>, - mc: &Mutation<'gc>, - ) -> Gc<'gc, Self> { - Gc::new( - mc, - Env { - let_: self.let_, - with: self.with.enter(map, mc), - args: self.args.clone(), - jit_store: Vec::new(), - last: Some(self), - }, - ) + pub fn enter_with(self: Rc, map: Rc>) -> Rc { + Rc::new(Self { + let_: self.let_.clone(), + with: self.with.clone().enter(map), + args: self.args.clone(), + last: Some(self), + }) } - #[inline] - pub fn jit_store(&'gc mut self, val: V) -> &'gc V { - self.jit_store.push(Box::new(val)); - &self.jit_store[self.jit_store.len() - 1] - } - - #[inline] - pub fn jit_stored(&self) -> usize { - self.jit_store.len() - } - - pub fn leave(&self) -> Gc<'gc, Self> { - self.last.unwrap() + pub fn leave(&self) -> Rc { + self.last.clone().unwrap() } } -impl<'gc, V: Clone + Collect<'gc>> LetEnv<'gc, V> { - pub fn new(map: Vec, mc: &Mutation<'gc>) -> Gc<'gc, Self> { - Gc::new( - mc, - Self { - map, - last: None, - }, - ) +impl LetEnv { + pub fn new(map: Vec) -> Rc { + Rc::new(Self { map, last: None }) } pub fn lookup(&self, level: usize, idx: usize) -> &V { let mut cur = self; for _ in 0..level { - let last = cur.last.unwrap(); - cur = last.as_ref(); + cur = cur.last.as_ref().unwrap(); } &cur.map[idx] } - pub fn enter_let( - self: Gc<'gc, Self>, - map: Vec, - mc: &Mutation<'gc>, - ) -> Gc<'gc, Self> { - Gc::new( - mc, - Self { - map, - last: Some(self), - }, - ) + pub fn enter_let(self: Rc, map: Vec) -> Rc { + Rc::new(Self { + map, + last: Some(self), + }) } } -impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> With<'gc, K, V> { +impl With { pub fn lookup(&self, symbol: &K) -> Option<&V> { if let Some(val) = self.map.as_ref()?.get(symbol) { return Some(val); @@ -193,17 +133,10 @@ impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> With<'gc self.last.as_ref().and_then(|env| env.lookup(symbol)) } - pub fn enter( - self: Gc<'gc, Self>, - map: Gc<'gc, HashMap>, - mc: &Mutation<'gc>, - ) -> Gc<'gc, Self> { - Gc::new( - mc, - Self { - map: Some(map), - last: Some(self), - }, - ) + pub fn enter(self: Rc, map: Rc>) -> Rc { + Rc::new(Self { + map: Some(map), + last: Some(self), + }) } } diff --git a/src/ir.rs b/src/ir.rs index 78a287f..df1765a 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -20,7 +20,7 @@ pub fn downgrade(expr: Expr) -> Result { Ok(Downgraded { top_level: ir, consts: ctx.consts.into(), - symbols: ctx.symbols.into(), + symbols: ctx.symbols, symmap: ctx.symmap, thunks: ctx.thunks.into(), funcs: ctx.funcs.into(), @@ -228,12 +228,10 @@ impl<'a, 'env> Env<'a, 'env> { Builtins(map) => { return if let Some(ir) = map.get(&ident) { Ok(LookupResult::Builtin(ir.clone())) + } else if has_with { + Ok(LookupResult::With) } else { - if has_with { - Ok(LookupResult::With) - } else { - Err(()) - } + Err(()) }; } Let(map) => { @@ -259,7 +257,7 @@ impl<'a, 'env> Env<'a, 'env> { level: arg_level, default: default.clone(), }); - } else if alias.map_or(false, |alias| alias == ident) { + } else if *alias == Some(ident) { return Ok(LookupResult::SingleArg { level: arg_level }); } else { arg_level += 1; @@ -299,7 +297,7 @@ pub struct Downgraded { pub funcs: Box<[Func]>, } -impl<'a> DowngradeContext { +impl DowngradeContext { fn new_thunk(&mut self, thunk: Ir) -> Thunk { let idx = self.thunks.len(); self.thunks.push(thunk); @@ -384,13 +382,10 @@ impl Attrs { .get_mut(&ident) .unwrap() .as_mut() - .try_unwrap_attrs() - .or_else(|_| { - Err(Error::DowngradeError(format!( + .try_unwrap_attrs().map_err(|_| Error::DowngradeError(format!( r#"attribute '{}' already defined"#, ctx.get_sym(ident) ))) - }) .and_then(|attrs: &mut Attrs| attrs._insert(path, name, value, ctx)) } else { let mut attrs = Attrs { @@ -795,7 +790,11 @@ impl Downgrade for ast::AttrSet { let rec = self.rec_token().is_some(); let attrs = downgrade_attrs(self, ctx)?; if rec { - let bindings = attrs.stcs.into_iter().sorted_by_key(|(k, _)| *k).collect::>(); + let bindings = attrs + .stcs + .into_iter() + .sorted_by_key(|(k, _)| *k) + .collect::>(); let stcs = bindings .iter() .map(|&(sym, _)| (sym, Var { sym }.ir())) @@ -1045,20 +1044,20 @@ impl Downgrade for ast::LetIn { impl Let { fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { - let map = self - .bindings - .iter() - .map(|(sym, _)| *sym) - .sorted() - .collect(); + let map = self.bindings.iter().map(|(sym, _)| *sym).sorted().collect(); let env = env.enter_let(&map); let bindings = self .bindings .into_iter() - .map(|(k, v)| Ok((k, match v.resolve(ctx, &env)? { - ir @ Ir::Const(_) => ir, - ir => ctx.new_thunk(ir).ir() - }))) + .map(|(k, v)| { + Ok(( + k, + match v.resolve(ctx, &env)? { + ir @ Ir::Const(_) => ir, + ir => ctx.new_thunk(ir).ir(), + }, + )) + }) .collect::>()?; let expr = self.expr.resolve(ctx, &env)?.boxed(); Self { bindings, expr }.ir().ok() diff --git a/src/jit/helpers.rs b/src/jit/helpers.rs index 0474d14..e8ccd65 100644 --- a/src/jit/helpers.rs +++ b/src/jit/helpers.rs @@ -1,4 +1,6 @@ -use gc_arena::{Gc, Mutation}; +use std::ptr::NonNull; +use std::rc::Rc; + use inkwell::AddressSpace; use inkwell::context::Context; use inkwell::execution_engine::ExecutionEngine; @@ -52,11 +54,11 @@ impl<'ctx> Helpers<'ctx> { let ptr_int_type = context.ptr_sized_int_type(execution_engine.get_target_data(), None); let ptr_type = context.ptr_type(AddressSpace::default()); let value_type = context.struct_type(&[int_type.into(), int_type.into()], false); - let func_type = value_type.fn_type(&[ptr_type.into(), ptr_type.into()], false); + let func_type = value_type.fn_type(&[ptr_type.into()], false); let new_thunk = module.add_function( "new_thunk", - value_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false), + value_type.fn_type(&[ptr_type.into()], false), None, ); let debug = module.add_function( @@ -66,10 +68,9 @@ impl<'ctx> Helpers<'ctx> { ); let capture_env = module.add_function( "capture_env", - context.void_type().fn_type( - &[value_type.into(), ptr_type.into(), ptr_type.into()], - false, - ), + context + .void_type() + .fn_type(&[value_type.into(), ptr_type.into()], false), None, ); let neg = module.add_function( @@ -105,13 +106,7 @@ impl<'ctx> Helpers<'ctx> { let call = module.add_function( "call", value_type.fn_type( - &[ - value_type.into(), - value_type.into(), - ptr_type.into(), - ptr_type.into(), - ptr_type.into(), - ], + &[value_type.into(), value_type.into(), ptr_type.into()], false, ), None, @@ -123,7 +118,10 @@ impl<'ctx> Helpers<'ctx> { ); let lookup_let = module.add_function( "lookup_let", - value_type.fn_type(&[ptr_int_type.into(), ptr_int_type.into(), ptr_type.into()], false), + value_type.fn_type( + &[ptr_int_type.into(), ptr_int_type.into(), ptr_type.into()], + false, + ), None, ); let lookup = module.add_function( @@ -134,12 +132,7 @@ impl<'ctx> Helpers<'ctx> { let force = module.add_function( "force", value_type.fn_type( - &[ - value_type.into(), - ptr_type.into(), - ptr_type.into(), - ptr_type.into(), - ], + &[value_type.into(), ptr_type.into(), ptr_type.into()], false, ), None, @@ -237,14 +230,11 @@ extern "C" fn helper_debug(value: JITValue) { dbg!(value.tag); } -extern "C" fn helper_capture_env<'gc>( - thunk: JITValue, - env: *const VmEnv<'gc>, - mc: *const Mutation<'gc>, -) { +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 { Gc::from_ptr(env) }; - thunk.capture_env(env, unsafe { mc.as_ref() }.unwrap()); + let env = unsafe { Rc::from_raw(env) }; + thunk.capture_env(env.clone()); + std::mem::forget(env); } extern "C" fn helper_neg(rhs: JITValue, _env: *const VmEnv) -> JITValue { @@ -347,21 +337,13 @@ extern "C" fn helper_or(lhs: JITValue, rhs: JITValue) -> JITValue { } } -extern "C" fn helper_call<'gc>( - func: JITValue, - arg: JITValue, - vm: *const VM<'gc>, - env: *mut VmEnv<'gc>, - mc: *const Mutation<'gc>, -) -> JITValue { +extern "C" fn helper_call<'gc>(func: JITValue, arg: JITValue, vm: *const VM<'gc>) -> JITValue { let vm = unsafe { vm.as_ref() }.unwrap(); - let mc = unsafe { mc.as_ref() }.unwrap(); let arg = Value::from(arg); match func.tag { ValueTag::Function => { let func = Value::from(func).unwrap_func(); - let env = unsafe { &mut *env }; - func.call_compile(arg, vm, mc).unwrap().clone().into() + func.call_compile(arg, vm).unwrap().clone().into() } _ => todo!(), } @@ -387,36 +369,28 @@ extern "C" fn helper_lookup(sym: usize, env: *const VmEnv) -> JITValue { extern "C" fn helper_force<'gc>( thunk: JITValue, - vm: *const VM<'gc>, - mc: *const Mutation<'gc>, + vm: NonNull>, jit: *const JITContext<'gc>, ) -> JITValue { if !matches!(thunk.tag, ValueTag::Thunk) { return thunk; } - let vm = unsafe { vm.as_ref() }.unwrap(); - let mc = unsafe { mc.as_ref() }.unwrap(); + let vm = unsafe { vm.as_ref() }; let thunk = Value::from(thunk).unwrap_thunk(); if let Some(val) = thunk.get_value() { return val.clone().into(); } - let (opcodes, env) = thunk.suspend(mc).unwrap(); + let (opcodes, env) = thunk.suspend().unwrap(); let func = unsafe { jit.as_ref() } .unwrap() .compile_seq(opcodes.iter().copied().rev(), vm) .unwrap(); - let val = unsafe { func(env.as_ref() as *const _, mc as *const _) }; - thunk.insert_value(val.into(), mc); + let val = unsafe { func(env.as_ref() as *const _) }; + thunk.insert_value(val.into()); val } -extern "C" fn helper_new_thunk<'gc>(opcodes: *const OpCodes, env: *mut VmEnv<'gc>, mc: *const Mutation<'gc>) -> JITValue { - let mc = unsafe { &*mc }; - let env = unsafe { &mut *env }; - Value::Thunk(Thunk::new( - unsafe { opcodes.as_ref() }.unwrap(), - mc - )) - .into() +extern "C" fn helper_new_thunk(opcodes: *const OpCodes) -> JITValue { + Value::Thunk(Thunk::new(unsafe { opcodes.as_ref() }.unwrap())).into() } diff --git a/src/jit/mod.rs b/src/jit/mod.rs index d30b947..baed8f7 100644 --- a/src/jit/mod.rs +++ b/src/jit/mod.rs @@ -1,8 +1,7 @@ use std::marker::PhantomData; -use std::rc::Rc; use std::ops::Deref; +use std::rc::Rc; -use gc_arena::{Collect, Gc, Mutation}; use inkwell::OptimizationLevel; use inkwell::builder::Builder; use inkwell::context::Context; @@ -66,7 +65,7 @@ impl<'gc> From for Value<'gc> { Null => Value::Null, Function => Value::Func(unsafe { Rc::from_raw(value.data.ptr as *const _) }), Thunk => Value::Thunk(self::Thunk { - thunk: unsafe { Gc::from_ptr(value.data.ptr as *const _) }, + thunk: unsafe { Rc::from_raw(value.data.ptr as *const _) }, }), _ => todo!("not implemented for {:?}", value.tag), } @@ -80,18 +79,16 @@ impl From> for JITValue { tag: ValueTag::Int, data: JITValueData { int }, }, - Value::Func(func) => { - JITValue { - tag: ValueTag::Function, - data: JITValueData { - ptr: Rc::into_raw(func) as *const _, - }, - } - } + Value::Func(func) => JITValue { + tag: ValueTag::Function, + data: JITValueData { + ptr: Rc::into_raw(func) as *const _, + }, + }, Value::Thunk(thunk) => JITValue { tag: ValueTag::Thunk, data: JITValueData { - ptr: Gc::as_ptr(thunk.thunk) as *const _, + ptr: Rc::into_raw(thunk.thunk) as *const _, }, }, _ => todo!(), @@ -99,19 +96,11 @@ impl From> for JITValue { } } -pub struct JITFunc<'gc>(F<'gc>, PhantomData) -> &'gc F<'gc>>); -type F<'gc> = unsafe extern "C" fn(*const VmEnv<'gc>, *const Mutation<'gc>) -> JITValue; +pub struct JITFunc<'gc>(F<'gc>, PhantomData<&'gc mut ()>); +type F<'gc> = unsafe extern "C" fn(*const VmEnv<'gc>) -> JITValue; -unsafe impl<'gc> Collect<'gc> for JITFunc<'gc> { - fn trace>(&self, _: &mut T) {} - const NEEDS_TRACE: bool = false; -} - -impl<'gc> From> for JITFunc<'gc> -{ - fn from( - value: F<'gc> - ) -> Self { +impl<'gc> From> for JITFunc<'gc> { + fn from(value: F<'gc>) -> Self { Self(value, PhantomData) } } @@ -132,11 +121,6 @@ pub struct JITContext<'gc> { helpers: Helpers<'gc>, } -unsafe impl<'gc> Collect<'gc> for JITContext<'gc> { - fn trace>(&self, _: &mut T) {} - const NEEDS_TRACE: bool = false; -} - impl<'gc> JITContext<'gc> { pub fn new(context: &'gc Context) -> Self { // force linker to link JIT engine @@ -172,18 +156,17 @@ impl<'gc> JITContext<'gc> { pub fn compile_seq( &self, mut opcodes: impl ExactSizeIterator, - vm: &'gc VM<'gc>, + vm: &VM<'gc>, ) -> Result> { let mut stack = Stack::<_, STACK_SIZE>::new(); let func_ = self .module .add_function("nixjit_function", self.helpers.func_type, None); let env = func_.get_nth_param(0).unwrap().into_pointer_value(); - let mc = func_.get_nth_param(1).unwrap().into_pointer_value(); let entry = self.context.append_basic_block(func_, "entry"); self.builder.position_at_end(entry); let len = opcodes.len(); - self.build_expr(&mut opcodes, vm, env, mc, &mut stack, func_, len)?; + self.build_expr(&mut opcodes, vm, env, &mut stack, func_, len)?; assert_eq!(stack.len(), 1); let value = stack.pop(); @@ -205,16 +188,15 @@ impl<'gc> JITContext<'gc> { fn build_expr( &self, iter: &mut impl Iterator, - vm: &'gc VM<'gc>, + vm: &VM<'gc>, env: PointerValue<'gc>, - mc: PointerValue<'gc>, stack: &mut Stack, CAP>, func: FunctionValue<'gc>, mut length: usize, ) -> Result { while length > 1 { let opcode = iter.next().unwrap(); - let br = self.single_op(opcode, vm, env, mc, stack)?; + let br = self.single_op(opcode, vm, env, stack)?; length -= 1; if br > 0 { let consq = self.context.append_basic_block(func, "consq"); @@ -246,13 +228,13 @@ impl<'gc> JITContext<'gc> { length -= br; self.builder.position_at_end(consq); - let br = self.build_expr(iter, vm, env, mc, stack, func, br)?; + let br = self.build_expr(iter, vm, env, stack, func, br)?; self.builder.build_store(result, stack.pop())?; self.builder.build_unconditional_branch(cont)?; length -= br; self.builder.position_at_end(alter); - self.build_expr(iter, vm, env, mc, stack, func, br)?; + self.build_expr(iter, vm, env, stack, func, br)?; self.builder.build_store(result, stack.pop())?; self.builder.build_unconditional_branch(cont)?; @@ -265,7 +247,7 @@ impl<'gc> JITContext<'gc> { } } if length > 0 { - self.single_op(iter.next().unwrap(), vm, env, mc, stack) + self.single_op(iter.next().unwrap(), vm, env, stack) } else { Ok(0) } @@ -275,9 +257,8 @@ impl<'gc> JITContext<'gc> { fn single_op( &self, opcode: OpCode, - vm: &'gc VM<'_>, + vm: &VM<'_>, env: PointerValue<'gc>, - mc: PointerValue<'gc>, stack: &mut Stack, CAP>, ) -> Result { match opcode { @@ -295,11 +276,7 @@ impl<'gc> JITContext<'gc> { self.builder .build_direct_call( self.helpers.new_thunk, - &[ - self.new_ptr(vm.get_thunk(idx) as *const _).into(), - env.into(), - mc.into(), - ], + &[self.new_ptr(vm.get_thunk(idx) as *const _).into()], "call_capture_env", )? .try_as_basic_value() @@ -314,7 +291,6 @@ impl<'gc> JITContext<'gc> { &[ thunk.into(), self.new_ptr(vm as *const _).into(), - mc.into(), self.new_ptr(self as *const _).into(), ], "call_force", @@ -475,7 +451,7 @@ impl<'gc> JITContext<'gc> { .builder .build_direct_call( self.helpers.call, - &[func.into(), arg.into(), self.new_ptr(vm).into(), env.into(), mc.into()], + &[func.into(), arg.into(), self.new_ptr(vm).into()], "call", )? .try_as_basic_value() diff --git a/src/stack.rs b/src/stack.rs index c5b7fb4..316ac85 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -1,8 +1,6 @@ use std::mem::{MaybeUninit, replace, transmute}; use std::ops::Deref; -use gc_arena::Collect; - use crate::error::*; macro_rules! into { @@ -20,13 +18,6 @@ pub struct Stack { top: usize, } -unsafe impl<'gc, T: Collect<'gc>, const CAP: usize> Collect<'gc> for Stack { - fn trace>(&self, cc: &mut Tr) { - for v in self.iter() { - v.trace(cc); - } - } -} impl Default for Stack { fn default() -> Self { diff --git a/src/ty/common.rs b/src/ty/common.rs index 134cce6..a829517 100644 --- a/src/ty/common.rs +++ b/src/ty/common.rs @@ -3,10 +3,8 @@ use std::hash::Hash; use derive_more::{Constructor, IsVariant, Unwrap}; use ecow::EcoString; -use gc_arena::Collect; -#[derive(Clone, Debug, PartialEq, Constructor, Hash, Collect)] -#[collect(no_drop)] +#[derive(Clone, Debug, PartialEq, Constructor, Hash)] pub struct Catchable { msg: String, } @@ -23,8 +21,7 @@ impl Display for Catchable { } } -#[derive(Debug, Clone, IsVariant, Unwrap, Collect)] -#[collect(require_static)] +#[derive(Debug, Clone, IsVariant, Unwrap)] pub enum Const { Bool(bool), Int(i64), @@ -115,5 +112,5 @@ impl Eq for Const {} pub enum MaybeThunk { Thunk(usize), - Const(Const) + Const(Const), } diff --git a/src/ty/internal/attrset.rs b/src/ty/internal/attrset.rs index 83a1cf8..e22907d 100644 --- a/src/ty/internal/attrset.rs +++ b/src/ty/internal/attrset.rs @@ -2,7 +2,6 @@ use std::ops::Deref; use std::rc::Rc; use derive_more::Constructor; -use gc_arena::Collect; use hashbrown::{HashMap, HashSet}; use itertools::Itertools; @@ -17,12 +16,6 @@ pub struct AttrSet<'gc> { data: HashMap>, } -unsafe impl<'gc> Collect<'gc> for AttrSet<'gc> { - fn trace>(&self, cc: &mut T) { - self.data.trace(cc); - } -} - impl<'gc> From>> for AttrSet<'gc> { fn from(data: HashMap>) -> Self { Self { data } diff --git a/src/ty/internal/func.rs b/src/ty/internal/func.rs index 47938b1..5bdaadc 100644 --- a/src/ty/internal/func.rs +++ b/src/ty/internal/func.rs @@ -1,7 +1,5 @@ -use std::cell::Cell; - -use gc_arena::lock::{GcRefLock, RefLock}; -use gc_arena::{Collect, Gc, Mutation}; +use std::cell::{Cell, OnceCell}; +use std::rc::Rc; use crate::bytecode::Func as BFunc; use crate::env::VmEnv; @@ -11,8 +9,7 @@ use crate::jit::JITFunc; use crate::ty::internal::Value; use crate::vm::VM; -#[derive(Debug, Clone, Collect)] -#[collect(no_drop)] +#[derive(Debug, Clone)] pub enum Param { Ident(usize), Formals { @@ -44,42 +41,25 @@ impl From for Param { pub struct Func<'gc> { pub func: &'gc BFunc, - pub env: Gc<'gc, VmEnv<'gc>>, - pub compiled: GcRefLock<'gc, Option>>, + pub env: Rc>, + pub compiled: OnceCell>, pub count: Cell, } -unsafe impl<'gc> Collect<'gc> for Func<'gc> { - fn trace>(&self, cc: &mut Tr) { - self.env.trace(cc); - self.compiled.trace(cc); - self.count.trace(cc); - } -} - impl<'gc> Func<'gc> { - pub fn new(func: &'gc BFunc, env: Gc<'gc, VmEnv<'gc>>, mc: &Mutation<'gc>) -> Self { + pub fn new(func: &'gc BFunc, env: Rc>) -> Self { Self { func, env, - compiled: Gc::new(mc, RefLock::new(None)), + compiled: OnceCell::new(), count: Cell::new(0), } } - pub fn call_compile( - &self, - arg: Value<'gc>, - vm: &'gc VM<'gc>, - mc: &Mutation<'gc>, - ) -> Result> { - let env = self.env.enter_arg(arg, mc); - let compiled = self - .compiled - .borrow_mut(mc) - .get_or_insert_with(|| vm.compile_func(self.func)) - .clone(); - let ret = unsafe { compiled(env.as_ref() as *const VmEnv, mc as *const _) }; + pub fn call_compile(&self, arg: Value<'gc>, vm: &'gc VM<'gc>) -> Result> { + let env = self.env.clone().enter_arg(arg); + let compiled = self.compiled.get_or_init(|| vm.compile_func(self.func)); + let ret = unsafe { compiled(env.as_ref() as *const VmEnv) }; Ok(ret.into()) } } diff --git a/src/ty/internal/list.rs b/src/ty/internal/list.rs index 7ae0a09..94b59a8 100644 --- a/src/ty/internal/list.rs +++ b/src/ty/internal/list.rs @@ -1,9 +1,10 @@ -use hashbrown::HashSet; -use gc_arena::{Collect, Gc, Mutation}; +use std::rc::Weak; +use hashbrown::HashSet; + +use crate::env::VmEnv; use crate::ty::public as p; use crate::vm::VM; -use crate::env::VmEnv; use super::Value; @@ -12,17 +13,15 @@ pub struct List<'gc> { data: Vec>, } -unsafe impl<'gc> Collect<'gc> for List<'gc> { - fn trace>(&self, cc: &mut T) { - self.data.trace(cc); +impl<'gc> Default for List<'gc> { + fn default() -> Self { + Self::new() } } impl<'gc> List<'gc> { pub fn new() -> Self { - List { - data: Vec::new(), - } + List { data: Vec::new() } } pub fn with_capacity(cap: usize) -> Self { @@ -41,10 +40,10 @@ impl<'gc> List<'gc> { } } - pub fn capture(&mut self, env: Gc<'gc, VmEnv<'gc>>, mc: &Mutation<'gc>) { + pub fn capture(&mut self, env: &Weak>) { self.data.iter().for_each(|v| { if let Value::Thunk(ref thunk) = v.clone() { - thunk.capture_env(env, mc); + thunk.capture_env_weak(env.clone()); } }) } diff --git a/src/ty/internal/mod.rs b/src/ty/internal/mod.rs index 61d1dd6..da7c9ca 100644 --- a/src/ty/internal/mod.rs +++ b/src/ty/internal/mod.rs @@ -1,10 +1,8 @@ +use std::cell::RefCell; use std::hash::Hash; -use std::rc::Rc; +use std::rc::{Rc, Weak}; use derive_more::{IsVariant, Unwrap}; -use gc_arena::Mutation; -use gc_arena::lock::RefLock; -use gc_arena::{Collect, Gc, lock::GcRefLock}; use hashbrown::HashSet; use replace_with::replace_with_or_abort; @@ -27,9 +25,7 @@ pub use func::*; pub use list::List; pub use primop::*; -#[derive(IsVariant, Unwrap, Clone, Collect)] -#[collect(no_drop)] -// #[derive(IsVariant, Unwrap, Clone)] +#[derive(IsVariant, Unwrap, Clone)] pub enum Value<'gc> { Int(i64), Float(f64), @@ -155,16 +151,15 @@ impl<'gc> Value<'gc> { } } - pub fn call(&mut self, arg: Self, vm: &VM, mc: &Mutation<'gc>) -> Result<()> { + pub fn call(&mut self, arg: Self, vm: &VM) -> Result<()> { use Value::*; if matches!(arg, Value::Catchable(_)) { *self = arg; return Ok(()); } *self = match self { - PrimOp(func) => func.call(arg, vm, mc), - PartialPrimOp(func) => func.call(arg, vm, mc), - // Value::Func(func) => func.call(arg, vm, mc), + PrimOp(func) => func.call(arg, vm), + PartialPrimOp(func) => func.call(arg, vm), Catchable(_) => return Ok(()), _ => todo!(), }?; @@ -238,20 +233,18 @@ impl<'gc> Value<'gc> { pub fn add(&mut self, other: Self) { use Value::*; - replace_with_or_abort(self, |a| { - match (a, other) { - (Int(a), Int(b)) => Int(a + b), - (Int(a), Float(b)) => Float(a as f64 + b), - (Float(a), Int(b)) => Float(a + b as f64), - (Float(a), Float(b)) => Float(a + b), - (String(mut a), String(b)) => { - Rc::make_mut(&mut a).push_str(&b); - String(a) - } - (a @ Value::Catchable(_), _) => a, - (_, x @ Value::Catchable(_)) => x, - _ => todo!(), + replace_with_or_abort(self, |a| match (a, other) { + (Int(a), Int(b)) => Int(a + b), + (Int(a), Float(b)) => Float(a as f64 + b), + (Float(a), Int(b)) => Float(a + b as f64), + (Float(a), Float(b)) => Float(a + b), + (String(mut a), String(b)) => { + Rc::make_mut(&mut a).push_str(&b); + String(a) } + (a @ Value::Catchable(_), _) => a, + (_, x @ Value::Catchable(_)) => x, + _ => todo!(), }); } @@ -430,54 +423,73 @@ impl<'gc> Value<'gc> { } #[repr(transparent)] -#[derive(Clone, Collect)] -#[collect(no_drop)] +#[derive(Clone)] pub struct Thunk<'gc> { - pub thunk: GcRefLock<'gc, _Thunk<'gc>>, + pub thunk: Rc>>, } #[derive(IsVariant, Unwrap)] pub enum _Thunk<'gc> { - Code(&'gc OpCodes, Option>>), + Code(&'gc OpCodes, Option>), Suspended, Value(Value<'gc>), } -unsafe impl<'gc> Collect<'gc> for _Thunk<'gc> { - fn trace>(&self, cc: &mut T) { - use _Thunk::*; - match self { - Code(_, env) => env.trace(cc), - Value(val) => val.trace(cc), - _ => (), +#[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, mc: &Mutation<'gc>) -> Self { + pub fn new(opcodes: &'gc OpCodes) -> Self { Thunk { - thunk: Gc::new(mc, RefLock::new(_Thunk::Code(opcodes, None))), + thunk: RefCell::new(_Thunk::Code(opcodes, None)).into(), } } - pub fn capture_env(&self, env: Gc<'gc, VmEnv<'gc>>, mc: &Mutation<'gc>) { - if let _Thunk::Code(_, envcell) = &mut *self.thunk.borrow_mut(mc) { - let _ = envcell.insert(env); + 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 suspend(&self, mc: &Mutation<'gc>) -> Result<(&'gc OpCodes, Gc<'gc, VmEnv<'gc>>)> { - let _Thunk::Code(opcodes, env) = - std::mem::replace(&mut *self.thunk.borrow_mut(mc), _Thunk::Suspended) - else { - return Err(Error::EvalError("infinite recursion encountered".into())); - }; - Ok((opcodes, env.unwrap())) + 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 insert_value(&self, value: Value<'gc>, mc: &Mutation<'gc>) { - *self.thunk.borrow_mut(mc) = _Thunk::Value(value); + 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>> { diff --git a/src/ty/internal/primop.rs b/src/ty/internal/primop.rs index 51ae36d..85c436d 100644 --- a/src/ty/internal/primop.rs +++ b/src/ty/internal/primop.rs @@ -1,20 +1,17 @@ use std::rc::Rc; use derive_more::Constructor; -use gc_arena::Collect; -use gc_arena::Mutation; use crate::error::Result; use crate::vm::VM; use super::Value; -#[derive(Debug, Clone, Constructor, Collect)] -#[collect(require_static)] +#[derive(Debug, Clone, Constructor)] pub struct PrimOp { pub name: &'static str, arity: usize, - func: for<'gc> fn(Vec>, &VM, &Mutation<'gc>) -> Result>, + func: for<'gc> fn(Vec>, &VM) -> Result>, } impl PartialEq for PrimOp { @@ -24,21 +21,19 @@ impl PartialEq for PrimOp { } impl PrimOp { - pub fn call<'gc>(&self, arg: Value<'gc>, vm: &VM, mc: &Mutation<'gc>) -> Result> { + pub fn call<'gc>(&self, arg: Value<'gc>, vm: &VM) -> Result> { let mut args = Vec::with_capacity(self.arity); args.push(arg); if self.arity > 1 { - Value::PartialPrimOp(Rc::new( - PartialPrimOp { - name: self.name, - arity: self.arity - 1, - args, - func: self.func, - }, - )) + Value::PartialPrimOp(Rc::new(PartialPrimOp { + name: self.name, + arity: self.arity - 1, + args, + func: self.func, + })) .ok() } else { - (self.func)(args, vm, mc) + (self.func)(args, vm) } } } @@ -48,15 +43,7 @@ pub struct PartialPrimOp<'gc> { pub name: &'static str, arity: usize, args: Vec>, - func: fn(Vec>, &VM, &Mutation<'gc>) -> Result>, -} - -unsafe impl<'gc> Collect<'gc> for PartialPrimOp<'gc> { - fn trace>(&self, cc: &mut Tr) { - for v in self.args.iter() { - v.trace(cc); - } - } + func: fn(Vec>, &VM) -> Result>, } impl PartialEq for PartialPrimOp<'_> { @@ -66,19 +53,14 @@ impl PartialEq for PartialPrimOp<'_> { } impl<'gc> PartialPrimOp<'gc> { - pub fn call( - self: &mut Rc, - arg: Value<'gc>, - vm: &VM, - mc: &Mutation<'gc>, - ) -> Result> { + pub fn call(self: &mut Rc, arg: Value<'gc>, vm: &VM) -> Result> { let func = self.func; let Some(ret) = ({ let self_mut = Rc::make_mut(self); self_mut.args.push(arg); self_mut.arity -= 1; if self_mut.arity == 0 { - Some(func(std::mem::take(&mut self_mut.args), vm, mc)) + Some(func(std::mem::take(&mut self_mut.args), vm)) } else { None } diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 237921f..9c7ed2c 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -1,8 +1,6 @@ use std::cell::RefCell; use std::rc::Rc; -use gc_arena::arena::CollectionPhase; -use gc_arena::{Arena, Collect, Gc, Mutation, Rootable}; use hashbrown::{HashMap, HashSet}; use inkwell::context::Context; @@ -25,110 +23,65 @@ mod test; const STACK_SIZE: usize = 8 * 1024 / size_of::(); const COLLECTOR_GRANULARITY: f64 = 1024.0; -#[derive(Collect)] -#[collect(require_static)] struct ContextWrapper(Context); pub fn run(mut prog: Program) -> Result { - let mut arena: Arena]> = Arena::new(|mc| { - let jit = Gc::new(mc, ContextWrapper(Context::create())); - let mut thunks = std::mem::take(&mut prog.thunks); - thunks.iter_mut().for_each(|code| code.reverse()); - let mut funcs = std::mem::take(&mut prog.funcs); - funcs - .iter_mut() - .for_each(|F { opcodes, .. }| opcodes.reverse()); - let symbols = std::mem::take(&mut prog.symbols); - let symmap = std::mem::take(&mut prog.symmap); - let consts = std::mem::take(&mut prog.consts); - let vm = VM { - thunks, - funcs, - consts, - symbols: RefCell::new(symbols), - symmap: RefCell::new(symmap), - jit: JITContext::new(&jit.as_ref().0), - }; - let vm = Gc::new(mc, vm); - GcRoot { - vm, - jit, - stack: Stack::new(), - envs: vec![vm_env(&vm, mc)], - } - }); + 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(); - eval(prog.top_level, &mut arena, |val, root, _| { - Ok(val.to_public(&root.vm, &mut HashSet::new())) - }) + Ok(eval(prog.top_level, &vm, vm_env(&vm))?.to_public(&vm, &mut HashSet::new())) } -pub fn eval FnOnce(Value<'gc>, &mut GcRoot<'gc>, &Mutation<'gc>) -> Result>( - opcodes: Box<[OpCode]>, - arena: &mut Arena Rootable<'gc, Root = GcRoot<'gc>>>, - f: F, -) -> Result { +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() { - arena.mutate_root(|mc, root| { - let consq = single_op( - &root.vm, - opcode, - &mut root.stack, - root.envs.last_mut().unwrap(), - mc, - )?; - match consq { - Consq::NoOp => (), - Consq::Jmp(step) => opcodes.resize_with(opcodes.len() - step, || unreachable!()), - Consq::Force => { - let thunk = root.stack.tos().as_ref().unwrap_thunk(); - let (code, env) = thunk.suspend(mc)?; - opcodes.push(OpCode::InsertValue); - opcodes.push(OpCode::PopEnv); - opcodes.extend(code); - root.envs.push(env); - } - Consq::PopEnv => { - let _ = root.envs.pop().unwrap(); - } - Consq::Call => { - let arg = root.stack.pop(); - let func = root.stack.pop().unwrap_func(); - let env = func.env.enter_arg(arg, mc); - let count = func.count.get(); - func.count.set(count + 1); - if count > 1 { - let compiled = func - .compiled - .borrow_mut(mc) - .get_or_insert_with(|| root.vm.compile_func(func.func)) - .clone(); - let ret = - unsafe { compiled(env.as_ref() as *const VmEnv, mc as *const _) }; - root.stack.push(ret.into())?; - } else { - root.envs.push(env); - opcodes.push(OpCode::PopEnv); - opcodes.extend(&func.func.opcodes); - } - } + 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); } - Result::Ok(()) - })?; - if arena.metrics().allocation_debt() > COLLECTOR_GRANULARITY { - if arena.collection_phase() == CollectionPhase::Sweeping { - arena.collect_debt(); - } else if let Some(marked) = arena.mark_debt() { - marked.start_sweeping(); + 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); + } } } } - arena.mutate_root(|mc, root| { - assert_eq!(root.stack.len(), 1); - let ret = root.stack.pop(); - f(ret, root, mc) - }) + stack.pop().ok() } enum Consq { @@ -139,18 +92,12 @@ enum Consq { NoOp, } -enum CycleAction { - Collect, - NoOp -} - #[inline(always)] fn single_op<'gc, const CAP: usize>( - vm: &'gc VM<'gc>, + vm: &VM<'gc>, opcode: OpCode, stack: &mut Stack, CAP>, - env: &mut Gc<'gc, VmEnv<'gc>>, - mc: &'gc Mutation<'gc>, + env: &mut Rc>, ) -> Result { match opcode { OpCode::Illegal => panic!("illegal opcode"), @@ -161,13 +108,13 @@ fn single_op<'gc, const CAP: usize>( 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), mc)))?, + 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), mc)))?; - stack.tos().as_ref().unwrap_thunk().capture_env(*env, mc); + 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, mc), + OpCode::CaptureEnv => stack.tos().as_ref().unwrap_thunk().capture_env(env.clone()), OpCode::ForceValue => { if !stack.tos().is_thunk() { return Ok(Consq::NoOp); @@ -179,7 +126,11 @@ fn single_op<'gc, const CAP: usize>( } OpCode::InsertValue => { let val = stack.pop(); - stack.tos().as_ref().unwrap_thunk().insert_value(val.clone(), mc); + stack + .tos() + .as_ref() + .unwrap_thunk() + .insert_value(val.clone()); *stack.tos_mut() = val; } OpCode::Jmp { step } => return Ok(Consq::Jmp(step)), @@ -195,11 +146,11 @@ fn single_op<'gc, const CAP: usize>( let _ = stack.push(arg); return Ok(Consq::Call); } - func.call(arg, vm, mc)?; + func.call(arg, vm)?; } OpCode::Func { idx } => { let func = vm.get_func(idx); - stack.push(Value::Func(Rc::new(Func::new(func, *env, mc))))?; + stack.push(Value::Func(Rc::new(Func::new(func, env.clone()))))?; } OpCode::Arg { level } => { stack.push(env.lookup_arg(level).clone())?; @@ -251,12 +202,9 @@ fn single_op<'gc, const CAP: usize>( } OpCode::FinalizeLet => { let mut list = stack.pop().unwrap_list(); - let map = list - .as_ref() - .clone() - .into_inner(); - *env = env.enter_let(map, mc); - Rc::make_mut(&mut list).capture(*env, mc); + 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(); @@ -305,7 +253,11 @@ fn single_op<'gc, const CAP: usize>( )?; } OpCode::LookUpLet { level, idx } => { - stack.push(env.lookup_let(level, idx).clone())?; + 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 => { @@ -317,7 +269,7 @@ fn single_op<'gc, const CAP: usize>( .iter() .map(|(&k, v)| (k, v.clone())) .collect_into(&mut new); - *env = env.enter_with(Gc::new(mc, new), mc); + *env = env.clone().enter_with(new.into()); } OpCode::PopEnv => return Ok(Consq::PopEnv), OpCode::Assert => { @@ -329,18 +281,7 @@ fn single_op<'gc, const CAP: usize>( Ok(Consq::NoOp) } -#[derive(Collect)] -#[collect(no_drop)] -pub struct GcRoot<'gc, const CAP: usize = STACK_SIZE> { - vm: Gc<'gc, VM<'gc>>, - jit: Gc<'gc, ContextWrapper>, - - stack: Stack, CAP>, - envs: Vec>>, -} - -#[derive(Constructor, Collect)] -#[collect(no_drop)] +#[derive(Constructor)] pub struct VM<'gc> { thunks: Box<[OpCodes]>, funcs: Box<[F]>, @@ -351,12 +292,12 @@ pub struct VM<'gc> { } impl<'gc> VM<'gc> { - pub fn get_thunk(&self, idx: usize) -> &OpCodes { - &self.thunks[idx] + pub fn get_thunk(&self, idx: usize) -> &'gc OpCodes { + unsafe { &*(&self.thunks[idx] as *const _) } } - pub fn get_func(&self, idx: usize) -> &F { - &self.funcs[idx] + pub fn get_func(&self, idx: usize) -> &'gc F { + unsafe { &*(&self.funcs[idx] as *const _) } } pub fn get_sym(&self, idx: usize) -> Symbol { @@ -380,7 +321,7 @@ impl<'gc> VM<'gc> { self.consts[idx].clone() } - pub fn compile_func(&'gc self, func: &'gc F) -> JITFunc<'gc> { + pub fn compile_func(&self, func: &'gc F) -> JITFunc<'gc> { self.jit .compile_seq(func.opcodes.iter().copied().rev(), self) .unwrap()