aboutsummaryrefslogtreecommitdiff
path: root/crates/sloth_vm/src/vm.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/sloth_vm/src/vm.rs')
-rw-r--r--crates/sloth_vm/src/vm.rs610
1 files changed, 0 insertions, 610 deletions
diff --git a/crates/sloth_vm/src/vm.rs b/crates/sloth_vm/src/vm.rs
deleted file mode 100644
index 3600719..0000000
--- a/crates/sloth_vm/src/vm.rs
+++ /dev/null
@@ -1,610 +0,0 @@
-use std::mem::MaybeUninit;
-
-use sloth_bytecode::Opcode;
-
-use crate::value::{Function, Object, ObjectType, Primitive};
-use crate::{native, vm, ObjectMap, Stack};
-
-#[derive(Clone, Copy)]
-pub struct CallFrame {
- pointer: usize,
- stack_offset: usize,
- function: *const Function, // TODO: Safety
-}
-
-impl CallFrame {
- fn new(stack_offset: usize, function: &Function) -> Self {
- Self {
- pointer: 0,
- stack_offset,
- function: function as *const _,
- }
- }
-
- #[inline]
- fn function(&self) -> &Function {
- unsafe { &*self.function }
- }
-}
-
-const CALL_STACK_SIZE: usize = 1024;
-
-pub struct CallStack {
- top: usize,
- frames: [MaybeUninit<CallFrame>; CALL_STACK_SIZE],
-}
-
-impl Default for CallStack {
- fn default() -> Self {
- Self {
- top: 0,
- frames: [MaybeUninit::uninit(); CALL_STACK_SIZE],
- }
- }
-}
-
-impl CallStack {
- fn push(&mut self, frame: CallFrame) {
- self.frames[self.top].write(frame);
- self.top += 1;
- }
-
- fn pop(&mut self) {
- self.top -= 1;
- }
-
- fn peek(&self) -> &CallFrame {
- unsafe { self.frames[self.top - 1].assume_init_ref() }
- }
-
- fn peek_mut(&mut self) -> &mut CallFrame {
- unsafe { self.frames[self.top - 1].assume_init_mut() }
- }
-}
-
-// TODO: Fix visibility
-pub struct VM {
- pub stack: Stack,
- call_stack: CallStack,
- pub objects: ObjectMap,
-}
-
-impl Default for VM {
- fn default() -> Self {
- Self::init(ObjectMap::default())
- }
-}
-
-impl VM {
- pub fn init(objects: ObjectMap) -> Self {
- Self {
- stack: Stack::default(),
- call_stack: CallStack::default(),
- objects,
- }
- }
-
- pub fn new(objects: ObjectMap, mut root: Function) -> Self {
- let mut this = Self::init(objects);
-
- // Allocating the root function
- root.chunk.code.push(Opcode::Halt as u8);
- this.call_stack.push(CallFrame::new(0, &root));
- this.objects
- .allocate(Object::new(ObjectType::Function(root)));
-
- this
- }
-
- pub fn step(&mut self) -> bool {
- use Primitive::*;
-
- let opcode = self.read_u8();
-
- match Opcode::from_u8(opcode) {
- Opcode::Constant => {
- let idx = self.read_u16() as usize;
- let value = self.call_stack.peek().function().chunk.constants[idx];
-
- self.stack.push(value);
- }
-
- Opcode::Dup => {
- let value = self.stack.pop();
- self.stack.push(value);
- self.stack.push(value);
- }
- Opcode::Pop => {
- self.stack.pop();
- }
- Opcode::GetLocal => {
- let idx = self.read_u16() as usize;
- let value = self.stack[self.call_stack.peek().stack_offset + idx];
-
- self.stack.push(value);
- }
- Opcode::SetLocal => {
- let idx = self.read_u16() as usize;
- let value = self.stack.pop();
-
- self.stack[self.call_stack.peek().stack_offset + idx] = value;
- }
- Opcode::Box => {
- // FIXME: TODO: MEGA CURSED
- let pos = self.read_u16() as usize;
- let value = self.stack.pop();
-
- let object = vm::Object::new(ObjectType::Box(value));
-
- self.objects.heap[pos] = object;
- self.stack.push(Object(pos as u32));
- }
-
- Opcode::Add => {
- let value = match self.stack.pop2() {
- (Integer(lhs), Integer(rhs)) => Integer(lhs + rhs),
- (Float(lhs), Float(rhs)) => Float(lhs + rhs),
- _ => panic!(),
- };
-
- self.stack.push(value);
- }
- Opcode::Sub => {
- let value = match self.stack.pop2() {
- (Integer(lhs), Integer(rhs)) => Integer(lhs - rhs),
- (Float(lhs), Float(rhs)) => Float(lhs - rhs),
- _ => panic!(),
- };
-
- self.stack.push(value);
- }
- Opcode::Mul => {
- let value = match self.stack.pop2() {
- (Integer(lhs), Integer(rhs)) => Integer(lhs * rhs),
- (Float(lhs), Float(rhs)) => Float(lhs * rhs),
- _ => panic!(),
- };
-
- self.stack.push(value);
- }
- Opcode::Div => {
- let value = match self.stack.pop2() {
- (Integer(_), Integer(0)) => panic!("Divide by 0"),
- (Integer(lhs), Integer(rhs)) => Integer(lhs / rhs),
- (Float(lhs), Float(rhs)) => Float(lhs / rhs),
- _ => panic!(),
- };
-
- self.stack.push(value);
- }
- Opcode::Mod => {
- let value = match self.stack.pop2() {
- (Integer(lhs), Integer(rhs)) => Integer(lhs % rhs),
- (Float(lhs), Float(rhs)) => Float(lhs % rhs),
- _ => panic!(),
- };
-
- self.stack.push(value);
- }
-
- Opcode::Eq => {
- let value = match self.stack.pop2() {
- (Integer(lhs), Integer(rhs)) => Bool(lhs == rhs),
- (Float(lhs), Float(rhs)) => Bool(lhs == rhs),
- (Bool(lhs), Bool(rhs)) => Bool(lhs == rhs),
- (Object(lhs), Object(rhs)) => Bool(lhs == rhs),
- (Empty, Empty) => Bool(true),
- _ => Bool(false),
- };
-
- self.stack.push(value);
- }
- Opcode::Ne => {
- let value = match self.stack.pop2() {
- (Integer(lhs), Integer(rhs)) => Bool(lhs != rhs),
- (Float(lhs), Float(rhs)) => Bool(lhs != rhs),
- (Bool(lhs), Bool(rhs)) => Bool(lhs != rhs),
- (Object(lhs), Object(rhs)) => Bool(lhs != rhs),
- (Empty, Empty) => Bool(false),
- _ => Bool(false),
- };
-
- self.stack.push(value);
- }
-
- Opcode::Jump => {
- let to = self.read_u16();
- self.call_stack.peek_mut().pointer = to as usize;
- }
- Opcode::JumpIf => {
- let to = self.read_u16();
- let value = self.stack.pop();
-
- if let Bool(true) = value {
- self.call_stack.peek_mut().pointer = to as usize;
- }
- }
-
- Opcode::Call => {
- let Primitive::Object(ptr) = self.stack.pop() else {
- panic!("Last element on stack was not an object");
- };
-
- self.call(ptr as usize);
- }
-
- Opcode::Return => {
- self.call_return();
- }
-
- Opcode::Halt => return false,
-
- opcode => unimplemented!("Opcode {:?} unimplemented", opcode),
- }
-
- true
- }
-
- pub fn run(&mut self) {
- while self.step() {}
- }
-
- pub fn call(&mut self, ptr: usize) {
- let Some(obj) = self.objects.get(ptr) else {
- panic!("Pointer referenced nothing");
- };
-
- match &obj.typ {
- ObjectType::Function(function) => {
- // Add a callstack entry for the function
- let offset = self.stack.top - (function.arity as usize);
- self.call_stack.push(CallFrame::new(offset, function));
- }
- ObjectType::NativeFunction(function) => {
- let mut args = Vec::with_capacity(function.arity as usize);
- for _ in 0..function.arity {
- args.push(self.stack.pop());
- }
-
- let name = function.name;
- let returns_value = function.returns_value;
-
- let internal = function.function;
- let result = internal(self, &args);
-
- match result {
- Ok(value) => {
- if returns_value {
- self.stack.push(value);
- }
- }
- Err(error) => match error {
- native::Error::InvalidArgument => {
- panic!("Invalid argument provided to '{name}'");
- }
- native::Error::Unknown(msg) => {
- panic!("Native function '{name}' failed due to '{msg}'");
- }
- },
- }
- }
- _ => panic!("Object was not a function"),
- }
- }
-
- fn call_return(&mut self) {
- let function = self.call_stack.peek().function();
- let stack_offset = self.call_stack.peek().stack_offset;
-
- let return_value = if function.returns_value {
- Some(self.stack.pop())
- } else {
- None
- };
-
- self.stack.top = stack_offset;
-
- if let Some(return_value) = return_value {
- self.stack.push(return_value);
- }
-
- self.call_stack.pop();
- }
-
- fn unwind(&mut self) {
- unimplemented!("Implement unwinding for error handling");
- }
-
- #[inline(always)]
- fn read_u8(&mut self) -> u8 {
- let frame = self.call_stack.peek_mut();
- let function = frame.function();
- let byte = function.chunk.code[frame.pointer];
- frame.pointer += 1;
- byte
- }
-
- #[inline(always)]
- fn read_u16(&mut self) -> u16 {
- let frame = self.call_stack.peek_mut();
- let chunk = &frame.function().chunk;
-
- let bytes = (chunk.code[frame.pointer], chunk.code[frame.pointer + 1]);
-
- frame.pointer += 2;
-
- ((bytes.0 as u16) << 8) + (bytes.1 as u16)
- }
-
- #[inline(always)]
- pub fn objects(&self) -> &ObjectMap {
- &self.objects
- }
-
- #[inline(always)]
- pub fn objects_mut(&mut self) -> &mut ObjectMap {
- &mut self.objects
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::value::{Function, Object, ObjectType, Primitive};
- use crate::{sloth_std, Chunk, ObjectMap, VM};
-
- #[test]
- fn arithmetic_ops() {
- // Addition
- let mut vm = VM::new(
- ObjectMap::default(),
- Function::root(Chunk {
- constants: vec![Primitive::Integer(7)],
- code: vec![
- 0x00, 0, 0, // Load constant from 0
- 0x10, // Duplicate
- 0x20, // Add
- 0xE0,
- ],
- }),
- );
-
- vm.run();
- assert_eq!(vm.stack.peek(), Primitive::Integer(14));
-
- let mut vm = VM::new(
- ObjectMap::default(),
- Function::root(Chunk {
- constants: vec![Primitive::Integer(2), Primitive::Integer(11)],
- code: vec![
- 0x00, 0, 0, // Load constant from 0
- 0x00, 0, 1, // Load constant from 1
- 0x20, // Add
- 0xE0,
- ],
- }),
- );
-
- vm.run();
- assert_eq!(vm.stack.peek(), Primitive::Integer(13));
- }
-
- #[test]
- fn basic_function() {
- let mut vm = VM::new(
- ObjectMap::from(vec![Object::new(ObjectType::Function(Function {
- name: Some("add".to_string()),
- chunk: Chunk {
- constants: vec![],
- code: vec![0x14, 0, 0, 0x14, 0, 1, 0x20, 0x52],
- },
- arity: 2,
- returns_value: true,
- }))]),
- Function::root(Chunk {
- constants: vec![
- Primitive::Integer(6),
- Primitive::Integer(3),
- Primitive::Object(0),
- Primitive::Object(1),
- Primitive::Object(2),
- ],
- code: vec![
- 0x00, 0, 0, // Load first function parameter from 0
- 0x00, 0, 1, // Load second function parameter from 1
- 0x00, 0, 2, // Load function constant from 2
- 0x50, // Call function
- ],
- }),
- );
-
- vm.run();
-
- assert_eq!(vm.stack.peek(), Primitive::Integer(9));
- }
-
- #[test]
- fn native_function() {
- let mut vm = VM::new(
- ObjectMap::from(vec![
- Object::new(ObjectType::NativeFunction(sloth_std::rand::GEN_FUNCTION)),
- Object::new(ObjectType::NativeFunction(
- sloth_std::rand::GEN_RANGE_FUNCTION,
- )),
- ]),
- Function::root(Chunk {
- constants: vec![
- Primitive::Object(0),
- Primitive::Object(1),
- Primitive::Integer(5),
- Primitive::Integer(10),
- ],
- code: vec![
- // First part
- 0x00, 0, 0, //
- 0x50, //
- 0xE0, //
- // Second part
- 0x00, 0, 3, //
- 0x00, 0, 2, //
- 0x00, 0, 1, //
- 0x50, //
- ],
- }),
- );
-
- vm.run();
-
- assert!({
- let Primitive::Float(i) = vm.stack.peek() else { panic!(); };
- (0.0..=1.0).contains(&i)
- });
-
- vm.run();
-
- assert!({
- let Primitive::Integer(i) = vm.stack.peek() else { panic!(); };
- (5..10).contains(&i)
- });
- }
-
- #[test]
- fn fibonacci() {
- #[rustfmt::skip]
- let mut vm = VM::new(
- ObjectMap::default(),
- Function::root(Chunk {
- constants: vec![
- Primitive::Integer(0),
- Primitive::Integer(1),
- Primitive::Integer(10),
- ],
- code: vec![
- // Load variables
- 0x00, 0, 0, // 0 Index
- 0x00, 0, 0, // 3 Me
- 0x00, 0, 0, // 6 Parent
- 0x00, 0, 1, // 9 Grandparent
-
- // Load parent and grandparent, sum them and put the value in me
- 0x14, 0, 2, // 12
- 0x14, 0, 3, // 15
- 0x20, // 16
- 0x15, 0, 1, // 19
-
- // Set grandparent to parent
- 0x14, 0, 2, // 22
- 0x15, 0, 3, // 25
-
- // Set parent to me
- 0x14, 0, 1, // 28
- 0x15, 0, 2, // 31
-
- // Increment Index by 1
- 0x00, 0, 1, // 34
- 0x14, 0, 0, // 37 Index
- 0x20, // 40
- 0x15, 0, 0, // 41 Index
-
- // Load me
- 0x14, 0, 1, // 44
- 0xE0, // 47
- 0x11, // 48
-
- // Repeat until Index is 9
- 0x00, 0, 2, // 49
- 0x14, 0, 0, // 52 Index
- 0x31, // 55
- 0x41, 0, 12, // 56
- ],
- }),
- );
-
- let mut values = Vec::new();
- for _ in 0..10 {
- vm.run();
- values.push(vm.stack.peek());
- }
-
- assert_eq!(&values, &[
- Primitive::Integer(1),
- Primitive::Integer(1),
- Primitive::Integer(2),
- Primitive::Integer(3),
- Primitive::Integer(5),
- Primitive::Integer(8),
- Primitive::Integer(13),
- Primitive::Integer(21),
- Primitive::Integer(34),
- Primitive::Integer(55),
- ]);
- }
-
- #[test]
- fn fibonacci_recursive() {
- #[rustfmt::skip]
- let mut vm = VM::new(
- ObjectMap::from(vec![Object::new(ObjectType::Function(Function {
- name: Some("fib".to_owned()),
- chunk: Chunk {
- constants: vec![
- Primitive::Object(0),
- Primitive::Integer(0),
- Primitive::Integer(1),
- Primitive::Integer(2),
- ],
- code: vec![
- 0x14, 0, 0, // 0
- 0x00, 0, 1, // 3
- 0x31, // 6
- 0x41, 0, 14, // 7
- 0x00, 0, 1, // 10
- 0x52, // 13
-
- 0x14, 0, 0, // 14
- 0x00, 0, 2, // 17
- 0x31, // 20
- 0x41, 0, 28, // 21
- 0x00, 0, 2, // 24
- 0x52, // 27
-
- // fib(n - 1)
- 0x00, 0, 2, // 28
- 0x14, 0, 0, // 31
- 0x21, // 34
- 0x00, 0, 0, // 35
- 0x50, // 38
-
- // fib(n - 2)
- 0x00, 0, 3, // 39
- 0x14, 0, 0, // 42
- 0x21, // 45
- 0x00, 0, 0, // 46
- 0x50, // 49
-
- // add & return
- 0x20, // 50
- 0x52, // 51
- ],
- },
- arity: 1,
- returns_value: true,
- }))]),
- Function::root(Chunk {
- constants: vec![
- Primitive::Object(0),
- Primitive::Integer(10),
- ],
- code: vec![
- // Load n and the function and call it
- 0x00, 0, 1, // 0
- 0x00, 0, 0, // 3
- 0x50, // 6
- ],
- }),
- );
-
- vm.run();
-
- assert_eq!(Primitive::Integer(55), vm.stack.peek());
- }
-}