rewrite VM to support reentry (WIP)
This commit is contained in:
+99
-57
@@ -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:
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user