feat: lookup at downgrade time

works, but leaks memory
This commit is contained in:
2025-06-01 09:20:04 +08:00
parent 7d6168fdae
commit 20b2b6f1ef
20 changed files with 762 additions and 651 deletions

View File

@@ -3,7 +3,6 @@ use std::cell::RefCell;
use gc_arena::{Arena, Collect, Gc, Mutation, Rootable};
use hashbrown::{HashMap, HashSet};
use inkwell::context::Context;
use itertools::Itertools;
use crate::builtins::vm_env;
use crate::bytecode::{BinOp, Func as F, OpCode, OpCodes, Program, UnOp};
@@ -88,49 +87,9 @@ pub fn eval<T, F: for<'gc> FnOnce(Value<'gc>, &mut GcRoot<'gc>, &Mutation<'gc>)
}
Consq::PopEnv => _ = root.envs.pop().unwrap(),
Consq::Call => {
use Param::*;
let arg = root.stack.pop();
let func = root.stack.pop().unwrap_func();
let env = func.env;
let env = match func.func.param.clone() {
Ident(ident) => env.enter_arg(ident, arg, mc),
Formals {
formals,
ellipsis,
alias,
} => {
let arg = arg.unwrap_attr_set();
let mut new =
HashMap::with_capacity(formals.len() + alias.iter().len());
if !ellipsis
&& arg
.iter()
.map(|(k, _)| k)
.sorted()
.ne(formals.iter().map(|(k, _)| k).sorted())
{
todo!()
}
for (formal, default) in formals {
// TODO: rec env
let arg = arg
.select(formal)
.or_else(|| {
default.map(|idx| {
Value::Thunk(
Thunk::new(root.vm.get_thunk(idx), mc),
)
})
})
.unwrap();
new.insert(formal, arg);
}
if let Some(alias) = alias {
new.insert(alias, Value::AttrSet(arg));
}
env.enter_let(Gc::new(mc, new), mc)
}
};
let env = func.env.enter_arg(arg, mc);
let count = func.count.get();
func.count.set(count + 1);
if count >= 1 {
@@ -200,13 +159,12 @@ fn single_op<'gc, const CAP: usize>(
let Some(val) = stack.tos().as_ref().unwrap_thunk().get_value() else {
return Ok(Consq::Force);
};
stack.pop();
stack.push(val)?;
*stack.tos_mut() = val;
}
OpCode::InsertValue => {
let val = stack.pop();
stack.pop().unwrap_thunk().insert_value(val.clone(), mc);
let _ = stack.push(val);
stack.tos().as_ref().unwrap_thunk().insert_value(val.clone(), mc);
*stack.tos_mut() = val;
}
OpCode::Jmp { step } => return Ok(Consq::Jmp(step)),
OpCode::JmpIfFalse { step } => {
@@ -218,7 +176,7 @@ fn single_op<'gc, const CAP: usize>(
let arg = stack.pop();
let func = stack.tos_mut();
if func.is_func() {
stack.push(arg)?;
let _ = stack.push(arg);
return Ok(Consq::Call);
}
func.call(arg, vm, mc)?;
@@ -227,6 +185,9 @@ fn single_op<'gc, const CAP: usize>(
let func = vm.get_func(idx);
stack.push(Value::Func(Gc::new(mc, Func::new(func, *env, mc))))?;
}
OpCode::Arg { level } => {
stack.push(env.lookup_arg(level))?;
}
OpCode::UnOp { op } => {
use UnOp::*;
let value = stack.tos_mut();
@@ -262,8 +223,8 @@ fn single_op<'gc, const CAP: usize>(
OpCode::Path => {
todo!()
}
OpCode::List => {
stack.push(Value::List(CoW::new(List::empty(), mc)))?;
OpCode::List { cap } => {
stack.push(Value::List(CoW::new(List::with_capacity(cap), mc)))?;
}
OpCode::PushElem => {
let elem = stack.pop();
@@ -272,22 +233,14 @@ fn single_op<'gc, const CAP: usize>(
OpCode::AttrSet { cap } => {
stack.push(Value::AttrSet(CoW::new(AttrSet::with_capacity(cap), mc)))?;
}
OpCode::FinalizeRec => {
let mut new = HashMap::new();
stack
.tos()
OpCode::FinalizeLet => {
let mut list = stack.pop().unwrap_list();
let map = list
.as_ref()
.unwrap_attr_set()
.as_inner()
.iter()
.map(|(&k, v)| (k, v.clone()))
.collect_into(&mut new);
*env = env.enter_let(Gc::new(mc, new), mc);
stack
.tos_mut()
.as_mut(mc)
.unwrap_attr_set()
.capture(*env, mc);
.clone()
.into_inner();
*env = env.enter_let(map, mc);
list.make_mut(|list| list.capture(*env, mc), mc);
}
OpCode::PushStaticAttr { name } => {
let val = stack.pop();
@@ -296,7 +249,7 @@ fn single_op<'gc, const CAP: usize>(
OpCode::PushDynamicAttr => {
let val = stack.pop();
let sym = stack.pop();
let sym = vm.new_sym(sym.unwrap_string().as_ref());
let sym = vm.new_sym::<&str>(&sym.unwrap_string());
stack.tos_mut().push_attr(sym, val, mc);
}
OpCode::Select { sym } => {
@@ -309,14 +262,14 @@ fn single_op<'gc, const CAP: usize>(
OpCode::SelectDynamic => {
let mut val = stack.pop();
val.coerce_to_string();
let sym = vm.new_sym(val.unwrap_string().as_ref());
let sym = vm.new_sym::<&str>(&val.unwrap_string());
stack.tos_mut().select(sym, vm)?;
}
OpCode::SelectDynamicOrDefault => {
let default = stack.pop();
let mut val = stack.pop();
val.coerce_to_string();
let sym = vm.new_sym(val.unwrap_string().as_ref());
let sym = vm.new_sym::<&str>(&val.unwrap_string());
stack.tos_mut().select_with_default(sym, default)?;
}
OpCode::HasAttr { sym } => {
@@ -325,25 +278,18 @@ fn single_op<'gc, const CAP: usize>(
OpCode::HasDynamicAttr => {
let mut val = stack.pop();
val.coerce_to_string();
let sym = vm.new_sym(val.unwrap_string().as_ref());
let sym = vm.new_sym::<&str>(&val.unwrap_string());
stack.tos_mut().has_attr(sym);
}
OpCode::LookUp { sym } => {
stack.push(
env.lookup_slow(&sym)
env.lookup_with(&sym)
.ok_or_else(|| Error::EvalError(format!("{} not found", vm.get_sym(sym))))?
.clone(),
)?;
}
OpCode::EnterLetEnv => {
let mut new = HashMap::new();
stack
.pop()
.unwrap_attr_set()
.iter()
.map(|(&k, v)| (k, v.clone()))
.collect_into(&mut new);
*env = env.enter_let(Gc::new(mc, new), mc);
OpCode::LookUpLet { level, idx } => {
stack.push(env.lookup_let(level, idx))?;
}
OpCode::LeaveEnv => *env = env.leave(),
OpCode::EnterWithEnv => {
@@ -401,7 +347,7 @@ impl<'gc> VM<'gc> {
self.symbols.borrow()[idx].clone().into()
}
pub fn new_sym(&self, sym: impl Into<EcoString>) -> usize {
pub fn new_sym<T: Into<EcoString>>(&self, sym: T) -> usize {
let sym = sym.into();
if let Some(&idx) = self.symmap.borrow().get(&sym) {
idx
@@ -420,7 +366,7 @@ impl<'gc> VM<'gc> {
pub fn compile_func(&'gc self, func: &'gc F) -> JITFunc<'gc> {
self.jit
.compile_seq(func.opcodes.iter().copied(), self)
.compile_seq(func.opcodes.iter().copied().rev(), self)
.unwrap()
}
}