feat: SCC analysis (WIP)
This commit is contained in:
234
src/ir/ctx.rs
Normal file
234
src/ir/ctx.rs
Normal file
@@ -0,0 +1,234 @@
|
||||
use ecow::EcoString;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
|
||||
use crate::error::Result;
|
||||
|
||||
use super::{Func, Ir, LoadFunc, MaybeThunk, Thunk};
|
||||
|
||||
pub struct DowngradeContext {
|
||||
pub thunks: Vec<Ir>,
|
||||
pub deps: Vec<HashSet<usize>>,
|
||||
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> {
|
||||
Builtins(&'a HashMap<EcoString, Ir>),
|
||||
Let(&'a Vec<(EcoString, MaybeThunk)>),
|
||||
SingleArg(EcoString),
|
||||
MultiArg(HashMap<EcoString, Option<Ir>>, Option<EcoString>),
|
||||
With,
|
||||
}
|
||||
|
||||
pub enum LookupResult {
|
||||
Builtin(Ir),
|
||||
MaybeThunk(MaybeThunk),
|
||||
Let { level: usize, idx: usize },
|
||||
SingleArg { level: usize },
|
||||
MultiArg { level: usize, default: Option<Ir> },
|
||||
With,
|
||||
}
|
||||
|
||||
impl<'a, 'env> Env<'a, 'env> {
|
||||
pub fn new(base: &'a HashMap<EcoString, Ir>) -> Self {
|
||||
Self {
|
||||
env: EnvNode::Builtins(base),
|
||||
prev: None,
|
||||
arg_level: 0,
|
||||
let_level: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enter_let(&'env self, map: &'a Vec<(EcoString, MaybeThunk)>) -> Self {
|
||||
Self {
|
||||
env: EnvNode::Let(map),
|
||||
prev: Some(self),
|
||||
arg_level: self.arg_level,
|
||||
let_level: self.let_level + 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enter_single_arg(&'env self, ident: EcoString) -> Self {
|
||||
Self {
|
||||
env: EnvNode::SingleArg(ident),
|
||||
prev: Some(self),
|
||||
arg_level: self.arg_level + 1,
|
||||
let_level: self.let_level,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enter_multi_arg(
|
||||
&'env self,
|
||||
map: HashMap<EcoString, Option<Ir>>,
|
||||
alias: Option<EcoString>,
|
||||
) -> Self {
|
||||
Self {
|
||||
env: EnvNode::MultiArg(map, alias),
|
||||
prev: Some(self),
|
||||
arg_level: self.arg_level + 1,
|
||||
let_level: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enter_with(&'env self) -> Self {
|
||||
Self {
|
||||
env: EnvNode::With,
|
||||
prev: Some(self),
|
||||
arg_level: self.arg_level,
|
||||
let_level: self.let_level,
|
||||
}
|
||||
}
|
||||
|
||||
fn _lookup(
|
||||
&self,
|
||||
ident: &EcoString,
|
||||
mut arg_level: usize,
|
||||
mut let_level: usize,
|
||||
has_with: bool,
|
||||
) -> core::result::Result<LookupResult, ()> {
|
||||
use EnvNode::*;
|
||||
let mut has_with = has_with;
|
||||
match &self.env {
|
||||
Builtins(map) => {
|
||||
return if let Some(ir) = map.get(ident) {
|
||||
Ok(LookupResult::Builtin(ir.clone()))
|
||||
} else if has_with {
|
||||
Ok(LookupResult::With)
|
||||
} else {
|
||||
Err(())
|
||||
};
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
SingleArg(arg) => {
|
||||
if arg == ident {
|
||||
return Ok(LookupResult::SingleArg {
|
||||
level: arg_level - 1,
|
||||
});
|
||||
} else {
|
||||
arg_level -= 1;
|
||||
}
|
||||
}
|
||||
MultiArg(set, alias) => {
|
||||
if let Some(default) = set.get(ident) {
|
||||
return Ok(LookupResult::MultiArg {
|
||||
level: arg_level - 1,
|
||||
default: default.clone(),
|
||||
});
|
||||
} else if alias.as_ref() == Some(ident) {
|
||||
return Ok(LookupResult::SingleArg {
|
||||
level: arg_level - 1,
|
||||
});
|
||||
} else {
|
||||
arg_level -= 1;
|
||||
}
|
||||
}
|
||||
With => has_with = true,
|
||||
}
|
||||
self.prev
|
||||
.map(|prev| prev._lookup(ident, arg_level, let_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)
|
||||
}
|
||||
}
|
||||
|
||||
impl DowngradeContext {
|
||||
pub fn new() -> Self {
|
||||
DowngradeContext {
|
||||
thunks: Vec::new(),
|
||||
deps: Vec::new(),
|
||||
funcs: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
Thunk { idx }
|
||||
}
|
||||
|
||||
pub fn maybe_thunk(&mut self, ir: Ir) -> MaybeThunk {
|
||||
match ir {
|
||||
Ir::Const(cnst) => MaybeThunk::Const(cnst),
|
||||
Ir::Thunk(thunk) => MaybeThunk::Thunk(thunk),
|
||||
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_func(&mut self, func: Func) -> LoadFunc {
|
||||
let idx = self.funcs.len();
|
||||
self.funcs.push(func);
|
||||
LoadFunc { idx }
|
||||
}
|
||||
|
||||
pub fn resolve_func(&mut self, thunk_idx: usize, func_idx: usize, env: &Env) -> Result<()> {
|
||||
let self_ptr = self as *mut Self;
|
||||
self.funcs.get_mut(func_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)?);
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn resolve_thunk(&mut self, thunk_idx: usize, func_idx: Option<usize>, env: &Env) -> Result<()> {
|
||||
let self_ptr = self as *mut Self;
|
||||
self.thunks.get_mut(thunk_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)?);
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Downgraded {
|
||||
pub thunks: Box<[Ir]>,
|
||||
pub func_offset: usize,
|
||||
}
|
||||
|
||||
impl Downgraded {
|
||||
pub fn new(ctx: DowngradeContext) -> Self {
|
||||
Self {
|
||||
func_offset: ctx.thunks.len(),
|
||||
thunks: ctx
|
||||
.thunks
|
||||
.into_iter()
|
||||
.chain(ctx.funcs.into_iter().map(|Func { body, .. }| *body))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user