refactor(vm): split cli and library code
This commit is contained in:
parent
0aa63e6c85
commit
a05ebb7f1e
19
Cargo.lock
generated
19
Cargo.lock
generated
|
@ -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",
|
||||
|
|
15
crates/fogtix-vm-cli/Cargo.toml
Normal file
15
crates/fogtix-vm-cli/Cargo.toml
Normal 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"
|
30
crates/fogtix-vm-cli/src/bin/fogtix-vm.rs
Normal file
30
crates/fogtix-vm-cli/src/bin/fogtix-vm.rs
Normal 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();
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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) {}
|
||||
|
|
Loading…
Reference in a new issue