feat: SCC analysis (thunk capture WIP)

This commit is contained in:
2025-06-17 11:53:54 +08:00
parent b2d2490327
commit 7f6848c9e5
19 changed files with 501 additions and 458 deletions

View File

@@ -1,21 +1,28 @@
use derive_more::Unwrap;
use ecow::EcoString;
use hashbrown::{HashMap, HashSet};
use crate::error::Result;
use crate::{error::{Error, Result}, ty::common::Const};
use super::{Func, Ir, LoadFunc, MaybeThunk, Thunk};
use super::{Dep, Func, Ir, LoadFunc, MaybeThunk, SccAnalyzer, SccNode, Thunk};
#[derive(Clone, Copy, Unwrap)]
pub enum Index {
Thunk(usize),
Func(usize),
}
pub struct DowngradeContext {
pub thunks: Vec<Ir>,
pub deps: Vec<HashSet<usize>>,
pub thunks: Vec<(Ir, bool)>,
pub thunk_deps: Vec<HashSet<usize>>,
pub func_deps: Vec<HashSet<Dep>>,
pub func_arg_dep: Vec<bool>,
pub funcs: Vec<Func>,
}
pub struct Env<'a, 'env> {
env: EnvNode<'a>,
prev: Option<&'env Env<'a, 'env>>,
arg_level: usize,
let_level: usize,
}
enum EnvNode<'a> {
@@ -40,8 +47,6 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::Builtins(base),
prev: None,
arg_level: 0,
let_level: 0,
}
}
@@ -49,8 +54,6 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::Let(map),
prev: Some(self),
arg_level: self.arg_level,
let_level: self.let_level + 1,
}
}
@@ -58,8 +61,6 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::SingleArg(ident),
prev: Some(self),
arg_level: self.arg_level + 1,
let_level: self.let_level,
}
}
@@ -71,8 +72,6 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::MultiArg(map, alias),
prev: Some(self),
arg_level: self.arg_level + 1,
let_level: 0,
}
}
@@ -80,8 +79,6 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::With,
prev: Some(self),
arg_level: self.arg_level,
let_level: self.let_level,
}
}
@@ -89,7 +86,6 @@ impl<'a, 'env> Env<'a, 'env> {
&self,
ident: &EcoString,
mut arg_level: usize,
mut let_level: usize,
has_with: bool,
) -> core::result::Result<LookupResult, ()> {
use EnvNode::*;
@@ -106,47 +102,41 @@ impl<'a, 'env> Env<'a, 'env> {
}
Let(map) => {
if let Ok(idx) = map.binary_search_by(|(k, _)| k.cmp(ident)) {
return Ok(LookupResult::MaybeThunk(map[idx].1))
/* return Ok(LookupResult::Let {
level: let_level - 1,
idx,
}); */
} else {
let_level -= 1;
return Ok(LookupResult::MaybeThunk(map[idx].1.clone()));
}
}
SingleArg(arg) => {
if arg == ident {
return Ok(LookupResult::SingleArg {
level: arg_level - 1,
level: arg_level,
});
} else {
arg_level -= 1;
arg_level += 1;
}
}
MultiArg(set, alias) => {
if let Some(default) = set.get(ident) {
return Ok(LookupResult::MultiArg {
level: arg_level - 1,
level: arg_level,
default: default.clone(),
});
} else if alias.as_ref() == Some(ident) {
return Ok(LookupResult::SingleArg {
level: arg_level - 1,
level: arg_level,
});
} else {
arg_level -= 1;
arg_level += 1;
}
}
With => has_with = true,
}
self.prev
.map(|prev| prev._lookup(ident, arg_level, let_level, has_with))
.map(|prev| prev._lookup(ident, arg_level, has_with))
.map_or_else(|| unreachable!(), |x| x)
}
pub fn lookup(&self, ident: &EcoString) -> core::result::Result<LookupResult, ()> {
self._lookup(ident, self.arg_level, self.let_level, false)
self._lookup(ident, 0, false)
}
}
@@ -154,7 +144,9 @@ impl DowngradeContext {
pub fn new() -> Self {
DowngradeContext {
thunks: Vec::new(),
deps: Vec::new(),
thunk_deps: Vec::new(),
func_deps: Vec::new(),
func_arg_dep: Vec::new(),
funcs: Vec::new(),
}
}
@@ -163,51 +155,79 @@ impl DowngradeContext {
impl DowngradeContext {
pub fn new_thunk(&mut self, thunk: Ir) -> Thunk {
let idx = self.thunks.len();
self.thunks.push(thunk);
self.deps.push(HashSet::new());
self.thunks.push((thunk, false));
self.thunk_deps.push(HashSet::new());
Thunk { idx }
}
pub fn maybe_thunk(&mut self, ir: Ir) -> MaybeThunk {
match ir {
Ir::Const(cnst) => MaybeThunk::Const(cnst),
Ir::String(string) => MaybeThunk::String(string),
Ir::Thunk(thunk) => MaybeThunk::Thunk(thunk),
ir => MaybeThunk::Thunk(self.new_thunk(ir))
ir => MaybeThunk::Thunk(self.new_thunk(ir)),
}
}
pub fn new_dep(&mut self, this: usize, dep: usize) {
self.deps[this].insert(dep);
pub fn new_dep(&mut self, this: Index, dep: Dep) -> Result<()> {
match this {
Index::Thunk(idx) => {
if dep == Dep::Thunk(idx) {
return Err(Error::DowngradeError("infinite recursion encountered".into()))
}
self.thunk_deps[idx].insert(dep.unwrap_thunk())
},
Index::Func(idx) => self.func_deps[idx].insert(dep),
};
Ok(())
}
pub fn new_func(&mut self, func: Func) -> LoadFunc {
let idx = self.funcs.len();
self.funcs.push(func);
self.func_deps.push(HashSet::new());
self.func_arg_dep.push(false);
LoadFunc { idx }
}
pub fn resolve_func(&mut self, thunk_idx: usize, func_idx: usize, env: &Env) -> Result<()> {
pub fn resolve_func(&mut self, idx: usize, env: &Env) -> Result<()> {
let self_ptr = self as *mut Self;
self.funcs.get_mut(func_idx).map_or_else(
self.funcs.get_mut(idx).map_or_else(
|| unreachable!(),
|func| {
unsafe {
let old = std::ptr::read(func);
std::ptr::write(func, old.resolve(thunk_idx, Some(func_idx), self_ptr.as_mut().unwrap(), env)?);
match old.resolve(Index::Func(idx), self_ptr.as_mut().unwrap(), env) {
Ok(ok) => std::ptr::write(func, ok),
Err(err) => {
std::ptr::write(func, Func { param: crate::ir::Param::Ident(EcoString::new()), body: super::Const { val: Const::Null }.ir().boxed() });
return Err(err)
},
}
}
Ok(())
},
)
}
pub fn resolve_thunk(&mut self, thunk_idx: usize, func_idx: Option<usize>, env: &Env) -> Result<()> {
pub fn resolve_thunk(&mut self, idx: usize, env: &Env) -> Result<()> {
if self.thunks[idx].1 {
return Ok(())
}
self.thunks[idx].1 = true;
let self_ptr = self as *mut Self;
self.thunks.get_mut(thunk_idx).map_or_else(
self.thunks.get_mut(idx).map_or_else(
|| unreachable!(),
|thunk| {
unsafe {
let old = std::ptr::read(thunk);
std::ptr::write(thunk, old.resolve(thunk_idx, func_idx, self_ptr.as_mut().unwrap(), env)?);
let (old, _) = std::ptr::read(thunk);
match old.resolve(Index::Thunk(idx), self_ptr.as_mut().unwrap(), env) {
Ok(ok) => std::ptr::write(&mut thunk.0, ok),
Err(err) => {
std::ptr::write(&mut thunk.0, Ir::Const(super::Const { val: Const::Null }));
return Err(err)
},
}
}
Ok(())
},
@@ -215,18 +235,24 @@ impl DowngradeContext {
}
}
#[derive(Debug)]
pub struct Downgraded {
pub thunks: Box<[Ir]>,
pub func_offset: usize,
pub func_deps: Vec<HashSet<Dep>>,
pub graph: Vec<SccNode>,
}
impl Downgraded {
pub fn new(ctx: DowngradeContext) -> Self {
Self {
graph: SccAnalyzer::new(&ctx).analyze(),
func_offset: ctx.thunks.len(),
func_deps: ctx.func_deps,
thunks: ctx
.thunks
.into_iter()
.map(|(ir, _)| ir)
.chain(ctx.funcs.into_iter().map(|Func { body, .. }| *body))
.collect(),
}