From c3ace28af13f6401d668e0a0c648ce3a1e8cc93f Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Tue, 27 May 2025 21:08:59 +0800 Subject: [PATCH] feat: gc (does compile, but WIP) --- Cargo.lock | 24 +-- Cargo.toml | 3 +- src/bin/eval.rs | 6 +- src/builtins/mod.rs | 34 +++-- src/bytecode.rs | 20 ++- src/compile.rs | 112 +++++++------- src/env.rs | 157 ++++++++++++-------- src/jit/helpers.rs | 70 ++++++--- src/jit/mod.rs | 95 +++++++----- src/jit/test.rs | 20 +-- src/lib.rs | 1 - src/stack.rs | 4 +- src/ty/common.rs | 38 +---- src/ty/internal/attrset.rs | 32 ++-- src/ty/internal/func.rs | 53 ++++--- src/ty/internal/list.rs | 19 +-- src/ty/internal/mod.rs | 292 ++++++++++++++++++------------------- src/ty/internal/primop.rs | 29 ++-- src/vm/mod.rs | 256 ++++++++++++++++++++------------ src/vm/test.rs | 6 +- 20 files changed, 696 insertions(+), 575 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26ea76d..b93370a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,15 +45,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" -[[package]] -name = "bumpalo" -version = "3.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" -dependencies = [ - "allocator-api2", -] - [[package]] name = "cc" version = "1.2.21" @@ -181,18 +172,16 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "gc-arena" version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd70cf88a32937834aae9614ff2569b5d9467fa0c42c5d7762fd94a8de88266" +source = "git+https://github.com/kyren/gc-arena?rev=d651e3b4363d525a2d502c2305bc73e291835c84#d651e3b4363d525a2d502c2305bc73e291835c84" dependencies = [ "gc-arena-derive", - "sptr", + "hashbrown 0.15.3", ] [[package]] name = "gc-arena-derive" version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c612a69f5557a11046b77a7408d2836fe77077f842171cd211c5ef504bd3cddd" +source = "git+https://github.com/kyren/gc-arena?rev=d651e3b4363d525a2d502c2305bc73e291835c84#d651e3b4363d525a2d502c2305bc73e291835c84" dependencies = [ "proc-macro2", "quote", @@ -338,7 +327,6 @@ dependencies = [ name = "nixjit" version = "0.0.0" dependencies = [ - "bumpalo", "derive_more", "ecow", "gc-arena", @@ -511,12 +499,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "sptr" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" - [[package]] name = "static_assertions" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 81ed3f7..75e3e5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,6 @@ ecow = "0.2" regex = "1.11" hashbrown = "0.15" inkwell = { version = "0.6.0", features = ["llvm18-1"] } -bumpalo = { version = "3.17", features = ["allocator-api2"] } -gc-arena = "0.5.3" +gc-arena = { git = "https://github.com/kyren/gc-arena", rev = "d651e3b4363d525a2d502c2305bc73e291835c84", features= ["hashbrown"] } rustyline = { version = "15.0", optional = true } diff --git a/src/bin/eval.rs b/src/bin/eval.rs index bbc8db5..20a8853 100644 --- a/src/bin/eval.rs +++ b/src/bin/eval.rs @@ -1,13 +1,11 @@ use std::process::exit; -use inkwell::context::Context; use itertools::Itertools; use nixjit::compile::compile; use nixjit::error::Error; use nixjit::error::Result; use nixjit::ir::downgrade; -use nixjit::jit::JITContext; use nixjit::vm::run; fn main() -> Result<()> { @@ -27,9 +25,7 @@ fn main() -> Result<()> { let expr = root.tree().expr().unwrap(); let downgraded = downgrade(expr)?; let prog = compile(downgraded); - let ctx = Context::create(); - let jit = JITContext::new(&ctx); - println!("{}", run(prog, jit)?); + println!("{}", run(prog)?); Ok(()) } diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index 5ea9137..fbcba68 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -2,25 +2,24 @@ use gc_arena::{Gc, Mutation}; use hashbrown::HashMap; use crate::env::VmEnv; -use crate::ty::common::Const; use crate::ty::internal::{AttrSet, CoW, PrimOp, Value}; use crate::vm::VM; -pub fn env<'gc>(vm: &VM<'gc>, mc: &Mutation<'gc>) -> Gc<'gc, VmEnv<'gc>> { - let primops: [PrimOp<'gc>; 7] = [ - PrimOp::new("add", 2, |args, _, _| { +pub fn env<'gc>(vm: &VM, mc: &Mutation<'gc>) -> Gc<'gc, VmEnv<'gc>> { + let primops = [ + PrimOp::new("add", 2, |args, _, mc| { let Ok([mut first, second]): Result<[Value; 2], _> = args.try_into() else { unreachable!() }; - first.add(second); + first.add(second, mc); first.ok() }), - PrimOp::new("sub", 2, |args, _, _| { + PrimOp::new("sub", 2, |args, _, mc| { let Ok([mut first, mut second]): Result<[Value; 2], _> = args.try_into() else { unreachable!() }; second.neg(); - first.add(second); + first.add(second, mc); first.ok() }), PrimOp::new("mul", 2, |args, _, _| { @@ -44,7 +43,7 @@ pub fn env<'gc>(vm: &VM<'gc>, mc: &Mutation<'gc>) -> Gc<'gc, VmEnv<'gc>> { first.lt(second); first.ok() }), - PrimOp::new("seq", 2, |args, vm, mc| { + /* PrimOp::new("seq", 2, |args, vm, mc| { let Ok([mut first, second]): Result<[_; 2], _> = args.try_into() else { unreachable!() }; @@ -52,17 +51,17 @@ pub fn env<'gc>(vm: &VM<'gc>, mc: &Mutation<'gc>) -> Gc<'gc, VmEnv<'gc>> { second.ok() }), PrimOp::new("deepSeq", 2, |args, vm, mc| { - let [mut first, second] = *args.into_boxed_slice() else { + let Ok([mut first, second]): Result<[_; 2], _> = args.try_into() else { unreachable!() }; first.force_deep(vm, mc).unwrap(); second.ok() - }), + }), */ ]; let mut env_map = HashMap::new(); - env_map.insert(vm.new_sym("true"), Value::Const(Const::Bool(true))); - env_map.insert(vm.new_sym("false"), Value::Const(Const::Bool(false))); + env_map.insert(vm.new_sym("true"), Value::Bool(true)); + env_map.insert(vm.new_sym("false"), Value::Bool(false)); let mut map = HashMap::new(); for primop in primops { @@ -74,10 +73,13 @@ pub fn env<'gc>(vm: &VM<'gc>, mc: &Mutation<'gc>) -> Gc<'gc, VmEnv<'gc>> { map.insert(vm.new_sym(primop.name), Value::PrimOp(primop)); } let sym = vm.new_sym("builtins"); - let attrs = CoW::new_cyclic(|this| { - map.insert(sym, Value::AttrSet(this)); - AttrSet::from_inner(map) - }, mc); + let attrs = CoW::new_cyclic( + |this| { + map.insert(sym, Value::AttrSet(this)); + AttrSet::from_inner(map) + }, + mc, + ); let builtins = Value::AttrSet(attrs); env_map.insert(sym, builtins); diff --git a/src/bytecode.rs b/src/bytecode.rs index 5524bf5..41e7f4f 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -1,6 +1,6 @@ -use hashbrown::HashMap; use ecow::EcoString; use gc_arena::Collect; +use hashbrown::HashMap; use crate::ty::common::Const; use crate::ty::internal::Param; @@ -18,10 +18,14 @@ pub enum OpCode { LookUp { sym: usize }, /// load a thunk lazily onto stack LoadThunk { idx: usize }, + /// load a thunk value onto stack + LoadValue { idx: usize }, /// let TOS capture current environment CaptureEnv, /// force TOS to value ForceValue, + /// TODO: + InsertValue, /// [ .. func arg ] consume 2 elements, call `func` with arg Call, @@ -73,18 +77,19 @@ pub enum OpCode { SelectDynamicOrDefault, /// enter the let environment of the attribute set at TOS EnterLetEnv, - /// exit current let envrironment - LeaveLetEnv, /// enter the with environment of the attribute set at TOS EnterWithEnv, - /// exit current with envrironment - LeaveWithEnv, + /// exit current envrironment + LeaveEnv, + /// TODO: + PopEnv, /// illegal operation, used as termporary placeholder Illegal, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Collect)] +#[collect(no_drop)] pub enum BinOp { Add, Sub, @@ -98,7 +103,8 @@ pub enum BinOp { Upd, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Collect)] +#[collect(no_drop)] pub enum UnOp { Neg, Not, diff --git a/src/compile.rs b/src/compile.rs index 2609ad0..b93d301 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -35,7 +35,7 @@ impl Compiler { } fn compile(mut self, ir: ir::Ir) -> OpCodes { - ir.compile(&mut self); + ir.compile_force(&mut self); self.opcodes() } @@ -60,8 +60,12 @@ impl Compiler { } } -pub trait Compile { +pub trait Compile: Sized { fn compile(self, comp: &mut Compiler); + fn compile_force(self, comp: &mut Compiler) { + self.compile(comp); + comp.push(OpCode::ForceValue); + } } pub trait CompileWithLength { @@ -81,6 +85,9 @@ impl Compile for ir::Const { fn compile(self, comp: &mut Compiler) { comp.push(OpCode::Const { idx: self.idx }); } + fn compile_force(self, comp: &mut Compiler) { + self.compile(comp); + } } impl Compile for ir::Var { @@ -93,6 +100,9 @@ impl Compile for ir::Thunk { fn compile(self, comp: &mut Compiler) { comp.push(OpCode::LoadThunk { idx: self.idx }); } + fn compile_force(self, comp: &mut Compiler) { + comp.push(OpCode::LoadValue { idx: self.idx }); + } } impl Compile for ir::Attrs { @@ -110,7 +120,7 @@ impl Compile for ir::Attrs { } for dynamic in self.dyns { let thunk = dynamic.1.is_thunk(); - dynamic.0.compile(comp); + dynamic.0.compile_force(comp); dynamic.1.compile(comp); if thunk && !self.rec { comp.push(OpCode::CaptureEnv); @@ -138,11 +148,11 @@ impl Compile for ir::UnOp { use ir::UnOpKind::*; match self.kind { Neg => { - self.rhs.compile(comp); + self.rhs.compile_force(comp); comp.push(OpCode::UnOp { op: UnOp::Neg }); } Not => { - self.rhs.compile(comp); + self.rhs.compile_force(comp); comp.push(OpCode::UnOp { op: UnOp::Not }); } } @@ -154,94 +164,94 @@ impl Compile for ir::BinOp { use ir::BinOpKind::*; match self.kind { Add => { - self.lhs.compile(comp); - self.rhs.compile(comp); + self.lhs.compile_force(comp); + self.rhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::Add }); } Mul => { - self.lhs.compile(comp); - self.rhs.compile(comp); + self.lhs.compile_force(comp); + self.rhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::Mul }); } Div => { - self.lhs.compile(comp); - self.rhs.compile(comp); + self.lhs.compile_force(comp); + self.rhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::Div }); } And => { - self.lhs.compile(comp); - self.rhs.compile(comp); + self.lhs.compile_force(comp); + self.rhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::And }); } Or => { - self.lhs.compile(comp); - self.rhs.compile(comp); + self.lhs.compile_force(comp); + self.rhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::Or }); } Eq => { - self.lhs.compile(comp); - self.rhs.compile(comp); + self.lhs.compile_force(comp); + self.rhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::Eq }); } Lt => { - self.lhs.compile(comp); - self.rhs.compile(comp); + self.lhs.compile_force(comp); + self.rhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::Lt }); } Con => { - self.lhs.compile(comp); - self.rhs.compile(comp); + self.lhs.compile_force(comp); + self.rhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::Con }); } Upd => { - self.lhs.compile(comp); - self.rhs.compile(comp); + self.lhs.compile_force(comp); + self.rhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::Upd }); } Sub => { - self.lhs.compile(comp); - self.rhs.compile(comp); + self.lhs.compile_force(comp); + self.rhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::Sub }); } Impl => { - self.lhs.compile(comp); + self.lhs.compile_force(comp); comp.push(OpCode::UnOp { op: UnOp::Not }); - self.rhs.compile(comp); + self.rhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::Or }); } Neq => { - self.lhs.compile(comp); - self.rhs.compile(comp); + self.lhs.compile_force(comp); + self.rhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::Eq }); comp.push(OpCode::UnOp { op: UnOp::Not }); } Gt => { - self.rhs.compile(comp); - self.lhs.compile(comp); + self.rhs.compile_force(comp); + self.lhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::Lt }); } Leq => { - self.rhs.compile(comp); - self.lhs.compile(comp); + self.rhs.compile_force(comp); + self.lhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::Lt }); comp.push(OpCode::UnOp { op: UnOp::Not }); } Geq => { - self.lhs.compile(comp); - self.rhs.compile(comp); + self.lhs.compile_force(comp); + self.rhs.compile_force(comp); comp.push(OpCode::BinOp { op: BinOp::Lt }); comp.push(OpCode::UnOp { op: UnOp::Not }); } PipeL => { - self.lhs.compile(comp); - self.rhs.compile(comp); + self.lhs.compile_force(comp); + self.rhs.compile_force(comp); comp.push(OpCode::Call); } PipeR => { - self.rhs.compile(comp); - self.lhs.compile(comp); + self.rhs.compile_force(comp); + self.lhs.compile_force(comp); comp.push(OpCode::Call); } } @@ -252,13 +262,14 @@ impl Compile for ir::HasAttr { fn compile(self, comp: &mut Compiler) { self.lhs.compile(comp); for attr in self.rhs { + comp.push(OpCode::ForceValue); match attr { ir::Attr::Str(sym) => { comp.push(OpCode::AttrSet { cap: 0 }); comp.push(OpCode::SelectOrDefault { sym }) } ir::Attr::Dynamic(dynamic) => { - dynamic.compile(comp); + dynamic.compile_force(comp); comp.push(OpCode::AttrSet { cap: 0 }); comp.push(OpCode::SelectDynamicOrDefault); } @@ -283,6 +294,7 @@ impl Compile for ir::Select { fn compile(self, comp: &mut Compiler) { self.expr.compile(comp); for attr in self.attrpath { + comp.push(OpCode::ForceValue); match attr { ir::Attr::Str(sym) => { comp.push(OpCode::AttrSet { cap: 0 }); @@ -305,11 +317,7 @@ impl Compile for ir::Select { let last = comp.pop().unwrap(); let _ = comp.pop(); default.compile(comp); - match last { - OpCode::SelectOrDefault { sym } => comp.push(OpCode::SelectOrDefault { sym }), - OpCode::SelectDynamicOrDefault => comp.push(OpCode::SelectDynamicOrDefault), - _ => unreachable!(), - } + comp.push(last); } None => { let last = comp.pop().unwrap(); @@ -322,14 +330,18 @@ impl Compile for ir::Select { } } } + fn compile_force(self, comp: &mut Compiler) { + self.compile(comp); + comp.push(OpCode::ForceValue); + } } impl Compile for ir::ConcatStrings { fn compile(self, comp: &mut Compiler) { let mut iter = self.parts.into_iter(); - iter.next().unwrap().compile(comp); + iter.next().unwrap().compile_force(comp); for item in iter { - item.compile(comp); + item.compile_force(comp); comp.push(OpCode::ConcatString); } } @@ -366,7 +378,7 @@ impl Compile for ir::Let { self.attrs.compile(comp); comp.push(OpCode::EnterLetEnv); self.expr.compile(comp); - comp.push(OpCode::LeaveLetEnv); + comp.push(OpCode::LeaveEnv); } } @@ -375,7 +387,7 @@ impl Compile for ir::With { self.namespace.compile(comp); comp.push(OpCode::EnterWithEnv); self.expr.compile(comp); - comp.push(OpCode::LeaveWithEnv); + comp.push(OpCode::LeaveEnv); } } @@ -395,7 +407,7 @@ impl Compile for ir::LoadFunc { impl Compile for ir::Call { fn compile(self, comp: &mut Compiler) { - self.func.compile(comp); + self.func.compile_force(comp); self.args.into_iter().for_each(|arg| { arg.compile(comp); comp.push(OpCode::Call); diff --git a/src/env.rs b/src/env.rs index 6253801..c5a8f62 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1,13 +1,13 @@ use std::hash::Hash; use gc_arena::{Collect, Gc, Mutation}; +use hashbrown::HashMap; use crate::ty::internal::Value; -use crate::ty::common::Map; #[derive(Collect)] #[collect(no_drop)] -pub struct Env<'gc, K: Hash + Eq + Collect, V: Collect> { +pub struct Env<'gc, K: Hash + Eq + Collect<'gc>, V: Collect<'gc>> { let_: Gc<'gc, LetEnv<'gc, K, V>>, with: Gc<'gc, With<'gc, K, V>>, last: Option>>, @@ -15,7 +15,7 @@ pub struct Env<'gc, K: Hash + Eq + Collect, V: Collect> { #[derive(Collect)] #[collect(no_drop)] -pub struct LetEnv<'gc, K: Hash + Eq + Collect, V: Collect> { +pub struct LetEnv<'gc, K: Hash + Eq + Collect<'gc>, V: Collect<'gc>> { map: LetNode<'gc, K, V>, last: Option>>, } @@ -24,17 +24,17 @@ pub type VmEnv<'gc> = Env<'gc, usize, Value<'gc>>; #[derive(Default, Clone, Collect)] #[collect(no_drop)] -pub struct With<'gc, K: Hash + Eq + Collect, V: Collect> { - map: Option>>, +pub struct With<'gc, K: Hash + Eq + Collect<'gc>, V: Collect<'gc>> { + map: Option>>, last: Option>>, } #[derive(Collect)] #[collect(no_drop)] -enum LetNode<'gc, K: Hash + Eq + Collect, V: Collect> { - Let(Gc<'gc, Map>), +enum LetNode<'gc, K: Hash + Eq + Collect<'gc>, V: Collect<'gc>> { + Let(Gc<'gc, HashMap>), SingleArg(K, V), - MultiArg(Gc<'gc, Map>), + MultiArg(Gc<'gc, HashMap>), } #[derive(Clone, Copy)] @@ -44,19 +44,25 @@ pub enum Type { With, } -impl<'gc, K: Hash + Eq + Clone + Collect, V: Clone + Collect> Env<'gc, K, V> { - pub fn new(map: Gc<'gc, Map>, mc: &Mutation<'gc>) -> Gc<'gc, Self> { - Gc::new(mc, Self { - let_: LetEnv::new(map, mc), - with: Gc::new(mc, With { - map: None, +impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> Env<'gc, K, V> { + pub fn new(map: Gc<'gc, HashMap>, mc: &Mutation<'gc>) -> Gc<'gc, Self> { + Gc::new( + mc, + Self { + let_: LetEnv::new(map, mc), + with: Gc::new( + mc, + With { + map: None, + last: None, + }, + ), last: None, - }), - last: None, - }) + }, + ) } - pub fn lookup(&'gc self, symbol: &K) -> Option<&'gc V> { + pub fn lookup(&self, symbol: &K) -> Option<&V> { if let Some(val) = self.let_.lookup(symbol) { return Some(val); } @@ -64,27 +70,44 @@ impl<'gc, K: Hash + Eq + Clone + Collect, V: Clone + Collect> Env<'gc, K, V> { } pub fn enter_arg(self: Gc<'gc, Self>, ident: K, val: V, mc: &Mutation<'gc>) -> Gc<'gc, Self> { - Gc::new(mc, Env { - let_: self.let_.enter_arg(ident, val, mc), - with: self.with, - last: Some(self), - }) + Gc::new( + mc, + Env { + let_: self.let_.enter_arg(ident, val, mc), + with: self.with, + last: Some(self), + }, + ) } - pub fn enter_let(self: Gc<'gc, Self>, map: Gc<'gc, Map>, mc: &Mutation<'gc>) -> Gc<'gc, Self> { - Gc::new(mc, Self { - let_: self.let_.enter_let(map, mc), - with: self.with, - last: Some(self), - }) + pub fn enter_let( + self: Gc<'gc, Self>, + map: Gc<'gc, HashMap>, + mc: &Mutation<'gc>, + ) -> Gc<'gc, Self> { + Gc::new( + mc, + Self { + let_: self.let_.enter_let(map, mc), + with: self.with, + last: Some(self), + }, + ) } - pub fn enter_with(self: Gc<'gc, Self>, map: Gc<'gc, Map>, mc: &Mutation<'gc>) -> Gc<'gc, Self> { - Gc::new(mc, Env { - let_: self.let_, - with: self.with.enter(map, mc), - last: Some(self), - }) + 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), + last: Some(self), + }, + ) } pub fn leave(&self) -> Gc<'gc, Self> { @@ -92,12 +115,15 @@ impl<'gc, K: Hash + Eq + Clone + Collect, V: Clone + Collect> Env<'gc, K, V> { } } -impl<'gc, K: Hash + Eq + Clone + Collect, V: Clone + Collect> LetEnv<'gc, K, V> { - pub fn new(map: Gc<'gc, Map>, mc: &Mutation<'gc>) -> Gc<'gc, Self> { - Gc::new(mc,Self { - map: LetNode::Let(map), - last: None, - }) +impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> LetEnv<'gc, K, V> { + pub fn new(map: Gc<'gc, HashMap>, mc: &Mutation<'gc>) -> Gc<'gc, Self> { + Gc::new( + mc, + Self { + map: LetNode::Let(map), + last: None, + }, + ) } pub fn lookup(&self, symbol: &K) -> Option<&V> { @@ -118,32 +144,49 @@ impl<'gc, K: Hash + Eq + Clone + Collect, V: Clone + Collect> LetEnv<'gc, K, V> } pub fn enter_arg(self: Gc<'gc, Self>, ident: K, val: V, mc: &Mutation<'gc>) -> Gc<'gc, Self> { - Gc::new(mc, Self { - map: LetNode::SingleArg(ident, val), - last: Some(self), - }) + Gc::new( + mc, + Self { + map: LetNode::SingleArg(ident, val), + last: Some(self), + }, + ) } - pub fn enter_let(self: Gc<'gc, Self>, map: Gc<'gc, Map>, mc: &Mutation<'gc>) -> Gc<'gc, Self> { - Gc::new(mc, Self { - map: LetNode::Let(map), - last: Some(self), - }) + pub fn enter_let( + self: Gc<'gc, Self>, + map: Gc<'gc, HashMap>, + mc: &Mutation<'gc>, + ) -> Gc<'gc, Self> { + Gc::new( + mc, + Self { + map: LetNode::Let(map), + last: Some(self), + }, + ) } } -impl<'gc, K: Hash + Eq + Clone + Collect, V: Clone + Collect> With<'gc, K, V> { - pub fn lookup(&'gc self, symbol: &K) -> Option<&'gc V> { +impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> With<'gc, K, V> { + pub fn lookup(&self, symbol: &K) -> Option<&V> { if let Some(val) = self.map.as_ref()?.get(symbol) { return Some(val); } self.last.as_ref().and_then(|env| env.lookup(symbol)) } - pub fn enter(self: Gc<'gc, Self>, map: Gc<'gc, Map>, mc: &Mutation<'gc>) -> Gc<'gc, Self> { - Gc::new(mc, Self { - map: Some(map), - last: Some(self), - }) + 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), + }, + ) } } diff --git a/src/jit/helpers.rs b/src/jit/helpers.rs index e593964..aef6a06 100644 --- a/src/jit/helpers.rs +++ b/src/jit/helpers.rs @@ -6,6 +6,7 @@ use inkwell::module::Module; use inkwell::types::{FloatType, FunctionType, IntType, PointerType, StructType}; use inkwell::values::{BasicValueEnum, FunctionValue}; +use crate::bytecode::OpCodes; use crate::env::VmEnv; use crate::jit::JITValueData; use crate::ty::internal::{Thunk, Value}; @@ -22,6 +23,8 @@ pub struct Helpers<'ctx> { pub value_type: StructType<'ctx>, pub func_type: FunctionType<'ctx>, + pub new_thunk: FunctionValue<'ctx>, + pub debug: FunctionValue<'ctx>, pub capture_env: FunctionValue<'ctx>, pub neg: FunctionValue<'ctx>, @@ -48,6 +51,12 @@ impl<'ctx> Helpers<'ctx> { 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 new_thunk = module.add_function( + "new_thunk", + value_type.fn_type(&[ptr_type.into(), ptr_type.into()], false), + None, + ); let debug = module.add_function( "debug", context.void_type().fn_type(&[value_type.into()], false), @@ -55,9 +64,10 @@ 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(), ptr_type.into()], + false, + ), None, ); let neg = module.add_function( @@ -109,6 +119,7 @@ impl<'ctx> Helpers<'ctx> { None, ); + execution_engine.add_global_mapping(&new_thunk, helper_new_thunk as _); execution_engine.add_global_mapping(&debug, helper_debug as _); execution_engine.add_global_mapping(&capture_env, helper_capture_env as _); execution_engine.add_global_mapping(&neg, helper_neg as _); @@ -130,6 +141,8 @@ impl<'ctx> Helpers<'ctx> { value_type, func_type, + new_thunk, + debug, capture_env, neg, @@ -188,25 +201,20 @@ impl<'ctx> Helpers<'ctx> { ]) .into() } - - pub fn new_thunk(&self, thunk: *const Thunk) -> BasicValueEnum<'ctx> { - self.value_type - .const_named_struct(&[ - self.int_type.const_int(ValueTag::Thunk as _, false).into(), - self.ptr_int_type.const_int(thunk as _, false).into(), - ]) - .into() - } } 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>, + mc: *const Mutation<'gc>, +) { let thunk = unsafe { (thunk.data.ptr as *const Thunk).as_ref().unwrap() }; let env = unsafe { Gc::from_ptr(env) }; - thunk.capture(env, unsafe { mc.as_ref() }.unwrap()); + thunk.capture_env(env, unsafe { mc.as_ref() }.unwrap()); } extern "C" fn helper_neg(rhs: JITValue, _env: *const VmEnv) -> JITValue { @@ -309,13 +317,22 @@ extern "C" fn helper_or(lhs: JITValue, rhs: JITValue) -> JITValue { } } -extern "C" fn helper_call(func: JITValue, arg: JITValue, vm: *const VM, mc: *const Mutation) -> JITValue { +extern "C" fn helper_call( + func: JITValue, + arg: JITValue, + vm: *const VM, + mc: *const Mutation, +) -> JITValue { use ValueTag::*; match func.tag { Function => { let mut func: Value = func.into(); - func.call(arg.into(), unsafe { vm.as_ref() }.unwrap(), unsafe { mc.as_ref() }.unwrap()) - .unwrap(); + func.call( + arg.into(), + unsafe { vm.as_ref() }.unwrap(), + unsafe { mc.as_ref() }.unwrap(), + ) + .unwrap(); func.into() } _ => todo!(), @@ -329,7 +346,20 @@ extern "C" fn helper_lookup(sym: usize, env: *const VmEnv) -> JITValue { } extern "C" fn helper_force(thunk: JITValue, vm: *const VM, mc: *const Mutation) -> JITValue { - let mut val = Value::from(thunk); - val.force(unsafe { vm.as_ref() }.unwrap(), unsafe { mc.as_ref() }.unwrap()).unwrap(); - val.into() + todo!() + /* let mut val = Value::from(thunk); + val.force( + unsafe { vm.as_ref() }.unwrap(), + unsafe { mc.as_ref() }.unwrap(), + ) + .unwrap(); + val.into() */ +} + +extern "C" fn helper_new_thunk(opcodes: *const OpCodes, mc: *const Mutation) -> JITValue { + Value::Thunk(Thunk::new( + unsafe { opcodes.as_ref() }.unwrap(), + unsafe { mc.as_ref() }.unwrap(), + )) + .into() } diff --git a/src/jit/mod.rs b/src/jit/mod.rs index 2365628..c6e609c 100644 --- a/src/jit/mod.rs +++ b/src/jit/mod.rs @@ -1,6 +1,6 @@ use std::ops::Deref; -use gc_arena::{Collect, Gc}; +use gc_arena::{Collect, Gc, Mutation}; use inkwell::OptimizationLevel; use inkwell::builder::Builder; use inkwell::context::Context; @@ -60,10 +60,12 @@ impl<'gc> From for Value<'gc> { fn from(value: JITValue) -> Self { use ValueTag::*; match value.tag { - Int => Value::Const(Const::Int(unsafe { value.data.int })), - Null => Value::Const(Const::Null), + Int => Value::Int(unsafe { value.data.int }), + Null => Value::Null, Function => Value::Func(unsafe { Gc::from_ptr(value.data.ptr as *const _) }), - Thunk => Value::Thunk(self::Thunk { thunk: unsafe { Gc::from_ptr(value.data.ptr as *const _) } }), + Thunk => Value::Thunk(self::Thunk { + thunk: unsafe { Gc::from_ptr(value.data.ptr as *const _) }, + }), _ => todo!("not implemented for {:?}", value.tag), } } @@ -72,7 +74,7 @@ impl<'gc> From for Value<'gc> { impl From<&Value<'_>> for JITValue { fn from(value: &Value<'_>) -> Self { match *value { - Value::Const(Const::Int(int)) => JITValue { + Value::Int(int) => JITValue { tag: ValueTag::Int, data: JITValueData { int }, }, @@ -96,7 +98,7 @@ impl From<&Value<'_>> for JITValue { impl From> for JITValue { fn from(value: Value) -> Self { match value { - Value::Const(Const::Int(int)) => JITValue { + Value::Int(int) => JITValue { tag: ValueTag::Int, data: JITValueData { int }, }, @@ -117,18 +119,33 @@ impl From> for JITValue { } } -#[derive(Collect)] -#[collect(require_static)] -pub struct JITFunc<'gc>(JitFunction<'gc, unsafe extern "C" fn(*const VM<'gc>, *const VmEnv<'gc>) -> JITValue>); +pub struct JITFunc<'gc>( + JitFunction<'gc, unsafe extern "C" fn(*const VmEnv<'gc>, *const Mutation<'gc>) -> JITValue>, +); -impl<'gc> From, *const VmEnv<'gc>) -> JITValue>> for JITFunc<'gc> { - fn from(value: JitFunction<'gc, unsafe extern "C" fn(*const VM<'gc>, *const VmEnv<'gc>) -> JITValue>) -> Self { +unsafe impl<'gc> Collect<'gc> for JITFunc<'gc> { + fn trace>(&self, _: &mut T) {} + const NEEDS_TRACE: bool = false; +} + +impl<'gc> + From< + JitFunction<'gc, unsafe extern "C" fn(*const VmEnv<'gc>, *const Mutation<'gc>) -> JITValue>, + > for JITFunc<'gc> +{ + fn from( + value: JitFunction< + 'gc, + unsafe extern "C" fn(*const VmEnv<'gc>, *const Mutation<'gc>) -> JITValue, + >, + ) -> Self { Self(value) } } impl<'gc> Deref for JITFunc<'gc> { - type Target = JitFunction<'gc, unsafe extern "C" fn(*const VM<'gc>, *const VmEnv<'gc>) -> JITValue>; + type Target = + JitFunction<'gc, unsafe extern "C" fn(*const VmEnv<'gc>, *const Mutation<'gc>) -> JITValue>; fn deref(&self) -> &Self::Target { &self.0 } @@ -143,15 +160,10 @@ pub struct JITContext<'gc> { helpers: Helpers<'gc>, } -unsafe impl<'gc> Collect for JITContext<'gc> { - fn trace(&self, _cc: &gc_arena::Collection) {} - fn needs_trace() -> bool - where - Self: Sized, { - false - } +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 @@ -184,20 +196,25 @@ impl<'gc> JITContext<'gc> { .unwrap() } - pub fn compile_function( - &self, - func: &'gc Func, - vm: &'gc VM<'gc>, - ) -> Result> { + pub fn compile_function(&self, func: &'gc Func, vm: &'gc VM<'gc>) -> Result> { let mut stack = Stack::<_, STACK_SIZE>::new(); let mut iter = func.opcodes.iter().copied(); let func_ = self .module .add_function("nixjit_function", self.helpers.func_type, None); - let env = func_.get_nth_param(1).unwrap().into_pointer_value(); + 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); - self.build_expr(&mut iter, vm, env, &mut stack, func_, func.opcodes.len())?; + self.build_expr( + &mut iter, + vm, + env, + mc, + &mut stack, + func_, + func.opcodes.len(), + )?; assert_eq!(stack.len(), 1); let value = stack.pop(); @@ -221,13 +238,14 @@ impl<'gc> JITContext<'gc> { iter: &mut impl Iterator, vm: &'gc 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, stack)?; + let br = self.single_op(opcode, vm, env, mc, stack)?; length -= 1; if br > 0 { let consq = self.context.append_basic_block(func, "consq"); @@ -259,13 +277,13 @@ impl<'gc> JITContext<'gc> { length -= br; self.builder.position_at_end(consq); - let br = self.build_expr(iter, vm, env, stack, func, br)?; + let br = self.build_expr(iter, vm, env, mc, 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, stack, func, br)?; + self.build_expr(iter, vm, env, mc, stack, func, br)?; self.builder.build_store(result, stack.pop())?; self.builder.build_unconditional_branch(cont)?; @@ -278,7 +296,7 @@ impl<'gc> JITContext<'gc> { } } if length > 0 { - self.single_op(iter.next().unwrap(), vm, env, stack) + self.single_op(iter.next().unwrap(), vm, env, mc, stack) } else { Ok(0) } @@ -290,6 +308,7 @@ impl<'gc> JITContext<'gc> { opcode: OpCode, vm: &'gc VM<'_>, env: PointerValue<'gc>, + mc: PointerValue<'gc>, stack: &mut Stack, CAP>, ) -> Result { match opcode { @@ -304,8 +323,18 @@ impl<'gc> JITContext<'gc> { } } OpCode::LoadThunk { idx } => stack.push( - self.helpers - .new_thunk(Thunk::new(vm.get_thunk(idx))), + self.builder + .build_direct_call( + self.helpers.new_thunk, + &[ + self.new_ptr(vm.get_thunk(idx) as *const _).into(), + mc.into(), + ], + "call_capture_env", + )? + .try_as_basic_value() + .unwrap_left() + .into(), )?, OpCode::CaptureEnv => { let thunk = *stack.tos(); diff --git a/src/jit/test.rs b/src/jit/test.rs index 1f6c2b0..a238540 100644 --- a/src/jit/test.rs +++ b/src/jit/test.rs @@ -2,7 +2,6 @@ extern crate test; -use bumpalo::Bump; use gc_arena::Arena; use hashbrown::{HashMap, HashSet}; @@ -17,29 +16,14 @@ use crate::ir::downgrade; use crate::jit::JITContext; use crate::ty::common::Const; use crate::ty::public::*; -use crate::vm::VM; +use crate::vm::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); - let ctx = Context::create(); - let jit = JITContext::new(&ctx); - let vm = VM::new( - prog.thunks, - prog.funcs, - prog.symbols.into(), - prog.symmap.into(), - prog.consts, - jit, - ); - let env = env(&vm); - let value = vm - .eval(prog.top_level.into_iter(), vm.bump.alloc(env)) - .unwrap() - .to_public(&vm, &mut HashSet::new()); - assert_eq!(value, expected); + assert_eq!(run(prog).unwrap(), expected); } macro_rules! map { diff --git a/src/lib.rs b/src/lib.rs index a5e96b4..f740dae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ #![cfg_attr(test, feature(test))] #![allow(dead_code)] - #![feature(iter_collect_into)] #![feature(arbitrary_self_types)] diff --git a/src/stack.rs b/src/stack.rs index e8489b2..ef391d2 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -20,8 +20,8 @@ pub struct Stack { top: usize, } -unsafe impl Collect for Stack { - fn trace(&self, cc: &gc_arena::Collection) { +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); } diff --git a/src/ty/common.rs b/src/ty/common.rs index 4abe234..6042c79 100644 --- a/src/ty/common.rs +++ b/src/ty/common.rs @@ -1,48 +1,22 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; use std::hash::Hash; -use std::ops::{Deref, DerefMut}; use derive_more::{Constructor, IsVariant, Unwrap}; use ecow::EcoString; -use hashbrown::HashMap; use gc_arena::Collect; -pub struct Map(HashMap); - -impl Deref for Map { - type Target = HashMap; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Map { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -unsafe impl Collect for Map { - fn trace(&self, cc: &gc_arena::Collection) { - for (k, v) in self.iter() { - k.trace(cc); - v.trace(cc); - } - } -} - -impl From> for Map { - fn from(value: HashMap) -> Self { - Self(value) - } -} - #[derive(Clone, Debug, PartialEq, Constructor, Hash, Collect)] #[collect(no_drop)] pub struct Catchable { msg: String, } +impl From for Catchable { + fn from(value: String) -> Self { + Catchable { msg: value } + } +} + impl Display for Catchable { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { write!(f, "", self.msg) diff --git a/src/ty/internal/attrset.rs b/src/ty/internal/attrset.rs index 079b084..57246cb 100644 --- a/src/ty/internal/attrset.rs +++ b/src/ty/internal/attrset.rs @@ -1,9 +1,10 @@ +use std::ops::Deref; use std::rc::Rc; -use hashbrown::{HashMap, HashSet}; use derive_more::Constructor; -use itertools::Itertools; use gc_arena::{Collect, Gc, Mutation}; +use hashbrown::{HashMap, HashSet}; +use itertools::Itertools; use crate::env::VmEnv; use crate::error::Result; @@ -13,25 +14,25 @@ use super::super::public as p; use super::Value; #[repr(transparent)] -#[derive(Constructor, Clone, PartialEq)] +#[derive(Constructor, Clone, PartialEq, Collect)] +#[collect(no_drop)] pub struct AttrSet<'gc> { data: HashMap>, } -unsafe impl<'jit: 'vm, 'vm, 'gc> Collect for AttrSet<'gc> { - fn trace(&self, cc: &gc_arena::Collection) { - for (_, v) in self.data.iter() { - v.trace(cc); - } - } -} - impl<'gc> From>> for AttrSet<'gc> { fn from(data: HashMap>) -> Self { Self { data } } } +impl<'gc> Deref for AttrSet<'gc> { + type Target = HashMap>; + fn deref(&self) -> &Self::Target { + &self.data + } +} + impl<'jit: 'vm, 'vm, 'gc> AttrSet<'gc> { pub fn with_capacity(cap: usize) -> Self { AttrSet { @@ -61,7 +62,7 @@ impl<'jit: 'vm, 'vm, 'gc> AttrSet<'gc> { pub fn capture(&mut self, env: Gc<'gc, VmEnv<'gc>>, mc: &Mutation<'gc>) { self.data.iter().for_each(|(_, v)| { if let Value::Thunk(ref thunk) = v.clone() { - thunk.capture(env, mc); + thunk.capture_env(env, mc); } }) } @@ -85,12 +86,13 @@ impl<'jit: 'vm, 'vm, 'gc> AttrSet<'gc> { } pub fn force_deep(&mut self, vm: &VM, mc: &Mutation<'gc>) -> Result<()> { - let mut map: Vec<_> = self.data.iter().map(|(k, v)| (*k, v.clone())).collect(); + todo!() + /* let mut map: Vec<_> = self.data.iter().map(|(k, v)| (*k, v.clone())).collect(); for (_, v) in map.iter_mut() { v.force_deep(vm, mc)?; } self.data = map.into_iter().collect(); - Ok(()) + Ok(()) */ } pub fn eq_impl(&self, other: &AttrSet<'gc>) -> bool { @@ -102,7 +104,7 @@ impl<'jit: 'vm, 'vm, 'gc> AttrSet<'gc> { .all(|((_, v1), (_, v2))| v1.eq_impl(v2)) } - pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet>) -> p::Value { + pub fn to_public(&self, vm: &VM<'gc>, 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 d1e5562..0a60b3d 100644 --- a/src/ty/internal/func.rs +++ b/src/ty/internal/func.rs @@ -1,11 +1,9 @@ use std::cell::Cell; -use derive_more::Constructor; -use gc_arena::lock::GcRefLock; +use gc_arena::lock::{GcRefLock, RefLock}; +use gc_arena::{Arena, Collect, Gc, Mutation, Rootable}; use hashbrown::HashMap; -use inkwell::execution_engine::JitFunction; use itertools::Itertools; -use gc_arena::{Collect, Gc, Mutation}; use crate::bytecode::Func as BFunc; use crate::env::VmEnv; @@ -13,7 +11,7 @@ use crate::error::Result; use crate::ir; use crate::jit::JITFunc; use crate::ty::internal::{Thunk, Value}; -use crate::vm::VM; +use crate::vm::{GcRoot, VM, eval}; #[derive(Debug, Clone, Collect)] #[collect(no_drop)] @@ -46,7 +44,7 @@ impl From for Param { } } -#[derive(Clone, Constructor)] +#[derive(Clone)] pub struct Func<'gc> { pub func: &'gc BFunc, pub env: Gc<'gc, VmEnv<'gc>>, @@ -54,17 +52,33 @@ pub struct Func<'gc> { pub count: Cell, } -unsafe impl<'gc> Collect for Func<'gc> { - fn trace(&self, cc: &gc_arena::Collection) { +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<'jit: 'vm, 'vm, 'gc> Func<'gc> { - pub fn call(&self, arg: Value<'gc>, vm: &VM, mc: &Mutation<'gc>) -> Result> { - use Param::*; +impl<'gc> Func<'gc> { + pub fn new(func: &'gc BFunc, env: Gc<'gc, VmEnv<'gc>>, mc: &Mutation<'gc>) -> Self { + Self { + func, + env, + compiled: Gc::new(mc, RefLock::new(None)), + count: Cell::new(0), + } + } + + pub fn call( + &self, + arg: Value<'gc>, + vm: &'gc VM<'gc>, + mc: &Mutation<'gc>, + arena: &Arena Rootable<'a, Root = GcRoot<'a>>>, + ) -> Result> { + todo!() + /* use Param::*; let mut env = self.env; env = match self.func.param.clone() { @@ -75,8 +89,7 @@ impl<'jit: 'vm, 'vm, 'gc> Func<'gc> { alias, } => { let arg = arg.unwrap_attr_set(); - let mut new = - HashMap::with_capacity(formals.len() + alias.iter().len()); + let mut new = HashMap::with_capacity(formals.len() + alias.iter().len()); if !ellipsis && arg .as_inner() @@ -91,7 +104,8 @@ impl<'jit: 'vm, 'vm, 'gc> Func<'gc> { let arg = arg .select(formal) .or_else(|| { - default.map(|idx| Value::Thunk(Thunk::new(vm.get_thunk(idx), mc).into())) + default + .map(|idx| Value::Thunk(Thunk::new(vm.get_thunk(idx), mc).into())) }) .unwrap(); new.insert(formal, arg); @@ -106,15 +120,18 @@ impl<'jit: 'vm, 'vm, 'gc> Func<'gc> { let count = self.count.get(); self.count.replace(count + 1); if count >= 1 { - let compiled = self.compiled.borrow_mut(mc).get_or_insert_with(|| vm.compile_func(self.func)); - let ret = unsafe { compiled.call(vm as *const VM, env.as_ref() as *const VmEnv) }; + let compiled = &mut *self.compiled.borrow_mut(mc); + let compiled = compiled.get_or_insert_with(|| vm.compile_func(self.func)); + let ret = unsafe { compiled.call(env.as_ref() as *const VmEnv, mc as *const _) }; return Ok(ret.into()); } - vm.eval(self.func.opcodes.iter().copied(), env) + eval(self.func.opcodes.iter().copied(), arena, |val, _| { + Ok(val) + }) */ } } -impl PartialEq for Func<'_, '_, '_> { +impl PartialEq for Func<'_> { fn eq(&self, _: &Self) -> bool { false } diff --git a/src/ty/internal/list.rs b/src/ty/internal/list.rs index e660661..902413a 100644 --- a/src/ty/internal/list.rs +++ b/src/ty/internal/list.rs @@ -1,22 +1,22 @@ use hashbrown::HashSet; use derive_more::Constructor; +use gc_arena::{Arena, Collect, Rootable}; use rpds::Vector; -use gc_arena::{Collect, Mutation}; use crate::error::Result; use crate::ty::public as p; -use crate::vm::VM; +use crate::vm::{GcRoot, VM}; use super::Value; #[derive(Constructor, Clone, PartialEq)] pub struct List<'gc> { - data: Vector> + data: Vector>, } -unsafe impl Collect for List<'_> { - fn trace(&self, cc: &gc_arena::Collection) { +unsafe impl<'gc> Collect<'gc> for List<'gc> { + fn trace>(&self, cc: &mut Tr) { for v in self.data.iter() { v.trace(cc); } @@ -40,15 +40,6 @@ impl<'gc> List<'gc> { } } - pub fn force_deep(&mut self, vm: &VM, mc: &Mutation<'gc>) -> Result<()> { - let mut vec: Vec<_> = self.data.iter().cloned().collect(); - for v in vec.iter_mut() { - v.force_deep(vm, mc)?; - } - self.data = vec.into_iter().collect(); - Ok(()) - } - pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet>) -> p::Value { p::Value::List(p::List::new( self.data diff --git a/src/ty/internal/mod.rs b/src/ty/internal/mod.rs index fde105d..a8a150c 100644 --- a/src/ty/internal/mod.rs +++ b/src/ty/internal/mod.rs @@ -1,5 +1,6 @@ use std::cell::Cell; use std::hash::Hash; +use std::marker::PhantomData; use std::mem::MaybeUninit; use std::ops::Deref; @@ -30,22 +31,24 @@ pub use primop::*; #[derive(Collect)] #[collect(unsafe_drop)] -pub struct CoW<'gc, T: Clone + Collect> { - inner: Gc<'gc, CoWInner>, +pub struct CoW<'gc, T: Clone + Collect<'gc>> { + inner: Gc<'gc, CoWInner<'gc, T>>, } -struct CoWInner { +struct CoWInner<'gc, T: Clone + Collect<'gc>> { ref_count: Cell, data: MaybeUninit, + _marker: PhantomData>, } -unsafe impl Collect for CoWInner { - fn trace(&self, cc: &gc_arena::Collection) { +unsafe impl<'gc, T: Clone + Collect<'gc>> Collect<'gc> for CoWInner<'gc, T> { + fn trace>(&self, cc: &mut Tr) { unsafe { self.data.assume_init_ref() }.trace(cc); } } -impl<'gc, T: Clone + Collect> CoW<'gc, T> { +#[allow(mutable_transmutes)] +impl<'gc, T: Clone + Collect<'gc>> CoW<'gc, T> { pub fn new(data: T, mc: &Mutation<'gc>) -> Self { Self { inner: Gc::new(mc, CoWInner::new(data)), @@ -58,6 +61,7 @@ impl<'gc, T: Clone + Collect> CoW<'gc, T> { CoWInner { ref_count: Cell::new(1), data: MaybeUninit::uninit(), + _marker: PhantomData, }, ); let data = datafn(CoW { inner }); @@ -81,37 +85,38 @@ impl<'gc, T: Clone + Collect> CoW<'gc, T> { } } -impl<'gc, T: Clone + Collect> AsRef for CoW<'gc, T> { +impl<'gc, T: Clone + Collect<'gc>> AsRef for CoW<'gc, T> { fn as_ref(&self) -> &T { unsafe { self.inner.data.assume_init_ref() } } } -impl<'gc, T: Clone + Collect> Deref for CoW<'gc, T> { +impl<'gc, T: Clone + Collect<'gc>> Deref for CoW<'gc, T> { type Target = T; fn deref(&self) -> &Self::Target { self.as_ref() } } -impl<'gc, T: Clone + Collect> Clone for CoW<'gc, T> { +impl<'gc, T: Clone + Collect<'gc>> Clone for CoW<'gc, T> { fn clone(&self) -> Self { self.inner.ref_count.set(self.inner.ref_count.get() + 1); Self { inner: self.inner } } } -impl<'gc, T: Clone + Collect> Drop for CoW<'gc, T> { +impl<'gc, T: Clone + Collect<'gc>> Drop for CoW<'gc, T> { fn drop(&mut self) { self.inner.ref_count.set(self.inner.ref_count.get() - 1); } } -impl CoWInner { +impl<'gc, T: Clone + Collect<'gc>> CoWInner<'gc, T> { fn new(data: T) -> Self { Self { ref_count: Cell::new(1), data: MaybeUninit::new(data), + _marker: PhantomData, } } } @@ -119,12 +124,16 @@ impl CoWInner { #[derive(IsVariant, Unwrap, Clone, Collect)] #[collect(no_drop)] pub enum Value<'gc> { - Const(Const), + Int(i64), + Float(f64), + Bool(bool), + String(CoW<'gc, String>), + Null, Thunk(Thunk<'gc>), AttrSet(CoW<'gc, AttrSet<'gc>>), List(CoW<'gc, List<'gc>>), - Catchable(Catchable), - PrimOp(Gc<'gc, PrimOp<'gc>>), + Catchable(Gc<'gc, String>), + PrimOp(Gc<'gc, PrimOp>), PartialPrimOp(CoW<'gc, PartialPrimOp<'gc>>), Func(Gc<'gc, Func<'gc>>), } @@ -134,7 +143,11 @@ impl Hash for Value<'_> { use Value::*; std::mem::discriminant(self).hash(state); match self { - Const(x) => x.hash(state), + Int(x) => x.hash(state), + Float(x) => x.to_bits().hash(state), + Bool(x) => x.hash(state), + Null => (), + String(x) => x.as_ptr().hash(state), Thunk(x) => (x as *const self::Thunk).hash(state), AttrSet(x) => x.as_ptr().hash(state), List(x) => x.as_ptr().hash(state), @@ -150,7 +163,13 @@ impl<'gc> Value<'gc> { fn eq_impl(&self, other: &Self) -> bool { use Value::*; match (self, other) { - (Const(a), Const(b)) => a.eq(b), + (Bool(a), Bool(b)) => a == b, + (Int(a), Int(b)) => a == b, + (Float(a), Float(b)) => a == b, + (Int(a), Float(b)) => *a as f64 == *b, + (Float(a), Int(b)) => *b as f64 == *a, + (String(a), String(b)) => a.as_str().eq(b.as_str()), + (Null, Null) => true, (AttrSet(a), AttrSet(b)) => a.eq_impl(b), (List(a), List(b)) => a.eq(b), _ => false, @@ -162,7 +181,6 @@ impl<'jit: 'vm, 'vm, 'gc> PartialEq for Value<'gc> { fn eq(&self, other: &Self) -> bool { use Value::*; match (self, other) { - (Const(a), Const(b)) => a.eq(b), (AttrSet(a), AttrSet(b)) => a.as_ptr().eq(&b.as_ptr()), (List(a), List(b)) => a.as_ptr().eq(&b.as_ptr()), _ => false, @@ -174,24 +192,32 @@ impl Eq for Value<'_> {} #[derive(IsVariant, Unwrap, Clone)] pub enum ValueAsRef<'v, 'gc> { - Const(&'v Const), + Int(i64), + Float(f64), + Bool(bool), + String(&'v str), + Null, Thunk(&'v Thunk<'gc>), AttrSet(&'v AttrSet<'gc>), List(&'v List<'gc>), - Catchable(&'v Catchable), - PrimOp(&'v PrimOp<'gc>), + Catchable(&'v str), + PrimOp(&'v PrimOp), PartialPrimOp(&'v PartialPrimOp<'gc>), Func(&'v Func<'gc>), } #[derive(IsVariant, Unwrap)] pub enum ValueAsMut<'v, 'gc> { - Const(&'v mut Const), + Int(i64), + Float(f64), + Bool(bool), + String(&'v str), + Null, Thunk(&'v Thunk<'gc>), AttrSet(&'v mut AttrSet<'gc>), List(&'v mut List<'gc>), - Catchable(&'v mut Catchable), - PrimOp(&'v PrimOp<'gc>), + Catchable(&'v str), + PrimOp(&'v PrimOp), PartialPrimOp(&'v mut PartialPrimOp<'gc>), Func(&'v Func<'gc>), } @@ -201,7 +227,11 @@ impl<'gc, 'v> Value<'gc> { use Value::*; use ValueAsRef as R; match self { - Const(x) => R::Const(x), + Int(x) => R::Int(*x), + Float(x) => R::Float(*x), + Bool(x) => R::Bool(*x), + String(x) => R::String(x), + Null => R::Null, Thunk(x) => R::Thunk(x), AttrSet(x) => R::AttrSet(x), List(x) => R::List(x), @@ -216,7 +246,11 @@ impl<'gc, 'v> Value<'gc> { use Value::*; use ValueAsMut as M; match self { - Const(x) => M::Const(x), + Int(x) => M::Int(*x), + Float(x) => M::Float(*x), + Bool(x) => M::Bool(*x), + String(x) => M::String(x), + Null => M::Null, Thunk(x) => M::Thunk(x), AttrSet(x) => M::AttrSet(x.make_mut(mc)), List(x) => M::List(x.make_mut(mc)), @@ -227,8 +261,6 @@ impl<'gc, 'v> Value<'gc> { } } } - -use Value::Const as VmConst; impl<'gc> Value<'gc> { pub fn ok(self) -> Result { Ok(self) @@ -237,11 +269,11 @@ impl<'gc> Value<'gc> { pub fn typename(&self) -> &'static str { use Value::*; match self { - Const(self::Const::Int(_)) => "int", - Const(self::Const::Float(_)) => "float", - Const(self::Const::Bool(_)) => "bool", - Const(self::Const::String(_)) => "string", - Const(self::Const::Null) => "null", + Int(_) => "int", + Float(_) => "float", + Bool(_) => "bool", + String(_) => "string", + Null => "null", Thunk(_) => "thunk", AttrSet(_) => "set", List(_) => "list", @@ -269,7 +301,7 @@ impl<'gc> Value<'gc> { *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), + // Value::Func(func) => func.call(arg, vm, mc), Catchable(_) => return Ok(()), _ => todo!(), }?; @@ -277,18 +309,18 @@ impl<'gc> Value<'gc> { } pub fn not(&mut self) { - use Const::*; + use Value::*; *self = match &*self { - VmConst(Bool(bool)) => VmConst(Bool(!bool)), + Bool(bool) => Bool(!bool), Value::Catchable(_) => return, _ => todo!(), } } pub fn and(&mut self, other: Self) { - use Const::*; + use Value::*; *self = match (&*self, other) { - (VmConst(Bool(a)), VmConst(Bool(b))) => VmConst(Bool(*a && b)), + (Bool(a), Bool(b)) => Bool(*a && b), (Value::Catchable(_), _) => return, (_, x @ Value::Catchable(_)) => x, _ => todo!(), @@ -296,9 +328,9 @@ impl<'gc> Value<'gc> { } pub fn or(&mut self, other: Self) { - use Const::*; + use Value::*; *self = match (&*self, other) { - (VmConst(Bool(a)), VmConst(Bool(b))) => VmConst(Bool(*a || b)), + (Bool(a), Bool(b)) => Bool(*a || b), (Value::Catchable(_), _) => return, (_, x @ Value::Catchable(_)) => x, _ => todo!(), @@ -306,52 +338,52 @@ impl<'gc> Value<'gc> { } pub fn eq(&mut self, other: Self) { - use Const::Bool; + use Value::Bool; *self = match (&*self, other) { (Value::Catchable(_), _) => return, (_, x @ Value::Catchable(_)) => x, - (s, other) => VmConst(Bool(s.eq_impl(&other))), + (s, other) => Bool(s.eq_impl(&other)), }; } pub fn lt(&mut self, other: Self) { - use Const::*; - *self = VmConst(Bool(match (&*self, other) { - (VmConst(Int(a)), VmConst(Int(b))) => *a < b, - (VmConst(Int(a)), VmConst(Float(b))) => (*a as f64) < b, - (VmConst(Float(a)), VmConst(Int(b))) => *a < b as f64, - (VmConst(Float(a)), VmConst(Float(b))) => *a < b, - (VmConst(String(a)), VmConst(String(b))) => a < &b, + use Value::*; + *self = Bool(match (&*self, other) { + (Int(a), Int(b)) => *a < b, + (Int(a), Float(b)) => (*a as f64) < b, + (Float(a), Int(b)) => *a < b as f64, + (Float(a), Float(b)) => *a < b, + (String(a), String(b)) => a.as_str() < b.as_str(), (Value::Catchable(_), _) => return, (_, x @ Value::Catchable(_)) => { *self = x; return; } _ => todo!(), - })) + }) } pub fn neg(&mut self) { - use Const::*; + use Value::*; *self = match &*self { - VmConst(Int(int)) => VmConst(Int(-int)), - VmConst(Float(float)) => VmConst(Float(-float)), + Int(int) => Int(-int), + Float(float) => Float(-float), Value::Catchable(_) => return, _ => todo!(), } } - pub fn add(&mut self, other: Self) { - use Const::*; + pub fn add(&mut self, other: Self, mc: &Mutation<'gc>) { + use Value::*; *self = match (&*self, other) { - (VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a + b)), - (VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(*a as f64 + b)), - (VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a + b as f64)), - (VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a + b)), - (VmConst(String(a)), VmConst(String(b))) => { - let mut string = a.clone(); - string.push_str(b.as_str()); - VmConst(String(string)) + (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(a), String(b)) => { + let mut a = a.clone(); + a.make_mut(mc).push_str(b.as_str()); + String(a) } (Value::Catchable(_), _) => return, (_, x @ Value::Catchable(_)) => x, @@ -360,12 +392,12 @@ impl<'gc> Value<'gc> { } pub fn mul(&mut self, other: Self) { - use Const::*; + use Value::*; *self = match (&*self, other) { - (VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a * b)), - (VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(*a as f64 * b)), - (VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a * b as f64)), - (VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a * b)), + (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), (Value::Catchable(_), _) => return, (_, x @ Value::Catchable(_)) => x, _ => todo!(), @@ -373,16 +405,16 @@ impl<'gc> Value<'gc> { } pub fn div(&mut self, other: Self) -> Result<()> { - use Const::*; + use Value::*; *self = match (&*self, other) { - (_, VmConst(Int(0))) => return Err(Error::EvalError("division by zero".to_string())), - (_, VmConst(Float(0.))) => { + (_, Int(0)) => return Err(Error::EvalError("division by zero".to_string())), + (_, Float(0.)) => { return Err(Error::EvalError("division by zero".to_string())); } - (VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a / b)), - (VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(*a as f64 / b)), - (VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a / b as f64)), - (VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a / b)), + (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), (Value::Catchable(_), _) => return Ok(()), (_, x @ Value::Catchable(_)) => x, _ => todo!(), @@ -390,9 +422,12 @@ impl<'gc> Value<'gc> { Ok(()) } - pub fn concat_string(&mut self, mut other: Self) -> &mut Self { + pub fn concat_string(&mut self, mut other: Self, mc: &Mutation<'gc>) -> &mut Self { match (self.coerce_to_string(), other.coerce_to_string()) { - (VmConst(Const::String(a)), VmConst(Const::String(b))) => a.push_str(b.as_str()), + (Value::String(a), Value::String(b)) => { + let mut a = a.clone(); + a.make_mut(mc).push_str(b.as_str()) + }, (_, Value::Catchable(_)) => *self = other, (Value::Catchable(_), _) => (), _ => todo!(), @@ -484,17 +519,17 @@ impl<'gc> Value<'gc> { pub fn has_attr(&mut self, sym: usize) -> &mut Self { if let Value::AttrSet(attrs) = self { - let val = VmConst(Const::Bool(attrs.has_attr(sym))); + let val = Value::Bool(attrs.has_attr(sym)); *self = val; } else if let Value::Catchable(_) = self { } else { - *self = VmConst(Const::Bool(false)); + *self = Value::Bool(false); } self } pub fn coerce_to_string(&mut self) -> &mut Self { - if let VmConst(Const::String(_)) = self { + if let Value::String(_) = self { } else if let Value::Catchable(_) = self { } else { todo!() @@ -502,33 +537,7 @@ impl<'gc> Value<'gc> { self } - pub fn force(&mut self, vm: &VM<'gc>, mc: &Mutation<'gc>) -> Result<&mut Self> { - if let Value::Thunk(thunk) = self { - let value = thunk.force(vm, mc)?.clone(); - *self = value - } - Ok(self) - } - - pub fn force_deep(&mut self, vm: &VM<'gc>, mc: &Mutation<'gc>) -> Result<&mut Self> { - match self { - Value::Thunk(thunk) => { - let mut value = thunk.force(vm, mc)?.clone(); - let _ = value.force_deep(vm, mc)?; - *self = value; - } - Value::List(list) => list.make_mut(mc).force_deep(vm, mc)?, - Value::AttrSet(attrs) => attrs.make_mut(mc).force_deep(vm, mc)?, - _ => (), - } - Ok(self) - } - - pub fn to_public( - &self, - vm: &VM<'gc>, - seen: &mut HashSet>, - ) -> p::Value { + pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet>) -> p::Value { use self::Value::*; use p::Value; if seen.contains(self) { @@ -543,8 +552,12 @@ impl<'gc> Value<'gc> { seen.insert(self.clone()); list.to_public(vm, seen) } - Catchable(catchable) => Value::Catchable(catchable.clone()), - Const(cnst) => Value::Const(cnst.clone()), + Catchable(catchable) => Value::Catchable(catchable.as_ref().clone().into()), + Int(x) => Value::Const(Const::Int(*x)), + Float(x) => Value::Const(Const::Float(*x)), + Bool(x) => Value::Const(Const::Bool(*x)), + String(x) => Value::Const(Const::String(x.as_ref().into())), + Null => Value::Const(Const::Null), Thunk(_) => Value::Thunk, PrimOp(primop) => Value::PrimOp(primop.name), PartialPrimOp(primop) => Value::PartialPrimOp(primop.name), @@ -562,16 +575,13 @@ pub struct Thunk<'gc> { #[derive(IsVariant, Unwrap)] pub enum _Thunk<'gc> { - Code( - &'gc OpCodes, - GcRefLock<'gc, Option>>>, - ), + Code(&'gc OpCodes, Option>>), Suspended, Value(Value<'gc>), } -unsafe impl<'gc> Collect for _Thunk<'gc> { - fn trace(&self, cc: &gc_arena::Collection) { +unsafe impl<'gc> Collect<'gc> for _Thunk<'gc> { + fn trace>(&self, cc: &mut T) { use _Thunk::*; match self { Code(_, env) => env.trace(cc), @@ -584,48 +594,34 @@ unsafe impl<'gc> Collect for _Thunk<'gc> { impl<'gc> Thunk<'gc> { pub fn new(opcodes: &'gc OpCodes, mc: &Mutation<'gc>) -> Self { Thunk { - thunk: Gc::new( - mc, - RefLock::new(_Thunk::Code(opcodes, Gc::new(mc, RefLock::new(None)))), - ), + thunk: Gc::new(mc, RefLock::new(_Thunk::Code(opcodes, None))), } } - pub fn capture(&self, env: Gc<'gc, VmEnv<'gc>>, mc: &Mutation<'gc>) { - if let _Thunk::Code(_, envcell) = &*self.thunk.borrow() { - envcell.borrow_mut(mc).insert(env); + 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 force(&self, vm: &VM<'gc>, mc: &Mutation<'gc>) -> Result> { - use _Thunk::*; - match &*self.thunk.borrow() { - Value(_) => { - return Ok(match unsafe { &*(&*self.thunk.borrow() as *const _) } { - Value(value) => value.clone(), - _ => unreachable!(), - }); - } - Suspended => { - return Err(Error::EvalError("infinite recursion encountered".into())); - } - Code(..) => (), - } - let (opcodes, env) = - std::mem::replace(&mut *self.thunk.borrow_mut(mc), _Thunk::Suspended).unwrap_code(); - let env = env.as_ref().borrow().unwrap(); - let value = vm.eval(opcodes.iter().copied(), env)?; - let _ = std::mem::replace(&mut *self.thunk.borrow_mut(mc), _Thunk::Value(value)); - Ok(match unsafe { &*(&*self.thunk.borrow() as *const _) } { - Value(value) => value.clone(), - _ => unreachable!(), - }) + pub fn suspend(&self, mc: &Mutation<'gc>) -> (&'gc OpCodes, Gc<'gc, VmEnv<'gc>>) { + let _Thunk::Code(opcodes, env) = + std::mem::replace(&mut *self.thunk.borrow_mut(mc), _Thunk::Suspended) + else { + unreachable!() + }; + (opcodes, env.unwrap()) } - pub fn value(&self) -> Option> { - match &*self.thunk.borrow() { - _Thunk::Value(value) => Some(value.clone()), - _ => None, + pub fn insert_value(&self, value: Value<'gc>, mc: &Mutation<'gc>) { + *self.thunk.borrow_mut(mc) = _Thunk::Value(value); + } + + pub fn get_value(&self) -> Option> { + if let _Thunk::Value(val) = &*self.thunk.borrow() { + Some(val.clone()) + } else { + None } } } diff --git a/src/ty/internal/primop.rs b/src/ty/internal/primop.rs index f20a475..b5e13d7 100644 --- a/src/ty/internal/primop.rs +++ b/src/ty/internal/primop.rs @@ -1,40 +1,41 @@ use derive_more::Constructor; -use gc_arena::Mutation; use gc_arena::Collect; +use gc_arena::Mutation; use crate::error::Result; use crate::vm::VM; -use super::Value; use super::CoW; +use super::Value; #[derive(Debug, Clone, Constructor, Collect)] #[collect(require_static)] -pub struct PrimOp<'gc> { +pub struct PrimOp { pub name: &'static str, arity: usize, - func: fn(Vec>, &VM, &Mutation<'gc>) -> Result>, + func: for<'gc> fn(Vec>, &VM, &Mutation<'gc>) -> Result>, } -impl PartialEq for PrimOp<'_> { +impl PartialEq for PrimOp { fn eq(&self, _: &Self) -> bool { false } } -impl<'gc> PrimOp<'gc> { - pub fn call(&self, arg: Value<'gc>, vm: &VM, mc: &Mutation<'gc>) -> Result> { +impl PrimOp { + pub fn call<'gc>(&self, arg: Value<'gc>, vm: &VM, mc: &Mutation<'gc>) -> Result> { let mut args = Vec::with_capacity(self.arity); args.push(arg); if self.arity > 1 { - Value::PartialPrimOp( - CoW::new(PartialPrimOp { + Value::PartialPrimOp(CoW::new( + PartialPrimOp { name: self.name, arity: self.arity - 1, args, func: self.func, - }, mc) - ) + }, + mc, + )) .ok() } else { (self.func)(args, vm, mc) @@ -50,8 +51,8 @@ pub struct PartialPrimOp<'gc> { func: fn(Vec>, &VM, &Mutation<'gc>) -> Result>, } -unsafe impl<'jit: 'vm, 'vm, 'gc> Collect for PartialPrimOp<'gc> { - fn trace(&self, cc: &gc_arena::Collection) { +unsafe impl<'jit: 'vm, 'vm, 'gc> Collect<'gc> for PartialPrimOp<'gc> { + fn trace>(&self, cc: &mut Tr) { for v in self.args.iter() { v.trace(cc); } @@ -69,7 +70,7 @@ impl<'gc> PartialPrimOp<'gc> { self: &mut CoW<'gc, Self>, arg: Value<'gc>, vm: &VM, - mc: &Mutation<'gc> + mc: &Mutation<'gc>, ) -> Result> { let func = self.func; let self_mut = self.make_mut(mc); diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 60f6e78..d7a4e90 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -1,9 +1,9 @@ -use std::cell::{Cell, RefCell}; +use std::cell::RefCell; -use gc_arena::lock::{GcRefLock, RefLock}; -use gc_arena::{Arena, Collect, DynamicRootSet, Gc, Mutation, Rootable}; +use gc_arena::{Arena, Collect, Gc, Mutation, Rootable}; use hashbrown::{HashMap, HashSet}; use inkwell::context::Context; +use itertools::Itertools; use crate::builtins::env; use crate::bytecode::{BinOp, Func as F, OpCode, OpCodes, Program, UnOp}; @@ -28,9 +28,8 @@ type GcArena = Arena GcRoot<'gc>]>; #[collect(require_static)] struct ContextWrapper(Context); - pub fn run(mut prog: Program) -> Result { - fn new<'gc>(prog: &mut Program, mc: &'gc Mutation<'gc>) -> GcRoot<'gc> { + let mut arena: Arena]> = Arena::new(|mc| { let jit = Gc::new(mc, ContextWrapper(Context::create())); let thunks = std::mem::take(&mut prog.thunks); let funcs = std::mem::take(&mut prog.funcs); @@ -49,46 +48,113 @@ pub fn run(mut prog: Program) -> Result { GcRoot { vm, jit, - ctxs: DynamicRootSet::new(mc) + stack: Stack::new(), + envs: vec![env(&vm, mc)], } - } - let arena: Arena]> = Arena::new(|mc| { - new(&mut prog, mc) }); - eval(prog.top_level.into_iter(), &arena, |val, vm| { - Ok(val.to_public(vm, &mut HashSet::new())) + eval(prog.top_level, &mut arena, |val, root, _| { + Ok(val.to_public(&root.vm, &mut HashSet::new())) }) } - -fn eval<'gc, T, F: for<'a> FnOnce(Value<'a>, &VM<'a>) -> Result>( - opcodes: impl Iterator, - arena: &Arena Rootable<'a, Root = GcRoot<'a>>>, - f: F +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 { - let mut iter = opcodes.into_iter(); - let dynroot: gc_arena::DynamicRoot GcRefLock<'a, EvalContext<'a>>]> = arena.mutate(|mc, root| { - root.ctxs.stash(mc, Gc::new(mc, RefLock::new( EvalContext::new(env(&root.vm, mc))))) - }); - while let Some(opcode) = iter.next() { - arena.mutate(|mc, root| { - let mut ctx_mut = root.ctxs.fetch(&dynroot).borrow_mut(mc); - let ctx = &mut *ctx_mut; - let jmp = single_op(&root.vm, opcode, &mut ctx.stack, &mut ctx.env, mc)?; - for _ in 0..jmp { - iter.next().unwrap(); + let mut opcodes = opcodes.into_vec(); + opcodes.reverse(); + 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); + let mut code = code.to_vec(); + code.reverse(); + opcodes.push(OpCode::InsertValue); + opcodes.push(OpCode::PopEnv); + opcodes.extend(code); + root.envs.push(env); + } + Consq::PopEnv => _ = root.envs.pop().unwrap(), + Consq::Call => { + use Param::*; + let arg = root.stack.pop(); + let func = root.stack.pop().unwrap_func(); + let mut env = func.env; + env = match func.func.param.clone() { + Ident(ident) => env.enter_arg(ident, arg, mc), + Formals { + formals, + ellipsis, + alias, + } => { + let arg = arg.unwrap_attr_set(); + let mut new = + HashMap::with_capacity(formals.len() + alias.iter().len()); + if !ellipsis + && arg + .iter() + .map(|(k, _)| k) + .sorted() + .ne(formals.iter().map(|(k, _)| k).sorted()) + { + todo!() + } + for (formal, default) in formals { + // TODO: rec env + let arg = arg + .select(formal) + .or_else(|| { + default.map(|idx| { + Value::Thunk( + Thunk::new(root.vm.get_thunk(idx), mc).into(), + ) + }) + }) + .unwrap(); + new.insert(formal, arg); + } + if let Some(alias) = alias { + new.insert(alias, Value::AttrSet(arg)); + } + env.enter_let(Gc::new(mc, new.into()), mc) + } + }; + root.envs.push(env); + let mut code = func.func.opcodes.to_vec(); + code.reverse(); + opcodes.push(OpCode::PopEnv); + opcodes.extend(code); + } } Result::Ok(()) })?; } - arena.mutate(|mc, root| { - let mut ctx = root.ctxs.fetch(&dynroot).borrow_mut(mc); - assert_eq!(ctx.stack.len(), 1); - let mut ret = ctx.stack.pop(); - ret.force(&root.vm, mc); - f(ret, &root.vm) + arena.mutate_root(|mc, root| { + assert_eq!(root.stack.len(), 1); + let ret = root.stack.pop(); + f(ret, root, mc) }) } +enum Consq { + Jmp(usize), + Call, + Force, + PopEnv, + NoOp, +} + #[inline(always)] fn single_op<'gc, const CAP: usize>( vm: &'gc VM<'gc>, @@ -96,39 +162,62 @@ fn single_op<'gc, const CAP: usize>( stack: &mut Stack, CAP>, env: &mut Gc<'gc, VmEnv<'gc>>, mc: &'gc Mutation<'gc>, -) -> Result { +) -> Result { match opcode { OpCode::Illegal => panic!("illegal opcode"), - OpCode::Const { idx } => stack.push(Value::Const(vm.get_const(idx)))?, + 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(CoW::new(x.into(), mc)), + Const::Null => Value::Null + })? + }, OpCode::LoadThunk { idx } => stack.push(Value::Thunk(Thunk::new(vm.get_thunk(idx), mc)))?, - OpCode::CaptureEnv => stack.tos().as_ref().unwrap_thunk().capture(*env, mc), - OpCode::ForceValue => { - stack.tos_mut().force(vm, mc)?; + OpCode::LoadValue { idx } => { + stack.push(Value::Thunk(Thunk::new(vm.get_thunk(idx), mc)))?; + stack.tos().as_ref().unwrap_thunk().capture_env(*env, mc); + return Ok(Consq::Force); } - OpCode::Jmp { step } => return Ok(step), + OpCode::CaptureEnv => stack.tos().as_ref().unwrap_thunk().capture_env(*env, mc), + 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.pop(); + stack.push(val)?; + } + OpCode::InsertValue => { + let val = stack.pop(); + stack.pop().unwrap_thunk().insert_value(val.clone(), mc); + let _ = stack.push(val); + } + OpCode::Jmp { step } => return Ok(Consq::Jmp(step)), OpCode::JmpIfFalse { step } => { - if let Value::Const(Const::Bool(false)) = stack.pop() { - return Ok(step); + if let Value::Bool(false) = stack.pop() { + return Ok(Consq::Jmp(step)); } } OpCode::Call => { let arg = stack.pop(); let func = stack.tos_mut(); - func.force(vm, mc)?; + if func.is_func() { + stack.push(arg)?; + return Ok(Consq::Call); + } func.call(arg, vm, mc)?; } OpCode::Func { idx } => { let func = vm.get_func(idx); - let compiled: GcRefLock<'gc, Option>> = Gc::new(mc, RefLock::new(None)); - stack.push(Value::Func(Gc::new( - mc, - Func::new(func, *env, compiled, Cell::new(0)), - )))?; + stack.push(Value::Func(Gc::new(mc, Func::new(func, *env, mc))))?; } OpCode::UnOp { op } => { use UnOp::*; let value = stack.tos_mut(); - value.force(vm, mc)?; match op { Neg => value.neg(), Not => value.not(), @@ -138,13 +227,11 @@ fn single_op<'gc, const CAP: usize>( use BinOp::*; let mut rhs = stack.pop(); let lhs = stack.tos_mut(); - lhs.force(vm, mc)?; - rhs.force(vm, mc)?; match op { - Add => lhs.add(rhs), + Add => lhs.add(rhs, mc), Sub => { rhs.neg(); - lhs.add(rhs); + lhs.add(rhs, mc); } Mul => lhs.mul(rhs), Div => lhs.div(rhs)?, @@ -157,9 +244,8 @@ fn single_op<'gc, const CAP: usize>( } } OpCode::ConcatString => { - let mut rhs = stack.pop(); - rhs.force(vm, mc)?; - stack.tos_mut().concat_string(rhs); + let rhs = stack.pop(); + stack.tos_mut().concat_string(rhs, mc); } OpCode::Path => { todo!() @@ -197,47 +283,38 @@ fn single_op<'gc, const CAP: usize>( } OpCode::PushDynamicAttr => { let val = stack.pop(); - let mut sym = stack.pop(); - sym.force(vm, mc)?.coerce_to_string(); - let sym = vm.new_sym(sym.unwrap_const().unwrap_string()); + let sym = stack.pop(); + let sym = vm.new_sym(sym.unwrap_string().as_ref()); stack.tos_mut().push_attr(sym, val, mc); } OpCode::Select { sym } => { - stack.tos_mut().force(vm, mc)?.select(sym, vm)?; + stack.tos_mut().select(sym, vm)?; } OpCode::SelectOrDefault { sym } => { let default = stack.pop(); - stack - .tos_mut() - .force(vm, mc)? - .select_with_default(sym, default)?; + stack.tos_mut().select_with_default(sym, default)?; } OpCode::SelectDynamic => { let mut val = stack.pop(); - val.force(vm, mc)?; val.coerce_to_string(); - let sym = vm.new_sym(val.unwrap_const().unwrap_string()); - stack.tos_mut().force(vm, mc)?.select(sym, vm)?; + let sym = vm.new_sym(val.unwrap_string().as_ref()); + stack.tos_mut().select(sym, vm)?; } OpCode::SelectDynamicOrDefault => { let default = stack.pop(); let mut val = stack.pop(); - val.force(vm, mc)?; val.coerce_to_string(); - let sym = vm.new_sym(val.unwrap_const().unwrap_string()); - stack - .tos_mut() - .force(vm, mc)? - .select_with_default(sym, default)?; + let sym = vm.new_sym(val.unwrap_string().as_ref()); + stack.tos_mut().select_with_default(sym, default)?; } OpCode::HasAttr { sym } => { - stack.tos_mut().force(vm, mc)?.has_attr(sym); + stack.tos_mut().has_attr(sym); } OpCode::HasDynamicAttr => { let mut val = stack.pop(); val.coerce_to_string(); - let sym = vm.new_sym(val.unwrap_const().unwrap_string()); - stack.tos_mut().force(vm, mc)?.has_attr(sym); + let sym = vm.new_sym(val.unwrap_string().as_ref()); + stack.tos_mut().has_attr(sym); } OpCode::LookUp { sym } => { stack.push( @@ -251,13 +328,12 @@ fn single_op<'gc, const CAP: usize>( stack .pop() .unwrap_attr_set() - .as_inner() .iter() .map(|(&k, v)| (k, v.clone())) .collect_into(&mut new); *env = env.enter_let(Gc::new(mc, new.into()), mc); } - OpCode::LeaveLetEnv => *env = env.leave(), + OpCode::LeaveEnv => *env = env.leave(), OpCode::EnterWithEnv => { let mut new = HashMap::new(); stack @@ -267,40 +343,26 @@ 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.into()), mc); + *env = env.enter_with(Gc::new(mc, new), mc); } - OpCode::LeaveWithEnv => *env = env.leave(), + OpCode::PopEnv => return Ok(Consq::PopEnv), OpCode::Assert => { - if !stack.pop().unwrap_const().unwrap_bool() { + if !stack.pop().unwrap_bool() { todo!() } } } - Ok(0) + Ok(Consq::NoOp) } #[derive(Collect)] #[collect(no_drop)] -pub struct GcRoot<'gc> { +pub struct GcRoot<'gc, const CAP: usize = STACK_SIZE> { vm: Gc<'gc, VM<'gc>>, jit: Gc<'gc, ContextWrapper>, - ctxs: DynamicRootSet<'gc> -} -#[derive(Collect)] -#[collect(no_drop)] -pub struct EvalContext<'gc, const CAP: usize = STACK_SIZE> { stack: Stack, CAP>, - env: Gc<'gc, VmEnv<'gc>> -} - -impl<'gc, const CAP: usize> EvalContext<'gc, CAP> { - pub fn new(env: Gc<'gc, VmEnv<'gc>>) -> Self { - Self { - stack: Stack::new(), - env - } - } + envs: Vec>>, } #[derive(Constructor, Collect)] diff --git a/src/vm/test.rs b/src/vm/test.rs index 66edca7..8c2b5dc 100644 --- a/src/vm/test.rs +++ b/src/vm/test.rs @@ -4,7 +4,6 @@ extern crate test; use hashbrown::HashMap; -use inkwell::context::Context; use test::{Bencher, black_box}; use ecow::EcoString; @@ -14,7 +13,6 @@ use crate::compile::compile; use crate::ir::downgrade; use crate::ty::common::Const; use crate::ty::public::*; -use crate::vm::JITContext; use super::run; @@ -23,9 +21,7 @@ fn test_expr(expr: &str, expected: Value) { let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap(); let prog = compile(downgraded); dbg!(&prog); - let ctx = Context::create(); - let jit = JITContext::new(&ctx); - assert_eq!(run(prog, jit).unwrap(), expected); + assert_eq!(run(prog).unwrap(), expected); } macro_rules! map {