diff --git a/Cargo.lock b/Cargo.lock index ae6f7e0..f118071 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,6 +80,26 @@ version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.3", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "derive_more" version = "2.0.1" @@ -251,6 +271,16 @@ dependencies = [ "semver", ] +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.27" @@ -297,6 +327,7 @@ dependencies = [ name = "nixjit" version = "0.0.0" dependencies = [ + "dashmap", "derive_more", "ecow", "hashbrown 0.15.3", @@ -315,6 +346,19 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -343,6 +387,15 @@ dependencies = [ "nibble_vec", ] +[[package]] +name = "redox_syscall" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.11.1" @@ -447,6 +500,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "semver" version = "1.0.26" diff --git a/Cargo.toml b/Cargo.toml index 0117ec0..25f89f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ derive_more = { version = "2.0", features = ["full"] } ecow = "0.2" regex = "1.11" hashbrown = "0.15" +dashmap = "6.1" replace_with = "0.1" inkwell = { version = "0.6.0", features = ["llvm18-1"] } diff --git a/src/bin/compile.rs b/src/bin/compile.rs deleted file mode 100644 index 31781ec..0000000 --- a/src/bin/compile.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::process::exit; - -use itertools::Itertools; - -use nixjit::compile::compile; -use nixjit::error::Error; -use nixjit::error::Result; -use nixjit::ir::downgrade; - -fn main() -> Result<()> { - let mut args = std::env::args(); - if args.len() != 2 { - eprintln!("Usage: {} expr", args.next().unwrap()); - exit(1); - } - args.next(); - let expr = args.next().unwrap(); - let root = rnix::Root::parse(&expr); - if !root.errors().is_empty() { - return Err(Error::ParseError( - root.errors().iter().map(|err| err.to_string()).join(";"), - )); - } - let expr = root.tree().expr().unwrap(); - let downgraded = downgrade(expr)?; - let prog = compile(downgraded); - println!("{prog:?}"); - - Ok(()) -} diff --git a/src/bin/eval.rs b/src/bin/eval.rs index 20a8853..4bed9bd 100644 --- a/src/bin/eval.rs +++ b/src/bin/eval.rs @@ -2,11 +2,10 @@ use std::process::exit; use itertools::Itertools; -use nixjit::compile::compile; use nixjit::error::Error; use nixjit::error::Result; use nixjit::ir::downgrade; -use nixjit::vm::run; +use nixjit::eval::eval; fn main() -> Result<()> { let mut args = std::env::args(); @@ -23,9 +22,8 @@ fn main() -> Result<()> { )); } let expr = root.tree().expr().unwrap(); - let downgraded = downgrade(expr)?; - let prog = compile(downgraded); - println!("{}", run(prog)?); + let expr = downgrade(expr)?; + println!("{}", eval(expr)?); Ok(()) } diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index ab506fd..f1a282f 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -1,12 +1,11 @@ +use ecow::EcoString; use hashbrown::HashMap; -use crate::ty::common::Const; -use crate::ir::{DowngradeContext, Ir}; +use crate::ir::{DowngradeContext, Ir, Const}; -pub fn ir_env(ctx: &mut DowngradeContext) -> HashMap { +pub fn ir_env(ctx: &mut DowngradeContext) -> HashMap { let mut map = HashMap::new(); - map.insert(ctx.new_sym("true"), ctx.new_const(Const::Bool(true)).ir()); - map.insert(ctx.new_sym("false"), ctx.new_const(Const::Bool(false)).ir()); - + map.insert("true".into(), Const::from(true).ir()); + map.insert("false".into(), Const::from(false).ir()); map } diff --git a/src/bytecode.rs b/src/bytecode.rs deleted file mode 100644 index 7de7938..0000000 --- a/src/bytecode.rs +++ /dev/null @@ -1,125 +0,0 @@ -use ecow::EcoString; -use hashbrown::HashMap; - -use crate::ty::common::Const; -use crate::ty::internal::Param; - -type Slice = Box<[T]>; - -pub type OpCodes = Slice; - -#[derive(Debug, Clone, Copy)] -pub enum OpCode { - /// load a constant onto stack - Const { idx: usize }, - /// load a dynamic var onto stack - LookUp { sym: usize }, - /// load a var from let binding onto stack - LookUpLet { level: usize, idx: 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, - /// make a function - Func { idx: usize }, - /// load a function argument - Arg { level: usize }, - - /// consume 1 element, assert TOS is true - Assert, - - /// jump forward - Jmp { step: usize }, - /// [ .. cond ] consume 1 element, if `cond` is false, then jump forward - JmpIfFalse { step: usize }, - - /// push an empty attribute set onto stack - AttrSet { cap: usize }, - /// finalize the recursive attribute set at TOS - FinalizeLet, - /// [ .. set value ] consume 1 element, push a static kv pair (`name`, `value`) into `set` - PushStaticAttr { name: usize }, - /// [ .. set name value ] consume 2 elements, push a dynamic kv pair (`name`, `value`) in to `set` - PushDynamicAttr, - - /// push an empty list onto stack - List { cap: usize }, - /// [ .. list elem ] consume 1 element, push `elem` into `list` - PushElem, - - /// convert the string as TOS to a path - Path, - - /// [ .. a b ] consume 2 elements, perform a string concatenation `a` + `b` - ConcatString, - /// [ .. a b ] consume 2 elements, perform a binary operation `a` `op` `b` - BinOp { op: BinOp }, - /// [ .. a ] consume 1 element, perform a unary operation `op` `a` - UnOp { op: UnOp }, - /// set TOS to the bool value of whether TOS contains `sym` - HasAttr { sym: usize }, - /// [ .. set sym ] consume 2 elements, set TOS to the bool value of whether `set` contains `sym` - HasDynamicAttr, - /// [ .. set ] select `sym` from `set` - Select { sym: usize }, - /// [ .. set default ] select `sym` from `set` or `default` - SelectOrDefault { sym: usize }, - /// [ .. set sym ] select `sym` from `set` - SelectDynamic, - /// [ .. set sym default ] select `sym` from `set` or `default` - SelectDynamicOrDefault, - /// enter the with environment of the attribute set at TOS - EnterWithEnv, - /// exit current envrironment - LeaveEnv, - /// TODO: - PopEnv, - - /// illegal operation, used as termporary placeholder - Illegal, -} - -#[derive(Debug, Clone, Copy)] -pub enum BinOp { - Add, - Sub, - Mul, - Div, - And, - Or, - Eq, - Lt, - Con, - Upd, -} - -#[derive(Debug, Clone, Copy)] -pub enum UnOp { - Neg, - Not, -} - -#[derive(Debug)] -pub struct Func { - pub param: Param, - pub opcodes: OpCodes, -} - -#[derive(Debug)] -pub struct Program { - pub top_level: OpCodes, - pub thunks: Slice, - pub funcs: Slice, - pub symbols: Vec, - pub symmap: HashMap, - pub consts: Box<[Const]>, -} diff --git a/src/compile.rs b/src/compile.rs deleted file mode 100644 index d93ea72..0000000 --- a/src/compile.rs +++ /dev/null @@ -1,447 +0,0 @@ -use crate::bytecode::*; -use crate::ir; - -pub struct Compiler { - opcodes: Vec, -} - -pub fn compile(downgraded: ir::Downgraded) -> Program { - Program { - top_level: Compiler::new().compile(downgraded.top_level), - thunks: downgraded - .thunks - .into_iter() - .map(|thunk| Compiler::new().compile(thunk)) - .collect(), - funcs: downgraded - .funcs - .into_iter() - .map(|func| Func { - param: func.param.into(), - opcodes: Compiler::new().compile(*func.body), - }) - .collect(), - symbols: downgraded.symbols, - symmap: downgraded.symmap, - consts: downgraded.consts, - } -} - -impl Compiler { - fn new() -> Self { - Self { - opcodes: Vec::new(), - } - } - - fn compile(mut self, ir: ir::Ir) -> OpCodes { - ir.compile_force(&mut self); - self.opcodes() - } - - fn push(&mut self, code: OpCode) { - self.opcodes.push(code); - } - - fn idx(&self) -> usize { - self.opcodes.len() - } - - fn modify(&mut self, idx: usize, code: OpCode) { - self.opcodes[idx] = code; - } - - fn pop(&mut self) -> Option { - self.opcodes.pop() - } - - fn opcodes(self) -> OpCodes { - self.opcodes.into() - } -} - -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 { - fn compile_with_length(self, comp: &mut Compiler) -> usize; -} - -impl CompileWithLength for T { - fn compile_with_length(self, comp: &mut Compiler) -> usize { - let start = comp.idx(); - self.compile(comp); - let end = comp.idx(); - end - start - } -} - -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 { - fn compile(self, comp: &mut Compiler) { - comp.push(OpCode::LookUp { sym: self.sym }); - } -} - -impl Compile for ir::Arg { - fn compile(self, comp: &mut Compiler) { - comp.push(OpCode::Arg { level: self.level }) - } -} - -impl Compile for ir::LetVar { - fn compile(self, comp: &mut Compiler) { - comp.push(OpCode::LookUpLet { - level: self.level, - idx: self.idx, - }); - } -} - -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 { - fn compile(self, comp: &mut Compiler) { - comp.push(OpCode::AttrSet { - cap: self.stcs.len() + self.dyns.len(), - }); - for stc in self.stcs { - let thunk = stc.1.is_thunk(); - stc.1.compile(comp); - if thunk { - comp.push(OpCode::CaptureEnv); - } - comp.push(OpCode::PushStaticAttr { name: stc.0 }); - } - for dynamic in self.dyns { - let thunk = dynamic.1.is_thunk(); - dynamic.0.compile_force(comp); - dynamic.1.compile(comp); - if thunk { - comp.push(OpCode::CaptureEnv); - } - comp.push(OpCode::PushDynamicAttr) - } - } -} - -impl Compile for ir::List { - fn compile(self, comp: &mut Compiler) { - comp.push(OpCode::List { - cap: self.items.len(), - }); - for item in self.items { - item.compile(comp); - comp.push(OpCode::PushElem); - } - } -} - -impl Compile for ir::UnOp { - fn compile(self, comp: &mut Compiler) { - use ir::UnOpKind::*; - match self.kind { - Neg => { - self.rhs.compile_force(comp); - comp.push(OpCode::UnOp { op: UnOp::Neg }); - } - Not => { - self.rhs.compile_force(comp); - comp.push(OpCode::UnOp { op: UnOp::Not }); - } - } - } -} - -impl Compile for ir::BinOp { - fn compile(self, comp: &mut Compiler) { - use ir::BinOpKind::*; - match self.kind { - Add => { - self.lhs.compile_force(comp); - self.rhs.compile_force(comp); - comp.push(OpCode::BinOp { op: BinOp::Add }); - } - Mul => { - self.lhs.compile_force(comp); - self.rhs.compile_force(comp); - comp.push(OpCode::BinOp { op: BinOp::Mul }); - } - Div => { - self.lhs.compile_force(comp); - self.rhs.compile_force(comp); - comp.push(OpCode::BinOp { op: BinOp::Div }); - } - And => { - self.lhs.compile_force(comp); - self.rhs.compile_force(comp); - comp.push(OpCode::BinOp { op: BinOp::And }); - } - Or => { - self.lhs.compile_force(comp); - self.rhs.compile_force(comp); - comp.push(OpCode::BinOp { op: BinOp::Or }); - } - Eq => { - self.lhs.compile_force(comp); - self.rhs.compile_force(comp); - comp.push(OpCode::BinOp { op: BinOp::Eq }); - } - Lt => { - self.lhs.compile_force(comp); - self.rhs.compile_force(comp); - comp.push(OpCode::BinOp { op: BinOp::Lt }); - } - Con => { - self.lhs.compile_force(comp); - self.rhs.compile_force(comp); - comp.push(OpCode::BinOp { op: BinOp::Con }); - } - Upd => { - self.lhs.compile_force(comp); - self.rhs.compile_force(comp); - comp.push(OpCode::BinOp { op: BinOp::Upd }); - } - - Sub => { - self.lhs.compile_force(comp); - self.rhs.compile_force(comp); - comp.push(OpCode::BinOp { op: BinOp::Sub }); - } - Impl => { - self.lhs.compile_force(comp); - comp.push(OpCode::UnOp { op: UnOp::Not }); - self.rhs.compile_force(comp); - comp.push(OpCode::BinOp { op: BinOp::Or }); - } - Neq => { - 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_force(comp); - self.lhs.compile_force(comp); - comp.push(OpCode::BinOp { op: BinOp::Lt }); - } - Leq => { - 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_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_force(comp); - self.rhs.compile_force(comp); - comp.push(OpCode::Call); - } - PipeR => { - self.rhs.compile_force(comp); - self.lhs.compile_force(comp); - comp.push(OpCode::Call); - } - } - } -} - -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_force(comp); - comp.push(OpCode::AttrSet { cap: 0 }); - comp.push(OpCode::SelectDynamicOrDefault); - } - ir::Attr::Strs(string) => { - string.compile(comp); - comp.push(OpCode::AttrSet { cap: 0 }); - comp.push(OpCode::SelectDynamicOrDefault); - } - } - } - let last = comp.pop().unwrap(); - let _ = comp.pop(); - match last { - OpCode::SelectOrDefault { sym } => comp.push(OpCode::HasAttr { sym }), - OpCode::SelectDynamicOrDefault => comp.push(OpCode::HasDynamicAttr), - _ => unreachable!(), - } - } -} - -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 }); - comp.push(OpCode::SelectOrDefault { sym }) - } - ir::Attr::Dynamic(dynamic) => { - dynamic.compile(comp); - comp.push(OpCode::AttrSet { cap: 0 }); - comp.push(OpCode::SelectDynamicOrDefault); - } - ir::Attr::Strs(string) => { - string.compile(comp); - comp.push(OpCode::AttrSet { cap: 0 }); - comp.push(OpCode::SelectDynamicOrDefault); - } - } - } - match self.default { - Some(default) => { - let last = comp.pop().unwrap(); - let _ = comp.pop(); - default.compile(comp); - comp.push(last); - } - None => { - let last = comp.pop().unwrap(); - let _ = comp.pop(); - match last { - OpCode::SelectOrDefault { sym } => comp.push(OpCode::Select { sym }), - OpCode::SelectDynamicOrDefault => comp.push(OpCode::SelectDynamic), - _ => unreachable!(), - } - } - } - } - 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_force(comp); - for item in iter { - item.compile_force(comp); - comp.push(OpCode::ConcatString); - } - } -} - -impl Compile for ir::If { - fn compile(self, comp: &mut Compiler) { - self.cond.compile(comp); - - let idx_jmp_if_false = comp.idx(); - // place holder - comp.push(OpCode::Illegal); - - let consq_length = self.consq.compile_with_length(comp); - - let idx_jmp = comp.idx(); - // place holder - comp.push(OpCode::Illegal); - - let alter_length = self.alter.compile_with_length(comp); - - comp.modify( - idx_jmp_if_false, - OpCode::JmpIfFalse { - step: consq_length + 1, - }, - ); - comp.modify(idx_jmp, OpCode::Jmp { step: alter_length }); - } -} - -impl Compile for ir::Let { - fn compile(self, comp: &mut Compiler) { - comp.push(OpCode::List { - 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); - } -} - -impl Compile for ir::With { - fn compile(self, comp: &mut Compiler) { - self.namespace.compile(comp); - comp.push(OpCode::EnterWithEnv); - self.expr.compile(comp); - comp.push(OpCode::LeaveEnv); - } -} - -impl Compile for ir::Assert { - fn compile(self, comp: &mut Compiler) { - self.assertion.compile(comp); - comp.push(OpCode::Assert); - self.expr.compile(comp); - } -} - -impl Compile for ir::LoadFunc { - fn compile(self, comp: &mut Compiler) { - comp.push(OpCode::Func { idx: self.idx }); - } -} - -impl Compile for ir::Call { - fn compile(self, comp: &mut Compiler) { - self.func.compile_force(comp); - self.args.into_iter().for_each(|arg| { - arg.compile(comp); - comp.push(OpCode::Call); - }); - } -} - -impl Compile for ir::Path { - fn compile(self, comp: &mut Compiler) { - self.expr.compile(comp); - comp.push(OpCode::Path); - } -} diff --git a/src/engine/mod.rs b/src/engine/mod.rs new file mode 100644 index 0000000..135c748 --- /dev/null +++ b/src/engine/mod.rs @@ -0,0 +1,29 @@ +use hashbrown::HashSet; + +use crate::eval::Evaluate; +use crate::ir::{Downgraded, Ir}; +use crate::ty::public::Value; +use crate::error::Result; + +#[cfg(test)] +mod test; + +pub struct Engine { + +} + +pub fn eval(downgraded: Downgraded) -> Result { + let mut engine = Engine::new(); + engine.eval(downgraded.top_level) +} + +impl Engine { + pub fn new() -> Self { + Self {} + } + + pub fn eval(&mut self, expr: Ir) -> Result { + expr.eval(self).map(|val| val.to_public(self, &mut HashSet::new())) + } +} + diff --git a/src/engine/test.rs b/src/engine/test.rs new file mode 100644 index 0000000..ca25022 --- /dev/null +++ b/src/engine/test.rs @@ -0,0 +1,229 @@ +#![allow(unused_macros)] + +extern crate test; + +use hashbrown::HashMap; + +use test::{Bencher, black_box}; + +use ecow::EcoString; + +use crate::ir::downgrade; +use crate::ty::common::Const; +use crate::ty::public::*; + +use super::eval; + +#[inline] +fn test_expr(expr: &str, expected: Value) { + let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap(); + assert_eq!(eval(downgraded).unwrap(), expected); +} + +macro_rules! map { + ($($k:expr => $v:expr),*) => { + { + #[allow(unused_mut)] + let mut m = HashMap::new(); + $( + m.insert($k, $v); + )* + m + } + }; +} + +macro_rules! thunk { + () => { + Value::Thunk + }; +} + +macro_rules! int { + ($e:expr) => { + Value::Const(Const::Int($e)) + }; +} + +macro_rules! float { + ($e:expr) => { + Value::Const(Const::Float($e as f64)) + }; +} + +macro_rules! boolean { + ($e:expr) => { + Value::Const(Const::Bool($e)) + }; +} + +macro_rules! string { + ($e:expr) => { + Value::String(EcoString::from($e)) + }; +} + +macro_rules! symbol { + ($e:expr) => { + Symbol::from($e.to_string()) + }; +} + +macro_rules! list { + ($($x:tt)*) => ( + Value::List(List::new(vec![$($x)*])) + ); +} + +macro_rules! attrs { + ($($x:tt)*) => ( + Value::AttrSet(AttrSet::new(map!{$($x)*})) + ) +} + +#[test] +fn test_arith() { + test_expr("1", int!(1)); + test_expr("1.", float!(1)); + test_expr("-1", int!(-1)); + test_expr("-1.", float!(-1)); + test_expr("1 + 1", int!(2)); + test_expr("1 + 1.", float!(2)); + test_expr("1. + 1", float!(2)); + test_expr("1. + 1.", float!(2)); + test_expr("1 - 1", int!(0)); + test_expr("1 - 1.", float!(0)); + test_expr("1. - 1", float!(0)); + test_expr("1. - 1.", float!(0)); + test_expr("1 * 1", int!(1)); + test_expr("1 * 1.", float!(1)); + test_expr("1. * 1", float!(1)); + test_expr("1. * 1.", float!(1)); + test_expr("1 / 1", int!(1)); + test_expr("1 / 1.", float!(1)); + test_expr("1. / 1", float!(1)); + test_expr("1. / 1.", float!(1)); +} + +#[test] +fn test_cmp() { + test_expr("1 < 2", boolean!(true)); + test_expr("1 < 1", boolean!(false)); + test_expr("1 > 0", boolean!(true)); + test_expr("1 > 1", boolean!(false)); + test_expr("1 <= 1", boolean!(true)); + test_expr("1 <= 0", boolean!(false)); + test_expr("1 >= 1", boolean!(true)); + test_expr("1 >= 2", boolean!(false)); +} + +#[test] +fn test_string() { + test_expr(r#""test""#, string!("test")); + test_expr(r#""hello" + " world""#, string!("hello world")); +} + +#[test] +fn test_bool() { + test_expr("true", boolean!(true)); + test_expr("false", boolean!(false)); + test_expr("!false", boolean!(true)); + test_expr("true && false", boolean!(false)); + test_expr("true || false", boolean!(true)); + test_expr("true -> false", boolean!(false)); +} + +#[test] +fn test_list() { + test_expr( + "[ 1 2 3 true ]", + list![int!(1), int!(2), int!(3), boolean!(true)], + ); + test_expr( + "[ 1 2 ] ++ [ 3 4 ]", + list![int!(1), int!(2), int!(3), int!(4)], + ); +} + +#[test] +fn test_attrs() { + test_expr( + "{ a = 1; }", + attrs! { + symbol!("a") => int!(1) + }, + ); + test_expr("{ a = 1; }.a", int!(1)); + test_expr("{ a = 1; }.b or 1", int!(1)); + test_expr( + "{ a = { a = 1; }; }.a", + attrs! { + symbol!("a") => int!(1) + }, + ); + test_expr("{ a.b = 1; }.a.b", int!(1)); + test_expr( + "{ a.b = 1; a.c = 2; }", + attrs! { symbol!("a") => attrs!{ symbol!("b") => int!(1), symbol!("c") => int!(2) } }, + ); + test_expr("{ a.b = 1; } ? a.b", boolean!(true)); + test_expr( + "{ a.b = 1; } // { a.c = 2; }", + attrs! { symbol!("a") => attrs!{ symbol!("c") => int!(2) } }, + ); +} + +#[test] +fn test_if() { + test_expr("if true || false then 1 else 2", int!(1)); +} + +#[test] +fn test_with() { + test_expr(r#"with { a = 1; }; a"#, int!(1)); +} + +#[test] +fn test_let() { + test_expr(r#"let a = 1; in a"#, int!(1)); + test_expr(r#"let a = 1; b = a; in b"#, int!(1)); + test_expr(r#"let a = { a = 1; }; b = "a"; in a.${b}"#, int!(1)); + test_expr( + r#"let b = "c"; in { a.b = 1; } // { a."a${b}" = 2; }"#, + attrs! { symbol!("a") => attrs!{ symbol!("ac") => int!(2) } }, + ); +} + +#[test] +fn test_func() { + test_expr("(x: x) 1", int!(1)); + test_expr("(x: x) (x: x) 1", int!(1)); + test_expr("(x: y: x + y) 1 1", int!(2)); + test_expr("({ x, y }: x + y) { x = 1; y = 2; }", int!(3)); + test_expr("({ x, y, ... }: x + y) { x = 1; y = 2; z = 3; }", int!(3)); + test_expr( + "(inputs@{ x, y, ... }: x + inputs.y) { x = 1; y = 2; z = 3; }", + int!(3), + ); +} + +#[test] +#[ignore] +fn test_fib() { + test_expr( + "let fib = n: if n == 1 || n == 2 then 1 else (fib (n - 1)) + (fib (n - 2)); in fib 30", + int!(832040), + ) +} + +#[bench] +fn bench_fib(b: &mut Bencher) { + b.iter(|| { + test_expr( + "let fib = n: if n == 1 || n == 2 then 1 else (fib (n - 1)) + (fib (n - 2)); in fib 30", + int!(832040), + ); + black_box(()) + }) +} + diff --git a/src/eval/jit/compile.rs b/src/eval/jit/compile.rs new file mode 100644 index 0000000..dea820a --- /dev/null +++ b/src/eval/jit/compile.rs @@ -0,0 +1,129 @@ +#![allow(unused_variables)] + +use crate::ir::*; + +use super::JITContext; + +pub trait JITCompile { + fn compile<'gc>(self, ctx: &JITContext<'gc>); +} + +impl JITCompile for Attrs { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for List { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for HasAttr { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for BinOp { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for UnOp { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for Select { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for If { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for LoadFunc { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for Call { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for Let { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for With { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for Assert { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for ConcatStrings { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for Const { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for String { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for Var { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for Arg { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for LetVar { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for Thunk { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} + +impl JITCompile for Path { + fn compile<'gc>(self, ctx: &JITContext<'gc>) { + todo!() + } +} diff --git a/src/jit/helpers.rs b/src/eval/jit/helpers.rs similarity index 90% rename from src/jit/helpers.rs rename to src/eval/jit/helpers.rs index e8ccd65..73d87a2 100644 --- a/src/jit/helpers.rs +++ b/src/eval/jit/helpers.rs @@ -8,13 +8,11 @@ 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::eval::Engine; use crate::ty::internal::{Thunk, Value}; -use crate::vm::VM; -use super::{JITContext, JITValue, ValueTag}; +use super::{JITContext, JITValue, ValueTag, JITValueData}; pub struct Helpers<'ctx> { pub int_type: IntType<'ctx>, @@ -337,16 +335,8 @@ extern "C" fn helper_or(lhs: JITValue, rhs: JITValue) -> JITValue { } } -extern "C" fn helper_call<'gc>(func: JITValue, arg: JITValue, vm: *const VM<'gc>) -> JITValue { - let vm = unsafe { vm.as_ref() }.unwrap(); - let arg = Value::from(arg); - match func.tag { - ValueTag::Function => { - let func = Value::from(func).unwrap_func(); - func.call_compile(arg, vm).unwrap().clone().into() - } - _ => todo!(), - } +extern "C" fn helper_call<'gc>(func: JITValue, arg: JITValue, engine: *mut Engine) -> JITValue { + todo!() } extern "C" fn helper_arg(idx: usize, env: *const VmEnv) -> JITValue { @@ -369,28 +359,15 @@ extern "C" fn helper_lookup(sym: usize, env: *const VmEnv) -> JITValue { extern "C" fn helper_force<'gc>( thunk: JITValue, - vm: NonNull>, + vm: NonNull, jit: *const JITContext<'gc>, ) -> JITValue { if !matches!(thunk.tag, ValueTag::Thunk) { return thunk; } - - 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().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 _) }; - thunk.insert_value(val.into()); - val + todo!() } -extern "C" fn helper_new_thunk(opcodes: *const OpCodes) -> JITValue { - Value::Thunk(Thunk::new(unsafe { opcodes.as_ref() }.unwrap())).into() +extern "C" fn helper_new_thunk(opcodes: *const ()) -> JITValue { + todo!() } diff --git a/src/eval/jit/mod.rs b/src/eval/jit/mod.rs new file mode 100644 index 0000000..fa77dce --- /dev/null +++ b/src/eval/jit/mod.rs @@ -0,0 +1,140 @@ +use std::ops::Deref; +use std::rc::Rc; +use std::marker::PhantomData; + +use inkwell::OptimizationLevel; +use inkwell::builder::Builder; +use inkwell::context::Context; +use inkwell::execution_engine::ExecutionEngine; +use inkwell::module::Module; + +use crate::env::VmEnv; +use crate::ty::internal::{Value, Thunk}; + +mod helpers; +mod compile; + +pub use compile::JITCompile; +use helpers::Helpers; + +#[cfg(test)] +mod test; + +#[repr(u64)] +#[derive(Debug, Clone, Copy)] +pub enum ValueTag { + Null, + Int, + Float, + String, + Bool, + AttrSet, + List, + Function, + Thunk, + Path, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct JITValue { + tag: ValueTag, + data: JITValueData, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union JITValueData { + int: i64, + float: f64, + bool: bool, + ptr: *const (), +} + +impl<'gc> From for Value<'gc> { + fn from(value: JITValue) -> Self { + use ValueTag::*; + match value.tag { + Int => Value::Int(unsafe { value.data.int }), + Null => Value::Null, + Function => Value::Func(unsafe { Rc::from_raw(value.data.ptr as *const _) }), + Thunk => Value::Thunk(self::Thunk { + thunk: unsafe { Rc::from_raw(value.data.ptr as *const _) }, + }), + _ => todo!("not implemented for {:?}", value.tag), + } + } +} + +impl From> for JITValue { + fn from(value: Value) -> Self { + match value { + Value::Int(int) => JITValue { + tag: ValueTag::Int, + data: JITValueData { int }, + }, + 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: Rc::into_raw(thunk.thunk) as *const _, + }, + }, + _ => todo!(), + } + } +} + +pub struct JITFunc<'gc>(F<'gc>, PhantomData<&'gc mut ()>); +type F<'gc> = unsafe extern "C" fn(*const VmEnv<'gc>) -> JITValue; + +impl<'gc> From> for JITFunc<'gc> { + fn from(value: F<'gc>) -> Self { + Self(value, PhantomData) + } +} + +impl<'gc> Deref for JITFunc<'gc> { + type Target = F<'gc>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + + +pub struct JITContext<'ctx> { + context: &'ctx Context, + module: Module<'ctx>, + builder: Builder<'ctx>, + execution_engine: ExecutionEngine<'ctx>, + + helpers: Helpers<'ctx>, +} + +impl<'ctx> JITContext<'ctx> { + pub fn new(context: &'ctx Context) -> Self { + // force linker to link JIT engine + unsafe { + inkwell::llvm_sys::execution_engine::LLVMLinkInMCJIT(); + } + let module = context.create_module("nixjit"); + let execution_engine = module + .create_jit_execution_engine(OptimizationLevel::Aggressive) + .unwrap(); + let helpers = Helpers::new(context, &module, &execution_engine); + + JITContext { + execution_engine, + builder: context.create_builder(), + context, + module, + + helpers, + } + } +} diff --git a/src/jit/test.rs b/src/eval/jit/test.rs similarity index 84% rename from src/jit/test.rs rename to src/eval/jit/test.rs index 4a46e6e..ade3c43 100644 --- a/src/jit/test.rs +++ b/src/eval/jit/test.rs @@ -8,19 +8,14 @@ use inkwell::context::Context; use ecow::EcoString; -use crate::compile::compile; use crate::ir::downgrade; -use crate::jit::JITContext; +use super::JITContext; use crate::ty::common::Const; use crate::ty::public::*; -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); - assert_eq!(run(prog).unwrap(), expected); + todo!() } macro_rules! map { diff --git a/src/eval/mod.rs b/src/eval/mod.rs new file mode 100644 index 0000000..71bdae3 --- /dev/null +++ b/src/eval/mod.rs @@ -0,0 +1,141 @@ +use crate::ty::common::Const; +use crate::ty::internal::Value; +use crate::ir::{self, Downgraded}; +use crate::ty::public as p; +use crate::error::Result; +use crate::engine::Engine; + +pub mod jit; + +pub trait Evaluate { + fn eval<'a>(self, engine: &'a Engine) -> Result>; +} + +impl Evaluate for ir::Attrs { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::List { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::HasAttr { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::BinOp { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::UnOp { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::Select { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::If { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::LoadFunc { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::Call { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::Let { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::With { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::Assert { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::ConcatStrings { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::String { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::Const { + fn eval<'a>(self, _: &'a Engine) -> Result> { + match self.val { + Const::Null => Value::Null, + Const::Int(x) => Value::Int(x), + Const::Float(x) => Value::Float(x), + Const::Bool(x) => Value::Bool(x), + }.ok() + } +} + +impl Evaluate for ir::Var { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::Arg { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::LetVar { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::Thunk { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +impl Evaluate for ir::Path { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + todo!() + } +} + +pub fn eval(expr: Downgraded) -> Result { + todo!() +} diff --git a/src/ir.rs b/src/ir/mod.rs similarity index 78% rename from src/ir.rs rename to src/ir/mod.rs index df1765a..442a3fb 100644 --- a/src/ir.rs +++ b/src/ir/mod.rs @@ -7,9 +7,16 @@ use rnix::ast::HasEntry; use rnix::ast::{self, Expr}; use crate::builtins::ir_env; -use crate::compile::*; use crate::error::*; +use crate::ty::public::Symbol; use crate::ty::common as c; +use crate::ty::internal::Value; +use crate::eval::jit::{JITContext, JITCompile}; +use crate::eval::Evaluate; +use crate::engine::Engine; + +mod utils; +use utils::*; pub fn downgrade(expr: Expr) -> Result { let mut ctx = DowngradeContext::new(); @@ -19,9 +26,6 @@ pub fn downgrade(expr: Expr) -> Result { let ir = ir.resolve(&mut ctx, &env)?; Ok(Downgraded { top_level: ir, - consts: ctx.consts.into(), - symbols: ctx.symbols, - symmap: ctx.symmap, thunks: ctx.thunks.into(), funcs: ctx.funcs.into(), }) @@ -90,20 +94,29 @@ macro_rules! ir { } } } - impl Compile for Ir { - fn compile(self, ctx: &mut Compiler) { + + impl JITCompile for Ir { + fn compile(self, ctx: &JITContext) { match self { $(Ir::$ty(ir) => ir.compile(ctx),)* } } } + impl Evaluate for Ir { + fn eval<'a>(self, engine: &'a Engine) -> Result> { + match self { + $(Ir::$ty(ir) => ir.eval(engine),)* + } + } + } + $( $( #[$($x)*] )* #[derive(Clone, Debug)] - pub struct $ty { + pub struct $ty { $( pub $name : $elemtype, )* @@ -119,7 +132,7 @@ macro_rules! ir { } ir! { - Attrs => { stcs: HashMap, dyns: Vec }, + Attrs => { stcs: HashMap, dyns: Vec }, List => { items: Vec }, HasAttr => { lhs: Box, rhs: Vec }, BinOp => { lhs: Box, rhs: Box, kind: BinOpKind }, @@ -129,14 +142,13 @@ ir! { LoadFunc => { idx: usize }, Call => { func: Box, args: Vec }, - Let => { bindings: Vec<(usize, Ir)>, expr: Box }, + Let => { bindings: Vec<(EcoString, Ir)>, expr: Box }, With => { namespace: Box, expr: Box }, Assert => { assertion: Box, expr: Box }, ConcatStrings => { parts: Vec }, - #[derive(Copy)] - Const => { idx: usize }, - #[derive(Copy)] - Var => { sym: usize }, + Const => { val: c::Const }, + String => { val: EcoString }, + Var => { sym: EcoString }, #[derive(Copy)] Arg => { level: usize }, #[derive(Copy)] @@ -146,16 +158,18 @@ ir! { Path => { expr: Box }, } +impl> From for Const { + fn from(value: T) -> Self { + Const { val: value.into() } + } +} + #[derive(Clone, Debug)] pub struct DynamicAttrPair(pub Ir, pub Ir); pub struct DowngradeContext { thunks: Vec, funcs: Vec, - consts: Vec, - constmap: HashMap, - symbols: Vec, - symmap: HashMap, } struct Env<'a, 'env> { @@ -164,10 +178,10 @@ struct Env<'a, 'env> { } enum EnvNode<'a> { - Builtins(&'a HashMap), - Let(&'a Vec), - SingleArg(usize), - MultiArg(HashMap>, Option), + Builtins(&'a HashMap), + Let(&'a Vec), + SingleArg(EcoString), + MultiArg(HashMap>, Option), With, } @@ -180,28 +194,28 @@ enum LookupResult { } impl<'a, 'env> Env<'a, 'env> { - fn new(base: &'a HashMap) -> Self { + fn new(base: &'a HashMap) -> Self { Self { env: EnvNode::Builtins(base), prev: None, } } - fn enter_let(&'env self, map: &'a Vec) -> Self { + fn enter_let(&'env self, map: &'a Vec) -> Self { Self { env: EnvNode::Let(map), prev: Some(self), } } - fn enter_single_arg(&'env self, ident: usize) -> Self { + fn enter_single_arg(&'env self, ident: EcoString) -> Self { Self { env: EnvNode::SingleArg(ident), prev: Some(self), } } - fn enter_multi_arg(&'env self, map: HashMap>, alias: Option) -> Self { + fn enter_multi_arg(&'env self, map: HashMap>, alias: Option) -> Self { Self { env: EnvNode::MultiArg(map, alias), prev: Some(self), @@ -217,7 +231,7 @@ impl<'a, 'env> Env<'a, 'env> { fn _lookup( &self, - ident: usize, + ident: &EcoString, mut arg_level: usize, mut let_level: usize, has_with: bool, @@ -226,7 +240,7 @@ impl<'a, 'env> Env<'a, 'env> { let mut has_with = has_with; match &self.env { Builtins(map) => { - return if let Some(ir) = map.get(&ident) { + return if let Some(ir) = map.get(ident) { Ok(LookupResult::Builtin(ir.clone())) } else if has_with { Ok(LookupResult::With) @@ -235,7 +249,7 @@ impl<'a, 'env> Env<'a, 'env> { }; } Let(map) => { - if let Ok(idx) = map.binary_search(&ident) { + if let Ok(idx) = map.binary_search(ident) { return Ok(LookupResult::Let { level: let_level, idx, @@ -245,19 +259,19 @@ impl<'a, 'env> Env<'a, 'env> { } } SingleArg(arg) => { - if *arg == ident { + if arg == ident { return Ok(LookupResult::SingleArg { level: arg_level }); } else { arg_level += 1; } } MultiArg(set, alias) => { - if let Some(default) = set.get(&ident) { + if let Some(default) = set.get(ident) { return Ok(LookupResult::MultiArg { level: arg_level, default: default.clone(), }); - } else if *alias == Some(ident) { + } else if alias.as_ref() == Some(ident) { return Ok(LookupResult::SingleArg { level: arg_level }); } else { arg_level += 1; @@ -270,7 +284,7 @@ impl<'a, 'env> Env<'a, 'env> { .map_or_else(|| unreachable!(), |x| x) } - fn lookup(&self, ident: usize) -> core::result::Result { + fn lookup(&self, ident: &EcoString) -> core::result::Result { self._lookup(ident, 0, 0, false) } } @@ -280,19 +294,12 @@ impl DowngradeContext { DowngradeContext { thunks: Vec::new(), funcs: Vec::new(), - consts: Vec::new(), - constmap: HashMap::new(), - symbols: Vec::new(), - symmap: HashMap::new(), } } } pub struct Downgraded { pub top_level: Ir, - pub consts: Box<[c::Const]>, - pub symbols: Vec, - pub symmap: HashMap, pub thunks: Box<[Ir]>, pub funcs: Box<[Func]>, } @@ -310,33 +317,6 @@ impl DowngradeContext { LoadFunc { idx } } - pub fn new_const(&mut self, cnst: c::Const) -> Const { - if let Some(&idx) = self.constmap.get(&cnst) { - Const { idx } - } else { - self.constmap.insert(cnst.clone(), self.consts.len()); - self.consts.push(cnst); - Const { - idx: self.consts.len() - 1, - } - } - } - - pub fn new_sym(&mut self, sym: impl Into) -> usize { - let sym = sym.into(); - if let Some(&idx) = self.symmap.get(&sym) { - idx - } else { - self.symmap.insert(sym.clone(), self.symbols.len()); - self.symbols.push(sym); - self.symbols.len() - 1 - } - } - - fn get_sym(&self, idx: usize) -> &EcoString { - &self.symbols[idx] - } - fn resolve_func(&mut self, idx: usize, env: &Env) -> Result<()> { let self_ptr = self as *mut Self; self.funcs.get_mut(idx).map_or_else( @@ -384,7 +364,7 @@ impl Attrs { .as_mut() .try_unwrap_attrs().map_err(|_| Error::DowngradeError(format!( r#"attribute '{}' already defined"#, - ctx.get_sym(ident) + Symbol::from(ident) ))) .and_then(|attrs: &mut Attrs| attrs._insert(path, name, value, ctx)) } else { @@ -422,7 +402,7 @@ impl Attrs { if self.stcs.get(&ident).is_some() { return Err(Error::DowngradeError(format!( r#"attribute '{}' already defined"#, - ctx.get_sym(ident) + Symbol::from(ident) ))); } self.stcs.insert(ident, value); @@ -470,7 +450,7 @@ impl Attrs { pub enum Attr { Dynamic(Ir), Strs(ConcatStrings), - Str(usize), + Str(EcoString), } impl Attr { @@ -561,12 +541,11 @@ pub struct Func { #[derive(Clone, Debug)] pub enum Param { - Ident(usize), + Ident(EcoString), Formals { - // formals: Vec<(usize, Option)>, - formals: Vec<(usize, Option)>, + formals: Vec<(EcoString, Option)>, ellipsis: bool, - alias: Option, + alias: Option, }, } @@ -662,7 +641,7 @@ impl Downgrade for ast::Path { let parts = self .parts() .map(|part| match part { - ast::InterpolPart::Literal(lit) => ctx.new_const(lit.to_string().into()).ir().ok(), + ast::InterpolPart::Literal(lit) => String { val: lit.to_string().into() }.ir().ok(), ast::InterpolPart::Interpolation(interpol) => { interpol.expr().unwrap().downgrade(ctx) } @@ -698,7 +677,7 @@ impl Downgrade for ast::Str { .normalized_parts() .into_iter() .map(|part| match part { - ast::InterpolPart::Literal(lit) => ctx.new_const(lit.into()).ir().ok(), + ast::InterpolPart::Literal(lit) => String { val: lit.into() }.ir().ok(), ast::InterpolPart::Interpolation(interpol) => { interpol.expr().unwrap().downgrade(ctx) } @@ -729,11 +708,10 @@ impl ConcatStrings { impl Downgrade for ast::Literal { fn downgrade(self, ctx: &mut DowngradeContext) -> Result { match self.kind() { - ast::LiteralKind::Integer(int) => ctx.new_const(int.value().unwrap().into()), - ast::LiteralKind::Float(float) => ctx.new_const(float.value().unwrap().into()), - ast::LiteralKind::Uri(uri) => ctx.new_const(uri.to_string().into()), + ast::LiteralKind::Integer(int) => Const::from(int.value().unwrap()).ir(), + ast::LiteralKind::Float(float) => Const::from(float.value().unwrap()).ir(), + ast::LiteralKind::Uri(uri) => String { val: uri.to_string().into() }.ir() } - .ir() .ok() } } @@ -744,9 +722,15 @@ impl Const { } } +impl String { + fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result { + self.ir().ok() + } +} + impl Downgrade for ast::Ident { fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - let sym = ctx.new_sym(self.ident_token().unwrap().text()); + let sym = self.ident_token().unwrap().to_string().into(); Var { sym }.ir().ok() } } @@ -754,10 +738,10 @@ impl Downgrade for ast::Ident { impl Var { fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { use LookupResult::*; - match env - .lookup(self.sym) - .map_err(|_| Error::DowngradeError(format!("{} not found", ctx.get_sym(self.sym))))? - { + let Ok(res) = env.lookup(&self.sym) else { + return Err(Error::DowngradeError(format!("{} not found", Symbol::from(self.sym)))) + }; + match res { Builtin(ir) => ir, Let { level, idx } => LetVar { level, idx }.ir(), SingleArg { level } => Arg { level }.ir(), @@ -793,11 +777,11 @@ impl Downgrade for ast::AttrSet { let bindings = attrs .stcs .into_iter() - .sorted_by_key(|(k, _)| *k) + .sorted_by(|(a, _), (b, _)| a.cmp(b)) .collect::>(); let stcs = bindings .iter() - .map(|&(sym, _)| (sym, Var { sym }.ir())) + .map(|(sym, _)| (sym.clone(), Var { sym: sym.clone() }.ir())) .collect(); Let { bindings, @@ -971,7 +955,7 @@ impl Downgrade for ast::LegacyLet { let attrs = downgrade_attrs(self, ctx)?; Select { expr: attrs.ir().boxed(), - attrpath: vec![Attr::Str(ctx.new_sym("body"))], + attrpath: vec![Attr::Str("body".into())], default: None, } .ir() @@ -997,7 +981,7 @@ impl Downgrade for ast::LetIn { let ident = unwrap_ident(path.next().unwrap())?; if path.len() > 1 { let mut attrs = bindings - .entry(ident) + .entry(ident.clone()) .or_insert_with(|| { Attrs { stcs: HashMap::new(), @@ -1010,7 +994,7 @@ impl Downgrade for ast::LetIn { .map_err(|_| { Error::DowngradeError(format!( r#"attribute '{}' already defined"#, - ctx.get_sym(ident) + Symbol::from(ident) )) })?; while path.len() > 1 { @@ -1037,14 +1021,14 @@ impl Downgrade for ast::LetIn { } } } - let bindings = bindings.into_iter().sorted_by_key(|(k, _)| *k).collect(); + let bindings = bindings.into_iter().sorted_by(|(a, _), (b, _)| a.cmp(b)).collect(); Let { bindings, expr }.ir().ok() } } impl Let { fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { - let map = self.bindings.iter().map(|(sym, _)| *sym).sorted().collect(); + let map = self.bindings.iter().map(|(sym, _)| sym.clone()).sorted().collect(); let env = env.enter_let(&map); let bindings = self .bindings @@ -1091,15 +1075,15 @@ impl Downgrade for ast::Lambda { impl Func { fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { - let env = match &self.param { - Param::Ident(ident) => env.enter_single_arg(*ident), + let env = match self.param.clone() { + Param::Ident(ident) => env.enter_single_arg(ident), Param::Formals { formals, alias, .. } => env.enter_multi_arg( formals .iter() .cloned() .map(|(ident, default)| (ident, default.map(Ir::Thunk))) .collect(), - *alias, + alias, ), }; let body = self.body.resolve(ctx, &env)?.boxed(); @@ -1135,143 +1119,3 @@ impl Call { .ok() } } - -fn downgrade_param(param: ast::Param, ctx: &mut DowngradeContext) -> Result { - match param { - ast::Param::IdentParam(ident) => Ok(Param::Ident(ctx.new_sym(ident.to_string()))), - ast::Param::Pattern(pattern) => downgrade_pattern(pattern, ctx), - } -} - -fn downgrade_pattern(pattern: ast::Pattern, ctx: &mut DowngradeContext) -> Result { - let formals = pattern - .pat_entries() - .map(|entry| { - let ident = ctx.new_sym(entry.ident().unwrap().to_string()); - if entry.default().is_none() { - Ok((ident, None)) - } else { - entry - .default() - .unwrap() - .downgrade(ctx) - .map(|ok| (ident, Some(ctx.new_thunk(ok)))) - // .map(|ok| (ident, Some(ok))) - } - }) - .collect::>>()?; - let ellipsis = pattern.ellipsis_token().is_some(); - let alias = pattern - .pat_bind() - .map(|alias| ctx.new_sym(alias.ident().unwrap().to_string())); - Ok(Param::Formals { - formals, - ellipsis, - alias, - }) -} - -fn downgrade_attrs(has_entry: impl ast::HasEntry, ctx: &mut DowngradeContext) -> Result { - let entires = has_entry.entries(); - let mut attrs = Attrs { - stcs: HashMap::new(), - dyns: Vec::new(), - }; - - for entry in entires { - match entry { - ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut attrs.stcs, ctx)?, - ast::Entry::AttrpathValue(value) => downgrade_attrpathvalue(value, &mut attrs, ctx)?, - } - } - - Ok(attrs) -} - -fn downgrade_inherit( - inherit: ast::Inherit, - stcs: &mut HashMap, - ctx: &mut DowngradeContext, -) -> Result<()> { - let from = if let Some(from) = inherit.from() { - let from = from.expr().unwrap().downgrade(ctx)?; - Some(ctx.new_thunk(from)) - } else { - None - }; - for attr in inherit.attrs() { - let ident = match downgrade_attr(attr, ctx)? { - Attr::Str(ident) => ident, - _ => { - return Err(Error::DowngradeError( - "dynamic attributes not allowed in inherit".to_string(), - )); - } - }; - let expr = from.map_or_else( - || Var { sym: ident }.ir().ok(), - |from| { - Ok(Select { - expr: from.ir().boxed(), - attrpath: vec![Attr::Str(ident)], - default: None, - } - .ir()) - }, - )?; - assert!(stcs.insert(ident, expr).is_none()); - } - Ok(()) -} - -fn downgrade_attr(attr: ast::Attr, ctx: &mut DowngradeContext) -> Result { - use ast::Attr::*; - use ast::InterpolPart::*; - match attr { - Ident(ident) => Ok(Attr::Str(ctx.new_sym(ident.to_string()))), - Str(string) => { - let parts = string.normalized_parts(); - if parts.is_empty() { - Ok(Attr::Str(ctx.new_sym(""))) - } else if parts.len() == 1 { - match parts.into_iter().next().unwrap() { - Literal(ident) => Ok(Attr::Str(ctx.new_sym(ident))), - Interpolation(interpol) => { - Ok(Attr::Dynamic(interpol.expr().unwrap().downgrade(ctx)?)) - } - } - } else { - let parts = parts - .into_iter() - .map(|part| match part { - Literal(lit) => ctx.new_const(lit.into()).ir().ok(), - Interpolation(interpol) => interpol.expr().unwrap().downgrade(ctx), - }) - .collect::>>()?; - Ok(Attr::Strs(ConcatStrings { parts })) - } - } - Dynamic(dynamic) => Ok(Attr::Dynamic(dynamic.expr().unwrap().downgrade(ctx)?)), - } -} - -fn downgrade_attrpath(attrpath: ast::Attrpath, ctx: &mut DowngradeContext) -> Result> { - attrpath - .attrs() - .map(|attr| downgrade_attr(attr, ctx)) - .collect::>>() -} - -fn downgrade_attrpathvalue( - value: ast::AttrpathValue, - attrs: &mut Attrs, - ctx: &mut DowngradeContext, -) -> Result<()> { - let path = downgrade_attrpath(value.attrpath().unwrap(), ctx)?; - let value = value.value().unwrap().downgrade(ctx)?; - let value = match value { - x @ Ir::Const(_) => x, - x => ctx.new_thunk(x).ir(), - }; - attrs.insert(path, value, ctx) -} diff --git a/src/ir/utils.rs b/src/ir/utils.rs new file mode 100644 index 0000000..f5529ce --- /dev/null +++ b/src/ir/utils.rs @@ -0,0 +1,144 @@ +use rnix::ast; + +use super::*; + + +pub fn downgrade_param(param: ast::Param, ctx: &mut DowngradeContext) -> Result { + match param { + ast::Param::IdentParam(ident) => Ok(Param::Ident(ident.to_string().into())), + ast::Param::Pattern(pattern) => downgrade_pattern(pattern, ctx), + } +} + +pub fn downgrade_pattern(pattern: ast::Pattern, ctx: &mut DowngradeContext) -> Result { + let formals = pattern + .pat_entries() + .map(|entry| { + let ident = entry.ident().unwrap().to_string().into(); + if entry.default().is_none() { + Ok((ident, None)) + } else { + entry + .default() + .unwrap() + .downgrade(ctx) + .map(|ok| (ident, Some(ctx.new_thunk(ok)))) + } + }) + .collect::>>()?; + let ellipsis = pattern.ellipsis_token().is_some(); + let alias = pattern + .pat_bind() + .map(|alias| alias.ident().unwrap().to_string().into()); + Ok(Param::Formals { + formals, + ellipsis, + alias, + }) +} + +pub fn downgrade_attrs(has_entry: impl ast::HasEntry, ctx: &mut DowngradeContext) -> Result { + let entires = has_entry.entries(); + let mut attrs = Attrs { + stcs: HashMap::new(), + dyns: Vec::new(), + }; + + for entry in entires { + match entry { + ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut attrs.stcs, ctx)?, + ast::Entry::AttrpathValue(value) => downgrade_attrpathvalue(value, &mut attrs, ctx)?, + } + } + + Ok(attrs) +} + +pub fn downgrade_inherit( + inherit: ast::Inherit, + stcs: &mut HashMap, + ctx: &mut DowngradeContext, +) -> Result<()> { + let from = if let Some(from) = inherit.from() { + let from = from.expr().unwrap().downgrade(ctx)?; + Some(ctx.new_thunk(from)) + } else { + None + }; + for attr in inherit.attrs() { + let ident = match downgrade_attr(attr, ctx)? { + Attr::Str(ident) => ident, + _ => { + return Err(Error::DowngradeError( + "dynamic attributes not allowed in inherit".to_string(), + )); + } + }; + let expr = from.map_or_else( + || Var { sym: ident.clone() }.ir().ok(), + |from| { + Ok(Select { + expr: from.ir().boxed(), + attrpath: vec![Attr::Str(ident.clone())], + default: None, + } + .ir()) + }, + )?; + // TODO: Error Handling + assert!(stcs.insert(ident, expr).is_none()); + } + Ok(()) +} + +pub fn downgrade_attr(attr: ast::Attr, ctx: &mut DowngradeContext) -> Result { + use ast::Attr::*; + use ast::InterpolPart::*; + match attr { + Ident(ident) => Ok(Attr::Str(ident.to_string().into())), + Str(string) => { + let parts = string.normalized_parts(); + if parts.is_empty() { + Ok(Attr::Str("".into())) + } else if parts.len() == 1 { + match parts.into_iter().next().unwrap() { + Literal(ident) => Ok(Attr::Str(ident.into())), + Interpolation(interpol) => { + Ok(Attr::Dynamic(interpol.expr().unwrap().downgrade(ctx)?)) + } + } + } else { + let parts = parts + .into_iter() + .map(|part| match part { + Literal(lit) => String { val: lit.into() }.ir().ok(), + Interpolation(interpol) => interpol.expr().unwrap().downgrade(ctx), + }) + .collect::>>()?; + Ok(Attr::Strs(ConcatStrings { parts })) + } + } + Dynamic(dynamic) => Ok(Attr::Dynamic(dynamic.expr().unwrap().downgrade(ctx)?)), + } +} + +pub fn downgrade_attrpath(attrpath: ast::Attrpath, ctx: &mut DowngradeContext) -> Result> { + attrpath + .attrs() + .map(|attr| downgrade_attr(attr, ctx)) + .collect::>>() +} + +pub fn downgrade_attrpathvalue( + value: ast::AttrpathValue, + attrs: &mut Attrs, + ctx: &mut DowngradeContext, +) -> Result<()> { + let path = downgrade_attrpath(value.attrpath().unwrap(), ctx)?; + let value = value.value().unwrap().downgrade(ctx)?; + let value = match value { + x @ Ir::Const(_) => x, + x => ctx.new_thunk(x).ir(), + }; + attrs.insert(path, value, ctx) +} diff --git a/src/jit/mod.rs b/src/jit/mod.rs deleted file mode 100644 index baed8f7..0000000 --- a/src/jit/mod.rs +++ /dev/null @@ -1,468 +0,0 @@ -use std::marker::PhantomData; -use std::ops::Deref; -use std::rc::Rc; - -use inkwell::OptimizationLevel; -use inkwell::builder::Builder; -use inkwell::context::Context; -use inkwell::execution_engine::ExecutionEngine; -use inkwell::module::Module; -use inkwell::values::{BasicValueEnum, FunctionValue, PointerValue}; - -use crate::bytecode::{OpCode, UnOp}; -use crate::env::VmEnv; -use crate::error::*; -use crate::stack::Stack; -use crate::ty::common::Const; -use crate::ty::internal::{Thunk, Value}; -use crate::vm::VM; - -mod helpers; - -use helpers::Helpers; - -#[cfg(test)] -mod test; - -const STACK_SIZE: usize = 8 * 1024 / size_of::(); - -#[repr(u64)] -#[derive(Debug, Clone, Copy)] -pub enum ValueTag { - Null, - Int, - Float, - String, - Bool, - AttrSet, - List, - Function, - Thunk, - Path, -} - -#[repr(C)] -#[derive(Clone, Copy)] -pub struct JITValue { - tag: ValueTag, - data: JITValueData, -} - -#[repr(C)] -#[derive(Clone, Copy)] -pub union JITValueData { - int: i64, - float: f64, - bool: bool, - ptr: *const (), -} - -impl<'gc> From for Value<'gc> { - fn from(value: JITValue) -> Self { - use ValueTag::*; - match value.tag { - Int => Value::Int(unsafe { value.data.int }), - Null => Value::Null, - Function => Value::Func(unsafe { Rc::from_raw(value.data.ptr as *const _) }), - Thunk => Value::Thunk(self::Thunk { - thunk: unsafe { Rc::from_raw(value.data.ptr as *const _) }, - }), - _ => todo!("not implemented for {:?}", value.tag), - } - } -} - -impl From> for JITValue { - fn from(value: Value) -> Self { - match value { - Value::Int(int) => JITValue { - tag: ValueTag::Int, - data: JITValueData { int }, - }, - 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: Rc::into_raw(thunk.thunk) as *const _, - }, - }, - _ => todo!(), - } - } -} - -pub struct JITFunc<'gc>(F<'gc>, PhantomData<&'gc mut ()>); -type F<'gc> = unsafe extern "C" fn(*const VmEnv<'gc>) -> JITValue; - -impl<'gc> From> for JITFunc<'gc> { - fn from(value: F<'gc>) -> Self { - Self(value, PhantomData) - } -} - -impl<'gc> Deref for JITFunc<'gc> { - type Target = F<'gc>; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -pub struct JITContext<'gc> { - context: &'gc Context, - module: Module<'gc>, - builder: Builder<'gc>, - execution_engine: ExecutionEngine<'gc>, - - helpers: Helpers<'gc>, -} - -impl<'gc> JITContext<'gc> { - pub fn new(context: &'gc Context) -> Self { - // force linker to link JIT engine - unsafe { - inkwell::llvm_sys::execution_engine::LLVMLinkInMCJIT(); - } - let module = context.create_module("nixjit"); - let execution_engine = module - .create_jit_execution_engine(OptimizationLevel::Aggressive) - .unwrap(); - let helpers = Helpers::new(context, &module, &execution_engine); - - JITContext { - execution_engine, - builder: context.create_builder(), - context, - module, - - helpers, - } - } - - pub fn new_ptr(&self, ptr: *const T) -> PointerValue<'gc> { - self.builder - .build_int_to_ptr( - self.helpers.int_type.const_int(ptr as _, false), - self.helpers.ptr_type, - "ptrconv", - ) - .unwrap() - } - - pub fn compile_seq( - &self, - mut opcodes: impl ExactSizeIterator, - 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 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, &mut stack, func_, len)?; - - assert_eq!(stack.len(), 1); - let value = stack.pop(); - let exit = self.context.append_basic_block(func_, "exit"); - self.builder.build_unconditional_branch(exit)?; - self.builder.position_at_end(exit); - self.builder.build_return(Some(&value))?; - if func_.verify(true) { - unsafe { - let name = func_.get_name().to_str().unwrap(); - let func = self.execution_engine.get_function(name).unwrap(); - Ok(JITFunc(func.as_raw(), PhantomData)) - } - } else { - todo!() - } - } - - fn build_expr( - &self, - iter: &mut impl Iterator, - vm: &VM<'gc>, - env: 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)?; - length -= 1; - if br > 0 { - let consq = self.context.append_basic_block(func, "consq"); - let alter = self.context.append_basic_block(func, "alter"); - let cont = self.context.append_basic_block(func, "cont"); - let cond = self - .builder - .build_alloca(self.helpers.value_type, "cond_alloca")?; - let result = self - .builder - .build_alloca(self.helpers.value_type, "result_alloca")?; - self.builder.build_store(cond, stack.pop())?; - self.builder.build_conditional_branch( - self.builder - .build_load( - self.context.bool_type(), - self.builder.build_struct_gep( - self.helpers.value_type, - cond, - 1, - "gep_cond", - )?, - "load_cond", - )? - .into_int_value(), - consq, - alter, - )?; - - length -= br; - self.builder.position_at_end(consq); - 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, stack, func, br)?; - self.builder.build_store(result, stack.pop())?; - self.builder.build_unconditional_branch(cont)?; - - self.builder.position_at_end(cont); - stack.push(self.builder.build_load( - self.helpers.value_type, - result, - "load_result", - )?)?; - } - } - if length > 0 { - self.single_op(iter.next().unwrap(), vm, env, stack) - } else { - Ok(0) - } - } - - #[inline(always)] - fn single_op( - &self, - opcode: OpCode, - vm: &VM<'_>, - env: PointerValue<'gc>, - stack: &mut Stack, CAP>, - ) -> Result { - match opcode { - OpCode::Const { idx } => { - use Const::*; - match vm.get_const(idx) { - Int(int) => stack.push(self.helpers.new_int(int))?, - Float(float) => stack.push(self.helpers.new_float(float))?, - Bool(bool) => stack.push(self.helpers.new_bool(bool))?, - String(string) => stack.push(self.helpers.const_string(string.as_ptr()))?, - Null => stack.push(self.helpers.new_null())?, - } - } - OpCode::LoadThunk { idx } => stack.push( - self.builder - .build_direct_call( - self.helpers.new_thunk, - &[self.new_ptr(vm.get_thunk(idx) as *const _).into()], - "call_capture_env", - )? - .try_as_basic_value() - .unwrap_left(), - )?, - OpCode::ForceValue => { - let thunk = stack.pop(); - let _ = stack.push( - self.builder - .build_direct_call( - self.helpers.force, - &[ - thunk.into(), - self.new_ptr(vm as *const _).into(), - self.new_ptr(self as *const _).into(), - ], - "call_force", - )? - .try_as_basic_value() - .left() - .unwrap(), - ); - } - OpCode::CaptureEnv => { - let thunk = *stack.tos(); - self.builder.build_direct_call( - self.helpers.capture_env, - &[thunk.into(), env.into()], - "call_capture_env", - )?; - } - OpCode::UnOp { op } => { - use UnOp::*; - let rhs = stack.pop(); - stack.push(match op { - Neg => self - .builder - .build_direct_call(self.helpers.neg, &[rhs.into(), env.into()], "call_neg")? - .try_as_basic_value() - .left() - .unwrap(), - Not => self - .builder - .build_direct_call(self.helpers.not, &[rhs.into(), env.into()], "call_neg")? - .try_as_basic_value() - .left() - .unwrap(), - })? - } - OpCode::BinOp { op } => { - use crate::bytecode::BinOp; - let rhs = stack.pop(); - let lhs = stack.pop(); - match op { - BinOp::Add => { - let result = self - .builder - .build_direct_call( - self.helpers.add, - &[lhs.into(), rhs.into()], - "call_add", - )? - .try_as_basic_value() - .left() - .unwrap(); - stack.push(result)?; - } - BinOp::Sub => { - let result = self - .builder - .build_direct_call( - self.helpers.sub, - &[lhs.into(), rhs.into()], - "call_add", - )? - .try_as_basic_value() - .left() - .unwrap(); - stack.push(result)?; - } - BinOp::Eq => { - let result = self - .builder - .build_direct_call( - self.helpers.eq, - &[lhs.into(), rhs.into()], - "call_eq", - )? - .try_as_basic_value() - .left() - .unwrap(); - stack.push(result)?; - } - BinOp::Or => { - let result = self - .builder - .build_direct_call( - self.helpers.or, - &[lhs.into(), rhs.into()], - "call_or", - )? - .try_as_basic_value() - .left() - .unwrap(); - stack.push(result)?; - } - _ => todo!("BinOp::{:?} not implemented in JIT", op), - } - } - OpCode::Arg { level } => stack.push( - self.builder - .build_direct_call( - self.helpers.arg, - &[ - self.helpers - .ptr_int_type - .const_int(level as u64, false) - .into(), - env.into(), - ], - "call_arg", - ) - .unwrap() - .try_as_basic_value() - .left() - .unwrap(), - )?, - OpCode::LookUpLet { level, idx } => stack.push( - self.builder - .build_direct_call( - self.helpers.lookup_let, - &[ - self.helpers - .ptr_int_type - .const_int(level as u64, false) - .into(), - self.helpers - .ptr_int_type - .const_int(idx as u64, false) - .into(), - env.into(), - ], - "call_lookup_let", - ) - .unwrap() - .try_as_basic_value() - .left() - .unwrap(), - )?, - OpCode::LookUp { sym } => stack.push( - self.builder - .build_direct_call( - self.helpers.lookup, - &[ - self.helpers - .ptr_int_type - .const_int(sym as u64, false) - .into(), - env.into(), - ], - "call_lookup", - ) - .unwrap() - .try_as_basic_value() - .left() - .unwrap(), - )?, - OpCode::Call => { - let arg = stack.pop(); - let func = stack.pop(); - let ret = self - .builder - .build_direct_call( - self.helpers.call, - &[func.into(), arg.into(), self.new_ptr(vm).into()], - "call", - )? - .try_as_basic_value() - .left() - .unwrap(); - stack.push(ret)?; - } - OpCode::JmpIfFalse { step } => return Ok(step), - OpCode::Jmp { step } => return Ok(step), - _ => todo!("{opcode:?} not implemented"), - } - Ok(0) - } -} diff --git a/src/lib.rs b/src/lib.rs index f740dae..b4076ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,13 @@ #![cfg_attr(test, feature(test))] #![allow(dead_code)] -#![feature(iter_collect_into)] -#![feature(arbitrary_self_types)] mod builtins; -mod bytecode; mod env; mod stack; mod ty; -pub mod compile; pub mod error; pub mod ir; -pub mod jit; -pub mod vm; - +pub mod eval; +pub mod engine; pub use ty::public::Value; diff --git a/src/ty/common.rs b/src/ty/common.rs index a829517..524a4ad 100644 --- a/src/ty/common.rs +++ b/src/ty/common.rs @@ -6,12 +6,12 @@ use ecow::EcoString; #[derive(Clone, Debug, PartialEq, Constructor, Hash)] pub struct Catchable { - msg: String, + msg: EcoString, } -impl From for Catchable { - fn from(value: String) -> Self { - Catchable { msg: value } +impl> From for Catchable { + fn from(value: T) -> Self { + Catchable { msg: value.into() } } } @@ -26,7 +26,6 @@ pub enum Const { Bool(bool), Int(i64), Float(f64), - String(EcoString), Null, } @@ -38,7 +37,6 @@ impl Hash for Const { Int(x) => x.hash(state), Float(x) => x.to_bits().hash(state), Bool(x) => x.hash(state), - String(x) => x.hash(state), Null => (), } } @@ -51,7 +49,6 @@ impl Display for Const { Int(x) => write!(f, "{x}"), Float(x) => write!(f, "{x}"), Bool(x) => write!(f, "{x}"), - String(x) => write!(f, "{x:?}"), Null => write!(f, "null"), } } @@ -75,24 +72,6 @@ impl From for Const { } } -impl From for Const { - fn from(value: EcoString) -> Self { - Const::String(value) - } -} - -impl From for Const { - fn from(value: String) -> Self { - Const::String(value.into()) - } -} - -impl From<&str> for Const { - fn from(value: &str) -> Self { - Const::String(value.into()) - } -} - impl PartialEq for Const { fn eq(&self, other: &Self) -> bool { use Const::*; @@ -102,7 +81,6 @@ impl PartialEq for Const { (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 == b, _ => false, } } diff --git a/src/ty/internal/attrset.rs b/src/ty/internal/attrset.rs index e22907d..3c18f3b 100644 --- a/src/ty/internal/attrset.rs +++ b/src/ty/internal/attrset.rs @@ -1,11 +1,12 @@ use std::ops::Deref; use std::rc::Rc; +use ecow::EcoString; use derive_more::Constructor; use hashbrown::{HashMap, HashSet}; use itertools::Itertools; -use crate::vm::VM; +use crate::engine::Engine; use super::super::public as p; use super::Value; @@ -13,17 +14,17 @@ use super::Value; #[repr(transparent)] #[derive(Constructor, Clone, PartialEq)] pub struct AttrSet<'gc> { - data: HashMap>, + data: HashMap>, } -impl<'gc> From>> for AttrSet<'gc> { - fn from(data: HashMap>) -> Self { +impl<'gc> From>> for AttrSet<'gc> { + fn from(data: HashMap>) -> Self { Self { data } } } impl<'gc> Deref for AttrSet<'gc> { - type Target = HashMap>; + type Target = HashMap>; fn deref(&self) -> &Self::Target { &self.data } @@ -36,57 +37,57 @@ impl<'gc> AttrSet<'gc> { } } - pub fn push_attr_force(&mut self, sym: usize, val: Value<'gc>) { + pub fn push_attr_force(&mut self, sym: EcoString, val: Value<'gc>) { self.data.insert(sym, val); } - pub fn push_attr(&mut self, sym: usize, val: Value<'gc>) { + pub fn push_attr(&mut self, sym: EcoString, val: Value<'gc>) { if self.data.get(&sym).is_some() { todo!() } self.data.insert(sym, val); } - pub fn select(&self, sym: usize) -> Option> { - self.data.get(&sym).cloned() + pub fn select(&self, sym: &EcoString) -> Option> { + self.data.get(sym).cloned() } - pub fn has_attr(&self, sym: usize) -> bool { - self.data.get(&sym).is_some() + pub fn has_attr(&self, sym: &EcoString) -> bool { + self.data.get(sym).is_some() } pub fn update(&mut self, other: &AttrSet<'gc>) { for (k, v) in other.data.iter() { - self.push_attr_force(*k, v.clone()) + self.push_attr_force(k.clone(), v.clone()) } } - pub fn as_inner(&self) -> &HashMap> { + pub fn as_inner(&self) -> &HashMap> { &self.data } - pub fn into_inner(self: Rc) -> Rc>> { + pub fn into_inner(self: Rc) -> Rc>> { unsafe { std::mem::transmute(self) } } - pub fn from_inner(data: HashMap>) -> Self { + pub fn from_inner(data: HashMap>) -> Self { Self { data } } pub fn eq_impl(&self, other: &AttrSet<'gc>) -> bool { self.data.iter().len() == other.data.iter().len() && std::iter::zip( - self.data.iter().sorted_by_key(|(k, _)| **k), - self.data.iter().sorted_by_key(|(k, _)| **k), + self.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)), + other.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)), ) .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, engine: &'gc Engine, seen: &mut HashSet>) -> p::Value { p::Value::AttrSet(p::AttrSet::new( self.data .iter() - .map(|(&sym, value)| (vm.get_sym(sym), value.to_public(vm, seen))) + .map(|(sym, value)| (sym.as_str().into(), value.to_public(engine, seen))) .collect(), )) } diff --git a/src/ty/internal/func.rs b/src/ty/internal/func.rs index 5bdaadc..7240b9e 100644 --- a/src/ty/internal/func.rs +++ b/src/ty/internal/func.rs @@ -1,67 +1,20 @@ -use std::cell::{Cell, OnceCell}; use std::rc::Rc; -use crate::bytecode::Func as BFunc; use crate::env::VmEnv; -use crate::error::Result; + use crate::ir; -use crate::jit::JITFunc; -use crate::ty::internal::Value; -use crate::vm::VM; - -#[derive(Debug, Clone)] -pub enum Param { - Ident(usize), - Formals { - formals: Vec<(usize, Option)>, - ellipsis: bool, - alias: Option, - }, -} - -impl From for Param { - fn from(value: ir::Param) -> Self { - match value { - ir::Param::Ident(ident) => Param::Ident(ident), - ir::Param::Formals { - formals, - ellipsis, - alias, - } => Param::Formals { - formals: formals - .into_iter() - .map(|(sym, default)| (sym, default.map(|default| default.idx))) - .collect(), - ellipsis, - alias, - }, - } - } -} - pub struct Func<'gc> { - pub func: &'gc BFunc, + pub func: &'gc ir::Func, pub env: Rc>, - pub compiled: OnceCell>, - pub count: Cell, } impl<'gc> Func<'gc> { - pub fn new(func: &'gc BFunc, env: Rc>) -> Self { + pub fn new(func: &'gc ir::Func, env: Rc>) -> Self { Self { func, env, - compiled: OnceCell::new(), - count: Cell::new(0), } } - - 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()) - } } impl PartialEq for Func<'_> { diff --git a/src/ty/internal/list.rs b/src/ty/internal/list.rs index 94b59a8..c611a8d 100644 --- a/src/ty/internal/list.rs +++ b/src/ty/internal/list.rs @@ -4,7 +4,7 @@ use hashbrown::HashSet; use crate::env::VmEnv; use crate::ty::public as p; -use crate::vm::VM; +use crate::engine::Engine; use super::Value; @@ -52,11 +52,11 @@ impl<'gc> List<'gc> { self.data } - pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet>) -> p::Value { + pub fn to_public(&self, engine: &'gc Engine, seen: &mut HashSet>) -> p::Value { p::Value::List(p::List::new( self.data .iter() - .map(|value| value.clone().to_public(vm, seen)) + .map(|value| value.clone().to_public(engine, seen)) .collect(), )) } diff --git a/src/ty/internal/mod.rs b/src/ty/internal/mod.rs index da7c9ca..783563e 100644 --- a/src/ty/internal/mod.rs +++ b/src/ty/internal/mod.rs @@ -3,16 +3,16 @@ use std::hash::Hash; use std::rc::{Rc, Weak}; use derive_more::{IsVariant, Unwrap}; +use ecow::EcoString; use hashbrown::HashSet; use replace_with::replace_with_or_abort; use super::common::*; -use super::public as p; +use super::public::{self as p, Symbol}; -use crate::bytecode::OpCodes; use crate::env::VmEnv; use crate::error::*; -use crate::vm::VM; +use crate::engine::Engine; mod attrset; mod func; @@ -30,12 +30,12 @@ pub enum Value<'gc> { Int(i64), Float(f64), Bool(bool), - String(Rc), + String(EcoString), Null, Thunk(Thunk<'gc>), AttrSet(Rc>), List(Rc>), - Catchable(Rc), + Catchable(EcoString), PrimOp(Rc), PartialPrimOp(Rc>), Func(Rc>), @@ -151,7 +151,7 @@ impl<'gc> Value<'gc> { } } - pub fn call(&mut self, arg: Self, vm: &VM) -> Result<()> { + pub fn call(&mut self, arg: Self, vm: &Engine) -> Result<()> { use Value::*; if matches!(arg, Value::Catchable(_)) { *self = arg; @@ -239,7 +239,7 @@ impl<'gc> Value<'gc> { (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); + a.push_str(&b); String(a) } (a @ Value::Catchable(_), _) => a, @@ -282,7 +282,7 @@ impl<'gc> Value<'gc> { pub fn concat_string(&mut self, mut other: Self) -> &mut Self { match (self.coerce_to_string(), other.coerce_to_string()) { (Value::String(a), Value::String(b)) => { - Rc::make_mut(a).push_str(b.as_str()); + a.push_str(b.as_str()); } (_, Value::Catchable(_)) => *self = other, (Value::Catchable(_), _) => (), @@ -317,7 +317,7 @@ impl<'gc> Value<'gc> { } } - pub fn push_attr(&mut self, sym: usize, val: Self) -> &mut Self { + pub fn push_attr(&mut self, sym: EcoString, val: Self) -> &mut Self { if let Value::AttrSet(attrs) = self { Rc::make_mut(attrs).push_attr(sym, val); } else if let Value::Catchable(_) = self { @@ -343,11 +343,11 @@ impl<'gc> Value<'gc> { } } - pub fn select(&mut self, sym: usize, vm: &VM<'gc>) -> Result<&mut Self> { + pub fn select(&mut self, sym: &EcoString) -> Result<&mut Self> { let val = match self { Value::AttrSet(attrs) => attrs .select(sym) - .ok_or_else(|| Error::EvalError(format!("{} not found", vm.get_sym(sym)))), + .ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(sym.as_str())))), Value::Catchable(_) => return Ok(self), _ => Err(Error::EvalError(format!( "cannot select from {:?}", @@ -358,7 +358,7 @@ impl<'gc> Value<'gc> { Ok(self) } - pub fn select_with_default(&mut self, sym: usize, default: Self) -> Result<&mut Self> { + pub fn select_with_default(&mut self, sym: &EcoString, default: Self) -> Result<&mut Self> { let val = match self { Value::AttrSet(attrs) => attrs.select(sym).unwrap_or(default), Value::Catchable(_) => return Ok(self), @@ -373,7 +373,7 @@ impl<'gc> Value<'gc> { Ok(self) } - pub fn has_attr(&mut self, sym: usize) -> &mut Self { + pub fn has_attr(&mut self, sym: &EcoString) -> &mut Self { if let Value::AttrSet(attrs) = self { let val = Value::Bool(attrs.has_attr(sym)); *self = val; @@ -393,7 +393,7 @@ impl<'gc> Value<'gc> { self } - pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet>) -> p::Value { + pub fn to_public(&self, vm: &'gc Engine, seen: &mut HashSet>) -> p::Value { use self::Value::*; use p::Value; if seen.contains(self) { @@ -408,11 +408,11 @@ impl<'gc> Value<'gc> { seen.insert(self.clone()); list.to_public(vm, seen) } - Catchable(catchable) => Value::Catchable(catchable.as_ref().clone().into()), + Catchable(catchable) => Value::Catchable(catchable.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_str().into())), + String(x) => Value::String(x.clone()), Null => Value::Const(Const::Null), Thunk(_) => Value::Thunk, PrimOp(primop) => Value::PrimOp(primop.name), @@ -428,6 +428,9 @@ pub struct Thunk<'gc> { pub thunk: Rc>>, } +// TODO: impl +type OpCodes = (); + #[derive(IsVariant, Unwrap)] pub enum _Thunk<'gc> { Code(&'gc OpCodes, Option>), diff --git a/src/ty/internal/primop.rs b/src/ty/internal/primop.rs index 85c436d..483d678 100644 --- a/src/ty/internal/primop.rs +++ b/src/ty/internal/primop.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use derive_more::Constructor; use crate::error::Result; -use crate::vm::VM; +use crate::engine::Engine; use super::Value; @@ -11,7 +11,7 @@ use super::Value; pub struct PrimOp { pub name: &'static str, arity: usize, - func: for<'gc> fn(Vec>, &VM) -> Result>, + func: for<'gc> fn(Vec>, &Engine) -> Result>, } impl PartialEq for PrimOp { @@ -21,7 +21,7 @@ impl PartialEq for PrimOp { } impl PrimOp { - pub fn call<'gc>(&self, arg: Value<'gc>, vm: &VM) -> Result> { + pub fn call<'gc>(&self, arg: Value<'gc>, ctx: &Engine) -> Result> { let mut args = Vec::with_capacity(self.arity); args.push(arg); if self.arity > 1 { @@ -33,7 +33,7 @@ impl PrimOp { })) .ok() } else { - (self.func)(args, vm) + (self.func)(args, ctx) } } } @@ -43,7 +43,7 @@ pub struct PartialPrimOp<'gc> { pub name: &'static str, arity: usize, args: Vec>, - func: fn(Vec>, &VM) -> Result>, + func: fn(Vec>, &Engine) -> Result>, } impl PartialEq for PartialPrimOp<'_> { @@ -53,14 +53,14 @@ impl PartialEq for PartialPrimOp<'_> { } impl<'gc> PartialPrimOp<'gc> { - pub fn call(self: &mut Rc, arg: Value<'gc>, vm: &VM) -> Result> { + pub fn call(self: &mut Rc, arg: Value<'gc>, ctx: &Engine) -> 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)) + Some(func(std::mem::take(&mut self_mut.args), ctx)) } else { None } diff --git a/src/ty/public.rs b/src/ty/public.rs index 22a17ee..2eb1aec 100644 --- a/src/ty/public.rs +++ b/src/ty/public.rs @@ -108,6 +108,7 @@ impl Display for List { #[derive(IsVariant, Unwrap, Clone, Debug, PartialEq)] pub enum Value { Const(Const), + String(EcoString), AttrSet(AttrSet), List(List), Catchable(Catchable), @@ -123,6 +124,7 @@ impl Display for Value { use Value::*; match self { Const(x) => write!(f, "{x}"), + String(x) => write!(f, "{x}"), AttrSet(x) => write!(f, "{x}"), List(x) => write!(f, "{x}"), Catchable(x) => write!(f, "{x}"),