feat+rf(vm): replace WrappedPointer with Pointer, reducing stack item size from 48 bytes to 32
This commit is contained in:
parent
6256336a5c
commit
911d662634
|
@ -44,7 +44,10 @@ impl From<Atom> for (u64, u64) {
|
|||
#[inline]
|
||||
fn from(Atom(x): Atom) -> (u64, u64) {
|
||||
let (a, b) = x.split_at(8);
|
||||
(u64::from_be_bytes(a.try_into().unwrap()), u64::from_be_bytes(b.try_into().unwrap()))
|
||||
(
|
||||
u64::from_be_bytes(a.try_into().unwrap()),
|
||||
u64::from_be_bytes(b.try_into().unwrap()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,14 @@ fn main() {
|
|||
.expect("unable to open/load main module"),
|
||||
);
|
||||
|
||||
fogtix_vm::Process::new(fogtix_vm::InstrPtr {
|
||||
m: main_mod,
|
||||
pos: 0,
|
||||
})
|
||||
.run();
|
||||
fogtix_vm::Process {
|
||||
korigs: fogtix_vm::make_default_origins(),
|
||||
stack: Vec::new(),
|
||||
callstack: Vec::new(),
|
||||
instrp: fogtix_vm::InstrPtr {
|
||||
m: main_mod,
|
||||
pos: 0,
|
||||
},
|
||||
}
|
||||
.run(None);
|
||||
}
|
||||
|
|
|
@ -12,10 +12,8 @@ tracing = "0.1"
|
|||
path = "../fogtix-bytecode"
|
||||
features = ["std"]
|
||||
|
||||
[dependencies.once_cell]
|
||||
version = "1.15"
|
||||
|
||||
[dev-dependencies]
|
||||
once_cell = "1.15"
|
||||
|
||||
[dev-dependencies.proptest]
|
||||
version = "1.0"
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
use fogtix_bytecode::{Atom, Instr, Parse, Pointer, Value as BcValue};
|
||||
use std::sync::Arc;
|
||||
|
||||
mod noop;
|
||||
use noop::NOOP_ORIGIN;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
pub type Module = Arc<dyn ModuleKind>;
|
||||
|
||||
|
@ -48,10 +45,18 @@ pub trait Origin: Send + Sync + core::fmt::Debug {
|
|||
fn call(&self, p: &Pointer, a: &Atom, stack: &mut Vec<StackEntValue>) -> InstrPtr;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WrappedPointer {
|
||||
orig: Arc<dyn Origin>,
|
||||
p: Pointer,
|
||||
pub type KnownOrigins = Arc<[RwLock<Option<Box<dyn Origin>>>; 0x10000]>;
|
||||
|
||||
fn make_large_arc_slice<const N: usize, T, F: Fn() -> T>(f: F) -> Arc<[T; N]> {
|
||||
// see also: https://stackoverflow.com/a/68122278
|
||||
// license: CC BY-SA 4.0; author: "Johannes Maria Frank"
|
||||
let bxs = core::iter::repeat_with(f).take(N).collect::<Arc<[T]>>();
|
||||
let ptr = Arc::into_raw(bxs) as *mut [T; N];
|
||||
unsafe { Arc::from_raw(ptr) }
|
||||
}
|
||||
|
||||
pub fn make_default_origins() -> KnownOrigins {
|
||||
make_large_arc_slice::<0x10000, RwLock<Option<Box<dyn Origin>>>, _>(|| RwLock::new(None))
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -59,56 +64,47 @@ pub enum StackEntValue {
|
|||
Bytes(Vec<u8>),
|
||||
Int(u64),
|
||||
Atom(Atom),
|
||||
Pointer(WrappedPointer),
|
||||
Pointer(Pointer),
|
||||
}
|
||||
|
||||
pub struct Process {
|
||||
pub korigs: KnownOrigins,
|
||||
pub stack: Vec<StackEntValue>,
|
||||
pub callstack: Vec<InstrPtr>,
|
||||
pub instrp: InstrPtr,
|
||||
pub fuel: Option<u64>,
|
||||
}
|
||||
|
||||
fn verify_jumptarget_explicit(previptr: usize, jinstr: &str, jtip: &InstrPtr) -> bool {
|
||||
if let Some(Ok((_, trgi))) = jtip.next_instr() {
|
||||
if trgi != Instr::Label {
|
||||
tracing::error!(
|
||||
"`{}` arrived at non-jump target {:?} @ {}",
|
||||
jinstr,
|
||||
trgi,
|
||||
previptr,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
impl Process {
|
||||
pub fn new(instrp: InstrPtr) -> Self {
|
||||
Self {
|
||||
stack: Vec::new(),
|
||||
callstack: Vec::new(),
|
||||
instrp,
|
||||
fuel: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_jumptarget_explicit(previptr: usize, jinstr: &str, jtip: &InstrPtr) -> bool {
|
||||
if let Some(Ok((_, trgi))) = jtip.next_instr() {
|
||||
if trgi != Instr::Label {
|
||||
tracing::error!(
|
||||
"`{}` arrived at non-jump target {:?} @ {}",
|
||||
jinstr,
|
||||
trgi,
|
||||
previptr,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn verify_jumptarget(&self, previptr: usize, jinstr: &str) -> bool {
|
||||
Self::verify_jumptarget_explicit(previptr, jinstr, &self.instrp)
|
||||
verify_jumptarget_explicit(previptr, jinstr, &self.instrp)
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
pub fn run(&mut self, mut fuel: Option<&mut u64>) {
|
||||
loop {
|
||||
use fogtix_bytecode::consts::MathBinOp;
|
||||
let previptr = self.instrp.pos;
|
||||
tracing::trace!("previptr = {}", previptr);
|
||||
if let Some(ref mut x) = &mut self.fuel {
|
||||
if *x == 0 {
|
||||
if let Some(ref mut x) = fuel {
|
||||
if **x == 0 {
|
||||
tracing::info!("out of fuel");
|
||||
break;
|
||||
}
|
||||
*x -= 1;
|
||||
**x -= 1;
|
||||
}
|
||||
let (nxtidelta, nxti) = match next_instr(&self.instrp.m, self.instrp.pos) {
|
||||
None => {
|
||||
|
@ -143,8 +139,32 @@ impl Process {
|
|||
Some(StackEntValue::Pointer(wp)) => {
|
||||
let ssl = self.stack.len();
|
||||
let mut args = self.stack.drain(ssl - usize::from(arity)..).collect();
|
||||
self.instrp = wp.orig.call(&wp.p, &atom, &mut args);
|
||||
self.stack.extend(args);
|
||||
let origin_id = usize::from(wp.origin());
|
||||
let origin = self.korigs[origin_id].read();
|
||||
match origin {
|
||||
Ok(origin) => match origin.as_ref() {
|
||||
Some(origin) => {
|
||||
self.instrp = origin.call(&wp, &atom, &mut args);
|
||||
self.stack.extend(args);
|
||||
}
|
||||
None => {
|
||||
tracing::error!(
|
||||
"`call-r` invoked on invalid origin {:x} @ {}",
|
||||
origin_id,
|
||||
previptr
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
tracing::error!(
|
||||
"`call-r` invoked on poisoned origin {:x} @ {}",
|
||||
origin_id,
|
||||
previptr
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(x) => {
|
||||
tracing::error!(
|
||||
|
@ -184,7 +204,7 @@ impl Process {
|
|||
m: self.instrp.m.clone(),
|
||||
pos,
|
||||
};
|
||||
if !Self::verify_jumptarget_explicit(previptr, "call-l-defer", &jtip) {
|
||||
if !verify_jumptarget_explicit(previptr, "call-l-defer", &jtip) {
|
||||
break;
|
||||
}
|
||||
if self.instrp.is_call2jump() {
|
||||
|
@ -248,10 +268,7 @@ impl Process {
|
|||
BcValue::Bytes(v) => StackEntValue::Bytes(v.to_vec()),
|
||||
BcValue::Int(i) => StackEntValue::Int(i),
|
||||
BcValue::Atom(a) => StackEntValue::Atom(a),
|
||||
BcValue::Pointer(p) => StackEntValue::Pointer(WrappedPointer {
|
||||
orig: Arc::clone(&*NOOP_ORIGIN),
|
||||
p,
|
||||
}),
|
||||
BcValue::Pointer(p) => StackEntValue::Pointer(p),
|
||||
});
|
||||
}
|
||||
Instr::Pop(_) if self.stack.is_empty() => {
|
||||
|
@ -372,19 +389,32 @@ impl Process {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn stack_item_size() {
|
||||
assert_eq!(core::mem::size_of::<StackEntValue>(), 32);
|
||||
}
|
||||
|
||||
proptest::proptest! {
|
||||
#![proptest_config(proptest::prelude::ProptestConfig::with_cases(4096))]
|
||||
|
||||
#[test]
|
||||
fn doesnt_crash(inp in proptest::collection::vec(0..=u8::MAX, 0..1024)) {
|
||||
use once_cell::sync::Lazy;
|
||||
// we use a static here to avoid allocating the very large array on every iteration
|
||||
static KORIGS: Lazy<KnownOrigins> = Lazy::new(|| make_default_origins());
|
||||
let inp: Arc<Vec<u8>> = Arc::new(inp);
|
||||
let module: Module = inp;
|
||||
let mut p = Process::new(InstrPtr {
|
||||
m: module,
|
||||
pos: 0,
|
||||
});
|
||||
p.fuel = Some(1024);
|
||||
p.run();
|
||||
let mut p = Process {
|
||||
korigs: Arc::clone(&*KORIGS),
|
||||
stack: Vec::new(),
|
||||
callstack: Vec::new(),
|
||||
instrp: InstrPtr {
|
||||
m: module,
|
||||
pos: 0,
|
||||
},
|
||||
};
|
||||
p.run(Some(&mut 1024));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
use fogtix_bytecode::{Atom, Pointer};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{InstrPtr, Module, Origin, StackEntValue};
|
||||
|
||||
pub static NOOP_MODULE: Lazy<Module> = Lazy::new(|| Arc::new(b"LR".as_slice()));
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NoopOrigin;
|
||||
impl Origin for NoopOrigin {
|
||||
fn call(&self, p: &Pointer, a: &Atom, _stack: &mut Vec<StackEntValue>) -> InstrPtr {
|
||||
tracing::error!(
|
||||
"tried to invoke pointer {:?}({:?}) without valid context",
|
||||
p,
|
||||
a
|
||||
);
|
||||
InstrPtr {
|
||||
m: Arc::clone(&NOOP_MODULE),
|
||||
pos: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub static NOOP_ORIGIN: Lazy<Arc<dyn Origin>> = Lazy::new(|| Arc::new(NoopOrigin));
|
Loading…
Reference in a new issue