diff --git a/src/bin/scc.rs b/src/bin/scc.rs index c617e0e..ca2d044 100644 --- a/src/bin/scc.rs +++ b/src/bin/scc.rs @@ -39,7 +39,7 @@ fn main() -> Result<()> { } let expr = root.tree().expr().unwrap(); let downgraded = unwrap!(downgrade(expr)); - println!("{:?}", downgraded.graph); + println!("{:?}", downgraded); } Err(ReadlineError::Interrupted) => { println!("CTRL-C"); diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 4527719..fe95a73 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -3,11 +3,11 @@ use std::rc::Rc; use hashbrown::HashSet; use priority_queue::PriorityQueue; -use crate::env::VmEnv; +use crate::env::Env; use crate::error::Result; use crate::eval::Evaluate; use crate::ir::{Dep, Downgraded, Ir, SccNode}; -use crate::ty::internal::{self as i, ThunkRef}; +use crate::ty::internal as i; use crate::ty::public::Value; #[cfg(test)] @@ -43,38 +43,46 @@ impl Engine { } pub fn eval(&mut self, graph: Vec) -> Result { - let mut env = VmEnv::new(); + let mut env = Env::new(); let last = graph.last().unwrap().members[0]; for SccNode { members, .. } in graph.into_iter() { - // TODO: - assert!(members.len() == 1); - for member in members.into_iter() { - let val = self.thunks[member].clone().eval(self, &mut env)?; - env.insert_cache(member, val); + if members.len() == 1 { + for member in members.into_iter() { + let val = self.thunks[member].clone().eval(self, &mut env)?; + env.insert_cache(member, val); + } + } else { + todo!(); + for member in members.into_iter() { + let val = self.thunks[member].clone().eval(self, &mut env)?; + env.insert_cache(member, val); + } } } env.lookup_cache(last, |_| unreachable!()).map(|mut val| { Ok(val - .force(self, &mut env)? .to_public(self, &mut HashSet::new())) })? } - pub fn eval_thunk(&mut self, idx: usize, env: &mut VmEnv) -> Result { - self.thunks[idx].clone().eval(self, env) + pub fn eval_thunk(&mut self, idx: usize, env: &mut Env) -> Result { + let self_mut = unsafe { &mut *(self as *mut Self) }; + self.thunks[idx].eval(self_mut, env) } - pub fn eval_func_deps(&mut self, idx: usize, env: &mut VmEnv) -> Result<()> { + pub fn eval_func_deps(&mut self, idx: usize, env: &mut Env) -> Result<()> { for dep in self.func_deps[idx - self.func_offset].clone() { match dep { Dep::Arg(idx) => { - if let i::Value::Thunk(ThunkRef { idx }) = env.lookup_arg(idx) { - let val = self.thunks[idx].clone().eval(self, env)?; + if let i::Value::Thunk(idx) = env.lookup_arg(idx) { + let self_mut = unsafe { &mut *(self as *mut Self) }; + let val = self.thunks[idx].eval(self_mut, env)?; env.insert_cache(idx, val) } } Dep::Thunk(idx) => { - let val = self.thunks[idx].clone().eval(self, env)?; + let self_mut = unsafe { &mut *(self as *mut Self) }; + let val = self.thunks[idx].eval(self_mut, env)?; env.insert_cache(idx, val) } } @@ -86,7 +94,7 @@ impl Engine { enum Thunk { Expr(Ir), Compiling, - Compiled(fn(Rc) -> Value), + Compiled(fn(Rc) -> Value), } impl Thunk { diff --git a/src/engine/test.rs b/src/engine/test.rs index 198fdff..50d793a 100644 --- a/src/engine/test.rs +++ b/src/engine/test.rs @@ -154,7 +154,7 @@ fn test_attrs() { "rec { a = 1; b = a; }", attrs! { symbol!("a") => int!(1), - symbol!("b") => thunk!() + symbol!("b") => int!(1) }, ); test_expr("{ a = 1; }.a", int!(1)); @@ -196,6 +196,7 @@ fn test_let() { r#"let b = "c"; in { a.b = 1; } // { a."a${b}" = 2; }"#, attrs! { symbol!("a") => attrs!{ symbol!("ac") => int!(2) } }, ); + test_expr("let f = (let f = n: let a = n; f = x: a + x; in f; in f 1); in f 0", int!(1)); } #[test] diff --git a/src/env.rs b/src/env.rs index 3463f6b..9ed9920 100644 --- a/src/env.rs +++ b/src/env.rs @@ -6,10 +6,10 @@ use hashbrown::HashMap; use crate::error::{Error, Result}; use crate::stack::Stack; -use crate::ty::internal::{EnvRef, Value}; +use crate::ty::internal::Value; #[derive(Clone)] -pub struct VmEnv { +pub struct Env { cache: Vec>, with: Vec>>, args: Vec, @@ -38,7 +38,7 @@ pub enum Type { With, } -impl VmEnv { +impl Env { pub fn new() -> Self { Self { cache: Vec::from([HashMap::new()]), @@ -66,7 +66,7 @@ impl VmEnv { pub fn lookup_cache( &mut self, idx: usize, - f: impl FnOnce(&mut VmEnv) -> Result, + f: impl FnOnce(&mut Env) -> Result, ) -> Result { for level in self.cache.iter().rev() { if let Some(ret) = level.get(&idx) { @@ -99,6 +99,10 @@ impl VmEnv { self.args.split_off(self.args.len() - len) } + pub fn reserve_args(&mut self, len: usize) { + self.args.reserve(len); + } + pub fn enter_args(&mut self, args: Vec) { self.args.extend(args); } diff --git a/src/eval/jit/compile.rs b/src/eval/jit/compile.rs index ba5d5a6..446b494 100644 --- a/src/eval/jit/compile.rs +++ b/src/eval/jit/compile.rs @@ -4,38 +4,204 @@ use crate::ir::*; use crate::ty::common as c; use crate::ty::internal::Value; -use super::JITContext; +use super::{JITContext, ValueTag}; pub trait JITCompile { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc>; + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc>; } impl JITCompile for Attrs { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for List { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for HasAttr { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for BinOp { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { - todo!() + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + use ValueTag::*; + use BinOpKind::*; + let lhs = self.lhs.compile(ctx, func); + let rhs = self.rhs.compile(ctx, func); + let lhs_tag = ctx.get_tag(lhs); + let rhs_tag = ctx.get_tag(rhs); + let tag = ctx + .builder + .build_int_add( + lhs_tag.const_shl(ctx.helpers.const_int(8)), + rhs_tag, + "calc_tag", + ) + .unwrap(); + let ret = ctx.context.append_basic_block(func, "fallback"); + let res = ctx + .builder + .build_alloca(ctx.helpers.value_type, "res_alloca") + .unwrap(); + match self.kind { + Add => { + let int_int = ctx.context.append_basic_block(func, "int_int"); + let int_float = ctx.context.append_basic_block(func, "int_float"); + let float_int = ctx.context.append_basic_block(func, "float_int"); + let float_float = ctx.context.append_basic_block(func, "float_float"); + let fallback = ctx.context.append_basic_block(func, "fallback"); + ctx.builder + .build_switch( + tag, + fallback, + &[ + ( + ctx.helpers.const_int(((Int as i64) << 8) + Int as i64), + int_int, + ), + ( + ctx.helpers.const_int(((Int as i64) << 8) + Float as i64), + int_float, + ), + ( + ctx.helpers.const_int(((Float as i64) << 8) + Int as i64), + float_int, + ), + ( + ctx.helpers.const_int(((Float as i64) << 8) + Float as i64), + float_float, + ), + ], + ) + .unwrap(); + ctx.builder.position_at_end(int_int); + let val = ctx + .builder + .build_int_add(ctx.get_int(lhs), ctx.get_int(rhs), "add") + .unwrap(); + ctx.builder + .build_store( + res, + ctx.helpers.value_type.const_named_struct(&[ + ctx.helpers.const_int(Int as i64).into(), + val.into(), + ]), + ) + .unwrap(); + ctx.builder.position_at_end(int_float); + let val = ctx + .builder + .build_float_add( + ctx.builder + .build_signed_int_to_float( + ctx.get_int(lhs), + ctx.helpers.float_type, + "lhs_to_float", + ) + .unwrap(), + ctx.get_float(rhs), + "add", + ) + .unwrap(); + ctx.builder + .build_store( + res, + ctx.helpers.value_type.const_named_struct(&[ + ctx.helpers.const_int(Float as i64).into(), + val.into(), + ]), + ) + .unwrap(); + ctx.builder.position_at_end(float_int); + let val = ctx + .builder + .build_float_add( + ctx.get_float(lhs), + ctx.builder + .build_signed_int_to_float( + ctx.get_int(rhs), + ctx.helpers.float_type, + "rhs_to_float", + ) + .unwrap(), + "add", + ) + .unwrap(); + ctx.builder + .build_store( + res, + ctx.helpers.value_type.const_named_struct(&[ + ctx.helpers.const_int(Float as i64).into(), + val.into(), + ]), + ) + .unwrap(); + ctx.builder.position_at_end(int_int); + let val = ctx + .builder + .build_float_add(ctx.get_float(lhs), ctx.get_float(rhs), "add") + .unwrap(); + ctx.builder + .build_store( + res, + ctx.helpers.value_type.const_named_struct(&[ + ctx.helpers.const_int(Float as i64).into(), + val.into(), + ]), + ) + .unwrap(); + ctx.builder.position_at_end(fallback); + } + Or => { + let bool_bool = ctx.context.append_basic_block(func, "int_int"); + let fallback = ctx.context.append_basic_block(func, "fallback"); + ctx.builder + .build_switch( + tag, + fallback, + &[ + ( + ctx.helpers.const_int(((Bool as i64) << 8) + Bool as i64), + bool_bool, + ), + ], + ) + .unwrap(); + ctx.builder.position_at_end(bool_bool); + let val = ctx + .builder + .build_or(ctx.get_bool(lhs), ctx.get_bool(rhs), "or") + .unwrap(); + ctx.builder + .build_store( + res, + ctx.helpers.value_type.const_named_struct(&[ + ctx.helpers.const_int(Bool as i64).into(), + val.into(), + ]), + ) + .unwrap(); + ctx.builder.position_at_end(fallback); + } + _ => todo!() + } + ctx.builder.position_at_end(ret); + ctx.builder + .build_load(ctx.helpers.value_type, res, "load_res") + .unwrap() + .try_into() + .unwrap() } } impl JITCompile for UnOp { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!(); let rhs = self.rhs.compile(ctx, func); let tag = ctx.get_tag(rhs); @@ -57,55 +223,55 @@ impl JITCompile for UnOp { } impl JITCompile for Select { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for If { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for LoadFunc { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for Call { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for Let { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for With { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for Assert { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for ConcatStrings { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for Const { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { use c::Const::*; match self.val { Bool(x) => ctx.helpers.new_bool(x), @@ -117,37 +283,37 @@ impl JITCompile for Const { } impl JITCompile for String { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for Var { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for Arg { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for LetVar { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for Thunk { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } impl JITCompile for Path { - fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { + fn compile<'gc>(&self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> { todo!() } } diff --git a/src/eval/jit/helpers.rs b/src/eval/jit/helpers.rs index f7fb59c..3458cad 100644 --- a/src/eval/jit/helpers.rs +++ b/src/eval/jit/helpers.rs @@ -7,8 +7,9 @@ use inkwell::module::Module; use inkwell::types::{FloatType, FunctionType, IntType, PointerType, StructType}; use inkwell::values::{BasicValueEnum, FloatValue, FunctionValue, IntValue, StructValue}; -use crate::env::VmEnv; +use crate::env::Env; use crate::eval::Engine; +use crate::ty::internal::Value; use super::{JITContext, JITValue, JITValueData, ValueTag}; @@ -54,7 +55,7 @@ impl<'ctx> Helpers<'ctx> { let new_thunk = module.add_function( "new_thunk", - value_type.fn_type(&[ptr_type.into()], false), + value_type.fn_type(&[ptr_type.into(), ptr_type.into()], false), None, ); let debug = module.add_function( @@ -102,7 +103,7 @@ impl<'ctx> Helpers<'ctx> { let call = module.add_function( "call", value_type.fn_type( - &[value_type.into(), value_type.into(), ptr_type.into()], + &[value_type.into(), ptr_type.into(), ptr_type.into(), ptr_type.into()], false, ), None, @@ -234,11 +235,11 @@ extern "C" fn helper_debug(value: JITValue) { dbg!(value.tag); } -extern "C" fn helper_capture_env(thunk: JITValue, env: *const VmEnv) { +extern "C" fn helper_capture_env(thunk: JITValue, env: *const Env) { todo!() } -extern "C" fn helper_neg(rhs: JITValue, _env: *const VmEnv) -> JITValue { +extern "C" fn helper_neg(rhs: JITValue, _env: *const Env) -> JITValue { use ValueTag::*; match rhs.tag { Int => JITValue { @@ -257,7 +258,7 @@ extern "C" fn helper_neg(rhs: JITValue, _env: *const VmEnv) -> JITValue { } } -extern "C" fn helper_not(rhs: JITValue, _env: *const VmEnv) -> JITValue { +extern "C" fn helper_not(rhs: JITValue, _env: *const Env) -> JITValue { use ValueTag::*; match rhs.tag { Bool => JITValue { @@ -338,21 +339,22 @@ extern "C" fn helper_or(lhs: JITValue, rhs: JITValue) -> JITValue { } } -extern "C" fn helper_call(func: JITValue, arg: JITValue, engine: *mut Engine) -> JITValue { +extern "C" fn helper_call(func: JITValue, args: Box<[JITValue]>, engine: NonNull, env: NonNull) -> JITValue { + let func = Value::from(func); todo!() } -extern "C" fn helper_arg(idx: usize, env: *const VmEnv) -> JITValue { +extern "C" fn helper_arg(idx: usize, env: *const Env) -> JITValue { let env = unsafe { env.as_ref() }.unwrap(); let val: JITValue = env.lookup_arg(idx).clone().into(); val } -extern "C" fn helper_lookup_let(level: usize, idx: usize, env: *const VmEnv) -> JITValue { +extern "C" fn helper_lookup_let(level: usize, idx: usize, env: *const Env) -> JITValue { todo!() } -extern "C" fn helper_lookup(sym: usize, env: *const VmEnv) -> JITValue { +extern "C" fn helper_lookup(sym: usize, env: *const Env) -> JITValue { todo!() } diff --git a/src/eval/jit/mod.rs b/src/eval/jit/mod.rs index bd198f8..ba6a330 100644 --- a/src/eval/jit/mod.rs +++ b/src/eval/jit/mod.rs @@ -7,11 +7,11 @@ use inkwell::context::Context; use inkwell::execution_engine::ExecutionEngine; use inkwell::module::Module; use inkwell::values::{ - AnyValue, AsValueRef, BasicMetadataValueEnum, BasicValueEnum, IntValue, StructValue, + AnyValue, BasicMetadataValueEnum, FloatValue, IntValue, StructValue }; -use crate::env::VmEnv; -use crate::ir::{Ir, UnOpKind}; +use crate::env::Env; +use crate::ir::Ir; use crate::ty::internal::Value; mod compile; @@ -60,10 +60,10 @@ impl From for Value { 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 _) }, - }), */ + Function => Value::Func(unsafe { value.data.int as usize}), + Thunk => Value::Thunk(unsafe { + value.data.int as usize + }), _ => todo!("not implemented for {:?}", value.tag), } } @@ -94,7 +94,7 @@ impl From for JITValue { } pub struct JITFunc<'ctx>(F, PhantomData<&'ctx mut ()>); -type F = unsafe extern "C" fn(*const VmEnv) -> JITValue; +type F = unsafe extern "C" fn(*const Env) -> JITValue; impl From for JITFunc<'_> { fn from(value: F) -> Self { @@ -144,6 +144,60 @@ impl<'ctx> JITContext<'ctx> { todo!() } + pub fn get_float(&self, val: StructValue<'ctx>) -> FloatValue<'ctx> { + let alloca = self + .builder + .build_alloca(self.helpers.int_type, "get_value_alloca") + .unwrap(); + self.builder.build_store(alloca, val).unwrap(); + self.builder + .build_load( + self.helpers.float_type, + self.builder + .build_struct_gep(self.helpers.value_type, alloca, 1, "get_value_gep") + .unwrap(), + "get_value", + ) + .unwrap() + .into_float_value() + } + + pub fn get_int(&self, val: StructValue<'ctx>) -> IntValue<'ctx> { + let alloca = self + .builder + .build_alloca(self.helpers.int_type, "get_value_alloca") + .unwrap(); + self.builder.build_store(alloca, val).unwrap(); + self.builder + .build_load( + self.helpers.int_type, + self.builder + .build_struct_gep(self.helpers.value_type, alloca, 1, "get_value_gep") + .unwrap(), + "get_value", + ) + .unwrap() + .into_int_value() + } + + pub fn get_bool(&self, val: StructValue<'ctx>) -> IntValue<'ctx> { + let alloca = self + .builder + .build_alloca(self.helpers.bool_type, "get_value_alloca") + .unwrap(); + self.builder.build_store(alloca, val).unwrap(); + self.builder + .build_load( + self.helpers.bool_type, + self.builder + .build_struct_gep(self.helpers.value_type, alloca, 1, "get_value_gep") + .unwrap(), + "get_value", + ) + .unwrap() + .into_int_value() + } + pub fn get_tag(&self, val: StructValue<'ctx>) -> IntValue<'ctx> { let alloca = self .builder @@ -152,9 +206,9 @@ impl<'ctx> JITContext<'ctx> { self.builder.build_store(alloca, val).unwrap(); self.builder .build_load( - self.context.bool_type(), + self.helpers.int_type, self.builder - .build_struct_gep(self.helpers.value_type, alloca, 1, "get_tag_gep") + .build_struct_gep(self.helpers.value_type, alloca, 0, "get_tag_gep") .unwrap(), "get_tag", ) diff --git a/src/eval/mod.rs b/src/eval/mod.rs index be8bdb5..8bfb813 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -1,30 +1,30 @@ use ecow::EcoVec; use crate::engine::Engine; -use crate::env::VmEnv; +use crate::env::Env; use crate::error::{Error, Result}; use crate::ir::{self, DynAttr}; use crate::ty::common::Const; -use crate::ty::internal::{AttrSet, EnvRef, List, ThunkRef, Value}; +use crate::ty::internal::{AttrSet, List, Value}; use crate::ty::public::Symbol; pub mod jit; pub trait Evaluate { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result; + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result; } impl Evaluate for ir::Attrs { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { let mut attrs = AttrSet::new( self.stcs - .into_iter() - .map(|(k, v)| Ok((k, v.eval(engine, env)?))) + .iter() + .map(|(k, v)| Ok((k.clone(), v.eval(engine, env)?))) .collect::>()?, ); - for DynAttr(k, v) in self.dyns { + for DynAttr(k, v) in self.dyns.iter() { let mut k = k.eval(engine, env)?; - k.force(engine, env)?.coerce_to_string(); + k.coerce_to_string(); attrs.push_attr(k.unwrap_string(), v.eval(engine, env)?); } Value::AttrSet(attrs.into()).ok() @@ -32,10 +32,10 @@ impl Evaluate for ir::Attrs { } impl Evaluate for ir::List { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { Value::List(List::from( self.items - .into_iter() + .iter() .map(|val| val.eval(engine, env)) .collect::>>()?, )) @@ -44,16 +44,16 @@ impl Evaluate for ir::List { } impl Evaluate for ir::HasAttr { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { use ir::Attr::*; let mut val = self.lhs.eval(engine, env)?; - val.has_attr(self.rhs.into_iter().map(|attr| { + val.has_attr(self.rhs.iter().map(|attr| { Ok(match attr { - Str(ident) => ident, + Str(ident) => ident.clone(), Strs(expr) => expr.eval(engine, env)?.unwrap_string(), Dynamic(expr) => { let mut val = expr.eval(engine, env)?; - val.force(engine, env)?.coerce_to_string(); + val.coerce_to_string(); val.unwrap_string() } }) @@ -63,12 +63,10 @@ impl Evaluate for ir::HasAttr { } impl Evaluate for ir::BinOp { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { use ir::BinOpKind::*; let mut lhs = self.lhs.eval(engine, env)?; let mut rhs = self.rhs.eval(engine, env)?; - lhs.force(engine, env)?; - rhs.force(engine, env)?; match self.kind { Add => lhs.add(rhs), Sub => { @@ -115,10 +113,9 @@ impl Evaluate for ir::BinOp { } impl Evaluate for ir::UnOp { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { use ir::UnOpKind::*; let mut rhs = self.rhs.eval(engine, env)?; - rhs.force(engine, env)?; match self.kind { Neg => rhs.neg(), Not => rhs.not(), @@ -128,19 +125,19 @@ impl Evaluate for ir::UnOp { } impl Evaluate for ir::Select { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { use ir::Attr::*; let mut val = self.expr.eval(engine, env)?; - if let Some(default) = self.default { + if let Some(default) = &self.default { let default = default.eval(engine, env)?; - val.force(engine, env)?.select_with_default( - self.attrpath.into_iter().map(|attr| { + val.select_with_default( + self.attrpath.iter().map(|attr| { Ok(match attr { - Str(ident) => ident, + Str(ident) => ident.clone(), Strs(expr) => expr.eval(engine, env)?.unwrap_string(), Dynamic(expr) => { let mut val = expr.eval(engine, env)?; - val.force(engine, env)?.coerce_to_string(); + val.coerce_to_string(); val.unwrap_string() } }) @@ -148,14 +145,14 @@ impl Evaluate for ir::Select { default, )?; } else { - val.force(engine, env)? - .select(self.attrpath.into_iter().map(|attr| { + val + .select(self.attrpath.iter().map(|attr| { Ok(match attr { - Str(ident) => ident, + Str(ident) => ident.clone(), Strs(expr) => expr.eval(engine, env)?.unwrap_string(), Dynamic(expr) => { let mut val = expr.eval(engine, env)?; - val.force(engine, env)?.coerce_to_string(); + val.coerce_to_string(); val.unwrap_string() } }) @@ -166,7 +163,7 @@ impl Evaluate for ir::Select { } impl Evaluate for ir::If { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { // TODO: Error Handling let cond = self.cond.eval(engine, env)?.unwrap_bool(); if cond { @@ -178,19 +175,18 @@ impl Evaluate for ir::If { } impl Evaluate for ir::LoadFunc { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { let idx = engine.func_offset + self.idx; Value::Func(idx).ok() } } impl Evaluate for ir::Call { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { let mut func = self.func.eval(engine, env)?; - func.force(engine, env)?; func.call( self.args - .into_iter() + .iter() .map(|arg| arg.eval(engine, env)) .collect::>()?, engine, @@ -205,13 +201,13 @@ impl Evaluate for ir::Call { } impl Evaluate for ir::Let { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { unreachable!() } } impl Evaluate for ir::With { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { let namespace = self.namespace.eval(engine, env)?; // TODO: Error Handling env.enter_with(namespace.unwrap_attr_set().into_inner()); @@ -222,19 +218,19 @@ impl Evaluate for ir::With { } impl Evaluate for ir::Assert { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { todo!() } } impl Evaluate for ir::ConcatStrings { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { let mut parts = self .parts - .into_iter() + .iter() .map(|part| { let mut part = part.eval(engine, env)?; - part.force(engine, env)?.coerce_to_string(); + part.coerce_to_string(); part.ok() }) .collect::>>()? @@ -250,13 +246,13 @@ impl Evaluate for ir::ConcatStrings { } impl Evaluate for ir::String { - fn eval(self, _: &mut Engine, _: &mut VmEnv) -> Result { - Value::String(self.val).ok() + fn eval(&self, _: &mut Engine, _: &mut Env) -> Result { + Value::String(self.val.clone()).ok() } } impl Evaluate for ir::Const { - fn eval(self, _: &mut Engine, _: &mut VmEnv) -> Result { + fn eval(&self, _: &mut Engine, _: &mut Env) -> Result { match self.val { Const::Null => Value::Null, Const::Int(x) => Value::Int(x), @@ -268,33 +264,34 @@ impl Evaluate for ir::Const { } impl Evaluate for ir::Var { - fn eval(self, _: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, _: &mut Engine, env: &mut Env) -> Result { env.lookup_with(&self.sym).ok_or_else(|| { - Error::EvalError(format!("variable {} not found", Symbol::from(self.sym))) + Error::EvalError(format!("variable {} not found", Symbol::from(self.sym.clone()))) }) } } impl Evaluate for ir::Arg { - fn eval(self, _: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, _: &mut Engine, env: &mut Env) -> Result { env.lookup_arg(self.level).clone().ok() } } impl Evaluate for ir::LetVar { - fn eval(self, _: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, _: &mut Engine, env: &mut Env) -> Result { unreachable!() } } impl Evaluate for ir::Thunk { - fn eval(self, _: &mut Engine, _: &mut VmEnv) -> Result { - Value::Thunk(ThunkRef::new(self.idx)).ok() + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { + env.lookup_cache(self.idx, |env| engine.eval_thunk(self.idx, env)) + // Value::Thunk(self.idx).ok() } } impl Evaluate for ir::MaybeThunk { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { match self { ir::MaybeThunk::Const(cnst) => cnst.eval(engine, env), ir::MaybeThunk::String(string) => string.eval(engine, env), @@ -304,7 +301,7 @@ impl Evaluate for ir::MaybeThunk { } impl Evaluate for ir::Path { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { todo!() } } diff --git a/src/ir/ctx.rs b/src/ir/ctx.rs index da40f17..3794ba3 100644 --- a/src/ir/ctx.rs +++ b/src/ir/ctx.rs @@ -9,7 +9,7 @@ use crate::{ use super::{Dep, Func, Ir, LoadFunc, MaybeThunk, SccAnalyzer, SccNode, Thunk}; -#[derive(Clone, Copy, Unwrap)] +#[derive(Clone, Copy, Unwrap, Debug)] pub enum Index { Thunk(usize), Func(usize), @@ -169,9 +169,11 @@ impl DowngradeContext { } pub fn new_dep(&mut self, this: Index, dep: Dep) -> Result<()> { + #[cfg(debug_assertions)] + println!("{this:?} => {dep:?}"); match this { Index::Thunk(idx) => { - if dep == Dep::Thunk(idx) { + if dep == Dep::Thunk(idx) && !matches!(self.thunks[idx].0, Ir::List(_)) { return Err(Error::DowngradeError( "infinite recursion encountered".into(), )); diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 377a81b..d5656a0 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -8,7 +8,7 @@ use rnix::ast::{self, Expr}; use crate::builtins::ir_env; use crate::engine::Engine; -use crate::env::VmEnv; +use crate::env::Env as VmEnv; use crate::error::*; use crate::eval::Evaluate; use crate::eval::jit::{JITCompile, JITContext}; @@ -90,7 +90,7 @@ macro_rules! ir { } impl JITCompile for Ir { - fn compile<'ctx>(self, ctx: &JITContext<'ctx>, func: FunctionValue<'ctx>) -> StructValue<'ctx>{ + fn compile<'ctx>(&self, ctx: &JITContext<'ctx>, func: FunctionValue<'ctx>) -> StructValue<'ctx>{ match self { $(Ir::$ty(ir) => ir.compile(ctx, func),)* } @@ -98,7 +98,7 @@ macro_rules! ir { } impl Evaluate for Ir { - fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + fn eval(&self, engine: &mut Engine, env: &mut VmEnv) -> Result { match self { $(Ir::$ty(ir) => ir.eval(engine, env),)* } @@ -1060,7 +1060,11 @@ impl Let { env: &Env<'a, 'env>, ) -> Result { let map = self.bindings.clone(); - let env = env.enter_let(&map); + let mut env = env.enter_let(&map); + self.bindings.into_iter().map(|(_, ir)| { + ir.resolve(self_idx, ctx, &mut env)?; + Ok(()) + }).collect::>()?; self.expr.resolve(self_idx, ctx, &env)?.ok() } } diff --git a/src/ir/scc.rs b/src/ir/scc.rs index 90a17bc..c061300 100644 --- a/src/ir/scc.rs +++ b/src/ir/scc.rs @@ -18,6 +18,7 @@ pub enum Dep { #[derive(Default, Debug)] pub struct SccGraph { nodes: HashMap, + root: usize, } impl SccGraph { @@ -38,6 +39,7 @@ impl SccGraph { }, ); } + graph.root = thunk_to_scc[&(ctx.thunks.len() - 1)]; for (from_node_id, from_deps) in ctx.thunk_deps.iter().enumerate() { let from_scc_id = thunk_to_scc[&from_node_id]; @@ -58,28 +60,51 @@ impl SccGraph { } fn sorted(self) -> Vec { - let mut in_degrees: HashMap = self.nodes.keys().map(|&id| (id, 0)).collect(); - for node in self.nodes.values() { - in_degrees.insert(node.id, node.deps.len()); + let mut reachable = HashSet::new(); + let mut stack = vec![self.root]; + reachable.insert(self.root); + + while let Some(id) = stack.pop() { + if let Some(node) = self.nodes.get(&id) { + for &dep_id in &node.deps { + if reachable.insert(dep_id) { + stack.push(dep_id); + } + } + } } + let mut in_degrees: HashMap = HashMap::new(); let mut reverse_adj: HashMap> = HashMap::new(); - for (node_id, node) in &self.nodes { - for &dep_id in &node.deps { - reverse_adj.entry(dep_id).or_default().push(*node_id); + + for &id in &reachable { + in_degrees.insert(id, 0); + } + + for &id in &reachable { + if let Some(node) = self.nodes.get(&id) { + for &dep_id in &node.deps { + if reachable.contains(&dep_id) { + reverse_adj.entry(dep_id).or_default().push(id); + *in_degrees.get_mut(&id).unwrap() += 1; + } + } } } let mut queue: std::collections::VecDeque = in_degrees .iter() - .filter_map(|(&id, °)| if deg == 0 { Some(id) } else { None }) + .filter(|(_, deg)| **deg == 0) + .map(|(&id, _)| id) .collect(); queue.make_contiguous().sort(); let mut sorted_order = Vec::new(); while let Some(u) = queue.pop_front() { - sorted_order.push(self.nodes[&u].clone()); + if let Some(node) = self.nodes.get(&u) { + sorted_order.push(node.clone()); + } if let Some(dependents) = reverse_adj.get(&u) { for &v in dependents { @@ -93,8 +118,8 @@ impl SccGraph { } } - if sorted_order.len() != self.nodes.len() { - panic!("Cycle detected in SCC graph, which is impossible!"); + if sorted_order.len() != reachable.len() { + panic!("Cycle detected in the reachable part of SCC graph!"); } sorted_order diff --git a/src/ty/internal/func.rs b/src/ty/internal/func.rs index ff64e1a..360f8d5 100644 --- a/src/ty/internal/func.rs +++ b/src/ty/internal/func.rs @@ -1,15 +1,15 @@ use std::rc::Rc; -use crate::env::VmEnv; +use crate::env::Env; use crate::ir; pub struct Func<'gc> { pub func: &'gc ir::Func, - pub env: Rc, + pub env: Rc, } impl<'gc> Func<'gc> { - pub fn new(func: &'gc ir::Func, env: Rc) -> Self { + pub fn new(func: &'gc ir::Func, env: Rc) -> Self { Self { func, env } } } diff --git a/src/ty/internal/list.rs b/src/ty/internal/list.rs index 3a49f46..da02ea7 100644 --- a/src/ty/internal/list.rs +++ b/src/ty/internal/list.rs @@ -5,7 +5,7 @@ use ecow::EcoVec; use hashbrown::HashSet; use crate::engine::Engine; -use crate::env::VmEnv; +use crate::env::Env; use crate::ty::public as p; use super::Value; diff --git a/src/ty/internal/mod.rs b/src/ty/internal/mod.rs index 1ed3666..3351830 100644 --- a/src/ty/internal/mod.rs +++ b/src/ty/internal/mod.rs @@ -10,7 +10,7 @@ use super::common::*; use super::public as p; use crate::engine::Engine; -use crate::env::{VmEnv, VmEnvWeak}; +use crate::env::{Env, VmEnvWeak}; use crate::error::*; mod attrset; @@ -23,45 +23,6 @@ pub use attrset::*; pub use list::List; pub use primop::*; -#[derive(Clone)] -pub enum EnvRef { - Strong(VmEnv), - Weak(VmEnvWeak), -} - -#[derive(Clone, Debug)] -pub struct ThunkRef { - pub idx: usize, - // pub env: Option, -} - -impl ThunkRef { - pub fn new(idx: usize) -> Self { - ThunkRef { - idx, /* env: None */ - } - } - /* - pub fn capture(&mut self, env: EnvRef) { - let _ = self.env.insert(env); - } - - pub fn upgrade(&mut self) { - replace_with_or_abort(&mut self.env, |env| { - env.map(|env| EnvRef::Strong(env.upgraded())) - }); - } */ -} -/* -impl EnvRef { - pub fn upgraded(self) -> VmEnv { - match self { - EnvRef::Weak(weak) => weak.upgrade(), - EnvRef::Strong(strong) => strong, - } - } -} */ - #[derive(IsVariant, Unwrap, Clone, Debug)] pub enum Value { Int(i64), @@ -69,7 +30,7 @@ pub enum Value { Bool(bool), String(EcoString), Null, - Thunk(ThunkRef), + Thunk(usize), AttrSet(Rc), List(List), Catchable(EcoString), @@ -129,7 +90,7 @@ pub enum ValueAsRef<'v> { Bool(bool), String(&'v EcoString), Null, - Thunk(&'v ThunkRef), + Thunk(usize), AttrSet(&'v AttrSet), List(&'v List), Catchable(&'v str), @@ -149,7 +110,7 @@ impl Value { Bool(x) => R::Bool(*x), String(x) => R::String(x), Null => R::Null, - Thunk(x) => R::Thunk(x), + Thunk(x) => R::Thunk(*x), AttrSet(x) => R::AttrSet(x), List(x) => R::List(x), Catchable(x) => R::Catchable(x), @@ -192,7 +153,7 @@ impl Value { } } - pub fn call(&mut self, args: Vec, engine: &mut Engine, env: &mut VmEnv) -> Result<()> { + pub fn call(&mut self, args: Vec, engine: &mut Engine, env: &mut Env) -> Result<()> { use Value::*; for arg in args.iter() { if matches!(arg, Value::Catchable(_)) { @@ -206,6 +167,7 @@ impl Value { PartialFunc(idx, old) => { let idx = *idx; let len = args.len() + old.len(); + env.reserve_args(len); env.enter_args(std::mem::take(old)); env.enter_cache_level(|env| { let mut args = args.into_iter().peekable(); @@ -240,6 +202,7 @@ impl Value { &mut Func(idx) => { let len = args.len(); let mut args = args.into_iter().peekable(); + env.reserve_args(len); env.enter_cache_level(|env| { env.enter_arg(args.next().unwrap()); let mut ret = engine.eval_thunk(idx, env)?; @@ -510,18 +473,9 @@ impl Value { self } - pub fn force(&mut self, engine: &mut Engine, env: &mut VmEnv) -> Result<&mut Self> { - if let Value::Thunk(thunk) = self { - unsafe { - let old = std::ptr::read(thunk); - match env.lookup_cache(old.idx, |env| engine.eval_thunk(old.idx, env)) { - Ok(ok) => std::ptr::write(self, ok), - Err(err) => { - std::ptr::write(self, Self::Null); - return Err(err); - } - } - } + pub fn force(&mut self, engine: &mut Engine, env: &mut Env) -> Result<&mut Self> { + if let &mut Value::Thunk(idx) = self { + *self = env.lookup_cache(idx, |env| engine.eval_thunk(idx, env))? } Ok(self) }