feat: less clone, avoid evaluating not depended thunk

This commit is contained in:
2025-06-18 09:32:00 +08:00
parent 3e9f0a72a0
commit d875951c09
14 changed files with 409 additions and 192 deletions

View File

@@ -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");

View File

@@ -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<SccNode>) -> Result<Value> {
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);
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<i::Value> {
self.thunks[idx].clone().eval(self, env)
pub fn eval_thunk(&mut self, idx: usize, env: &mut Env) -> Result<i::Value> {
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<VmEnv>) -> Value),
Compiled(fn(Rc<Env>) -> Value),
}
impl Thunk {

View File

@@ -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]

View File

@@ -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<HashMap<usize, Value>>,
with: Vec<Rc<HashMap<EcoString, Value>>>,
args: Vec<Value>,
@@ -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<Value>,
f: impl FnOnce(&mut Env) -> Result<Value>,
) -> Result<Value> {
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<Value>) {
self.args.extend(args);
}

View File

@@ -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!()
}
}

View File

@@ -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<Engine>, env: NonNull<Env>) -> 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!()
}

View File

@@ -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<JITValue> 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<Value> 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<F> 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",
)

View File

@@ -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<Value>;
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value>;
}
impl Evaluate for ir::Attrs {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
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::<Result<_>>()?,
);
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<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
Value::List(List::from(
self.items
.into_iter()
.iter()
.map(|val| val.eval(engine, env))
.collect::<Result<EcoVec<_>>>()?,
))
@@ -44,16 +44,16 @@ impl Evaluate for ir::List {
}
impl Evaluate for ir::HasAttr {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
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<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
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<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
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<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
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<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
// 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<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
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<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
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::<Result<_>>()?,
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<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
unreachable!()
}
}
impl Evaluate for ir::With {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
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<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
todo!()
}
}
impl Evaluate for ir::ConcatStrings {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
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::<Result<Vec<_>>>()?
@@ -250,13 +246,13 @@ impl Evaluate for ir::ConcatStrings {
}
impl Evaluate for ir::String {
fn eval(self, _: &mut Engine, _: &mut VmEnv) -> Result<Value> {
Value::String(self.val).ok()
fn eval(&self, _: &mut Engine, _: &mut Env) -> Result<Value> {
Value::String(self.val.clone()).ok()
}
}
impl Evaluate for ir::Const {
fn eval(self, _: &mut Engine, _: &mut VmEnv) -> Result<Value> {
fn eval(&self, _: &mut Engine, _: &mut Env) -> Result<Value> {
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<Value> {
fn eval(&self, _: &mut Engine, env: &mut Env) -> Result<Value> {
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<Value> {
fn eval(&self, _: &mut Engine, env: &mut Env) -> Result<Value> {
env.lookup_arg(self.level).clone().ok()
}
}
impl Evaluate for ir::LetVar {
fn eval(self, _: &mut Engine, env: &mut VmEnv) -> Result<Value> {
fn eval(&self, _: &mut Engine, env: &mut Env) -> Result<Value> {
unreachable!()
}
}
impl Evaluate for ir::Thunk {
fn eval(self, _: &mut Engine, _: &mut VmEnv) -> Result<Value> {
Value::Thunk(ThunkRef::new(self.idx)).ok()
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
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<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
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<Value> {
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
todo!()
}
}

View File

@@ -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(),
));

View File

@@ -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<Value> {
fn eval(&self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
match self {
$(Ir::$ty(ir) => ir.eval(engine, env),)*
}
@@ -1060,7 +1060,11 @@ impl Let {
env: &Env<'a, 'env>,
) -> Result<Ir> {
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::<Result<()>>()?;
self.expr.resolve(self_idx, ctx, &env)?.ok()
}
}

View File

@@ -18,6 +18,7 @@ pub enum Dep {
#[derive(Default, Debug)]
pub struct SccGraph {
nodes: HashMap<usize, SccNode>,
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<SccNode> {
let mut in_degrees: HashMap<usize, usize> = 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<usize, usize> = HashMap::new();
let mut reverse_adj: HashMap<usize, Vec<usize>> = HashMap::new();
for (node_id, node) in &self.nodes {
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 {
reverse_adj.entry(dep_id).or_default().push(*node_id);
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<usize> = in_degrees
.iter()
.filter_map(|(&id, &deg)| 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

View File

@@ -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<VmEnv>,
pub env: Rc<Env>,
}
impl<'gc> Func<'gc> {
pub fn new(func: &'gc ir::Func, env: Rc<VmEnv>) -> Self {
pub fn new(func: &'gc ir::Func, env: Rc<Env>) -> Self {
Self { func, env }
}
}

View File

@@ -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;

View File

@@ -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<EnvRef>,
}
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<AttrSet>),
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<Self>, engine: &mut Engine, env: &mut VmEnv) -> Result<()> {
pub fn call(&mut self, args: Vec<Self>, 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)
}