Optimize VM; Add two new register layouts
This commit is contained in:
parent
9cf873bd39
commit
121d2aae7b
@ -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"] }
|
||||
|
@ -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); })
|
||||
}
|
||||
"#;
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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> {
|
||||
|
@ -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<Add> for Instruction {
|
||||
let b_type = add.left_type;
|
||||
let c_type = add.right_type;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
|
@ -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<Instruction> 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<Call> for Instruction {
|
||||
let c_field = call.argument_count;
|
||||
let d_field = call.is_recursive;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation: Operation::CALL,
|
||||
a_field,
|
||||
b_field,
|
||||
|
@ -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<Instruction> 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<CallNative> 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 => {
|
||||
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, ", ")?;
|
||||
}
|
||||
1 => write!(f, "{function}(R{arguments_start})"),
|
||||
_ => write!(f, "{function}(R{arguments_start}..R{arguments_end})"),
|
||||
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Close> for Instruction {
|
||||
let b_field = close.from;
|
||||
let c_field = close.to;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation,
|
||||
b_field,
|
||||
c_field,
|
||||
|
@ -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<Divide> for Instruction {
|
||||
let b_type = divide.left_type;
|
||||
let c_type = divide.right_type;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
|
@ -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<Equal> for Instruction {
|
||||
let b_type = equal_bool.left_type;
|
||||
let c_type = equal_bool.right_type;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation,
|
||||
b_field,
|
||||
c_field,
|
||||
|
@ -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<GetLocal> for Instruction {
|
||||
let b_field = get_local.local_index;
|
||||
let b_type = get_local.r#type;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
|
@ -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<Jump> for Instruction {
|
||||
let b_field = jump.offset;
|
||||
let c_field = jump.is_positive as u16;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation,
|
||||
b_field,
|
||||
c_field,
|
||||
|
@ -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<Less> for Instruction {
|
||||
let b_type = less.left_type;
|
||||
let c_type = less.right_type;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation,
|
||||
b_field,
|
||||
c_field,
|
||||
|
@ -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<LessEqual> 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,
|
||||
|
@ -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<Instruction> for LoadConstant {
|
||||
|
||||
impl From<LoadConstant> 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,
|
||||
|
@ -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<Instruction> for LoadFunction {
|
||||
|
||||
impl From<LoadFunction> 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,
|
||||
|
@ -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<LoadInline> for Instruction {
|
||||
let c_field = load_boolean.boolean as u16;
|
||||
let d_field = load_boolean.jump_next;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
|
@ -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<Instruction> for LoadList {
|
||||
|
||||
impl From<LoadList> 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,
|
||||
|
@ -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<Instruction> for LoadSelf {
|
||||
|
||||
impl From<LoadSelf> 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,
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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<Modulo> for Instruction {
|
||||
let b_type = modulo.left_type;
|
||||
let c_type = modulo.right_type;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
|
@ -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<Move> for Instruction {
|
||||
let b_field = r#move.to;
|
||||
let b_type = r#move.type_code;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
|
@ -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<Multiply> for Instruction {
|
||||
let b_type = multiply.left_type;
|
||||
let c_type = multiply.right_type;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
|
@ -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<Negate> 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,
|
||||
|
@ -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<Not> 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,
|
||||
|
@ -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<Return> for Instruction {
|
||||
let b_type = r#return.return_type;
|
||||
let c_field = r#return.return_register;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation,
|
||||
b_field,
|
||||
b_type,
|
||||
|
@ -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<SetLocal> for Instruction {
|
||||
let b_field = set_local.register_index;
|
||||
let c_field = set_local.local_index;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation,
|
||||
b_field,
|
||||
c_field,
|
||||
|
@ -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<Subtract> for Instruction {
|
||||
let b_type = subtract.left_type;
|
||||
let c_type = subtract.right_type;
|
||||
|
||||
InstructionBuilder {
|
||||
TwoOperandLayout {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
|
@ -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<Test> 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,
|
||||
|
@ -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<TestSet> 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,
|
||||
|
@ -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<fn(&mut ActionData, &mut usize)>,
|
||||
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,
|
||||
let (logic, optimal_logic): (ActionLogic, Option<fn(&mut ActionData, &mut usize)>) =
|
||||
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<bool>; 2],
|
||||
pub integer_register_pointers: [*mut Register<i64>; 2],
|
||||
pub integer_pointers: [*mut i64; 3],
|
||||
pub runs: usize,
|
||||
pub condensed_actions: Vec<Action>,
|
||||
pub loop_actions: Vec<Action>,
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -12,7 +12,7 @@ pub struct CallFrame {
|
||||
pub prototype: Arc<Chunk>,
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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<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)]
|
||||
pub struct RegisterTable {
|
||||
booleans: SmallVec<[Register<bool>; 64]>,
|
||||
bytes: SmallVec<[Register<u8>; 64]>,
|
||||
characters: SmallVec<[Register<char>; 64]>,
|
||||
floats: SmallVec<[Register<f64>; 64]>,
|
||||
integers: SmallVec<[Register<i64>; 64]>,
|
||||
strings: SmallVec<[Register<DustString>; 64]>,
|
||||
booleans: SmallVec<[Register<bool>; BOOLEAN_REGISTER_COUNT]>,
|
||||
bytes: SmallVec<[Register<u8>; BYTE_REGISTER_COUNT]>,
|
||||
characters: SmallVec<[Register<char>; CHARACTER_REGISTER_COUNT]>,
|
||||
floats: SmallVec<[Register<f64>; FLOAT_REGISTER_COUNT]>,
|
||||
integers: SmallVec<[Register<i64>; INTEGER_REGISTER_COUNT]>,
|
||||
strings: SmallVec<[Register<DustString>; STRING_REGISTER_COUNT]>,
|
||||
lists: SmallVec<[Register<AbstractList>; LIST_REGISTER_COUNT]>,
|
||||
pointers: SmallVec<[Register<Pointer>; 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<u8> {
|
||||
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> {
|
||||
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> {
|
||||
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> {
|
||||
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>(
|
||||
&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<DustString> {
|
||||
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<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 {
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user