feat(vm): callstack doesn't really need to always store the module pointer for each callframe
This commit is contained in:
parent
e101c9fd9f
commit
25ae04cb37
|
@ -18,20 +18,18 @@ fn main() {
|
|||
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))
|
||||
readfilez::read_from_file(std::fs::File::open(main_mod_path))
|
||||
.expect("unable to open/load main module"),
|
||||
);
|
||||
|
||||
let mut p = fogtix_vm::Process {
|
||||
stack: Vec::new(),
|
||||
callstack: Vec::new(),
|
||||
instrp: fogtix_vm::InstrPtr {
|
||||
m: main_mod,
|
||||
pos: 0,
|
||||
},
|
||||
m: main_mod,
|
||||
instrp: 0,
|
||||
};
|
||||
|
||||
if let Err(e) = p.run(None) {
|
||||
eprintln!("ERROR@{}: {}", p.instrp.pos, e);
|
||||
eprintln!("ERROR@{}: {}", p.instrp, e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,27 +20,7 @@ impl<T: AsRef<[u8]> + Send + Sync + ?Sized> ModuleKind for T {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InstrPtr {
|
||||
pub m: Module,
|
||||
pub pos: usize,
|
||||
}
|
||||
|
||||
impl fmt::Display for InstrPtr {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "@{:p}:{:x}", self.m, self.pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for InstrPtr {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
<Self as fmt::Display>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
fn next_instr(m: &Module, pos: usize) -> Option<Result<(usize, Instr), &[u8]>> {
|
||||
fn next_instr(m: &dyn ModuleKind, pos: usize) -> Option<Result<(usize, Instr), &[u8]>> {
|
||||
m.as_slice()
|
||||
.get(pos..)
|
||||
.map(|nxti_arr| match Instr::parse(nxti_arr) {
|
||||
|
@ -49,16 +29,9 @@ fn next_instr(m: &Module, pos: usize) -> Option<Result<(usize, Instr), &[u8]>> {
|
|||
})
|
||||
}
|
||||
|
||||
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))))
|
||||
}
|
||||
fn is_call2jump(m: &dyn ModuleKind, pos: usize) -> bool {
|
||||
// tail-call optimization (would otherwise require much more opcodes)
|
||||
matches!(next_instr(m, pos), Some(Ok((_, Instr::Return))))
|
||||
}
|
||||
|
||||
pub type StackEntValue = u64;
|
||||
|
@ -67,8 +40,8 @@ pub type StackEntValue = u64;
|
|||
pub enum Error {
|
||||
InvalidJumpTarget {
|
||||
invoked_by: &'static str,
|
||||
from: InstrPtr,
|
||||
to: InstrPtr,
|
||||
from: usize,
|
||||
to: usize,
|
||||
},
|
||||
InstrpOutOfBounds,
|
||||
UnparsableInstruction(Vec<u8>),
|
||||
|
@ -104,31 +77,33 @@ impl fmt::Display for Error {
|
|||
|
||||
pub struct Process {
|
||||
pub stack: Vec<StackEntValue>,
|
||||
pub callstack: Vec<InstrPtr>,
|
||||
pub instrp: InstrPtr,
|
||||
pub m: Module,
|
||||
pub callstack: Vec<usize>,
|
||||
pub instrp: usize,
|
||||
}
|
||||
|
||||
fn verify_jumptarget_explicit(
|
||||
jinstr: &'static str,
|
||||
from: &InstrPtr,
|
||||
to: &InstrPtr,
|
||||
m: &dyn ModuleKind,
|
||||
from: usize,
|
||||
to: usize,
|
||||
) -> Result<(), Error> {
|
||||
if to.pos == 0 {
|
||||
if to == 0 {
|
||||
Ok(())
|
||||
} else if let Some(Ok((_, Instr::Label))) = to.next_instr() {
|
||||
} else if let Some(Ok((_, Instr::Label))) = next_instr(m, to) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidJumpTarget {
|
||||
invoked_by: jinstr,
|
||||
from: from.clone(),
|
||||
to: to.clone(),
|
||||
from,
|
||||
to,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Process {
|
||||
fn verify_jumptarget(&self, jinstr: &'static str, from: &InstrPtr) -> Result<(), Error> {
|
||||
verify_jumptarget_explicit(jinstr, from, &self.instrp)
|
||||
fn verify_jumptarget(&self, jinstr: &'static str, from: usize) -> Result<(), Error> {
|
||||
verify_jumptarget_explicit(jinstr, &*self.m, from, self.instrp)
|
||||
}
|
||||
|
||||
fn stpop(&mut self) -> Result<StackEntValue, Error> {
|
||||
|
@ -137,7 +112,7 @@ impl Process {
|
|||
|
||||
pub fn run(&mut self, mut fuel: Option<&mut u64>) -> Result<(), Error> {
|
||||
loop {
|
||||
let previptr = self.instrp.pos;
|
||||
let previptr = self.instrp;
|
||||
tracing::trace!("previptr = {}", previptr);
|
||||
if let Some(ref mut x) = fuel {
|
||||
if **x == 0 {
|
||||
|
@ -145,51 +120,43 @@ impl Process {
|
|||
}
|
||||
**x -= 1;
|
||||
}
|
||||
let (nxtidelta, nxti) = match next_instr(&self.instrp.m, self.instrp.pos) {
|
||||
let (nxtidelta, nxti) = match next_instr(&*self.m, self.instrp) {
|
||||
None => return Err(Error::InstrpOutOfBounds),
|
||||
Some(Err(code)) => return Err(Error::UnparsableInstruction(code.to_vec())),
|
||||
Some(Ok(x)) => x,
|
||||
};
|
||||
self.instrp.pos += nxtidelta;
|
||||
self.instrp += nxtidelta;
|
||||
match nxti {
|
||||
Instr::Label => {}
|
||||
Instr::CallRemote => {
|
||||
if !self.instrp.is_call2jump() {
|
||||
self.callstack.push(self.instrp.clone());
|
||||
if !is_call2jump(&*self.m, self.instrp) {
|
||||
self.callstack.push(self.instrp);
|
||||
}
|
||||
return Err(Error::RemoteCall(self.stpop()?));
|
||||
}
|
||||
Instr::CallLocal(x) => {
|
||||
if !self.instrp.is_call2jump() {
|
||||
if !is_call2jump(&*self.m, self.instrp) {
|
||||
self.callstack.push(self.instrp.clone());
|
||||
}
|
||||
self.instrp.pos = match x.try_into() {
|
||||
self.instrp = match x.try_into() {
|
||||
Ok(y) => y,
|
||||
Err(_) => return Err(Error::InstrpOutOfBounds),
|
||||
};
|
||||
self.verify_jumptarget(
|
||||
"call-l",
|
||||
&InstrPtr {
|
||||
m: self.instrp.m.clone(),
|
||||
pos: previptr,
|
||||
},
|
||||
previptr,
|
||||
)?;
|
||||
}
|
||||
Instr::CallLDefer(x) => match x.try_into() {
|
||||
Ok(pos) => {
|
||||
let jtip = InstrPtr {
|
||||
m: self.instrp.m.clone(),
|
||||
pos,
|
||||
};
|
||||
let jtip = pos;
|
||||
verify_jumptarget_explicit(
|
||||
"call-l-defer",
|
||||
&InstrPtr {
|
||||
m: self.instrp.m.clone(),
|
||||
pos: previptr,
|
||||
},
|
||||
&jtip,
|
||||
&*self.m,
|
||||
previptr,
|
||||
jtip,
|
||||
)?;
|
||||
if self.instrp.is_call2jump() {
|
||||
if is_call2jump(&*self.m, self.instrp) {
|
||||
self.instrp = jtip;
|
||||
} else {
|
||||
self.callstack.push(jtip);
|
||||
|
@ -203,13 +170,10 @@ impl Process {
|
|||
Err(_) => return Err(Error::InstrpOutOfBounds),
|
||||
};
|
||||
if self.stpop()? != 0 {
|
||||
self.instrp.pos = x;
|
||||
self.instrp = x;
|
||||
self.verify_jumptarget(
|
||||
"jump-cond",
|
||||
&InstrPtr {
|
||||
m: self.instrp.m.clone(),
|
||||
pos: previptr,
|
||||
},
|
||||
previptr,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
@ -312,10 +276,8 @@ mod tests {
|
|||
let mut p = Process {
|
||||
stack: Vec::new(),
|
||||
callstack: Vec::new(),
|
||||
instrp: InstrPtr {
|
||||
m: module,
|
||||
pos: 0,
|
||||
},
|
||||
m: module,
|
||||
instrp: 0,
|
||||
};
|
||||
let _ = p.run(Some(&mut 2048));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue