feat: proper support for local (inside the same module) calls
This commit is contained in:
parent
d5148669b9
commit
993ce94fbd
|
@ -20,10 +20,11 @@ pub enum ValueType {
|
|||
#[repr(u16)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, IntEnum)]
|
||||
pub enum OpType {
|
||||
Label = 0x6c62, /* lb */
|
||||
Call = 0x636c, /* cl */
|
||||
Jump = 0x6a70, /* jp */
|
||||
Return = 0x7274, /* rt */
|
||||
Label = 0x6c62, /* lb */
|
||||
CallRemote = 0x6372, /* cr */
|
||||
CallLocal = 0x636c, /* cl */
|
||||
Jump = 0x6a70, /* jp */
|
||||
Return = 0x7274, /* rt */
|
||||
|
||||
Push = 0x7073, /* ps */
|
||||
Pop = 0x7071, /* pq */
|
||||
|
|
|
@ -11,7 +11,10 @@ pub enum Instr<'a> {
|
|||
/// pops the descriptor table pointer from the stack,
|
||||
/// then tries to call the $0 on it.
|
||||
/// $1 is the amount of arguments (arity) extracted from the stack after the pointer
|
||||
Call(Atom, u8),
|
||||
CallRemote(Atom, u8),
|
||||
|
||||
/// calls the destination $0 inside of the current module
|
||||
CallLocal(u64),
|
||||
|
||||
/// jumps to the destination $0 inside of the current module
|
||||
Jump(u64),
|
||||
|
@ -44,7 +47,8 @@ impl Instr<'_> {
|
|||
pub fn typ(&self) -> OpType {
|
||||
match self {
|
||||
Instr::Label => OpType::Label,
|
||||
Instr::Call(_, _) => OpType::Call,
|
||||
Instr::CallRemote(_, _) => OpType::CallRemote,
|
||||
Instr::CallLocal(_) => OpType::CallLocal,
|
||||
Instr::Jump(_) => OpType::Jump,
|
||||
Instr::Return => OpType::Return,
|
||||
Instr::Push(_) => OpType::Push,
|
||||
|
@ -90,11 +94,12 @@ impl<'a> crate::Parse<'a> for Instr<'a> {
|
|||
}
|
||||
let (inp, otyp) = OpType::parse(inp)?;
|
||||
match otyp {
|
||||
OpType::Call => {
|
||||
OpType::CallRemote => {
|
||||
let (inp, val) = Atom::parse(inp)?;
|
||||
let (inp, arity) = u8::parse(inp)?;
|
||||
Ok((inp, Instr::Call(val, arity)))
|
||||
Ok((inp, Instr::CallRemote(val, arity)))
|
||||
}
|
||||
OpType::CallLocal => u64::parse(inp).map(|(inp, val)| (inp, Instr::CallLocal(val))),
|
||||
OpType::Jump => u64::parse(inp).map(|(inp, val)| (inp, Instr::Jump(val))),
|
||||
OpType::Push => Ok(Value::parse(inp).map(|(inp, val)| (inp, Instr::Push(val)))?),
|
||||
OpType::Pop => u32::parse(inp).map(|(inp, val)| (inp, Instr::Pop(val))),
|
||||
|
@ -115,10 +120,11 @@ impl Instr<'_> {
|
|||
use int_enum::IntEnum;
|
||||
writer.write_all(&self.typ().int_value().to_be_bytes())?;
|
||||
match self {
|
||||
Instr::Call(val, arity) => {
|
||||
Instr::CallRemote(val, arity) => {
|
||||
val.write_to(&mut writer)?;
|
||||
writer.write_all(&[*arity])?;
|
||||
}
|
||||
Instr::CallLocal(val) => writer.write_all(&val.to_be_bytes())?,
|
||||
Instr::Jump(val) => writer.write_all(&val.to_be_bytes())?,
|
||||
Instr::Push(val) => val.write_to(writer)?,
|
||||
Instr::Pop(val) => writer.write_all(&val.to_be_bytes())?,
|
||||
|
|
|
@ -11,4 +11,4 @@ pub use parse::Parse;
|
|||
mod pointer;
|
||||
pub use pointer::{Atom, Pointer};
|
||||
mod value;
|
||||
pub use value::{CallTarget, ParseError as ValueParseError, Value};
|
||||
pub use value::{ParseError as ValueParseError, Value};
|
||||
|
|
|
@ -1,24 +1,8 @@
|
|||
use crate::consts::{CallType, ValueType};
|
||||
use crate::consts::ValueType;
|
||||
use crate::{Atom, Pointer};
|
||||
use core::fmt;
|
||||
use int_enum::IntEnum;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum CallTarget<'a> {
|
||||
Extern(&'a [u8]),
|
||||
Local { offset: u64, length: u32 },
|
||||
}
|
||||
|
||||
impl CallTarget<'_> {
|
||||
#[inline]
|
||||
pub fn typ(&self) -> CallType {
|
||||
match self {
|
||||
CallTarget::Extern(_) => CallType::Extern,
|
||||
CallTarget::Local { .. } => CallType::Local,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Value<'a> {
|
||||
Bytes(&'a [u8]),
|
||||
|
@ -69,48 +53,6 @@ impl From<core::num::TryFromIntError> for ParseError {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> crate::Parse<'a> for CallTarget<'a> {
|
||||
type Err = ParseError;
|
||||
|
||||
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), ParseError> {
|
||||
if inp.is_empty() {
|
||||
return Err(ParseError);
|
||||
}
|
||||
let (vtyp, inp) = (CallType::from_int(inp[0])?, &inp[1..]);
|
||||
Ok(match vtyp {
|
||||
CallType::Extern => {
|
||||
let (inp, data) = <&[u8]>::parse(inp)?;
|
||||
(inp, CallTarget::Extern(data))
|
||||
}
|
||||
|
||||
CallType::Local => {
|
||||
let (inp, offset) = u64::parse(inp)?;
|
||||
let (inp, length) = u32::parse(inp)?;
|
||||
(inp, CallTarget::Local { offset, length })
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "std"))]
|
||||
impl CallTarget<'_> {
|
||||
pub fn write_to<W: std::io::Write>(&self, mut writer: W) -> std::io::Result<()> {
|
||||
writer.write_all(&[self.typ().int_value()])?;
|
||||
match self {
|
||||
CallTarget::Extern(b) => {
|
||||
let len: u64 = b.len().try_into().unwrap();
|
||||
writer.write_all(&len.to_be_bytes())?;
|
||||
writer.write_all(b)?;
|
||||
}
|
||||
CallTarget::Local { offset, length } => {
|
||||
writer.write_all(&offset.to_be_bytes())?;
|
||||
writer.write_all(&length.to_be_bytes())?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> crate::Parse<'a> for Value<'a> {
|
||||
type Err = ParseError;
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ impl Process {
|
|||
self.instrp.1 += nxtiptr.len() - nxti_arr.len();
|
||||
match nxti {
|
||||
Instr::Label => {}
|
||||
Instr::Call(atom, arity) => {
|
||||
Instr::CallRemote(atom, arity) => {
|
||||
let mut call2jump = false;
|
||||
if let Some(n2xti_arr) = self.instrp.0.h.get(self.instrp.1..) {
|
||||
if let Ok((_, Instr::Return)) = Instr::parse(n2xti_arr) {
|
||||
|
@ -102,7 +102,7 @@ impl Process {
|
|||
}
|
||||
match self.stack.pop() {
|
||||
None => {
|
||||
eprintln!("ERROR: `call` invoked on empty stack @ {}", previptr);
|
||||
eprintln!("ERROR: `call-r` invoked on empty stack @ {}", previptr);
|
||||
break;
|
||||
}
|
||||
Some(StackEntValue::Pointer(wp)) => {
|
||||
|
@ -113,7 +113,7 @@ impl Process {
|
|||
}
|
||||
Some(x) => {
|
||||
eprintln!(
|
||||
"ERROR: `call` invoked on non-pointer {:?} @ {}",
|
||||
"ERROR: `call-r` invoked on non-pointer {:?} @ {}",
|
||||
x, previptr
|
||||
);
|
||||
break;
|
||||
|
@ -123,7 +123,40 @@ impl Process {
|
|||
if let Ok((_, trgi)) = Instr::parse(n2xti_arr) {
|
||||
if trgi != Instr::Label {
|
||||
eprintln!(
|
||||
"ERROR: `call` arrived at non-jump target {:?} @ {}",
|
||||
"ERROR: `call-r` arrived at non-jump target {:?} @ {}",
|
||||
trgi, previptr
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Instr::CallLocal(x) => {
|
||||
let mut call2jump = false;
|
||||
if let Some(n2xti_arr) = self.instrp.0.h.get(self.instrp.1..) {
|
||||
if let Ok((_, Instr::Return)) = Instr::parse(n2xti_arr) {
|
||||
// tail-call optimization (would otherwise require another opcode)
|
||||
call2jump = true;
|
||||
}
|
||||
}
|
||||
if !call2jump {
|
||||
self.callstack.push(self.instrp.clone());
|
||||
}
|
||||
self.instrp.1 = match x.try_into() {
|
||||
Ok(y) => y,
|
||||
Err(_) => {
|
||||
eprintln!(
|
||||
"ERROR: jump to out-of-bounds address @ {} -> {}",
|
||||
previptr, x
|
||||
);
|
||||
break;
|
||||
}
|
||||
};
|
||||
if let Some(n2xti_arr) = self.instrp.0.h.get(self.instrp.1..) {
|
||||
if let Ok((_, trgi)) = Instr::parse(n2xti_arr) {
|
||||
if trgi != Instr::Label {
|
||||
eprintln!(
|
||||
"ERROR: `call-l` arrived at non-jump target {:?} @ {}",
|
||||
trgi, previptr
|
||||
);
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue