1
0

Optimize VM; Add two new register layouts

This commit is contained in:
Jeff 2025-01-21 19:53:58 -05:00
parent 9cf873bd39
commit 121d2aae7b
34 changed files with 668 additions and 182 deletions

View File

@ -22,7 +22,7 @@ smartstring = { version = "1.0.1", features = [
], default-features = false } ], default-features = false }
tracing = "0.1.41" tracing = "0.1.41"
crossbeam-channel = "0.5.14" crossbeam-channel = "0.5.14"
smallvec = { version = "1.13.2", features = ["serde"] } smallvec = { version = "1.13.2", features = ["const_generics", "serde"] }
[dev-dependencies] [dev-dependencies]
criterion = { version = "0.3.4", features = ["html_reports"] } criterion = { version = "0.3.4", features = ["html_reports"] }

View File

@ -9,9 +9,7 @@ const SOURCE: &str = r#"
while i < 1_000 { while i < 1_000 {
i += 1 i += 1
spawn( spawn(fn () { random_int(0, 10); })
fn () { random_int(0, 10); }
)
} }
"#; "#;

View File

@ -67,6 +67,10 @@ pub enum CompileError {
}, },
// Type errors // Type errors
ArgumentTypeConflict {
conflict: TypeConflict,
position: Span,
},
CannotAddType { CannotAddType {
argument_type: Type, argument_type: Type,
position: Span, position: Span,
@ -180,6 +184,7 @@ impl AnnotatedError for CompileError {
fn description(&self) -> &'static str { fn description(&self) -> &'static str {
match self { match self {
Self::ArgumentTypeConflict { .. } => "Argument type conflict",
Self::CannotAddArguments { .. } => "Cannot add these types", Self::CannotAddArguments { .. } => "Cannot add these types",
Self::CannotAddType { .. } => "Cannot add to this type", Self::CannotAddType { .. } => "Cannot add to this type",
Self::ComparisonChain { .. } => "Cannot chain comparison operations", Self::ComparisonChain { .. } => "Cannot chain comparison operations",

View File

@ -36,7 +36,9 @@ use crate::{
Chunk, DustError, DustString, FunctionType, Instruction, Lexer, Local, NativeFunction, Operand, Chunk, DustError, DustString, FunctionType, Instruction, Lexer, Local, NativeFunction, Operand,
Operation, Scope, Span, Token, TokenKind, Type, Operation, Scope, Span, Token, TokenKind, Type,
chunk::ConstantTable, 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. /// Compiles the input and returns a chunk.
@ -340,6 +342,30 @@ impl<'src> Compiler<'src> {
.unwrap_or(self.minimum_register) .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> { fn advance(&mut self) -> Result<(), CompileError> {
if self.is_eof() { if self.is_eof() {
return Ok(()); return Ok(());
@ -1567,46 +1593,65 @@ impl<'src> Compiler<'src> {
} }
fn parse_call_native(&mut self, function: NativeFunction) -> Result<(), CompileError> { 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; self.expect(Token::LeftParenthesis)?;
// let start_register = self.next_register();
// self.expect(Token::LeftParenthesis)?; while !self.allow(Token::RightParenthesis)? {
self.parse_expression()?;
// while !self.allow(Token::RightParenthesis)? { let expected_argument_type = function_type
// let expected_register = self.next_register(); .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; if argument_count == 0 {
// let registers_to_close = actual_register - expected_register; let argument = self
.instructions
.last()
.map(|(instruction, _, _)| instruction)
.unwrap()
.a_field();
// if registers_to_close > 0 { first_argument = Some(argument);
// let close = Instruction::from(Close { }
// from: expected_register,
// to: actual_register,
// });
// 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 end = self.previous_position.1;
// let destination = self.next_register(); let destination = match function.r#type().return_type {
// let argument_count = destination - start_register; Type::Boolean => self.next_boolean_register(),
// let return_type = function.r#type().return_type; Type::Byte => self.next_byte_register(),
// let call_native = Instruction::from(CallNative { Type::Character => self.next_character_register(),
// destination, Type::Float => self.next_float_register(),
// function, Type::Integer => self.next_integer_register(),
// argument_count, 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> { fn parse_semicolon(&mut self) -> Result<(), CompileError> {

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode};
pub struct Add { pub struct Add {
pub destination: u16, pub destination: u16,
@ -36,7 +36,7 @@ impl From<Add> for Instruction {
let b_type = add.left_type; let b_type = add.left_type;
let c_type = add.right_type; let c_type = add.right_type;
InstructionBuilder { TwoOperandLayout {
operation, operation,
a_field, a_field,
b_field, b_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionBuilder; use super::TwoOperandLayout;
pub struct Call { pub struct Call {
pub destination: u16, pub destination: u16,
@ -11,8 +11,8 @@ pub struct Call {
pub is_recursive: bool, pub is_recursive: bool,
} }
impl From<Instruction> for Call { impl From<&Instruction> for Call {
fn from(instruction: Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let function_register = instruction.b_field(); let function_register = instruction.b_field();
let argument_count = instruction.c_field(); let argument_count = instruction.c_field();
@ -34,7 +34,7 @@ impl From<Call> for Instruction {
let c_field = call.argument_count; let c_field = call.argument_count;
let d_field = call.is_recursive; let d_field = call.is_recursive;
InstructionBuilder { TwoOperandLayout {
operation: Operation::CALL, operation: Operation::CALL,
a_field, a_field,
b_field, b_field,

View File

@ -1,24 +1,28 @@
use std::fmt::Display; 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 struct CallNative {
pub destination: u16, pub destination: u16,
pub function: NativeFunction, pub function: NativeFunction,
pub argument_count: u16, pub first_argument: u16,
pub argument_count: u8,
} }
impl From<Instruction> for CallNative { impl From<&Instruction> for CallNative {
fn from(instruction: Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let function = NativeFunction::from(instruction.b_field()); let function = NativeFunction::from(instruction.b_field());
let first_argument = instruction.c_field();
let argument_count = instruction.e_field();
CallNative { CallNative {
destination, destination,
function, function,
argument_count: instruction.c_field(), first_argument,
argument_count,
} }
} }
} }
@ -28,14 +32,15 @@ impl From<CallNative> for Instruction {
let operation = Operation::CALL_NATIVE; let operation = Operation::CALL_NATIVE;
let a_field = call_native.destination; let a_field = call_native.destination;
let b_field = call_native.function as u16; 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, operation,
a_field, a_field,
b_field, b_field,
c_field, c_field,
..Default::default() e_field,
} }
.build() .build()
} }
@ -46,21 +51,58 @@ impl Display for CallNative {
let CallNative { let CallNative {
destination, destination,
function, function,
first_argument,
argument_count, argument_count,
} = self; } = self;
let arguments_start = destination.saturating_sub(*argument_count); let function_type = function.r#type();
let arguments_end = arguments_start + argument_count;
if function.returns_value() { 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 { if *argument_count == 0 {
0 => { write!(f, "{function}()")
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, ")")
} }
} }
} }

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionBuilder; use super::TwoOperandLayout;
pub struct Close { pub struct Close {
pub from: u16, pub from: u16,
@ -24,7 +24,7 @@ impl From<Close> for Instruction {
let b_field = close.from; let b_field = close.from;
let c_field = close.to; let c_field = close.to;
InstructionBuilder { TwoOperandLayout {
operation, operation,
b_field, b_field,
c_field, c_field,

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode};
pub struct Divide { pub struct Divide {
pub destination: u16, pub destination: u16,
@ -36,7 +36,7 @@ impl From<Divide> for Instruction {
let b_type = divide.left_type; let b_type = divide.left_type;
let c_type = divide.right_type; let c_type = divide.right_type;
InstructionBuilder { TwoOperandLayout {
operation, operation,
a_field, a_field,
b_field, b_field,

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode};
pub struct Equal { pub struct Equal {
pub comparator: bool, pub comparator: bool,
@ -36,7 +36,7 @@ impl From<Equal> for Instruction {
let b_type = equal_bool.left_type; let b_type = equal_bool.left_type;
let c_type = equal_bool.right_type; let c_type = equal_bool.right_type;
InstructionBuilder { TwoOperandLayout {
operation, operation,
b_field, b_field,
c_field, c_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::{InstructionBuilder, TypeCode}; use super::{TwoOperandLayout, TypeCode};
pub struct GetLocal { pub struct GetLocal {
pub destination: u16, pub destination: u16,
@ -31,7 +31,7 @@ impl From<GetLocal> for Instruction {
let b_field = get_local.local_index; let b_field = get_local.local_index;
let b_type = get_local.r#type; let b_type = get_local.r#type;
InstructionBuilder { TwoOperandLayout {
operation, operation,
a_field, a_field,
b_field, b_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionBuilder; use super::TwoOperandLayout;
pub struct Jump { pub struct Jump {
pub offset: u16, pub offset: u16,
@ -24,7 +24,7 @@ impl From<Jump> for Instruction {
let b_field = jump.offset; let b_field = jump.offset;
let c_field = jump.is_positive as u16; let c_field = jump.is_positive as u16;
InstructionBuilder { TwoOperandLayout {
operation, operation,
b_field, b_field,
c_field, c_field,

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode};
pub struct Less { pub struct Less {
pub comparator: bool, pub comparator: bool,
@ -36,7 +36,7 @@ impl From<Less> for Instruction {
let b_type = less.left_type; let b_type = less.left_type;
let c_type = less.right_type; let c_type = less.right_type;
InstructionBuilder { TwoOperandLayout {
operation, operation,
b_field, b_field,
c_field, c_field,

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode};
pub struct LessEqual { pub struct LessEqual {
pub comparator: bool, pub comparator: bool,
@ -36,7 +36,7 @@ impl From<LessEqual> for Instruction {
let b_type = less_equal_byte.left_type; let b_type = less_equal_byte.left_type;
let c_type = less_equal_byte.right_type; let c_type = less_equal_byte.right_type;
InstructionBuilder { TwoOperandLayout {
operation, operation,
b_field, b_field,
c_field, c_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::{InstructionBuilder, TypeCode}; use super::{TwoOperandLayout, TypeCode};
pub struct LoadConstant { pub struct LoadConstant {
pub destination: u16, pub destination: u16,
@ -29,7 +29,7 @@ impl From<Instruction> for LoadConstant {
impl From<LoadConstant> for Instruction { impl From<LoadConstant> for Instruction {
fn from(load_constant: LoadConstant) -> Self { fn from(load_constant: LoadConstant) -> Self {
InstructionBuilder { TwoOperandLayout {
operation: Operation::LOAD_CONSTANT, operation: Operation::LOAD_CONSTANT,
a_field: load_constant.destination, a_field: load_constant.destination,
b_type: load_constant.type_code, b_type: load_constant.type_code,

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operation}; use super::{Instruction, TwoOperandLayout, Operation};
pub struct LoadFunction { pub struct LoadFunction {
pub destination: u16, pub destination: u16,
@ -24,7 +24,7 @@ impl From<Instruction> for LoadFunction {
impl From<LoadFunction> for Instruction { impl From<LoadFunction> for Instruction {
fn from(load_function: LoadFunction) -> Self { fn from(load_function: LoadFunction) -> Self {
InstructionBuilder { TwoOperandLayout {
operation: Operation::LOAD_FUNCTION, operation: Operation::LOAD_FUNCTION,
a_field: load_function.destination, a_field: load_function.destination,
b_field: load_function.prototype_index, b_field: load_function.prototype_index,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::{InstructionBuilder, TypeCode}; use super::{TwoOperandLayout, TypeCode};
pub struct LoadInline { pub struct LoadInline {
pub destination: u16, pub destination: u16,
@ -33,7 +33,7 @@ impl From<LoadInline> for Instruction {
let c_field = load_boolean.boolean as u16; let c_field = load_boolean.boolean as u16;
let d_field = load_boolean.jump_next; let d_field = load_boolean.jump_next;
InstructionBuilder { TwoOperandLayout {
operation, operation,
a_field, a_field,
b_field, b_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionBuilder; use super::TwoOperandLayout;
pub struct LoadList { pub struct LoadList {
pub destination: u16, pub destination: u16,
@ -26,7 +26,7 @@ impl From<Instruction> for LoadList {
impl From<LoadList> for Instruction { impl From<LoadList> for Instruction {
fn from(load_list: LoadList) -> Self { fn from(load_list: LoadList) -> Self {
InstructionBuilder { TwoOperandLayout {
operation: Operation::LOAD_LIST, operation: Operation::LOAD_LIST,
a_field: load_list.destination, a_field: load_list.destination,
b_field: load_list.start_register, b_field: load_list.start_register,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionBuilder; use super::TwoOperandLayout;
pub struct LoadSelf { pub struct LoadSelf {
pub destination: u16, pub destination: u16,
@ -23,7 +23,7 @@ impl From<Instruction> for LoadSelf {
impl From<LoadSelf> for Instruction { impl From<LoadSelf> for Instruction {
fn from(load_self: LoadSelf) -> Self { fn from(load_self: LoadSelf) -> Self {
InstructionBuilder { TwoOperandLayout {
operation: Operation::LOAD_SELF, operation: Operation::LOAD_SELF,
a_field: load_self.destination, a_field: load_self.destination,
c_field: load_self.jump_next as u16, c_field: load_self.jump_next as u16,

View File

@ -1,8 +1,17 @@
//! The Dust instruction set. //! 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 //! Bits | Description
//! ----- | ----------- //! ----- | -----------
@ -10,15 +19,50 @@
//! 7 | Flag indicating if the B field is a constant //! 7 | Flag indicating if the B field is a constant
//! 8 | Flag indicating if the C field is a constant //! 8 | Flag indicating if the C field is a constant
//! 9 | D field (boolean) //! 9 | D field (boolean)
//! 10-12 | B field type //! 10-12 | B field type (basic type)
//! 13-15 | C field type //! 13-15 | C field type (basic type)
//! 16-31 | A field (unsigned 16-bit integer) //! 16-31 | A field (unsigned 16-bit integer)
//! 32-47 | B field (unsigned 16-bit integer) //! 32-47 | B field (unsigned 16-bit integer)
//! 48-63 | C 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, //! ## Six-Field Layout
//! 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. //! 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 //! # Examples
//! //!
@ -155,7 +199,38 @@ pub use type_code::TypeCode;
use crate::NativeFunction; use crate::NativeFunction;
#[derive(Clone, Copy, PartialEq, PartialOrd)] #[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 operation: Operation,
pub a_field: u16, pub a_field: u16,
pub b_field: u16, pub b_field: u16,
@ -167,9 +242,9 @@ pub struct InstructionBuilder {
pub c_type: TypeCode, pub c_type: TypeCode,
} }
impl From<&Instruction> for InstructionBuilder { impl From<&Instruction> for TwoOperandLayout {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
InstructionBuilder { TwoOperandLayout {
operation: instruction.operation(), operation: instruction.operation(),
a_field: instruction.a_field(), a_field: instruction.a_field(),
b_field: instruction.b_field(), b_field: instruction.b_field(),
@ -183,7 +258,7 @@ impl From<&Instruction> for InstructionBuilder {
} }
} }
impl InstructionBuilder { impl TwoOperandLayout {
pub fn build(self) -> Instruction { pub fn build(self) -> Instruction {
let bits = ((self.operation.0 as u64) << 57) let bits = ((self.operation.0 as u64) << 57)
| ((self.b_is_constant as u64) << 56) | ((self.b_is_constant as u64) << 56)
@ -199,9 +274,9 @@ impl InstructionBuilder {
} }
} }
impl Default for InstructionBuilder { impl Default for TwoOperandLayout {
fn default() -> Self { fn default() -> Self {
InstructionBuilder { TwoOperandLayout {
operation: Operation::MOVE, operation: Operation::MOVE,
a_field: 0, a_field: 0,
b_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 { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{self}") write!(f, "{self}")
} }
} }
impl Display for InstructionBuilder { impl Display for TwoOperandLayout {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.build()) write!(f, "{}", self.build())
} }
@ -264,6 +339,10 @@ impl Instruction {
TypeCode(byte) TypeCode(byte)
} }
pub fn e_field(&self) -> u8 {
(self.0 >> 47) as u8
}
pub fn a_field(&self) -> u16 { pub fn a_field(&self) -> u16 {
((self.0 >> 32) & 0xFFFF) as u16 ((self.0 >> 32) & 0xFFFF) as u16
} }
@ -554,11 +633,13 @@ impl Instruction {
pub fn call_native( pub fn call_native(
destination: u16, destination: u16,
function: NativeFunction, function: NativeFunction,
argument_count: u16, first_argument: u16,
argument_count: u8,
) -> Instruction { ) -> Instruction {
Instruction::from(CallNative { Instruction::from(CallNative {
destination, destination,
function, function,
first_argument,
argument_count, argument_count,
}) })
} }
@ -778,8 +859,8 @@ impl Instruction {
Operation::LESS_EQUAL => LessEqual::from(*self).to_string(), Operation::LESS_EQUAL => LessEqual::from(*self).to_string(),
Operation::TEST => Test::from(*self).to_string(), Operation::TEST => Test::from(*self).to_string(),
Operation::TEST_SET => TestSet::from(*self).to_string(), Operation::TEST_SET => TestSet::from(*self).to_string(),
Operation::CALL => Call::from(*self).to_string(), Operation::CALL => Call::from(self).to_string(),
Operation::CALL_NATIVE => CallNative::from(*self).to_string(), Operation::CALL_NATIVE => CallNative::from(self).to_string(),
Operation::JUMP => Jump::from(*self).to_string(), Operation::JUMP => Jump::from(*self).to_string(),
Operation::RETURN => Return::from(*self).to_string(), Operation::RETURN => Return::from(*self).to_string(),
@ -959,4 +1040,11 @@ mod tests {
assert_eq!(TypeCode::CHARACTER, instruction.c_type()); 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());
}
} }

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode};
pub struct Modulo { pub struct Modulo {
pub destination: u16, pub destination: u16,
@ -36,7 +36,7 @@ impl From<Modulo> for Instruction {
let b_type = modulo.left_type; let b_type = modulo.left_type;
let c_type = modulo.right_type; let c_type = modulo.right_type;
InstructionBuilder { TwoOperandLayout {
operation, operation,
a_field, a_field,
b_field, b_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::{InstructionBuilder, TypeCode}; use super::{TwoOperandLayout, TypeCode};
pub struct Move { pub struct Move {
pub from: u16, pub from: u16,
@ -27,7 +27,7 @@ impl From<Move> for Instruction {
let b_field = r#move.to; let b_field = r#move.to;
let b_type = r#move.type_code; let b_type = r#move.type_code;
InstructionBuilder { TwoOperandLayout {
operation, operation,
a_field, a_field,
b_field, b_field,

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode};
pub struct Multiply { pub struct Multiply {
pub destination: u16, pub destination: u16,
@ -36,7 +36,7 @@ impl From<Multiply> for Instruction {
let b_type = multiply.left_type; let b_type = multiply.left_type;
let c_type = multiply.right_type; let c_type = multiply.right_type;
InstructionBuilder { TwoOperandLayout {
operation, operation,
a_field, a_field,
b_field, b_field,

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode};
pub struct Negate { pub struct Negate {
pub destination: u16, pub destination: u16,
@ -29,7 +29,7 @@ impl From<Negate> for Instruction {
let (b_field, b_is_constant) = negate.argument.as_index_and_constant_flag(); let (b_field, b_is_constant) = negate.argument.as_index_and_constant_flag();
let b_type = negate.argument_type; let b_type = negate.argument_type;
InstructionBuilder { TwoOperandLayout {
operation, operation,
a_field, a_field,
b_field, b_field,

View File

@ -2,7 +2,7 @@ use std::fmt::Display;
use crate::{Instruction, Operand, Operation}; use crate::{Instruction, Operand, Operation};
use super::InstructionBuilder; use super::TwoOperandLayout;
pub struct Not { pub struct Not {
pub destination: u16, pub destination: u16,
@ -27,7 +27,7 @@ impl From<Not> for Instruction {
let a_field = not.destination; let a_field = not.destination;
let (b_field, b_is_constant) = not.argument.as_index_and_constant_flag(); let (b_field, b_is_constant) = not.argument.as_index_and_constant_flag();
InstructionBuilder { TwoOperandLayout {
operation, operation,
a_field, a_field,
b_field, b_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::{InstructionBuilder, TypeCode}; use super::{TwoOperandLayout, TypeCode};
pub struct Return { pub struct Return {
pub should_return_value: bool, pub should_return_value: bool,
@ -31,7 +31,7 @@ impl From<Return> for Instruction {
let b_type = r#return.return_type; let b_type = r#return.return_type;
let c_field = r#return.return_register; let c_field = r#return.return_register;
InstructionBuilder { TwoOperandLayout {
operation, operation,
b_field, b_field,
b_type, b_type,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionBuilder; use super::TwoOperandLayout;
pub struct SetLocal { pub struct SetLocal {
pub register_index: u16, pub register_index: u16,
@ -27,7 +27,7 @@ impl From<SetLocal> for Instruction {
let b_field = set_local.register_index; let b_field = set_local.register_index;
let c_field = set_local.local_index; let c_field = set_local.local_index;
InstructionBuilder { TwoOperandLayout {
operation, operation,
b_field, b_field,
c_field, c_field,

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; use super::{Instruction, Operand, Operation, TwoOperandLayout, TypeCode};
pub struct Subtract { pub struct Subtract {
pub destination: u16, pub destination: u16,
@ -36,7 +36,7 @@ impl From<Subtract> for Instruction {
let b_type = subtract.left_type; let b_type = subtract.left_type;
let c_type = subtract.right_type; let c_type = subtract.right_type;
InstructionBuilder { TwoOperandLayout {
operation, operation,
a_field, a_field,
b_field, b_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionBuilder; use super::TwoOperandLayout;
pub struct Test { pub struct Test {
pub operand_register: u16, pub operand_register: u16,
@ -26,7 +26,7 @@ impl From<Test> for Instruction {
let b_field = test.operand_register; let b_field = test.operand_register;
let c_field = test.test_value as u16; let c_field = test.test_value as u16;
InstructionBuilder { TwoOperandLayout {
operation: Operation::TEST, operation: Operation::TEST,
b_field, b_field,
c_field, c_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operand, Operation}; use crate::{Instruction, Operand, Operation};
use super::InstructionBuilder; use super::TwoOperandLayout;
pub struct TestSet { pub struct TestSet {
pub destination: u16, pub destination: u16,
@ -31,7 +31,7 @@ impl From<TestSet> for Instruction {
let (b_field, b_is_constant) = test_set.argument.as_index_and_constant_flag(); let (b_field, b_is_constant) = test_set.argument.as_index_and_constant_flag();
let c_field = test_set.test_value as u16; let c_field = test_set.test_value as u16;
InstructionBuilder { TwoOperandLayout {
operation, operation,
a_field, a_field,
b_field, b_field,

View File

@ -9,7 +9,7 @@ use tracing::trace;
use crate::{ use crate::{
Instruction, Operation, Type, Value, Instruction, Operation, Type, Value,
instruction::{InstructionBuilder, Jump, TypeCode}, instruction::{Jump, TwoOperandLayout, TypeCode},
}; };
use super::{Pointer, Register, thread::ThreadData}; use super::{Pointer, Register, thread::ThreadData};
@ -43,14 +43,15 @@ impl ActionSequence {
if !is_positive { if !is_positive {
let action = Action { let action = Action {
logic: loop_optimized, logic: loop_optimized,
optimal_logic: None,
data: ActionData { data: ActionData {
name: "LOOP_OPTIMIZED", name: "LOOP_OPTIMIZED",
instruction: InstructionBuilder::from(instruction), instruction: TwoOperandLayout::from(instruction),
integer_pointers: [ptr::null_mut(); 3], integer_pointers: [ptr::null_mut(); 3],
boolean_register_pointers: [ptr::null_mut(); 2], boolean_register_pointers: [ptr::null_mut(); 2],
integer_register_pointers: [ptr::null_mut(); 2], integer_register_pointers: [ptr::null_mut(); 2],
runs: 0, runs: 0,
condensed_actions: loop_actions.take().unwrap(), loop_actions: loop_actions.take().unwrap(),
}, },
}; };
@ -82,32 +83,34 @@ impl ActionSequence {
#[derive(Clone)] #[derive(Clone)]
pub struct Action { pub struct Action {
pub logic: ActionLogic, pub logic: ActionLogic,
pub optimal_logic: Option<fn(&mut ActionData, &mut usize)>,
pub data: ActionData, pub data: ActionData,
} }
impl From<&Instruction> for Action { impl From<&Instruction> for Action {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let builder = InstructionBuilder::from(instruction); let builder = TwoOperandLayout::from(instruction);
let operation = builder.operation; let operation = builder.operation;
let logic = match operation { let (logic, optimal_logic): (ActionLogic, Option<fn(&mut ActionData, &mut usize)>) =
Operation::MOVE => r#move, match operation {
Operation::CLOSE => close, Operation::MOVE => (r#move, Some(move_optimal)),
Operation::LOAD_INLINE => load_inline, Operation::LOAD_INLINE => (load_inline, None),
Operation::LOAD_CONSTANT => load_constant, Operation::LOAD_CONSTANT => (load_constant, None),
Operation::LOAD_LIST => load_list, Operation::LOAD_LIST => (load_list, None),
Operation::LOAD_FUNCTION => load_function, Operation::LOAD_FUNCTION => (load_function, None),
Operation::LOAD_SELF => load_self, Operation::LOAD_SELF => (load_self, None),
Operation::GET_LOCAL => get_local, Operation::GET_LOCAL => (get_local, None),
Operation::SET_LOCAL => set_local, Operation::SET_LOCAL => (set_local, None),
Operation::ADD => add, Operation::ADD => (add, Some(add_optimal)),
Operation::LESS => less, Operation::LESS => (less, Some(less_optimal)),
Operation::JUMP => jump, Operation::JUMP => (jump, Some(jump_optimal)),
Operation::RETURN => r#return, Operation::RETURN => (r#return, None),
unknown => unknown.panic_from_unknown_code(), unknown => unknown.panic_from_unknown_code(),
}; };
Action { Action {
logic, logic,
optimal_logic,
data: ActionData { data: ActionData {
name: operation.name(), name: operation.name(),
instruction: builder, instruction: builder,
@ -115,7 +118,7 @@ impl From<&Instruction> for Action {
boolean_register_pointers: [ptr::null_mut(); 2], boolean_register_pointers: [ptr::null_mut(); 2],
integer_register_pointers: [ptr::null_mut(); 2], integer_register_pointers: [ptr::null_mut(); 2],
runs: 0, runs: 0,
condensed_actions: Vec::new(), loop_actions: Vec::new(),
}, },
} }
} }
@ -136,13 +139,13 @@ impl Debug for Action {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ActionData { pub struct ActionData {
pub name: &'static str, pub name: &'static str,
pub instruction: InstructionBuilder, pub instruction: TwoOperandLayout,
pub boolean_register_pointers: [*mut Register<bool>; 2], pub boolean_register_pointers: [*mut Register<bool>; 2],
pub integer_register_pointers: [*mut Register<i64>; 2], pub integer_register_pointers: [*mut Register<i64>; 2],
pub integer_pointers: [*mut i64; 3], pub integer_pointers: [*mut i64; 3],
pub runs: usize, pub runs: usize,
pub condensed_actions: Vec<Action>, pub loop_actions: Vec<Action>,
} }
pub type ActionLogic = fn(&mut ThreadData, &mut ActionData); 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; let mut local_ip = 0;
loop { loop {
if local_ip >= action_data.condensed_actions.len() { if local_ip >= action_data.loop_actions.len() {
break; break;
} }
let action = &mut action_data.condensed_actions[local_ip]; let action = &mut action_data.loop_actions[local_ip];
local_ip += 1; local_ip += 1;
if action.data.runs == 0 { 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); (action.logic)(thread_data, &mut action.data);
continue; continue;
} }
trace!("Condensed action optimized: {}", action.data.name); trace!("Action: {} Optimized", action.data.name);
match action.data.name { match action.data.name {
"LESS" => unsafe { "LESS_INT" => unsafe {
asm!( asm!(
"cmp {0}, {1}", "cmp {0}, {1}",
"jns 2f", "jns 2f",
@ -205,12 +208,94 @@ fn loop_optimized(thread_data: &mut ThreadData, action_data: &mut ActionData) {
} else { } else {
local_ip -= (offset + 1) as usize; 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!(), _ => 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) { fn r#move(thread_data: &mut ThreadData, action_data: &mut ActionData) {
let ActionData { instruction, .. } = action_data; let ActionData { instruction, .. } = action_data;
let current_frame = thread_data.current_frame_mut(); 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; *old_register = new_register;
if instruction.c_field != 0 { 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 { 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 { if *runs > 0 {
unsafe { unsafe {
if *pointers[0] < *pointers[1] { if *pointers[0] < *pointers[1] {
current_frame.instruction_pointer += 1; current_frame.ip += 1;
} }
} }
} else { } else {
@ -538,7 +623,7 @@ fn less(thread_data: &mut ThreadData, action_data: &mut ActionData) {
}; };
if is_less { if is_less {
current_frame.instruction_pointer += 1; current_frame.ip += 1;
} }
pointers[0] = left_pointer; 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; let is_positive = instruction.c_field != 0;
if is_positive { if is_positive {
current_frame.instruction_pointer += offset; current_frame.ip += offset;
} else { } else {
current_frame.instruction_pointer -= offset + 1; current_frame.ip -= offset + 1;
} }
action_data.runs += 1; action_data.runs += 1;

View File

@ -12,7 +12,7 @@ pub struct CallFrame {
pub prototype: Arc<Chunk>, pub prototype: Arc<Chunk>,
pub registers: RegisterTable, pub registers: RegisterTable,
pub return_register: u16, pub return_register: u16,
pub instruction_pointer: usize, pub ip: usize,
} }
impl CallFrame { impl CallFrame {
@ -20,7 +20,7 @@ impl CallFrame {
Self { Self {
prototype, prototype,
return_register, return_register,
instruction_pointer: 0, ip: 0,
registers: RegisterTable::new(), registers: RegisterTable::new(),
} }
} }
@ -35,7 +35,7 @@ impl Display for CallFrame {
.name .name
.as_ref() .as_ref()
.unwrap_or(&DustString::from("anonymous")), .unwrap_or(&DustString::from("anonymous")),
self.instruction_pointer, self.ip,
) )
} }
} }

View File

@ -4,7 +4,7 @@ use std::slice::SliceIndex;
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
use tracing::trace; use tracing::trace;
use crate::DustString; use crate::{AbstractList, DustString};
use super::Pointer; use super::Pointer;
@ -33,25 +33,38 @@ impl<T: Clone> Register<T> {
} }
} }
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)] #[derive(Debug)]
pub struct RegisterTable { pub struct RegisterTable {
booleans: SmallVec<[Register<bool>; 64]>, booleans: SmallVec<[Register<bool>; BOOLEAN_REGISTER_COUNT]>,
bytes: SmallVec<[Register<u8>; 64]>, bytes: SmallVec<[Register<u8>; BYTE_REGISTER_COUNT]>,
characters: SmallVec<[Register<char>; 64]>, characters: SmallVec<[Register<char>; CHARACTER_REGISTER_COUNT]>,
floats: SmallVec<[Register<f64>; 64]>, floats: SmallVec<[Register<f64>; FLOAT_REGISTER_COUNT]>,
integers: SmallVec<[Register<i64>; 64]>, integers: SmallVec<[Register<i64>; INTEGER_REGISTER_COUNT]>,
strings: SmallVec<[Register<DustString>; 64]>, strings: SmallVec<[Register<DustString>; STRING_REGISTER_COUNT]>,
lists: SmallVec<[Register<AbstractList>; LIST_REGISTER_COUNT]>,
pointers: SmallVec<[Register<Pointer>; POINTER_REGISTER_COUNT]>,
} }
impl RegisterTable { impl RegisterTable {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
booleans: smallvec![Register::Empty; 64], booleans: smallvec![Register::Empty; BOOLEAN_REGISTER_COUNT],
bytes: smallvec![Register::Empty; 64], bytes: smallvec![Register::Empty; BYTE_REGISTER_COUNT],
characters: smallvec![Register::Empty; 64], characters: smallvec![Register::Empty; CHARACTER_REGISTER_COUNT],
floats: smallvec![Register::Empty; 64], floats: smallvec![Register::Empty; FLOAT_REGISTER_COUNT],
integers: smallvec![Register::Empty; 64], integers: smallvec![Register::Empty; INTEGER_REGISTER_COUNT],
strings: smallvec![Register::Empty; 64], 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<u8> { pub fn get_byte(&self, index: u16) -> &Register<u8> {
let index = index as usize; 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<char> { pub fn get_character(&self, index: u16) -> &Register<char> {
let index = index as usize; 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<f64> { pub fn get_float(&self, index: u16) -> &Register<f64> {
let index = index as usize; 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<i64> { pub fn get_integer(&self, index: u16) -> &Register<i64> {
let index = index as usize; 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<I, const N: usize>( pub fn get_many_integer_mut<I, const N: usize>(
&mut self, &mut self,
indices: [I; N], 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<DustString> { pub fn get_string(&self, index: u16) -> &Register<DustString> {
let index = index as usize; let index = index as usize;
@ -205,6 +260,26 @@ impl RegisterTable {
unsafe { self.strings.get_mut(index).unwrap_unchecked() } 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<T: Clone, const REGISTER_COUNT: usize>(
registers: &mut SmallVec<[Register<T>; REGISTER_COUNT]>,
) {
if REGISTER_COUNT >= registers.len() {
let new_length = registers.len() + REGISTER_COUNT;
registers.resize(new_length, Register::Empty);
}
}
} }
impl Default for RegisterTable { impl Default for RegisterTable {

View File

@ -3,7 +3,13 @@ use std::{sync::Arc, thread::JoinHandle};
use smallvec::SmallVec; use smallvec::SmallVec;
use tracing::{info, span, trace}; 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}; use super::{Action, CallFrame};
@ -36,7 +42,7 @@ impl Thread {
let mut thread_data = ThreadData { let mut thread_data = ThreadData {
stack: call_stack, stack: call_stack,
return_value: None, return_value: None,
spawned_threads: Vec::with_capacity(0), spawned_threads: Vec::new(),
}; };
let mut action_sequence = ActionSequence::new(&self.chunk.instructions); let mut action_sequence = ActionSequence::new(&self.chunk.instructions);
@ -44,12 +50,154 @@ impl Thread {
loop { loop {
let current_frame = thread_data.current_frame_mut(); let current_frame = thread_data.current_frame_mut();
let current_action = action_sequence.get_mut(current_frame.instruction_pointer); let current_action = action_sequence.get_mut(current_frame.ip);
current_frame.instruction_pointer += 1; current_frame.ip += 1;
trace!("Action: {}", current_action); 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 { if let Some(value_option) = thread_data.return_value {
return value_option; return value_option;