refactor(vm): split cli and library code

This commit is contained in:
Alain Zscheile 2022-09-26 03:29:25 +02:00
parent 0aa63e6c85
commit a05ebb7f1e
6 changed files with 134 additions and 92 deletions

19
Cargo.lock generated
View file

@ -100,11 +100,18 @@ dependencies = [
name = "fogtix-vm"
version = "0.0.0"
dependencies = [
"clap",
"fogtix-bytecode",
"once_cell",
"readfilez",
"tracing",
]
[[package]]
name = "fogtix-vm-cli"
version = "0.1.0"
dependencies = [
"clap",
"fogtix-vm",
"readfilez",
"tracing-subscriber",
]
@ -179,9 +186,9 @@ dependencies = [
[[package]]
name = "memmap2"
version = "0.2.3"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4"
checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498"
dependencies = [
"libc",
]
@ -296,9 +303,9 @@ dependencies = [
[[package]]
name = "readfilez"
version = "0.2.4"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f220548aa8d7917441a6950adc5c9f8046b3dc7920f3281aea9e9019b1ccfc21"
checksum = "473e7e2af4ac8670109658b25641a83c4d4eec7239ee756394c782f4a31750fc"
dependencies = [
"delegate-attr",
"memmap2",

View file

@ -0,0 +1,15 @@
[package]
name = "fogtix-vm-cli"
version = "0.1.0"
edition = "2021"
license = "GPL-2+"
publish = false
[dependencies]
clap = "3.2"
fogtix-vm.path = "../fogtix-vm"
readfilez = "0.3"
tracing-subscriber = "0.3"
[[bin]]
name = "fogtix-vm"

View file

@ -0,0 +1,30 @@
use std::sync::Arc;
fn main() {
use clap::Arg;
let matches = clap::Command::new("fogtix-vm")
.version(env!("CARGO_PKG_VERSION"))
.arg(
Arg::new("main")
.short('m')
.help("the module whose entry point should be used")
.takes_value(true)
.required(true),
)
.get_matches();
tracing_subscriber::fmt::init();
let main_mod_path = matches.get_one::<String>("main").unwrap();
let main_mod = Arc::new(
readfilez::read_from_file(std::fs::File::open(&main_mod_path))
.expect("unable to open/load main module"),
);
fogtix_vm::Process::new(fogtix_vm::InstrPtr {
m: main_mod,
pos: 0,
})
.run();
}

View file

@ -6,10 +6,7 @@ license = "GPL-2+"
publish = false
[dependencies]
clap = "3.2"
readfilez = "0.2"
tracing = "0.1"
tracing-subscriber = "0.3"
[dependencies.fogtix-bytecode]
path = "../fogtix-bytecode"

View file

@ -1,15 +1,49 @@
use core::marker::PhantomData;
use fogtix_bytecode::{Atom, Instr, Parse, Pointer, Value as BcValue};
use std::marker::PhantomData;
use std::sync::Arc;
mod noop;
use noop::NOOP_ORIGIN;
pub struct Module {
h: readfilez::FileHandle,
pub type Module = Arc<dyn ModuleKind>;
pub trait ModuleKind: Send + Sync {
fn as_slice(&self) -> &[u8];
}
type InstrPtr = (Arc<Module>, usize);
impl<T: AsRef<[u8]> + Send + Sync + ?Sized> ModuleKind for T {
#[inline(always)]
fn as_slice(&self) -> &[u8] {
self.as_ref()
}
}
#[derive(Clone)]
pub struct InstrPtr {
pub m: Module,
pub pos: usize,
}
fn next_instr<'a>(m: &'a Module, pos: usize) -> Option<Result<(usize, Instr<'_>), &[u8]>> {
m.as_slice()
.get(pos..)
.map(|nxti_arr| match Instr::parse(nxti_arr) {
Err(_) => Err(&nxti_arr[..core::cmp::min(nxti_arr.len(), 20)]),
Ok((ptr, i)) => Ok((nxti_arr.len() - ptr.len(), i)),
})
}
impl InstrPtr {
#[inline(always)]
pub fn next_instr(&self) -> Option<Result<(usize, Instr<'_>), &[u8]>> {
next_instr(&self.m, self.pos)
}
fn is_call2jump(&self) -> bool {
// tail-call optimization (would otherwise require much more opcodes)
matches!(self.next_instr(), Some(Ok((_, Instr::Return))))
}
}
pub trait Origin: Send + Sync + core::fmt::Debug {
fn call(&self, p: &Pointer, a: &Atom, stack: &mut Vec<StackEntValue>) -> InstrPtr;
@ -52,36 +86,31 @@ pub enum StackEntValue {
Pointer(WrappedPointer),
}
struct Process {
pub struct Process {
stack: Vec<StackEntValue>,
callstack: Vec<InstrPtr>,
instrp: InstrPtr,
}
impl Process {
fn is_call2jump(instrp: &InstrPtr) -> bool {
let mut call2jump = false;
if let Some(n2xti_arr) = instrp.0.h.get(instrp.1..) {
if let Ok((_, Instr::Return)) = Instr::parse(n2xti_arr) {
// tail-call optimization (would otherwise require another opcode)
call2jump = true;
}
pub fn new(instrp: InstrPtr) -> Self {
Self {
stack: Vec::new(),
callstack: Vec::new(),
instrp,
}
call2jump
}
fn verify_jumptarget_explicit(previptr: usize, jinstr: &str, jtip: &InstrPtr) -> bool {
if let Some(n2xti_arr) = jtip.0.h.get(jtip.1..) {
if let Ok((_, trgi)) = Instr::parse(n2xti_arr) {
if trgi != Instr::Label {
tracing::error!(
"`{}` arrived at non-jump target {:?} @ {}",
jinstr,
trgi,
previptr,
);
return false;
}
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
@ -91,12 +120,12 @@ impl Process {
Self::verify_jumptarget_explicit(previptr, jinstr, &self.instrp)
}
fn run(&mut self) {
pub fn run(&mut self) {
loop {
use fogtix_bytecode::consts::MathBinOp;
let previptr = self.instrp.1;
let previptr = self.instrp.pos;
tracing::trace!("previptr = {}", previptr);
let nxti_arr = match self.instrp.0.h.get(previptr..) {
let (nxtidelta, nxti) = match next_instr(&self.instrp.m, self.instrp.pos) {
None => {
tracing::error!(
"reached EOF of module or jumped out of bounds -> {}",
@ -104,24 +133,21 @@ impl Process {
);
break;
}
Some(x) => x,
};
let (nxtiptr, nxti) = match Instr::parse(nxti_arr) {
Err(_) => {
Some(Err(code)) => {
tracing::error!(
"reached unparsable instruction {:?} @ {}",
&nxti_arr[..core::cmp::min(nxti_arr.len(), 20)],
self.instrp.1
code,
self.instrp.pos
);
break;
}
Ok(x) => x,
Some(Ok(x)) => x,
};
self.instrp.1 += nxti_arr.len() - nxtiptr.len();
self.instrp.pos += nxtidelta;
match nxti {
Instr::Label => {}
Instr::CallRemote(atom, arity) => {
if !Self::is_call2jump(&self.instrp) {
if !self.instrp.is_call2jump() {
self.callstack.push(self.instrp.clone());
}
match self.stack.pop() {
@ -149,10 +175,10 @@ impl Process {
}
}
Instr::CallLocal(x) => {
if !Self::is_call2jump(&self.instrp) {
if !self.instrp.is_call2jump() {
self.callstack.push(self.instrp.clone());
}
self.instrp.1 = match x.try_into() {
self.instrp.pos = match x.try_into() {
Ok(y) => y,
Err(_) => {
tracing::error!(
@ -168,12 +194,15 @@ impl Process {
}
}
Instr::CallLDefer(x) => match x.try_into() {
Ok(dfi) => {
let jtip = (self.instrp.0.clone(), dfi);
Ok(pos) => {
let jtip = InstrPtr {
m: self.instrp.m.clone(),
pos,
};
if !Self::verify_jumptarget_explicit(previptr, "call-l-defer", &jtip) {
break;
}
if Self::is_call2jump(&self.instrp) {
if self.instrp.is_call2jump() {
self.instrp = jtip;
} else {
self.callstack.push(jtip);
@ -216,7 +245,7 @@ impl Process {
}
};
if doit {
self.instrp.1 = x;
self.instrp.pos = x;
if !self.verify_jumptarget(previptr, "jump-cond") {
break;
}
@ -354,34 +383,3 @@ impl Process {
}
}
}
fn main() {
use clap::Arg;
let matches = clap::Command::new("fogtix-vm")
.version(env!("CARGO_PKG_VERSION"))
.arg(
Arg::new("main")
.short('m')
.help("the module whose entry point should be used")
.takes_value(true)
.required(true),
)
.get_matches();
tracing_subscriber::fmt::init();
let main_mod_path = matches.get_one::<String>("main").unwrap();
let main_mod = Arc::new(Module {
h: readfilez::read_from_file(std::fs::File::open(&main_mod_path))
.expect("unable to open/load main module"),
});
let mut prc = Process {
stack: Vec::new(),
callstack: Vec::new(),
instrp: (main_mod, 0),
};
prc.run();
}

View file

@ -4,15 +4,7 @@ use std::sync::Arc;
use crate::{InstrPtr, Module, Origin, StackEntValue};
pub static NOOP_MODULE: Lazy<Arc<Module>> = Lazy::new(|| {
let mut v = Vec::new();
use fogtix_bytecode::Instr;
Instr::Label.write_to(&mut v).unwrap();
Instr::Return.write_to(&mut v).unwrap();
Arc::new(Module {
h: readfilez::FileHandle::Buffered(v),
})
});
pub static NOOP_MODULE: Lazy<Module> = Lazy::new(|| Arc::new(b"LR".as_slice()));
#[derive(Debug)]
struct NoopOrigin;
@ -23,7 +15,10 @@ impl Origin for NoopOrigin {
p,
a
);
(Arc::clone(&NOOP_MODULE), 0)
InstrPtr {
m: Arc::clone(&NOOP_MODULE),
pos: 0,
}
}
fn incr_refcount(&self, _p: &Pointer) {}
fn decr_refcount(&self, _p: &Pointer) {}