mirror of
https://github.com/Noratrieb/dilaria.git
synced 2026-01-14 09:25:02 +01:00
call ABI
This commit is contained in:
parent
d715213731
commit
23728f26dd
2 changed files with 53 additions and 2 deletions
|
|
@ -14,6 +14,43 @@
|
|||
//! Variables offsets are calculated as `local offsets`. Local offsets are calculated relative to
|
||||
//! the start of the space of the stack required by that function. The interpreter must keep track
|
||||
//! of the stack start of each function, to be able to calculate these offsets.
|
||||
//!
|
||||
//! # Function calls
|
||||
//! After the function returns, the interpreter resets its stack manually back
|
||||
//! to the length before the call. This means the interpreter has to do some bookkeeping, but it has
|
||||
//! to do that anyways.
|
||||
//!
|
||||
//! # ABI
|
||||
//! Function arguments are passed on the stack and can be loaded just like local variables. They belong
|
||||
//! to the stack frame of the new function and are cleaned up after returning, leaving the return value where
|
||||
//! the stack frame was.
|
||||
//!
|
||||
//! When a call happens, the current stack offset is pushed onto the stack as a `Value::Native` and
|
||||
//! the element before it is stored as the new offset.
|
||||
//! Then all parameters are pushed onto the stack, from last to first.
|
||||
//! Afterwards, execution of the code is started. A function always has to return, and compiler
|
||||
//! inserts `return null` at the end of every function implicitly.
|
||||
//!
|
||||
//! If a return happens, the VM loads the current value on the stack. It then goes to the start
|
||||
//! of the stack frame and saves the `Value::Native` that stores the old stack offset and loads that
|
||||
//! into its stack offset. It then removes the whole stack frame from the stack, and pushes the
|
||||
//! returned value.
|
||||
//!
|
||||
//! ```text
|
||||
//! old stack offset ╮ ╭ Parameters ╮ ╭ local
|
||||
//! v v v v
|
||||
//! ───────┬────────────┬───────────┬──────────┬─────────╮
|
||||
//! Num(6) │ Native(20) │ Num(5) │ Num(6) │ Num(5) │
|
||||
//! ───────┴────────────┴───────────┴──────────┴─────────╯
|
||||
//! ╰────────────────────────────────────────────────── current stack frame
|
||||
//! ^ ^
|
||||
//! ╰─ old local ╰╮
|
||||
//! │
|
||||
//! │
|
||||
//! Vm │
|
||||
//! Current stack offset ╯
|
||||
//!
|
||||
//! ```
|
||||
|
||||
use crate::errors::Span;
|
||||
use crate::vm::Value;
|
||||
|
|
@ -24,7 +61,7 @@ use debug2::Formatter;
|
|||
#[derive(Debug)]
|
||||
pub struct FnBlock<'bc> {
|
||||
/// The bytecode of the function
|
||||
pub code: Vec<'bc, Instr>,
|
||||
pub code: Vec<'bc, Instr<'bc>>,
|
||||
/// The sizes of the stack required by the function after the instruction at the same index. This is only used
|
||||
/// during compilation to calculate local variable offsets.
|
||||
pub stack_sizes: Vec<'bc, usize>,
|
||||
|
|
@ -50,7 +87,7 @@ impl debug2::Debug for FnBlock<'_> {
|
|||
/// A bytecode instruction. For more details on the structure of the bytecode, read the module level docs [`bytecode`](`self`)
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg_attr(feature = "pretty", derive(debug2::Debug))]
|
||||
pub enum Instr {
|
||||
pub enum Instr<'bc> {
|
||||
/// An operation that does nothing.
|
||||
Nop,
|
||||
|
||||
|
|
@ -86,6 +123,10 @@ pub enum Instr {
|
|||
/// Same as `JmpFalse`, but unconditional
|
||||
Jmp(isize),
|
||||
|
||||
Call(&'bc FnBlock<'bc>),
|
||||
|
||||
Return,
|
||||
|
||||
/// Shrinks the stack by `usize` elements, should always be emitted before backwards jumps
|
||||
ShrinkStack(usize),
|
||||
}
|
||||
|
|
|
|||
10
src/vm.rs
10
src/vm.rs
|
|
@ -28,12 +28,20 @@ pub fn execute<'bc>(
|
|||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg_attr(feature = "pretty", derive(debug2::Debug))]
|
||||
pub enum Value {
|
||||
/// `null`
|
||||
Null,
|
||||
/// A boolean value
|
||||
Bool(bool),
|
||||
/// A floating point number
|
||||
Num(f64),
|
||||
/// An interned string
|
||||
String(Symbol),
|
||||
/// An array of values
|
||||
Array,
|
||||
/// A map from string to value
|
||||
Object(Object),
|
||||
/// A value that is stored by the vm for bookkeeping and should never be accessed for anythign else
|
||||
Native(usize),
|
||||
}
|
||||
|
||||
const _: () = _check_val_size();
|
||||
|
|
@ -170,6 +178,7 @@ impl<'bc> Vm<'bc, '_> {
|
|||
}
|
||||
}
|
||||
Instr::Jmp(pos) => self.pc = (self.pc as isize + pos) as usize,
|
||||
Instr::Call(_) => todo!(),
|
||||
Instr::ShrinkStack(size) => {
|
||||
assert!(self.stack.len() >= size);
|
||||
let new_len = self.stack.len() - size;
|
||||
|
|
@ -223,6 +232,7 @@ impl Display for Value {
|
|||
Value::String(str) => f.write_str(str.as_str()),
|
||||
Value::Array => todo!(),
|
||||
Value::Object(_) => todo!(),
|
||||
Value::Native(_) => panic!("Called display on native value!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue