From 121d2aae7ba04dc0d826909d03c93cb916881e9f Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 21 Jan 2025 19:53:58 -0500 Subject: [PATCH] Optimize VM; Add two new register layouts --- dust-lang/Cargo.toml | 2 +- dust-lang/benches/threads.rs | 4 +- dust-lang/src/compiler/error.rs | 5 + dust-lang/src/compiler/mod.rs | 105 ++++++++++---- dust-lang/src/instruction/add.rs | 4 +- dust-lang/src/instruction/call.rs | 8 +- dust-lang/src/instruction/call_native.rs | 76 +++++++--- dust-lang/src/instruction/close.rs | 4 +- dust-lang/src/instruction/divide.rs | 4 +- dust-lang/src/instruction/equal.rs | 4 +- dust-lang/src/instruction/get_local.rs | 4 +- dust-lang/src/instruction/jump.rs | 4 +- dust-lang/src/instruction/less.rs | 4 +- dust-lang/src/instruction/less_equal.rs | 4 +- dust-lang/src/instruction/load_constant.rs | 4 +- dust-lang/src/instruction/load_function.rs | 4 +- dust-lang/src/instruction/load_inline.rs | 4 +- dust-lang/src/instruction/load_list.rs | 4 +- dust-lang/src/instruction/load_self.rs | 4 +- dust-lang/src/instruction/mod.rs | 124 +++++++++++++--- dust-lang/src/instruction/modulo.rs | 4 +- dust-lang/src/instruction/move.rs | 4 +- dust-lang/src/instruction/multiply.rs | 4 +- dust-lang/src/instruction/negate.rs | 4 +- dust-lang/src/instruction/not.rs | 4 +- dust-lang/src/instruction/return.rs | 4 +- dust-lang/src/instruction/set_local.rs | 4 +- dust-lang/src/instruction/subtract.rs | 4 +- dust-lang/src/instruction/test.rs | 4 +- dust-lang/src/instruction/test_set.rs | 4 +- dust-lang/src/vm/action.rs | 153 +++++++++++++++----- dust-lang/src/vm/call_frame.rs | 6 +- dust-lang/src/vm/register_table.rs | 117 ++++++++++++--- dust-lang/src/vm/thread.rs | 158 ++++++++++++++++++++- 34 files changed, 668 insertions(+), 182 deletions(-) diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml index 5d01ec0..12438d4 100644 --- a/dust-lang/Cargo.toml +++ b/dust-lang/Cargo.toml @@ -22,7 +22,7 @@ smartstring = { version = "1.0.1", features = [ ], default-features = false } tracing = "0.1.41" crossbeam-channel = "0.5.14" -smallvec = { version = "1.13.2", features = ["serde"] } +smallvec = { version = "1.13.2", features = ["const_generics", "serde"] } [dev-dependencies] criterion = { version = "0.3.4", features = ["html_reports"] } diff --git a/dust-lang/benches/threads.rs b/dust-lang/benches/threads.rs index 8c4ef2e..894245e 100644 --- a/dust-lang/benches/threads.rs +++ b/dust-lang/benches/threads.rs @@ -9,9 +9,7 @@ const SOURCE: &str = r#" while i < 1_000 { i += 1 - spawn( - fn () { random_int(0, 10); } - ) + spawn(fn () { random_int(0, 10); }) } "#; diff --git a/dust-lang/src/compiler/error.rs b/dust-lang/src/compiler/error.rs index 38be652..cc7d4d6 100644 --- a/dust-lang/src/compiler/error.rs +++ b/dust-lang/src/compiler/error.rs @@ -67,6 +67,10 @@ pub enum CompileError { }, // Type errors + ArgumentTypeConflict { + conflict: TypeConflict, + position: Span, + }, CannotAddType { argument_type: Type, position: Span, @@ -180,6 +184,7 @@ impl AnnotatedError for CompileError { fn description(&self) -> &'static str { match self { + Self::ArgumentTypeConflict { .. } => "Argument type conflict", Self::CannotAddArguments { .. } => "Cannot add these types", Self::CannotAddType { .. } => "Cannot add to this type", Self::ComparisonChain { .. } => "Cannot chain comparison operations", diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index 4199f8c..e83228a 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -36,7 +36,9 @@ use crate::{ Chunk, DustError, DustString, FunctionType, Instruction, Lexer, Local, NativeFunction, Operand, Operation, Scope, Span, Token, TokenKind, Type, chunk::ConstantTable, - instruction::{CallNative, Close, GetLocal, Jump, LoadList, Move, Return, SetLocal, TypeCode}, + instruction::{ + Call, CallNative, Close, GetLocal, Jump, LoadList, Move, Return, SetLocal, TypeCode, + }, }; /// Compiles the input and returns a chunk. @@ -340,6 +342,30 @@ impl<'src> Compiler<'src> { .unwrap_or(self.minimum_register) } + fn next_pointer_register(&self) -> u16 { + self.instructions + .iter() + .rev() + .find_map(|(instruction, _, _)| match instruction.operation() { + Operation::CALL => { + let Call { destination, .. } = Call::from(instruction); + + Some(destination + 1) + } + Operation::CALL_NATIVE => { + let CallNative { + first_argument, + argument_count, + .. + } = CallNative::from(instruction); + + Some(first_argument + argument_count as u16) + } + _ => None, + }) + .unwrap_or(self.minimum_register) + } + fn advance(&mut self) -> Result<(), CompileError> { if self.is_eof() { return Ok(()); @@ -1567,46 +1593,65 @@ impl<'src> Compiler<'src> { } fn parse_call_native(&mut self, function: NativeFunction) -> Result<(), CompileError> { - todo!(); + let start = self.current_position.0; + let function_type = function.r#type(); + let mut first_argument = None; + let mut argument_count = 0; - // let start = self.previous_position.0; - // let start_register = self.next_register(); + self.expect(Token::LeftParenthesis)?; - // self.expect(Token::LeftParenthesis)?; + while !self.allow(Token::RightParenthesis)? { + self.parse_expression()?; - // while !self.allow(Token::RightParenthesis)? { - // let expected_register = self.next_register(); + let expected_argument_type = function_type + .value_parameters + .get(argument_count as usize) + .map(|(_, r#type)| r#type) + .unwrap_or(&Type::None); + let (_, actual_argument_type, position) = self.instructions.last().unwrap().clone(); - // self.parse_expression()?; + expected_argument_type + .check(&actual_argument_type) + .map_err(|conflict| CompileError::ArgumentTypeConflict { conflict, position })?; - // let actual_register = self.next_register() - 1; - // let registers_to_close = actual_register - expected_register; + if argument_count == 0 { + let argument = self + .instructions + .last() + .map(|(instruction, _, _)| instruction) + .unwrap() + .a_field(); - // if registers_to_close > 0 { - // let close = Instruction::from(Close { - // from: expected_register, - // to: actual_register, - // }); + first_argument = Some(argument); + } - // self.emit_instruction(close, Type::None, self.current_position); - // } + argument_count += 1; - // self.allow(Token::Comma)?; - // } + self.allow(Token::Comma)?; + } - // let end = self.previous_position.1; - // let destination = self.next_register(); - // let argument_count = destination - start_register; - // let return_type = function.r#type().return_type; - // let call_native = Instruction::from(CallNative { - // destination, - // function, - // argument_count, - // }); + let end = self.previous_position.1; + let destination = match function.r#type().return_type { + Type::Boolean => self.next_boolean_register(), + Type::Byte => self.next_byte_register(), + Type::Character => self.next_character_register(), + Type::Float => self.next_float_register(), + Type::Integer => self.next_integer_register(), + Type::String => self.next_string_register(), + Type::None => 0, + _ => todo!(), + }; + let return_type = function.r#type().return_type; + let call_native = Instruction::from(CallNative { + destination, + function, + first_argument: first_argument.unwrap_or(0), + argument_count, + }); - // self.emit_instruction(call_native, return_type, Span(start, end)); + self.emit_instruction(call_native, return_type, Span(start, end)); - // Ok(()) + Ok(()) } fn parse_semicolon(&mut self) -> Result<(), CompileError> { diff --git a/dust-lang/src/instruction/add.rs b/dust-lang/src/instruction/add.rs index 5349e3c..e040738 100644 --- a/dust-lang/src/instruction/add.rs +++ b/dust-lang/src/instruction/add.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display, Formatter}; -use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; +use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode}; pub struct Add { pub destination: u16, @@ -36,7 +36,7 @@ impl From for Instruction { let b_type = add.left_type; let c_type = add.right_type; - InstructionBuilder { + TwoOperandLayout { operation, a_field, b_field, diff --git a/dust-lang/src/instruction/call.rs b/dust-lang/src/instruction/call.rs index ff69533..1dacf76 100644 --- a/dust-lang/src/instruction/call.rs +++ b/dust-lang/src/instruction/call.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::InstructionBuilder; +use super::TwoOperandLayout; pub struct Call { pub destination: u16, @@ -11,8 +11,8 @@ pub struct Call { pub is_recursive: bool, } -impl From for Call { - fn from(instruction: Instruction) -> Self { +impl From<&Instruction> for Call { + fn from(instruction: &Instruction) -> Self { let destination = instruction.a_field(); let function_register = instruction.b_field(); let argument_count = instruction.c_field(); @@ -34,7 +34,7 @@ impl From for Instruction { let c_field = call.argument_count; let d_field = call.is_recursive; - InstructionBuilder { + TwoOperandLayout { operation: Operation::CALL, a_field, b_field, diff --git a/dust-lang/src/instruction/call_native.rs b/dust-lang/src/instruction/call_native.rs index f66fac4..3b1bfc9 100644 --- a/dust-lang/src/instruction/call_native.rs +++ b/dust-lang/src/instruction/call_native.rs @@ -1,24 +1,28 @@ use std::fmt::Display; -use crate::{Instruction, NativeFunction, Operation}; +use crate::{Instruction, NativeFunction, Operation, Type}; -use super::InstructionBuilder; +use super::ZeroOperandLayout; pub struct CallNative { pub destination: u16, pub function: NativeFunction, - pub argument_count: u16, + pub first_argument: u16, + pub argument_count: u8, } -impl From for CallNative { - fn from(instruction: Instruction) -> Self { +impl From<&Instruction> for CallNative { + fn from(instruction: &Instruction) -> Self { let destination = instruction.a_field(); let function = NativeFunction::from(instruction.b_field()); + let first_argument = instruction.c_field(); + let argument_count = instruction.e_field(); CallNative { destination, function, - argument_count: instruction.c_field(), + first_argument, + argument_count, } } } @@ -28,14 +32,15 @@ impl From for Instruction { let operation = Operation::CALL_NATIVE; let a_field = call_native.destination; let b_field = call_native.function as u16; - let c_field = call_native.argument_count; + let c_field = call_native.first_argument; + let e_field = call_native.argument_count; - InstructionBuilder { + ZeroOperandLayout { operation, a_field, b_field, c_field, - ..Default::default() + e_field, } .build() } @@ -46,21 +51,58 @@ impl Display for CallNative { let CallNative { destination, function, + first_argument, argument_count, } = self; - let arguments_start = destination.saturating_sub(*argument_count); - let arguments_end = arguments_start + argument_count; + let function_type = function.r#type(); if function.returns_value() { - write!(f, "R{destination} = ")?; + match function_type.return_type { + Type::Boolean => write!(f, "R_BOOL_{destination} = ")?, + Type::Byte => write!(f, "R_BYTE_{destination} = ")?, + Type::Character => write!(f, "R_CHAR_{destination} = ")?, + Type::Float => write!(f, "R_FLOAT_{destination} = ")?, + Type::Integer => write!(f, "R_INT_{destination} = ")?, + Type::String => write!(f, "R_STR_{destination} = ")?, + Type::None => {} + _ => todo!(), + } } - match argument_count { - 0 => { - write!(f, "{function}()") + if *argument_count == 0 { + write!(f, "{function}()") + } else { + let last_argument = first_argument + (*argument_count as u16).saturating_sub(1); + let arguments = *first_argument..=last_argument; + let parameter_types = function_type + .value_parameters + .iter() + .map(|(_, r#type)| r#type); + + write!(f, "{function}(")?; + + let mut is_first = true; + + for (argument, parameter_type) in arguments.zip(parameter_types) { + if is_first { + is_first = false; + } else { + write!(f, ", ")?; + } + + match parameter_type { + Type::Boolean => write!(f, "R_BOOL_{argument}")?, + Type::Byte => write!(f, "R_BYTE_{argument}")?, + Type::Character => write!(f, "R_CHAR_{argument}")?, + Type::Float => write!(f, "R_FLOAT_{argument}")?, + Type::Integer => write!(f, "R_INT_{argument}")?, + Type::String => write!(f, "R_STR_{argument}")?, + Type::None => {} + _ => todo!(), + } } - 1 => write!(f, "{function}(R{arguments_start})"), - _ => write!(f, "{function}(R{arguments_start}..R{arguments_end})"), + + write!(f, ")") } } } diff --git a/dust-lang/src/instruction/close.rs b/dust-lang/src/instruction/close.rs index 9174438..cc4e4ac 100644 --- a/dust-lang/src/instruction/close.rs +++ b/dust-lang/src/instruction/close.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::InstructionBuilder; +use super::TwoOperandLayout; pub struct Close { pub from: u16, @@ -24,7 +24,7 @@ impl From for Instruction { let b_field = close.from; let c_field = close.to; - InstructionBuilder { + TwoOperandLayout { operation, b_field, c_field, diff --git a/dust-lang/src/instruction/divide.rs b/dust-lang/src/instruction/divide.rs index d039f90..7628b36 100644 --- a/dust-lang/src/instruction/divide.rs +++ b/dust-lang/src/instruction/divide.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display, Formatter}; -use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; +use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode}; pub struct Divide { pub destination: u16, @@ -36,7 +36,7 @@ impl From for Instruction { let b_type = divide.left_type; let c_type = divide.right_type; - InstructionBuilder { + TwoOperandLayout { operation, a_field, b_field, diff --git a/dust-lang/src/instruction/equal.rs b/dust-lang/src/instruction/equal.rs index 6d1aed6..61f70d2 100644 --- a/dust-lang/src/instruction/equal.rs +++ b/dust-lang/src/instruction/equal.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display, Formatter}; -use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; +use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode}; pub struct Equal { pub comparator: bool, @@ -36,7 +36,7 @@ impl From for Instruction { let b_type = equal_bool.left_type; let c_type = equal_bool.right_type; - InstructionBuilder { + TwoOperandLayout { operation, b_field, c_field, diff --git a/dust-lang/src/instruction/get_local.rs b/dust-lang/src/instruction/get_local.rs index 2b25c9e..bbe9bbf 100644 --- a/dust-lang/src/instruction/get_local.rs +++ b/dust-lang/src/instruction/get_local.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::{InstructionBuilder, TypeCode}; +use super::{TwoOperandLayout, TypeCode}; pub struct GetLocal { pub destination: u16, @@ -31,7 +31,7 @@ impl From for Instruction { let b_field = get_local.local_index; let b_type = get_local.r#type; - InstructionBuilder { + TwoOperandLayout { operation, a_field, b_field, diff --git a/dust-lang/src/instruction/jump.rs b/dust-lang/src/instruction/jump.rs index 23b82ce..e0d104d 100644 --- a/dust-lang/src/instruction/jump.rs +++ b/dust-lang/src/instruction/jump.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::InstructionBuilder; +use super::TwoOperandLayout; pub struct Jump { pub offset: u16, @@ -24,7 +24,7 @@ impl From for Instruction { let b_field = jump.offset; let c_field = jump.is_positive as u16; - InstructionBuilder { + TwoOperandLayout { operation, b_field, c_field, diff --git a/dust-lang/src/instruction/less.rs b/dust-lang/src/instruction/less.rs index 9b21bc9..e6f45cd 100644 --- a/dust-lang/src/instruction/less.rs +++ b/dust-lang/src/instruction/less.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display, Formatter}; -use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; +use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode}; pub struct Less { pub comparator: bool, @@ -36,7 +36,7 @@ impl From for Instruction { let b_type = less.left_type; let c_type = less.right_type; - InstructionBuilder { + TwoOperandLayout { operation, b_field, c_field, diff --git a/dust-lang/src/instruction/less_equal.rs b/dust-lang/src/instruction/less_equal.rs index fc3238f..7ab63f2 100644 --- a/dust-lang/src/instruction/less_equal.rs +++ b/dust-lang/src/instruction/less_equal.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display, Formatter}; -use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; +use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode}; pub struct LessEqual { pub comparator: bool, @@ -36,7 +36,7 @@ impl From for Instruction { let b_type = less_equal_byte.left_type; let c_type = less_equal_byte.right_type; - InstructionBuilder { + TwoOperandLayout { operation, b_field, c_field, diff --git a/dust-lang/src/instruction/load_constant.rs b/dust-lang/src/instruction/load_constant.rs index a0a7c10..f9d2ad3 100644 --- a/dust-lang/src/instruction/load_constant.rs +++ b/dust-lang/src/instruction/load_constant.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::{InstructionBuilder, TypeCode}; +use super::{TwoOperandLayout, TypeCode}; pub struct LoadConstant { pub destination: u16, @@ -29,7 +29,7 @@ impl From for LoadConstant { impl From for Instruction { fn from(load_constant: LoadConstant) -> Self { - InstructionBuilder { + TwoOperandLayout { operation: Operation::LOAD_CONSTANT, a_field: load_constant.destination, b_type: load_constant.type_code, diff --git a/dust-lang/src/instruction/load_function.rs b/dust-lang/src/instruction/load_function.rs index 1692e02..92486d5 100644 --- a/dust-lang/src/instruction/load_function.rs +++ b/dust-lang/src/instruction/load_function.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display, Formatter}; -use super::{Instruction, InstructionBuilder, Operation}; +use super::{Instruction, TwoOperandLayout, Operation}; pub struct LoadFunction { pub destination: u16, @@ -24,7 +24,7 @@ impl From for LoadFunction { impl From for Instruction { fn from(load_function: LoadFunction) -> Self { - InstructionBuilder { + TwoOperandLayout { operation: Operation::LOAD_FUNCTION, a_field: load_function.destination, b_field: load_function.prototype_index, diff --git a/dust-lang/src/instruction/load_inline.rs b/dust-lang/src/instruction/load_inline.rs index a46a593..f28a042 100644 --- a/dust-lang/src/instruction/load_inline.rs +++ b/dust-lang/src/instruction/load_inline.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::{InstructionBuilder, TypeCode}; +use super::{TwoOperandLayout, TypeCode}; pub struct LoadInline { pub destination: u16, @@ -33,7 +33,7 @@ impl From for Instruction { let c_field = load_boolean.boolean as u16; let d_field = load_boolean.jump_next; - InstructionBuilder { + TwoOperandLayout { operation, a_field, b_field, diff --git a/dust-lang/src/instruction/load_list.rs b/dust-lang/src/instruction/load_list.rs index d891ec5..378ecf6 100644 --- a/dust-lang/src/instruction/load_list.rs +++ b/dust-lang/src/instruction/load_list.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::InstructionBuilder; +use super::TwoOperandLayout; pub struct LoadList { pub destination: u16, @@ -26,7 +26,7 @@ impl From for LoadList { impl From for Instruction { fn from(load_list: LoadList) -> Self { - InstructionBuilder { + TwoOperandLayout { operation: Operation::LOAD_LIST, a_field: load_list.destination, b_field: load_list.start_register, diff --git a/dust-lang/src/instruction/load_self.rs b/dust-lang/src/instruction/load_self.rs index 1cb10e2..4f3052b 100644 --- a/dust-lang/src/instruction/load_self.rs +++ b/dust-lang/src/instruction/load_self.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::InstructionBuilder; +use super::TwoOperandLayout; pub struct LoadSelf { pub destination: u16, @@ -23,7 +23,7 @@ impl From for LoadSelf { impl From for Instruction { fn from(load_self: LoadSelf) -> Self { - InstructionBuilder { + TwoOperandLayout { operation: Operation::LOAD_SELF, a_field: load_self.destination, c_field: load_self.jump_next as u16, diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index f92f063..71b9e76 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -1,8 +1,17 @@ //! The Dust instruction set. //! -//! Each instruction is 64 bits and uses up to seven distinct fields. +//! Each instruction is 64 bits and uses a layout that divides the bits into fields. //! -//! # Layout +//! # Layouts +//! +//! ## Seven-Field Layout +//! +//! Used for most instructions, this layout has A, B and C fields for a destination and two +//! operands, including basic type codes and constant flags for the operands and an additional +//! boolean in the D field. +//! +//! Only shorter type codes (up to 3 bits) can fit in this layout. These type codes are relegated to +//! Dust's basic types: `bool`, `byte`, `char`, `float`, `int` and `str`. //! //! Bits | Description //! ----- | ----------- @@ -10,15 +19,50 @@ //! 7 | Flag indicating if the B field is a constant //! 8 | Flag indicating if the C field is a constant //! 9 | D field (boolean) -//! 10-12 | B field type -//! 13-15 | C field type +//! 10-12 | B field type (basic type) +//! 13-15 | C field type (basic type) //! 16-31 | A field (unsigned 16-bit integer) //! 32-47 | B field (unsigned 16-bit integer) //! 48-63 | C field (unsigned 16-bit integer) //! -//! **Be careful when working with instructions directly**. When modifying an instruction's fields, -//! you may also need to modify its flags. It is usually best to remove instructions and insert new -//! ones in their place instead of mutating them. +//! ## Six-Field Layout +//! +//! Instructions that have only one operand and/or need to support all of Dust's types use this +//! layout. +//! +//! Bits | Description +//! ----- | ----------- +//! 0-6 | Operation +//! 7 | Flag indicating if the B field is a constant +//! 8 | Flag indicating if the C field is a constant +//! 9 | D field (boolean) +//! 10-15 | B field type (any type) +//! 16-31 | A field (unsigned 16-bit integer) +//! 32-47 | B field (unsigned 16-bit integer) +//! 48-63 | C field (unsigned 16-bit integer) +//! +//! ## Five-Field Layout +//! +//! If the instruction's types can be inferred from the operation, the type codes can be omitted and +//! an extra byte can be used for the E field. +//! +//! Bits | Description +//! ----- | ----------- +//! 0-6 | Operation +//! 7-14 | E field (unsigned 8-bit integer) +//! 15 | Unused +//! 16-31 | A field (unsigned 16-bit integer) +//! 32-47 | B field (unsigned 16-bit integer) +//! 48-63 | C field (unsigned 16-bit integer) +//! +//! **Be careful when working with instructions.** +//! +//! Do not read instruction fields by calling methods on the instruction directly, except for the +//! `operation` method. These methods read bits directly from the underlying `u64`, ignoring the +//! layout. Instead, to avoid mistakes and persist future changes, call `operation`, then convert +//! the instruction to a struct for that specific operation. In performance-critical code, convert +//! the instruction to a layout struct and cache it, being careful to interpret the fields +//! correctly. //! //! # Examples //! @@ -155,7 +199,38 @@ pub use type_code::TypeCode; use crate::NativeFunction; #[derive(Clone, Copy, PartialEq, PartialOrd)] -pub struct InstructionBuilder { +pub struct ZeroOperandLayout { + pub operation: Operation, + pub a_field: u16, + pub b_field: u16, + pub c_field: u16, + pub e_field: u8, +} + +impl ZeroOperandLayout { + pub fn build(self) -> Instruction { + let bits = ((self.operation.0 as u64) << 57) + | ((self.e_field as u64) << 47) + | ((self.a_field as u64) << 32) + | ((self.b_field as u64) << 16) + | (self.c_field as u64); + + Instruction(bits) + } +} + +#[derive(Clone, Copy, PartialEq, PartialOrd)] +pub struct OneOperandLayout { + pub operation: Operation, + pub a_field: u16, + pub b_field: u16, + pub c_field: u16, + pub d_field: bool, + pub r#type: TypeCode, +} + +#[derive(Clone, Copy, PartialEq, PartialOrd)] +pub struct TwoOperandLayout { pub operation: Operation, pub a_field: u16, pub b_field: u16, @@ -167,9 +242,9 @@ pub struct InstructionBuilder { pub c_type: TypeCode, } -impl From<&Instruction> for InstructionBuilder { +impl From<&Instruction> for TwoOperandLayout { fn from(instruction: &Instruction) -> Self { - InstructionBuilder { + TwoOperandLayout { operation: instruction.operation(), a_field: instruction.a_field(), b_field: instruction.b_field(), @@ -183,7 +258,7 @@ impl From<&Instruction> for InstructionBuilder { } } -impl InstructionBuilder { +impl TwoOperandLayout { pub fn build(self) -> Instruction { let bits = ((self.operation.0 as u64) << 57) | ((self.b_is_constant as u64) << 56) @@ -199,9 +274,9 @@ impl InstructionBuilder { } } -impl Default for InstructionBuilder { +impl Default for TwoOperandLayout { fn default() -> Self { - InstructionBuilder { + TwoOperandLayout { operation: Operation::MOVE, a_field: 0, b_field: 0, @@ -215,13 +290,13 @@ impl Default for InstructionBuilder { } } -impl Debug for InstructionBuilder { +impl Debug for TwoOperandLayout { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{self}") } } -impl Display for InstructionBuilder { +impl Display for TwoOperandLayout { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}", self.build()) } @@ -264,6 +339,10 @@ impl Instruction { TypeCode(byte) } + pub fn e_field(&self) -> u8 { + (self.0 >> 47) as u8 + } + pub fn a_field(&self) -> u16 { ((self.0 >> 32) & 0xFFFF) as u16 } @@ -554,11 +633,13 @@ impl Instruction { pub fn call_native( destination: u16, function: NativeFunction, - argument_count: u16, + first_argument: u16, + argument_count: u8, ) -> Instruction { Instruction::from(CallNative { destination, function, + first_argument, argument_count, }) } @@ -778,8 +859,8 @@ impl Instruction { Operation::LESS_EQUAL => LessEqual::from(*self).to_string(), Operation::TEST => Test::from(*self).to_string(), Operation::TEST_SET => TestSet::from(*self).to_string(), - Operation::CALL => Call::from(*self).to_string(), - Operation::CALL_NATIVE => CallNative::from(*self).to_string(), + Operation::CALL => Call::from(self).to_string(), + Operation::CALL_NATIVE => CallNative::from(self).to_string(), Operation::JUMP => Jump::from(*self).to_string(), Operation::RETURN => Return::from(*self).to_string(), @@ -959,4 +1040,11 @@ mod tests { assert_eq!(TypeCode::CHARACTER, instruction.c_type()); } + + #[test] + fn decond_e_field() { + let instruction = Instruction::call_native(42, NativeFunction::ReadLine, 4, 255); + + assert_eq!(255, instruction.e_field()); + } } diff --git a/dust-lang/src/instruction/modulo.rs b/dust-lang/src/instruction/modulo.rs index 64d3548..5f70321 100644 --- a/dust-lang/src/instruction/modulo.rs +++ b/dust-lang/src/instruction/modulo.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display, Formatter}; -use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; +use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode}; pub struct Modulo { pub destination: u16, @@ -36,7 +36,7 @@ impl From for Instruction { let b_type = modulo.left_type; let c_type = modulo.right_type; - InstructionBuilder { + TwoOperandLayout { operation, a_field, b_field, diff --git a/dust-lang/src/instruction/move.rs b/dust-lang/src/instruction/move.rs index ef882ad..b07e5c0 100644 --- a/dust-lang/src/instruction/move.rs +++ b/dust-lang/src/instruction/move.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::{InstructionBuilder, TypeCode}; +use super::{TwoOperandLayout, TypeCode}; pub struct Move { pub from: u16, @@ -27,7 +27,7 @@ impl From for Instruction { let b_field = r#move.to; let b_type = r#move.type_code; - InstructionBuilder { + TwoOperandLayout { operation, a_field, b_field, diff --git a/dust-lang/src/instruction/multiply.rs b/dust-lang/src/instruction/multiply.rs index 62fa34e..a5d3148 100644 --- a/dust-lang/src/instruction/multiply.rs +++ b/dust-lang/src/instruction/multiply.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display, Formatter}; -use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; +use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode}; pub struct Multiply { pub destination: u16, @@ -36,7 +36,7 @@ impl From for Instruction { let b_type = multiply.left_type; let c_type = multiply.right_type; - InstructionBuilder { + TwoOperandLayout { operation, a_field, b_field, diff --git a/dust-lang/src/instruction/negate.rs b/dust-lang/src/instruction/negate.rs index e2a4aa4..04afc19 100644 --- a/dust-lang/src/instruction/negate.rs +++ b/dust-lang/src/instruction/negate.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display, Formatter}; -use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; +use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode}; pub struct Negate { pub destination: u16, @@ -29,7 +29,7 @@ impl From for Instruction { let (b_field, b_is_constant) = negate.argument.as_index_and_constant_flag(); let b_type = negate.argument_type; - InstructionBuilder { + TwoOperandLayout { operation, a_field, b_field, diff --git a/dust-lang/src/instruction/not.rs b/dust-lang/src/instruction/not.rs index 51a06db..a597162 100644 --- a/dust-lang/src/instruction/not.rs +++ b/dust-lang/src/instruction/not.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use crate::{Instruction, Operand, Operation}; -use super::InstructionBuilder; +use super::TwoOperandLayout; pub struct Not { pub destination: u16, @@ -27,7 +27,7 @@ impl From for Instruction { let a_field = not.destination; let (b_field, b_is_constant) = not.argument.as_index_and_constant_flag(); - InstructionBuilder { + TwoOperandLayout { operation, a_field, b_field, diff --git a/dust-lang/src/instruction/return.rs b/dust-lang/src/instruction/return.rs index 850bc05..25fb265 100644 --- a/dust-lang/src/instruction/return.rs +++ b/dust-lang/src/instruction/return.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::{InstructionBuilder, TypeCode}; +use super::{TwoOperandLayout, TypeCode}; pub struct Return { pub should_return_value: bool, @@ -31,7 +31,7 @@ impl From for Instruction { let b_type = r#return.return_type; let c_field = r#return.return_register; - InstructionBuilder { + TwoOperandLayout { operation, b_field, b_type, diff --git a/dust-lang/src/instruction/set_local.rs b/dust-lang/src/instruction/set_local.rs index 57b48a2..be74f37 100644 --- a/dust-lang/src/instruction/set_local.rs +++ b/dust-lang/src/instruction/set_local.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::InstructionBuilder; +use super::TwoOperandLayout; pub struct SetLocal { pub register_index: u16, @@ -27,7 +27,7 @@ impl From for Instruction { let b_field = set_local.register_index; let c_field = set_local.local_index; - InstructionBuilder { + TwoOperandLayout { operation, b_field, c_field, diff --git a/dust-lang/src/instruction/subtract.rs b/dust-lang/src/instruction/subtract.rs index df3f68f..f1d7420 100644 --- a/dust-lang/src/instruction/subtract.rs +++ b/dust-lang/src/instruction/subtract.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display, Formatter}; -use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; +use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode}; pub struct Subtract { pub destination: u16, @@ -36,7 +36,7 @@ impl From for Instruction { let b_type = subtract.left_type; let c_type = subtract.right_type; - InstructionBuilder { + TwoOperandLayout { operation, a_field, b_field, diff --git a/dust-lang/src/instruction/test.rs b/dust-lang/src/instruction/test.rs index e826ed3..c6a26ea 100644 --- a/dust-lang/src/instruction/test.rs +++ b/dust-lang/src/instruction/test.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::InstructionBuilder; +use super::TwoOperandLayout; pub struct Test { pub operand_register: u16, @@ -26,7 +26,7 @@ impl From for Instruction { let b_field = test.operand_register; let c_field = test.test_value as u16; - InstructionBuilder { + TwoOperandLayout { operation: Operation::TEST, b_field, c_field, diff --git a/dust-lang/src/instruction/test_set.rs b/dust-lang/src/instruction/test_set.rs index c946767..35646c3 100644 --- a/dust-lang/src/instruction/test_set.rs +++ b/dust-lang/src/instruction/test_set.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operand, Operation}; -use super::InstructionBuilder; +use super::TwoOperandLayout; pub struct TestSet { pub destination: u16, @@ -31,7 +31,7 @@ impl From for Instruction { let (b_field, b_is_constant) = test_set.argument.as_index_and_constant_flag(); let c_field = test_set.test_value as u16; - InstructionBuilder { + TwoOperandLayout { operation, a_field, b_field, diff --git a/dust-lang/src/vm/action.rs b/dust-lang/src/vm/action.rs index 2187f0e..c9b2919 100644 --- a/dust-lang/src/vm/action.rs +++ b/dust-lang/src/vm/action.rs @@ -9,7 +9,7 @@ use tracing::trace; use crate::{ Instruction, Operation, Type, Value, - instruction::{InstructionBuilder, Jump, TypeCode}, + instruction::{Jump, TwoOperandLayout, TypeCode}, }; use super::{Pointer, Register, thread::ThreadData}; @@ -43,14 +43,15 @@ impl ActionSequence { if !is_positive { let action = Action { logic: loop_optimized, + optimal_logic: None, data: ActionData { name: "LOOP_OPTIMIZED", - instruction: InstructionBuilder::from(instruction), + instruction: TwoOperandLayout::from(instruction), integer_pointers: [ptr::null_mut(); 3], boolean_register_pointers: [ptr::null_mut(); 2], integer_register_pointers: [ptr::null_mut(); 2], runs: 0, - condensed_actions: loop_actions.take().unwrap(), + loop_actions: loop_actions.take().unwrap(), }, }; @@ -82,32 +83,34 @@ impl ActionSequence { #[derive(Clone)] pub struct Action { pub logic: ActionLogic, + pub optimal_logic: Option, pub data: ActionData, } impl From<&Instruction> for Action { fn from(instruction: &Instruction) -> Self { - let builder = InstructionBuilder::from(instruction); + let builder = TwoOperandLayout::from(instruction); let operation = builder.operation; - let logic = match operation { - Operation::MOVE => r#move, - Operation::CLOSE => close, - Operation::LOAD_INLINE => load_inline, - Operation::LOAD_CONSTANT => load_constant, - Operation::LOAD_LIST => load_list, - Operation::LOAD_FUNCTION => load_function, - Operation::LOAD_SELF => load_self, - Operation::GET_LOCAL => get_local, - Operation::SET_LOCAL => set_local, - Operation::ADD => add, - Operation::LESS => less, - Operation::JUMP => jump, - Operation::RETURN => r#return, - unknown => unknown.panic_from_unknown_code(), - }; + let (logic, optimal_logic): (ActionLogic, Option) = + match operation { + Operation::MOVE => (r#move, Some(move_optimal)), + Operation::LOAD_INLINE => (load_inline, None), + Operation::LOAD_CONSTANT => (load_constant, None), + Operation::LOAD_LIST => (load_list, None), + Operation::LOAD_FUNCTION => (load_function, None), + Operation::LOAD_SELF => (load_self, None), + Operation::GET_LOCAL => (get_local, None), + Operation::SET_LOCAL => (set_local, None), + Operation::ADD => (add, Some(add_optimal)), + Operation::LESS => (less, Some(less_optimal)), + Operation::JUMP => (jump, Some(jump_optimal)), + Operation::RETURN => (r#return, None), + unknown => unknown.panic_from_unknown_code(), + }; Action { logic, + optimal_logic, data: ActionData { name: operation.name(), instruction: builder, @@ -115,7 +118,7 @@ impl From<&Instruction> for Action { boolean_register_pointers: [ptr::null_mut(); 2], integer_register_pointers: [ptr::null_mut(); 2], runs: 0, - condensed_actions: Vec::new(), + loop_actions: Vec::new(), }, } } @@ -136,13 +139,13 @@ impl Debug for Action { #[derive(Debug, Clone)] pub struct ActionData { pub name: &'static str, - pub instruction: InstructionBuilder, + pub instruction: TwoOperandLayout, pub boolean_register_pointers: [*mut Register; 2], pub integer_register_pointers: [*mut Register; 2], pub integer_pointers: [*mut i64; 3], pub runs: usize, - pub condensed_actions: Vec, + pub loop_actions: Vec, } pub type ActionLogic = fn(&mut ThreadData, &mut ActionData); @@ -151,25 +154,25 @@ fn loop_optimized(thread_data: &mut ThreadData, action_data: &mut ActionData) { let mut local_ip = 0; loop { - if local_ip >= action_data.condensed_actions.len() { + if local_ip >= action_data.loop_actions.len() { break; } - let action = &mut action_data.condensed_actions[local_ip]; + let action = &mut action_data.loop_actions[local_ip]; local_ip += 1; if action.data.runs == 0 { - trace!("Condensed action initial: {}", action.data.name); + trace!("Action: {} Optimizing", action.data.name); (action.logic)(thread_data, &mut action.data); continue; } - trace!("Condensed action optimized: {}", action.data.name); + trace!("Action: {} Optimized", action.data.name); match action.data.name { - "LESS" => unsafe { + "LESS_INT" => unsafe { asm!( "cmp {0}, {1}", "jns 2f", @@ -205,12 +208,94 @@ fn loop_optimized(thread_data: &mut ThreadData, action_data: &mut ActionData) { } else { local_ip -= (offset + 1) as usize; } + + // unsafe { + // asm!( + // "cmp {0}, 0", + // "je 2f", + // "add {1}, {2}", + // "jmp 3f", + // "2:", + // "sub {1}, {3}", + // "3:", + // in(reg) is_positive as i64, + // inout(reg) local_ip, + // in(reg) offset as i64, + // in(reg) (offset + 1) as i64, + // ) + // } } _ => todo!(), }; } } +const OPTIMAL_LOGIC: [fn(&mut ActionData, &mut usize); 3] = + [less_optimal, add_optimal, move_optimal]; + +fn less_optimal(action_data: &mut ActionData, local_ip: &mut usize) { + unsafe { + asm!( + "cmp {0}, {1}", + "jns 2f", + "add {2}, 1", + "2:", + in(reg) *action_data.integer_pointers[0], + in(reg) *action_data.integer_pointers[1], + inout(reg) *local_ip, + ) + } +} + +fn add_optimal(action_data: &mut ActionData, _: &mut usize) { + unsafe { + asm!( + "add {0}, {1}", + inout(reg) *action_data.integer_pointers[1] => *action_data.integer_pointers[0], + in(reg) *action_data.integer_pointers[2], + ) + } +} + +fn move_optimal(action_data: &mut ActionData, _: &mut usize) { + unsafe { + asm!( + "mov {0}, {1}", + out(reg) action_data.integer_register_pointers[0], + in(reg) action_data.integer_register_pointers[1], + ) + } +} + +fn jump_optimal(action_data: &mut ActionData, local_ip: &mut usize) { + let Jump { + offset, + is_positive, + } = Jump::from(action_data.instruction.build()); + + if is_positive { + *local_ip += offset as usize; + } else { + *local_ip -= (offset + 1) as usize; + } + + // unsafe { + // asm!( + // "cmp {0}, 0", + // "je 2f", + // "add {1}, {2}", + // "jmp 3f", + // "2:", + // "sub {1}, {3}", + // "3:", + // in(reg) is_positive as i64, + // inout(reg) *local_ip, + // in(reg) offset as i64, + // in(reg) (offset + 1) as i64, + // ) + // } +} + fn r#move(thread_data: &mut ThreadData, action_data: &mut ActionData) { let ActionData { instruction, .. } = action_data; let current_frame = thread_data.current_frame_mut(); @@ -272,7 +357,7 @@ fn load_inline(thread_data: &mut ThreadData, action_data: &mut ActionData) { *old_register = new_register; if instruction.c_field != 0 { - current_frame.instruction_pointer += 1; + current_frame.ip += 1; } } @@ -299,7 +384,7 @@ fn load_constant(thread_data: &mut ThreadData, action_data: &mut ActionData) { }; if instruction.c_field != 0 { - current_frame.instruction_pointer += 1; + current_frame.ip += 1; } } @@ -471,7 +556,7 @@ fn less(thread_data: &mut ThreadData, action_data: &mut ActionData) { if *runs > 0 { unsafe { if *pointers[0] < *pointers[1] { - current_frame.instruction_pointer += 1; + current_frame.ip += 1; } } } else { @@ -538,7 +623,7 @@ fn less(thread_data: &mut ThreadData, action_data: &mut ActionData) { }; if is_less { - current_frame.instruction_pointer += 1; + current_frame.ip += 1; } pointers[0] = left_pointer; @@ -554,9 +639,9 @@ fn jump(thread_data: &mut ThreadData, action_data: &mut ActionData) { let is_positive = instruction.c_field != 0; if is_positive { - current_frame.instruction_pointer += offset; + current_frame.ip += offset; } else { - current_frame.instruction_pointer -= offset + 1; + current_frame.ip -= offset + 1; } action_data.runs += 1; diff --git a/dust-lang/src/vm/call_frame.rs b/dust-lang/src/vm/call_frame.rs index bf89316..a2215dd 100644 --- a/dust-lang/src/vm/call_frame.rs +++ b/dust-lang/src/vm/call_frame.rs @@ -12,7 +12,7 @@ pub struct CallFrame { pub prototype: Arc, pub registers: RegisterTable, pub return_register: u16, - pub instruction_pointer: usize, + pub ip: usize, } impl CallFrame { @@ -20,7 +20,7 @@ impl CallFrame { Self { prototype, return_register, - instruction_pointer: 0, + ip: 0, registers: RegisterTable::new(), } } @@ -35,7 +35,7 @@ impl Display for CallFrame { .name .as_ref() .unwrap_or(&DustString::from("anonymous")), - self.instruction_pointer, + self.ip, ) } } diff --git a/dust-lang/src/vm/register_table.rs b/dust-lang/src/vm/register_table.rs index 5a4abc7..93d2d5c 100644 --- a/dust-lang/src/vm/register_table.rs +++ b/dust-lang/src/vm/register_table.rs @@ -4,7 +4,7 @@ use std::slice::SliceIndex; use smallvec::{SmallVec, smallvec}; use tracing::trace; -use crate::DustString; +use crate::{AbstractList, DustString}; use super::Pointer; @@ -33,25 +33,38 @@ impl Register { } } +const BOOLEAN_REGISTER_COUNT: usize = 64; +const BYTE_REGISTER_COUNT: usize = 64; +const CHARACTER_REGISTER_COUNT: usize = 64; +const FLOAT_REGISTER_COUNT: usize = 64; +const INTEGER_REGISTER_COUNT: usize = 64; +const STRING_REGISTER_COUNT: usize = 64; +const LIST_REGISTER_COUNT: usize = 16; +const POINTER_REGISTER_COUNT: usize = 256; + #[derive(Debug)] pub struct RegisterTable { - booleans: SmallVec<[Register; 64]>, - bytes: SmallVec<[Register; 64]>, - characters: SmallVec<[Register; 64]>, - floats: SmallVec<[Register; 64]>, - integers: SmallVec<[Register; 64]>, - strings: SmallVec<[Register; 64]>, + booleans: SmallVec<[Register; BOOLEAN_REGISTER_COUNT]>, + bytes: SmallVec<[Register; BYTE_REGISTER_COUNT]>, + characters: SmallVec<[Register; CHARACTER_REGISTER_COUNT]>, + floats: SmallVec<[Register; FLOAT_REGISTER_COUNT]>, + integers: SmallVec<[Register; INTEGER_REGISTER_COUNT]>, + strings: SmallVec<[Register; STRING_REGISTER_COUNT]>, + lists: SmallVec<[Register; LIST_REGISTER_COUNT]>, + pointers: SmallVec<[Register; POINTER_REGISTER_COUNT]>, } impl RegisterTable { pub fn new() -> Self { Self { - booleans: smallvec![Register::Empty; 64], - bytes: smallvec![Register::Empty; 64], - characters: smallvec![Register::Empty; 64], - floats: smallvec![Register::Empty; 64], - integers: smallvec![Register::Empty; 64], - strings: smallvec![Register::Empty; 64], + booleans: smallvec![Register::Empty; BOOLEAN_REGISTER_COUNT], + bytes: smallvec![Register::Empty; BYTE_REGISTER_COUNT], + characters: smallvec![Register::Empty; CHARACTER_REGISTER_COUNT], + floats: smallvec![Register::Empty; FLOAT_REGISTER_COUNT], + integers: smallvec![Register::Empty; INTEGER_REGISTER_COUNT], + strings: smallvec![Register::Empty; STRING_REGISTER_COUNT], + lists: smallvec![Register::Empty; LIST_REGISTER_COUNT], + pointers: smallvec![Register::Empty; POINTER_REGISTER_COUNT], } } @@ -75,6 +88,16 @@ impl RegisterTable { } } + pub fn set_boolean(&mut self, index: u16, value: bool) { + trace!("Set R_BOOL_{index} to value {value}"); + + let index = index as usize; + + self.booleans[index] = Register::Value(value); + + Self::handle_growth(&mut self.booleans); + } + pub fn get_byte(&self, index: u16) -> &Register { let index = index as usize; @@ -95,6 +118,16 @@ impl RegisterTable { } } + pub fn set_byte(&mut self, index: u16, value: u8) { + trace!("Set R_BYTE_{index} to value {value}"); + + let index = index as usize; + + self.bytes[index] = Register::Value(value); + + Self::handle_growth(&mut self.bytes); + } + pub fn get_character(&self, index: u16) -> &Register { let index = index as usize; @@ -115,6 +148,16 @@ impl RegisterTable { } } + pub fn set_character(&mut self, index: u16, value: char) { + trace!("Set R_CHAR_{index} to value {value}"); + + let index = index as usize; + + self.characters[index] = Register::Value(value); + + Self::handle_growth(&mut self.characters); + } + pub fn get_float(&self, index: u16) -> &Register { let index = index as usize; @@ -135,6 +178,16 @@ impl RegisterTable { } } + pub fn set_float(&mut self, index: u16, value: f64) { + trace!("Set R_FLOAT_{index} to value {value}"); + + let index = index as usize; + + self.floats[index] = Register::Value(value); + + Self::handle_growth(&mut self.floats); + } + pub fn get_integer(&self, index: u16) -> &Register { let index = index as usize; @@ -164,14 +217,6 @@ impl RegisterTable { } } - pub fn set_integer(&mut self, index: u16, value: i64) { - trace!("Set R_INT_{index} to value {value}"); - - let index = index as usize; - - self.integers[index] = Register::Value(value); - } - pub fn get_many_integer_mut( &mut self, indices: [I; N], @@ -186,6 +231,16 @@ impl RegisterTable { } } + pub fn set_integer(&mut self, index: u16, value: i64) { + trace!("Set R_INT_{index} to value {value}"); + + let index = index as usize; + + self.integers[index] = Register::Value(value); + + Self::handle_growth(&mut self.integers); + } + pub fn get_string(&self, index: u16) -> &Register { let index = index as usize; @@ -205,6 +260,26 @@ impl RegisterTable { unsafe { self.strings.get_mut(index).unwrap_unchecked() } } } + + pub fn set_string(&mut self, index: u16, value: DustString) { + trace!("Set R_STR_{index} to value {value}"); + + let index = index as usize; + + self.strings[index] = Register::Value(value); + + Self::handle_growth(&mut self.strings); + } + + fn handle_growth( + registers: &mut SmallVec<[Register; REGISTER_COUNT]>, + ) { + if REGISTER_COUNT >= registers.len() { + let new_length = registers.len() + REGISTER_COUNT; + + registers.resize(new_length, Register::Empty); + } + } } impl Default for RegisterTable { diff --git a/dust-lang/src/vm/thread.rs b/dust-lang/src/vm/thread.rs index f57d201..132088f 100644 --- a/dust-lang/src/vm/thread.rs +++ b/dust-lang/src/vm/thread.rs @@ -3,7 +3,13 @@ use std::{sync::Arc, thread::JoinHandle}; use smallvec::SmallVec; use tracing::{info, span, trace}; -use crate::{Chunk, DustString, Value, vm::action::ActionSequence}; +use crate::{ + Chunk, DustString, TypeCode, Value, + vm::{ + Register, + action::{ActionData, ActionSequence}, + }, +}; use super::{Action, CallFrame}; @@ -36,7 +42,7 @@ impl Thread { let mut thread_data = ThreadData { stack: call_stack, return_value: None, - spawned_threads: Vec::with_capacity(0), + spawned_threads: Vec::new(), }; let mut action_sequence = ActionSequence::new(&self.chunk.instructions); @@ -44,12 +50,154 @@ impl Thread { loop { let current_frame = thread_data.current_frame_mut(); - let current_action = action_sequence.get_mut(current_frame.instruction_pointer); - current_frame.instruction_pointer += 1; + let current_action = action_sequence.get_mut(current_frame.ip); + current_frame.ip += 1; trace!("Action: {}", current_action); - (current_action.logic)(&mut thread_data, &mut current_action.data); + // (current_action.logic)(&mut thread_data, &mut current_action.data); + + match current_action.data.name { + "LOAD_CONSTANT" => { + let ActionData { + instruction, + integer_pointers, + integer_register_pointers, + runs, + .. + } = &mut current_action.data; + let r#type = instruction.b_type; + + if *runs > 0 { + match r#type { + TypeCode::INTEGER => unsafe { + *integer_register_pointers[0] = + Register::Value(*integer_pointers[0]); + }, + unknown => unknown.panic_from_unknown_code(), + } + + continue; + } + + let current_frame = thread_data.current_frame_mut(); + let destination = instruction.a_field; + let constant_index = instruction.b_field; + + match r#type { + TypeCode::INTEGER => { + let mut value = *current_frame + .prototype + .constants + .get_integer(constant_index) + .unwrap(); + let new_register = Register::Value(value); + let old_register = current_frame.registers.get_integer_mut(destination); + + *old_register = new_register; + integer_pointers[0] = &mut value; + integer_register_pointers[0] = old_register; + } + unknown => unknown.panic_from_unknown_code(), + }; + + if instruction.c_field != 0 { + current_frame.ip += 1; + } + + *runs += 1; + } + "LESS_INT" => { + let ActionData { + instruction, + integer_pointers, + integer_register_pointers, + runs, + .. + } = &mut current_action.data; + let r#type = instruction.b_type; + + if *runs > 0 { + unsafe { + // if *pointers[0] < *pointers[1] { + // current_frame.ip += 1; + // } + } + } else { + let (is_less, left_pointer, right_pointer) = + match (instruction.b_is_constant, instruction.c_is_constant) { + (true, true) => { + let left = current_frame + .prototype + .constants + .get_integer(instruction.b_field) + .unwrap(); + let right = current_frame + .prototype + .constants + .get_integer(instruction.c_field) + .unwrap(); + let is_less = left < right; + + ( + is_less, + Box::into_raw(Box::new(*left)), + Box::into_raw(Box::new(*right)), + ) + } + (true, false) => { + let left = *current_frame + .prototype + .constants + .get_integer(instruction.b_field) + .unwrap(); + let right = current_frame + .registers + .get_integer_mut(instruction.c_field) + .expect_value_mut(); + let is_less = left < *right; + + (is_less, Box::into_raw(Box::new(left)), right as *mut i64) + } + (false, true) => { + let right = *current_frame + .prototype + .constants + .get_integer(instruction.c_field) + .unwrap(); + let left = current_frame + .registers + .get_integer_mut(instruction.b_field) + .expect_value_mut(); + let is_less = *left < right; + + (is_less, left as *mut i64, Box::into_raw(Box::new(right))) + } + (false, false) => { + let [left, right] = + current_frame.registers.get_many_integer_mut([ + instruction.b_field as usize, + instruction.c_field as usize, + ]); + let left = left.expect_value_mut(); + let right = right.expect_value_mut(); + let is_less = *left < *right; + + (is_less, left as *mut i64, right as *mut i64) + } + }; + + if is_less { + current_frame.ip += 1; + } + + integer_pointers[0] = left_pointer; + integer_pointers[1] = right_pointer; + *runs += 1; + } + } + _ => todo!(), + } if let Some(value_option) = thread_data.return_value { return value_option;