rewrite VM to support reentry (WIP)

This commit is contained in:
2026-03-22 16:41:13 +08:00
parent b3f1f4f6ff
commit 6567ed4058
18 changed files with 1728 additions and 3315 deletions
+99 -57
View File
@@ -1,75 +1,99 @@
use std::hash::BuildHasher;
use bumpalo::Bump;
use gc_arena::{Arena, Rootable, arena::CollectionPhase};
use gc_arena::{Arena, Rootable};
use ghost_cell::{GhostCell, GhostToken};
use hashbrown::{DefaultHashBuilder, HashMap, HashSet, HashTable};
use rnix::TextRange;
use string_interner::DefaultStringInterner;
use string_interner::symbol::SymbolU32;
use string_interner::{DefaultStringInterner, Symbol as _};
use crate::codegen::{BytecodeContext, InstructionPtr};
use crate::disassembler::{Disassembler, DisassemblerContext};
use crate::downgrade::{Downgrade as _, DowngradeContext};
use crate::error::{Error, Result, Source};
use crate::ir::{ArgId, Ir, IrKey, IrRef, RawIrRef, StringId, ThunkId, ir_content_eq};
use crate::runtime::builtins::new_builtins_env;
use crate::runtime::builtins::init_builtins;
use crate::runtime::value::StaticValue;
use crate::runtime::vm::{ForceMode, new_gc_root};
use crate::store::{DaemonStore, StoreConfig};
use crate::value::Symbol;
mod builtins;
mod primops;
mod stack;
mod value;
pub(crate) mod value;
mod vm;
use vm::{Action, VM};
pub(crate) use builtins::{BUILTINS, BuiltinId};
use stack::Stack;
use vm::{ErrorFrame, GcRoot};
pub struct Runtime {
bytecode: Vec<u8>,
global_env: HashMap<StringId, Ir<'static, RawIrRef<'static>>>,
// global
sources: Vec<Source>,
store: DaemonStore,
spans: Vec<(usize, TextRange)>,
thunk_count: usize,
store: DaemonStore,
strings: DefaultStringInterner,
arena: Arena<Rootable![VM<'_>]>,
// FIXME: remove?
thunk_count: usize,
bytecode: Vec<u8>,
pub(crate) constants: Vec<StaticValue>,
constant_dedup: HashMap<u64, u32>,
// downgrade
global_env: HashMap<StringId, Ir<'static, RawIrRef<'static>>>,
// eval
pc: usize,
fuel: usize,
error_contexts: Stack<8192, ErrorFrame>,
arena: Arena<Rootable![GcRoot<'_>]>,
}
impl Runtime {
const COLLECTOR_GRANULARITY: f64 = 1024.0;
const DEFAULT_FUEL_AMOUNT: usize = 2048;
pub fn new() -> Result<Self> {
// HACK: what the heck...
let mut global_env = HashMap::new();
let mut strings = DefaultStringInterner::new();
let global_env = new_builtins_env(&mut strings);
let arena = Arena::new(|mc| {
let (root, env) = new_gc_root(mc, &mut strings);
global_env = env;
root
});
let config = StoreConfig::from_env();
let store = DaemonStore::connect(&config.daemon_socket)?;
Ok(Self {
arena: Arena::new(|mc| VM::new(mc, &mut strings)),
global_env,
sources: Vec::new(),
spans: Vec::new(),
store,
strings,
thunk_count: 0,
bytecode: Vec::new(),
sources: Vec::new(),
spans: Vec::new(),
constants: Vec::new(),
constant_dedup: HashMap::new(),
global_env,
pc: 0,
fuel: Self::DEFAULT_FUEL_AMOUNT,
error_contexts: Stack::new(),
arena,
})
}
pub fn eval(&mut self, source: Source) -> Result<crate::value::Value> {
let root = self.downgrade(source, None)?;
let ip = crate::codegen::compile_bytecode(root.as_ref(), self);
self.run(ip)
self.do_eval(source, None, ForceMode::AsIs)
}
pub fn eval_shallow(&mut self, _source: Source) -> Result<crate::value::Value> {
todo!()
pub fn eval_shallow(&mut self, source: Source) -> Result<crate::value::Value> {
self.do_eval(source, None, ForceMode::Shallow)
}
pub fn eval_deep(&mut self, source: Source) -> Result<crate::value::Value> {
// FIXME: deep
let root = self.downgrade(source, None)?;
let ip = crate::codegen::compile_bytecode(root.as_ref(), self);
self.run(ip)
self.do_eval(source, None, ForceMode::Deep)
}
pub fn eval_repl(
@@ -77,10 +101,18 @@ impl Runtime {
source: Source,
scope: &HashSet<StringId>,
) -> Result<crate::value::Value> {
// FIXME: shallow
let root = self.downgrade(source, Some(Scope::Repl(scope)))?;
self.do_eval(source, Some(Scope::Repl(scope)), ForceMode::Shallow)
}
fn do_eval<'ctx>(
&'ctx mut self,
source: Source,
extra_scope: Option<Scope<'ctx>>,
force_mode: ForceMode,
) -> Result<crate::value::Value> {
let root = self.downgrade(source, extra_scope)?;
let ip = crate::codegen::compile_bytecode(root.as_ref(), self);
self.run(ip)
self.run(ip, force_mode)
}
pub fn add_binding(
@@ -92,6 +124,20 @@ impl Runtime {
todo!()
}
pub fn compile_bytecode(&mut self, source: Source) -> Result<InstructionPtr> {
let root = self.downgrade(source, None)?;
let ip = crate::codegen::compile_bytecode(root.as_ref(), self);
Ok(ip)
}
pub fn disassemble_colored(&mut self, ip: InstructionPtr) -> String {
Disassembler::new(ip, self).disassemble_colored()
}
pub fn disassemble(&mut self, ip: InstructionPtr) -> String {
Disassembler::new(ip, self).disassemble()
}
fn downgrade_ctx<'a, 'bump, 'id>(
&'a mut self,
bump: &'bump Bump,
@@ -147,34 +193,6 @@ impl Runtime {
Ok(OwnedIr { _bump: bump, ir })
})
}
fn run(&mut self, ip: InstructionPtr) -> Result<crate::value::Value> {
let mut pc = ip.0;
loop {
let Runtime {
bytecode,
strings,
arena,
..
} = self;
let action =
arena.mutate_root(|mc, root| root.run_batch(bytecode, &mut pc, mc, strings));
match action {
Action::NeedGc => {
if self.arena.collection_phase() == CollectionPhase::Sweeping {
self.arena.collect_debt();
} else if let Some(marked) = self.arena.mark_debt() {
marked.start_sweeping();
}
}
Action::Done(done) => {
break done;
}
Action::Continue => (),
Action::IoRequest(_) => todo!(),
}
}
}
}
fn parse_error_span(error: &rnix::ParseError) -> Option<rnix::TextRange> {
@@ -536,4 +554,28 @@ impl BytecodeContext for Runtime {
fn get_code_mut(&mut self) -> &mut Vec<u8> {
&mut self.bytecode
}
fn add_constant(&mut self, val: StaticValue) -> u32 {
let bits = val.to_bits();
*self.constant_dedup.entry(bits).or_insert_with(|| {
let idx = self.constants.len() as u32;
self.constants.push(val);
idx
})
}
}
impl DisassemblerContext for Runtime {
fn lookup_string(&self, id: u32) -> &str {
self.strings
.resolve(SymbolU32::try_from_usize(id as usize).expect("invalid string id"))
.expect("string not found")
}
fn get_code(&self) -> &[u8] {
&self.bytecode
}
}
struct WellKnownSymbols {
// TODO:
}