avoid thunking trivial values

This commit is contained in:
2026-05-02 14:57:52 +08:00
parent 47b1344ebe
commit 06e73fc9be
7 changed files with 208 additions and 113 deletions
+60 -42
View File
@@ -8,7 +8,10 @@ use fix_codegen::{BytecodeContext, InstructionPtr, Op};
use fix_common::{StringId, Symbol};
use fix_error::{Error, Result, Source};
use fix_ir::downgrade::{Downgrade as _, DowngradeContext};
use fix_ir::{GhostMaybeThunkRef, GhostRef, Ir, IrRef, MaybeThunk, RawIrRef, RawRef, ThunkId};
use fix_ir::{
GhostMaybeThunkRef, GhostRoIrRef, GhostRoMaybeThunkRef, GhostRoRef, Ir, MaybeThunk, RawIrRef,
ThunkId,
};
use fix_vm::{ForceMode, StaticValue, Vm, VmCode, VmContext, VmRuntimeCtx};
use ghost_cell::{GhostCell, GhostToken};
use hashbrown::{HashMap, HashSet};
@@ -30,7 +33,7 @@ pub struct CodeState {
pub sources: Vec<Source>,
pub spans: Vec<(usize, rnix::TextRange)>,
pub thunk_count: usize,
pub global_env: HashMap<StringId, Ir<'static, RawRef<'static>>>,
pub global_env: HashMap<StringId, MaybeThunk>,
}
pub struct Evaluator {
@@ -323,7 +326,7 @@ impl<'ctx, 'id, 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
bump: &'ir Bump,
token: GhostToken<'id>,
runtime: &'ctx mut R,
global: &'ctx HashMap<StringId, Ir<'static, RawRef<'static>>>,
global: &'ctx HashMap<StringId, MaybeThunk>,
extra_scope: Option<Scope<'ctx, 'id, 'ir>>,
thunk_count: &'ctx mut usize,
source: Source,
@@ -347,11 +350,11 @@ impl<'ctx, 'id, 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
for DowngradeCtx<'ctx, 'id, 'ir, R>
{
fn new_expr(&self, expr: Ir<'ir, GhostRef<'id, 'ir>>) -> IrRef<'id, 'ir> {
self.bump.alloc(GhostCell::new(expr))
fn new_expr(&self, expr: Ir<'ir, GhostRoRef<'id, 'ir>>) -> GhostRoIrRef<'id, 'ir> {
self.bump.alloc(GhostCell::new(expr).into())
}
fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> GhostMaybeThunkRef<'id, 'ir> {
fn maybe_thunk(&mut self, ir: GhostRoIrRef<'id, 'ir>) -> GhostRoMaybeThunkRef<'id, 'ir> {
use MaybeThunk::*;
let expr = (|| {
let expr = match *ir.borrow(&self.token) {
@@ -366,7 +369,7 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
Ir::MaybeThunk(thunk) => return Some(thunk),
_ => return None,
};
Some(self.bump.alloc(GhostCell::new(expr)))
Some(self.bump.alloc(GhostCell::new(expr).into()))
})();
if let Some(thunk) = expr {
return thunk;
@@ -376,8 +379,8 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
self.thunk_scopes
.last_mut()
.expect("no active cache scope")
.add_binding(id, ir, &self.token);
self.bump.alloc(GhostCell::new(Thunk(id)))
.add_binding(id, ir);
self.bump.alloc(GhostCell::new(Thunk(id)).into())
}
fn intern_string(&mut self, sym: impl AsRef<str>) -> StringId {
@@ -388,39 +391,35 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
self.runtime.resolve_string(id).into()
}
fn lookup(&self, sym: StringId, span: rnix::TextRange) -> Result<GhostMaybeThunkRef<'id, 'ir>> {
fn lookup(
&self,
sym: StringId,
span: rnix::TextRange,
) -> Result<GhostRoMaybeThunkRef<'id, 'ir>> {
for scope in self.scopes.iter().rev() {
match scope {
&Scope::Global(global_scope) => {
use MaybeThunk::*;
if let Some(expr) = global_scope.get(&sym) {
let val = match expr {
Ir::Builtins => Builtins,
Ir::Builtin(s) => Builtin(*s),
Ir::Bool(b) => Bool(*b),
Ir::Null => Null,
_ => unreachable!("globals should only contain leaf IR nodes"),
};
return Ok(self.bump.alloc(GhostCell::new(val)));
return Ok(expr.into());
}
}
&Scope::Repl(repl_bindings) => {
if repl_bindings.contains(&sym) {
return Ok(self
.bump
.alloc(GhostCell::new(MaybeThunk::ReplBinding(sym))));
.alloc(GhostCell::new(MaybeThunk::ReplBinding(sym)).into()));
}
}
Scope::ScopedImport(scoped_bindings) => {
if scoped_bindings.contains(&sym) {
return Ok(self
.bump
.alloc(GhostCell::new(MaybeThunk::ScopedImportBinding(sym))));
.alloc(GhostCell::new(MaybeThunk::ScopedImportBinding(sym)).into()));
}
}
Scope::Let(let_scope) => {
if let Some(&expr) = let_scope.get(&sym) {
return Ok(expr);
return Ok(expr.into());
}
}
&Scope::Param {
@@ -431,14 +430,18 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
let layers: u8 =
self.thunk_scopes.len().try_into().expect("scope too deep!");
let layer = layers - abs_layer;
return Ok(self.bump.alloc(GhostCell::new(MaybeThunk::Arg { layer })));
return Ok(self
.bump
.alloc(GhostCell::new(MaybeThunk::Arg { layer }).into()));
}
}
}
}
if self.with_scope_count > 0 {
Ok(self.bump.alloc(GhostCell::new(MaybeThunk::WithLookup(sym))))
Ok(self
.bump
.alloc(GhostCell::new(MaybeThunk::WithLookup(sym)).into()))
} else {
Err(Error::downgrade_error(
format!("'{}' not found", self.resolve_sym(sym)),
@@ -454,28 +457,40 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
fn with_let_scope<F, Ret>(&mut self, keys: &[StringId], f: F) -> Result<Ret>
where
F: FnOnce(&mut Self) -> Result<(bumpalo::collections::Vec<'ir, IrRef<'id, 'ir>>, Ret)>,
F: FnOnce(
&mut Self,
) -> Result<(
bumpalo::collections::Vec<'ir, GhostRoMaybeThunkRef<'id, 'ir>>,
Ret,
)>,
{
let base = *self.thunk_count;
*self.thunk_count = self
.thunk_count
.checked_add(keys.len())
.expect("thunk id overflow");
let scope = {
let mut scope = HashMap::new();
for (offset, &key) in keys.iter().enumerate() {
scope.insert(key, &*self.bump.alloc(GhostCell::new(MaybeThunk::Thunk(ThunkId(base + offset)))));
}
scope
};
let handles = (base..base + keys.len())
.map(|id| {
&*self
.bump
.alloc(GhostCell::new(MaybeThunk::Thunk(ThunkId(id))))
})
.collect::<Vec<_>>();
let scope = keys.iter().copied().zip(handles.iter().copied()).collect();
self.scopes.push(Scope::Let(scope));
let (vals, ret) = {
let mut guard = ScopeGuard { ctx: self };
f(guard.as_ctx())?
};
let (vals, ret) = { f(self)? };
self.scopes.pop();
assert_eq!(keys.len(), vals.len());
let scope = self.thunk_scopes.last_mut().expect("no active thunk scope");
scope.extend_bindings((base..base + keys.len()).map(ThunkId).zip(vals));
for (i, (val, handle)) in vals.into_iter().zip(handles).enumerate() {
let thunk = *val.borrow(&self.token);
*handle.borrow_mut(&mut self.token) = thunk;
let id = ThunkId(base + i);
let ir_ref = self
.bump
.alloc(GhostCell::new(Ir::MaybeThunk(handle.into())).into());
scope.add_binding(id, ir_ref);
}
Ok(ret)
}
@@ -506,7 +521,7 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
f: F,
) -> (
Ret,
bumpalo::collections::Vec<'ir, (ThunkId, IrRef<'id, 'ir>)>,
bumpalo::collections::Vec<'ir, (ThunkId, GhostRoIrRef<'id, 'ir>)>,
)
where
F: FnOnce(&mut Self) -> Ret,
@@ -546,7 +561,7 @@ impl<'id, 'ir, 'ctx: 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
}
struct ThunkScope<'id, 'ir> {
bindings: bumpalo::collections::Vec<'ir, (ThunkId, IrRef<'id, 'ir>)>,
bindings: bumpalo::collections::Vec<'ir, (ThunkId, GhostRoIrRef<'id, 'ir>)>,
}
impl<'id, 'ir> ThunkScope<'id, 'ir> {
@@ -556,17 +571,20 @@ impl<'id, 'ir> ThunkScope<'id, 'ir> {
}
}
fn add_binding(&mut self, id: ThunkId, ir: IrRef<'id, 'ir>, _token: &GhostToken<'id>) {
fn add_binding(&mut self, id: ThunkId, ir: GhostRoIrRef<'id, 'ir>) {
self.bindings.push((id, ir));
}
fn extend_bindings(&mut self, iter: impl IntoIterator<Item = (ThunkId, IrRef<'id, 'ir>)>) {
fn extend_bindings(
&mut self,
iter: impl IntoIterator<Item = (ThunkId, GhostRoIrRef<'id, 'ir>)>,
) {
self.bindings.extend(iter);
}
}
enum Scope<'ctx, 'id, 'ir> {
Global(&'ctx HashMap<StringId, Ir<'static, RawRef<'static>>>),
Global(&'ctx HashMap<StringId, MaybeThunk>),
Repl(&'ctx HashSet<StringId>),
ScopedImport(HashSet<StringId>),
Let(HashMap<StringId, GhostMaybeThunkRef<'id, 'ir>>),