1
0

Improve control flow register consolidation

This commit is contained in:
Jeff 2025-02-06 12:42:55 -05:00
parent 6d17ba9a2c
commit 6f0955c29a
30 changed files with 281 additions and 227 deletions

View File

@ -27,7 +27,7 @@ use std::sync::Arc;
use serde::{Deserialize, Serialize};
use crate::{DustString, Function, FunctionType, Instruction, Span, Value};
use crate::{ConcreteValue, DustString, Function, FunctionType, Instruction, Span, Value};
/// Representation of a Dust program or function.
///
@ -39,38 +39,22 @@ pub struct Chunk {
pub(crate) instructions: Vec<Instruction>,
pub(crate) positions: Vec<Span>,
pub(crate) constants: Vec<Value>,
pub(crate) constants: Vec<ConcreteValue>,
pub(crate) locals: Vec<Local>,
pub(crate) prototypes: Vec<Arc<Chunk>>,
pub(crate) register_count: usize,
pub(crate) boolean_register_count: usize,
pub(crate) byte_register_count: usize,
pub(crate) character_register_count: usize,
pub(crate) float_register_count: usize,
pub(crate) integer_register_count: usize,
pub(crate) string_register_count: usize,
pub(crate) list_register_count: usize,
pub(crate) function_register_count: usize,
pub(crate) prototype_index: u16,
}
impl Chunk {
#[cfg(any(test, debug_assertions))]
pub fn with_data(
name: Option<DustString>,
r#type: FunctionType,
instructions: impl Into<Vec<Instruction>>,
positions: impl Into<Vec<Span>>,
constants: impl Into<Vec<Value>>,
locals: impl Into<Vec<Local>>,
prototypes: impl IntoIterator<Item = Chunk>,
) -> Self {
Self {
name,
r#type,
instructions: instructions.into(),
positions: positions.into(),
constants: constants.into(),
locals: locals.into(),
prototypes: prototypes.into_iter().map(Arc::new).collect(),
register_count: 0,
prototype_index: 0,
}
}
pub fn as_function(&self) -> Function {
Function {
name: self.name.clone(),

View File

@ -19,7 +19,6 @@
//! pass that chunk to the VM. Otherwise, if the compiler gives no errors and the VM encounters a
//! runtime error, it is the compiler's fault and the error should be fixed here.
mod error;
mod optimize;
mod parse_rule;
mod type_checks;
@ -30,12 +29,10 @@ use type_checks::{check_math_type, check_math_types};
use std::{mem::replace, sync::Arc};
use optimize::control_flow_register_consolidation;
use crate::{
Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
NativeFunction, Operand, Operation, Scope, Span, Token, TokenKind, Type, Value,
instruction::{CallNative, Close, Jump, LoadList, Point, Return, TypeCode},
NativeFunction, Operand, Operation, Scope, Span, Token, TokenKind, Type,
instruction::{CallNative, Jump, LoadList, Point, Return, TypeCode},
};
/// Compiles the input and returns a chunk.
@ -88,7 +85,7 @@ pub struct Compiler<'src> {
/// Constants that have been compiled. These are assigned to the chunk when [`Compiler::finish`]
/// is called.
constants: Vec<Value>,
constants: Vec<ConcreteValue>,
/// Block-local variables and their types. The locals are assigned to the chunk when
/// [`Compiler::finish`] is called. The types are discarded after compilation.
@ -98,10 +95,6 @@ pub struct Compiler<'src> {
/// [`Compiler::finish`] is called.
prototypes: Vec<Arc<Chunk>>,
/// Maximum stack size required by the chunk. This is assigned to the chunk when
/// [`Compiler::finish`] is called.
stack_size: usize,
/// The first boolean register index that the compiler should use. This is used to avoid reusing
/// the registers that are used for the function's arguments.
minimum_boolean_register: u16,
@ -175,7 +168,6 @@ impl<'src> Compiler<'src> {
constants: Vec::new(),
locals: Vec::new(),
prototypes: Vec::new(),
stack_size: 0,
lexer,
minimum_byte_register: 0,
minimum_boolean_register: 0,
@ -234,6 +226,14 @@ impl<'src> Compiler<'src> {
/// will allow [`Compiler::function_name`] to be both the name used for recursive calls and the
/// name of the function when it is compiled. The name can later be seen in the VM's call stack.
pub fn finish(self) -> Chunk {
let boolean_register_count = self.next_boolean_register() as usize;
let byte_register_count = self.next_byte_register() as usize;
let character_register_count = self.next_character_register() as usize;
let float_register_count = self.next_float_register() as usize;
let integer_register_count = self.next_integer_register() as usize;
let string_register_count = self.next_string_register() as usize;
let list_register_count = self.next_list_register() as usize;
let function_register_count = self.next_function_register() as usize;
let (instructions, positions): (Vec<Instruction>, Vec<Span>) = self
.instructions
.into_iter()
@ -253,7 +253,14 @@ impl<'src> Compiler<'src> {
constants: self.constants,
locals,
prototypes: self.prototypes,
register_count: self.stack_size,
boolean_register_count,
byte_register_count,
character_register_count,
float_register_count,
integer_register_count,
string_register_count,
list_register_count,
function_register_count,
prototype_index: self.prototype_index,
}
}
@ -411,12 +418,11 @@ impl<'src> Compiler<'src> {
.rev()
.find_map(|(index, (local, _))| {
let constant = self.constants.get(local.identifier_index as usize)?;
let identifier =
if let Value::Concrete(ConcreteValue::String(identifier)) = constant {
identifier
} else {
return None;
};
let identifier = if let ConcreteValue::String(identifier) = constant {
identifier
} else {
return None;
};
if identifier == identifier_text {
Some(index as u16)
@ -440,7 +446,7 @@ impl<'src> Compiler<'src> {
) -> (u16, u16) {
info!("Declaring local {identifier}");
let identifier = Value::Concrete(ConcreteValue::string(identifier));
let identifier = ConcreteValue::string(identifier);
let identifier_index = self.push_or_get_constant(identifier);
let local_index = self.locals.len() as u16;
@ -462,7 +468,7 @@ impl<'src> Compiler<'src> {
})
}
fn push_or_get_constant(&mut self, value: Value) -> u16 {
fn push_or_get_constant(&mut self, value: ConcreteValue) -> u16 {
if let Some(index) = self
.constants
.iter()
@ -592,12 +598,6 @@ impl<'src> Compiler<'src> {
position.to_string()
);
if instruction.yields_value() {
let destination = instruction.a_field() as usize;
self.stack_size = (destination + 1).max(self.stack_size);
}
self.instructions.push((instruction, r#type, position));
}
@ -607,7 +607,7 @@ impl<'src> Compiler<'src> {
position: Span,
) -> Result<(), CompileError> {
let r#type = constant.r#type();
let constant_index = self.push_or_get_constant(Value::Concrete(constant));
let constant_index = self.push_or_get_constant(constant);
let destination = match r#type {
Type::Character => self.next_character_register(),
Type::Float => self.next_float_register(),
@ -615,7 +615,8 @@ impl<'src> Compiler<'src> {
Type::String => self.next_string_register(),
_ => todo!(),
};
let load_constant = Instruction::load_constant(destination, constant_index, false);
let load_constant =
Instruction::load_constant(destination, constant_index, r#type.type_code(), false);
self.emit_instruction(load_constant, r#type, position);
@ -1420,7 +1421,7 @@ impl<'src> Compiler<'src> {
}
let if_block_end = self.instructions.len();
let mut if_block_distance = (if_block_end - if_block_start) as u16;
let mut if_block_distance = if_block_end - if_block_start;
let if_block_type = self.get_last_instruction_type();
if let Token::Else = self.current_token {
@ -1442,7 +1443,7 @@ impl<'src> Compiler<'src> {
}
let else_block_end = self.instructions.len();
let else_block_distance = (else_block_end - if_block_end) as u16;
let else_block_distance = else_block_end - if_block_end;
let else_block_type = self.get_last_instruction_type();
if let Err(conflict) = if_block_type.check(&else_block_type) {
@ -1455,16 +1456,17 @@ impl<'src> Compiler<'src> {
match else_block_distance {
0 => {}
1 => {
if let Some(Operation::LOAD_BOOLEAN | Operation::LOAD_CONSTANT) =
self.get_last_operation()
if let Some([Operation::LOAD_BOOLEAN | Operation::LOAD_CONSTANT, _]) =
self.get_last_operations()
{
let (loader, _, _) = self.instructions.last_mut().unwrap();
let loader_index = self.instructions.len() - 2;
let (loader, _, _) = self.instructions.get_mut(loader_index).unwrap();
loader.set_c_field(true as u16);
} else {
if_block_distance += 1;
let jump = Instruction::from(Jump {
offset: else_block_distance,
offset: else_block_distance as u16,
is_positive: true,
});
@ -1475,7 +1477,7 @@ impl<'src> Compiler<'src> {
2.. => {
if_block_distance += 1;
let jump = Instruction::from(Jump {
offset: else_block_distance,
offset: else_block_distance as u16,
is_positive: true,
});
@ -1484,11 +1486,21 @@ impl<'src> Compiler<'src> {
}
}
let jump = Instruction::jump(if_block_distance, true);
let jump = Instruction::jump(if_block_distance as u16, true);
self.instructions
.insert(if_block_start, (jump, Type::None, if_block_start_position));
control_flow_register_consolidation(self);
let if_block_last_instruction_index = self.instructions.len() - else_block_distance - 1;
let else_block_last_instruction_index = self.instructions.len() - 1;
let if_block_last_instruction = self.instructions[if_block_last_instruction_index].0;
let else_block_last_instruction =
&mut self.instructions[else_block_last_instruction_index].0;
else_block_last_instruction.set_a_field(if_block_last_instruction.a_field());
println!("{if_block_last_instruction_index} {else_block_last_instruction_index}");
Ok(())
}

View File

@ -1,64 +0,0 @@
//! Functions used by the compiler to optimize a chunk's bytecode during compilation.
use tracing::debug;
use crate::{Compiler, Instruction, Operation};
/// Optimizes a control flow pattern to use fewer registers and avoid using a `POINT` instruction.
/// Use this after parsing an if/else statement.
///
/// This makes the following examples compile to the same bytecode:
///
/// ```dust
/// 4 == 4
/// ```
///
/// ```dust
/// if 4 == 4 { true } else { false }
/// ```
///
/// When they occur in the sequence shown below, instructions can be optimized by taking advantage
/// of the loaders' ability to skip an instruction after loading a value. If these instructions are
/// the result of a binary expression, this will not change anything because they were already
/// emitted optimally. Control flow patterns, however, can be optimized because the load
/// instructions are from seperate expressions that each uses its own register. Since only one of
/// the two branches will be executed, this is wasteful. It would also require the compiler to emit
/// a `POINT` instruction to prevent the VM from encountering an empty register.
///
/// The instructions must be in the following order:
/// - `TEST` or any of the `EQUAL`, `LESS` or `LESS_EQUAL` instructions
/// - `JUMP`
/// - `LOAD_BOOLEAN` or `LOAD_CONSTANT`
/// - `LOAD_BOOLEAN` or `LOAD_CONSTANT`
///
/// This optimization was taken from `A No-Frills Introduction to Lua 5.1 VM Instructions` by
/// Kein-Hong Man.
pub fn control_flow_register_consolidation(compiler: &mut Compiler) {
if !matches!(
compiler.get_last_operations(),
Some([
Operation::TEST | Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL,
Operation::JUMP,
Operation::LOAD_BOOLEAN | Operation::LOAD_CONSTANT,
Operation::LOAD_BOOLEAN | Operation::LOAD_CONSTANT,
])
) {
return;
}
debug!("Consolidating registers for control flow optimization");
let first_loader_index = compiler.instructions.len() - 2;
let (first_loader, _, _) = &mut compiler.instructions.get_mut(first_loader_index).unwrap();
let first_loader_destination = first_loader.a_field();
*first_loader =
Instruction::load_boolean(first_loader.a_field(), first_loader.b_field() != 0, true);
let second_loader_index = compiler.instructions.len() - 1;
let (second_loader, _, _) = &mut compiler.instructions.get_mut(second_loader_index).unwrap();
*second_loader = Instruction::load_boolean(
first_loader_destination,
second_loader.b_field() != 0,
false,
);
}

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
use super::{Instruction, InstructionFields, Operand, Operation, 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 {
InstructionFields {
operation,
a_field,
b_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::InstructionBuilder;
use super::InstructionFields;
pub struct Call {
pub destination: u16,
@ -34,7 +34,7 @@ impl From<Call> for Instruction {
let c_field = call.argument_count;
let d_field = call.is_recursive;
InstructionBuilder {
InstructionFields {
operation: Operation::CALL,
a_field,
b_field,

View File

@ -2,7 +2,7 @@ use std::fmt::Display;
use crate::{Instruction, NativeFunction, Operation};
use super::InstructionBuilder;
use super::InstructionFields;
pub struct CallNative {
pub destination: u16,
@ -30,7 +30,7 @@ impl From<CallNative> for Instruction {
let b_field = call_native.function as u16;
let c_field = call_native.argument_count;
InstructionBuilder {
InstructionFields {
operation,
a_field,
b_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::InstructionBuilder;
use super::InstructionFields;
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 {
InstructionFields {
operation,
b_field,
c_field,

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
use super::{Instruction, InstructionFields, Operand, Operation, 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 {
InstructionFields {
operation,
a_field,
b_field,

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
use super::{Instruction, InstructionFields, Operand, Operation, 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 {
InstructionFields {
operation,
b_field,
c_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::InstructionBuilder;
use super::InstructionFields;
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 {
InstructionFields {
operation,
b_field,
c_field,

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
use super::{Instruction, InstructionFields, Operand, Operation, 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 {
InstructionFields {
operation,
b_field,
c_field,

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
use super::{Instruction, InstructionFields, Operand, Operation, 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 {
InstructionFields {
operation,
b_field,
c_field,

View File

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

View File

@ -2,11 +2,12 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::InstructionBuilder;
use super::{InstructionFields, TypeCode};
pub struct LoadConstant {
pub destination: u16,
pub constant_index: u16,
pub constant_type: TypeCode,
pub jump_next: bool,
}
@ -14,11 +15,13 @@ impl From<Instruction> for LoadConstant {
fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field();
let constant_index = instruction.b_field();
let constant_type = instruction.b_type();
let jump_next = instruction.c_field() != 0;
LoadConstant {
destination,
constant_index,
constant_type,
jump_next,
}
}
@ -26,10 +29,11 @@ impl From<Instruction> for LoadConstant {
impl From<LoadConstant> for Instruction {
fn from(load_constant: LoadConstant) -> Self {
InstructionBuilder {
InstructionFields {
operation: Operation::LOAD_CONSTANT,
a_field: load_constant.destination,
b_field: load_constant.constant_index,
b_type: load_constant.constant_type,
c_field: load_constant.jump_next as u16,
..Default::default()
}
@ -42,10 +46,21 @@ impl Display for LoadConstant {
let LoadConstant {
destination,
constant_index,
constant_type,
jump_next,
} = self;
write!(f, "R{destination} = C{constant_index}")?;
match *constant_type {
TypeCode::BOOLEAN => write!(f, "R_BOOL_{destination}")?,
TypeCode::BYTE => write!(f, "R_BYTE_{destination}")?,
TypeCode::CHARACTER => write!(f, "R_CHAR_{destination}")?,
TypeCode::FLOAT => write!(f, "R_FLOAT_{destination}")?,
TypeCode::INTEGER => write!(f, "R_INT_{destination}")?,
TypeCode::STRING => write!(f, "R_STR_{destination}")?,
unsupported => panic!("Unsupported type code: {}", unsupported.0),
}
write!(f, " = C{constant_index}")?;
if *jump_next {
write!(f, " JUMP +1")?;

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operation};
use super::{Instruction, InstructionFields, 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 {
InstructionFields {
operation: Operation::LOAD_FUNCTION,
a_field: load_function.destination,
b_field: load_function.prototype_index,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::InstructionBuilder;
use super::InstructionFields;
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 {
InstructionFields {
operation: Operation::LOAD_LIST,
a_field: load_list.destination,
b_field: load_list.start_register,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::InstructionBuilder;
use super::InstructionFields;
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 {
InstructionFields {
operation: Operation::LOAD_SELF,
a_field: load_self.destination,
c_field: load_self.jump_next as u16,

View File

@ -151,7 +151,7 @@ pub use type_code::TypeCode;
use crate::NativeFunction;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct InstructionBuilder {
pub struct InstructionFields {
pub operation: Operation,
pub a_field: u16,
pub b_field: u16,
@ -163,7 +163,7 @@ pub struct InstructionBuilder {
pub c_type: TypeCode,
}
impl InstructionBuilder {
impl InstructionFields {
pub fn build(self) -> Instruction {
let bits = ((self.operation.0 as u64) << 57)
| ((self.b_is_constant as u64) << 56)
@ -179,9 +179,9 @@ impl InstructionBuilder {
}
}
impl From<&Instruction> for InstructionBuilder {
impl From<&Instruction> for InstructionFields {
fn from(instruction: &Instruction) -> Self {
InstructionBuilder {
InstructionFields {
operation: instruction.operation(),
a_field: instruction.a_field(),
b_field: instruction.b_field(),
@ -195,9 +195,9 @@ impl From<&Instruction> for InstructionBuilder {
}
}
impl Default for InstructionBuilder {
impl Default for InstructionFields {
fn default() -> Self {
InstructionBuilder {
InstructionFields {
operation: Operation::POINT,
a_field: 0,
b_field: 0,
@ -261,15 +261,21 @@ impl Instruction {
}
pub fn set_a_field(&mut self, bits: u16) {
self.0 = (bits as u64) << 31;
let mut fields = InstructionFields::from(&*self);
fields.a_field = bits;
*self = fields.build();
}
pub fn set_b_field(&mut self, bits: u16) {
self.0 = (bits as u64) << 47;
let mut fields = InstructionFields::from(&*self);
fields.b_field = bits;
*self = fields.build();
}
pub fn set_c_field(&mut self, bits: u16) {
self.0 = (bits as u64) << 63;
let mut fields = InstructionFields::from(&*self);
fields.c_field = bits;
*self = fields.build();
}
pub fn point(destination: u16, to: Operand) -> Instruction {
@ -288,10 +294,16 @@ impl Instruction {
})
}
pub fn load_constant(destination: u16, constant_index: u16, jump_next: bool) -> Instruction {
pub fn load_constant(
destination: u16,
constant_index: u16,
constant_type: TypeCode,
jump_next: bool,
) -> Instruction {
Instruction::from(LoadConstant {
destination,
constant_index,
constant_type,
jump_next,
})
}

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
use super::{Instruction, InstructionFields, Operand, Operation, 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 {
InstructionFields {
operation,
a_field,
b_field,

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
use super::{Instruction, InstructionFields, Operand, Operation, 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 {
InstructionFields {
operation,
a_field,
b_field,

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
use super::{Instruction, InstructionFields, Operand, Operation, 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 {
InstructionFields {
operation,
a_field,
b_field,

View File

@ -2,7 +2,7 @@ use std::fmt::Display;
use crate::{Instruction, Operand, Operation};
use super::InstructionBuilder;
use super::InstructionFields;
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 {
InstructionFields {
operation,
a_field,
b_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::{InstructionBuilder, Operand};
use super::{InstructionFields, Operand};
pub struct Point {
pub destination: u16,
@ -24,7 +24,7 @@ impl From<Point> for Instruction {
let a_field = r#move.destination;
let (b_field, b_is_constant) = r#move.to.as_index_and_constant_flag();
InstructionBuilder {
InstructionFields {
operation,
a_field,
b_field,

View File

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

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
use super::{Instruction, InstructionFields, Operand, Operation, 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 {
InstructionFields {
operation,
a_field,
b_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::InstructionBuilder;
use super::InstructionFields;
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 {
InstructionFields {
operation: Operation::TEST,
b_field,
c_field,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operand, Operation};
use super::InstructionBuilder;
use super::InstructionFields;
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 {
InstructionFields {
operation,
a_field,
b_field,

View File

@ -35,6 +35,46 @@ impl ConcreteValue {
ConcreteValue::String(to_string.into())
}
pub fn as_boolean(&self) -> Option<bool> {
if let ConcreteValue::Boolean(boolean) = self {
Some(*boolean)
} else {
None
}
}
pub fn as_byte(&self) -> Option<u8> {
if let ConcreteValue::Byte(byte) = self {
Some(*byte)
} else {
None
}
}
pub fn as_character(&self) -> Option<char> {
if let ConcreteValue::Character(character) = self {
Some(*character)
} else {
None
}
}
pub fn as_float(&self) -> Option<f64> {
if let ConcreteValue::Float(float) = self {
Some(*float)
} else {
None
}
}
pub fn as_integer(&self) -> Option<i64> {
if let ConcreteValue::Integer(integer) = self {
Some(*integer)
} else {
None
}
}
pub fn as_string(&self) -> Option<&DustString> {
if let ConcreteValue::String(string) = self {
Some(string)
@ -43,6 +83,22 @@ impl ConcreteValue {
}
}
pub fn as_list(&self) -> Option<&Vec<ConcreteValue>> {
if let ConcreteValue::List(list) = self {
Some(list)
} else {
None
}
}
pub fn as_range(&self) -> Option<&RangeValue> {
if let ConcreteValue::Range(range) = self {
Some(range)
} else {
None
}
}
pub fn display(&self) -> DustString {
DustString::from(self.to_string())
}
@ -147,7 +203,7 @@ impl ConcreteValue {
return Err(ValueError::CannotMultiply(
self.clone().to_value(),
other.clone().to_value(),
))
));
}
};
@ -165,7 +221,7 @@ impl ConcreteValue {
return Err(ValueError::CannotMultiply(
self.clone().to_value(),
other.clone().to_value(),
))
));
}
};
@ -185,7 +241,7 @@ impl ConcreteValue {
return Err(ValueError::CannotMultiply(
self.clone().to_value(),
other.clone().to_value(),
))
));
}
};
@ -255,7 +311,7 @@ impl ConcreteValue {
return Err(ValueError::CannotCompare(
self.clone().to_value(),
other.clone().to_value(),
))
));
}
};
@ -278,7 +334,7 @@ impl ConcreteValue {
return Err(ValueError::CannotCompare(
self.clone().to_value(),
other.clone().to_value(),
))
));
}
};

View File

@ -1,16 +1,9 @@
use tracing::trace;
use crate::{
AbstractList, ConcreteValue, Instruction, Operand, Type, Value,
instruction::{
Add, Call, CallNative, Divide, Equal, InstructionBuilder, Jump, Less, LessEqual,
LoadBoolean, LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not,
Return, Subtract, Test, TestSet, TypeCode,
},
vm::CallFrame,
Instruction, Value,
instruction::{InstructionFields, TypeCode},
};
use super::{Pointer, Register, thread::Thread};
use super::thread::Thread;
#[derive(Debug)]
pub struct ActionSequence {
@ -34,20 +27,20 @@ impl ActionSequence {
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Action {
pub logic: RunnerLogic,
pub instruction: InstructionBuilder,
pub instruction: InstructionFields,
}
impl From<&Instruction> for Action {
fn from(instruction: &Instruction) -> Self {
let operation = instruction.operation();
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
let instruction = InstructionBuilder::from(instruction);
let instruction = InstructionFields::from(instruction);
Action { logic, instruction }
}
}
pub type RunnerLogic = fn(InstructionBuilder, &mut Thread);
pub type RunnerLogic = fn(InstructionFields, &mut Thread);
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 23] = [
point,
@ -75,21 +68,21 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 23] = [
r#return,
];
pub fn point(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn point(instruction: InstructionFields, thread: &mut Thread) {}
pub fn close(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn close(instruction: InstructionFields, thread: &mut Thread) {}
pub fn load_boolean(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn load_boolean(instruction: InstructionFields, thread: &mut Thread) {}
pub fn load_constant(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn load_constant(instruction: InstructionFields, thread: &mut Thread) {}
pub fn load_list(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn load_list(instruction: InstructionFields, thread: &mut Thread) {}
pub fn load_function(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn load_function(instruction: InstructionFields, thread: &mut Thread) {}
pub fn load_self(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn load_self(instruction: InstructionFields, thread: &mut Thread) {}
pub fn add(instruction: InstructionBuilder, thread: &mut Thread) {
pub fn add(instruction: InstructionFields, thread: &mut Thread) {
let destination = instruction.a_field as usize;
let left = instruction.b_field as usize;
let left_is_constant = instruction.b_is_constant;
@ -161,35 +154,81 @@ pub fn add(instruction: InstructionBuilder, thread: &mut Thread) {
}
}
pub fn subtract(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn subtract(instruction: InstructionFields, thread: &mut Thread) {}
pub fn multiply(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn multiply(instruction: InstructionFields, thread: &mut Thread) {}
pub fn divide(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn divide(instruction: InstructionFields, thread: &mut Thread) {}
pub fn modulo(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn modulo(instruction: InstructionFields, thread: &mut Thread) {}
pub fn test(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn test(instruction: InstructionFields, thread: &mut Thread) {}
pub fn test_set(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn test_set(instruction: InstructionFields, thread: &mut Thread) {}
pub fn equal(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn equal(instruction: InstructionFields, thread: &mut Thread) {}
pub fn less(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn less(instruction: InstructionFields, thread: &mut Thread) {
let comparator = instruction.d_field;
let left = instruction.b_field as usize;
let left_type = instruction.b_type;
let left_is_constant = instruction.b_is_constant;
let right = instruction.c_field as usize;
let right_type = instruction.c_type;
let right_is_constant = instruction.c_is_constant;
pub fn less_equal(instruction: InstructionBuilder, thread: &mut Thread) {}
match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_integer().unwrap()
} else {
unsafe { thread.get_constant(left).as_integer().unwrap_unchecked() }
}
} else {
thread.get_integer_register(left)
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_integer().unwrap()
} else {
unsafe { thread.get_constant(right).as_integer().unwrap_unchecked() }
}
} else {
thread.get_integer_register(right)
};
let result = left_value < right_value;
pub fn negate(instruction: InstructionBuilder, thread: &mut Thread) {}
if result == comparator {
thread.current_frame_mut().ip += 1;
}
}
_ => unimplemented!(),
}
}
pub fn not(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn less_equal(instruction: InstructionFields, thread: &mut Thread) {}
pub fn jump(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn negate(instruction: InstructionFields, thread: &mut Thread) {}
pub fn call(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn not(instruction: InstructionFields, thread: &mut Thread) {}
pub fn call_native(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn jump(instruction: InstructionFields, thread: &mut Thread) {
let offset = instruction.b_field as usize;
let is_positive = instruction.c_field != 0;
pub fn r#return(instruction: InstructionBuilder, thread: &mut Thread) {
if is_positive {
thread.current_frame_mut().ip += offset;
} else {
thread.current_frame_mut().ip -= offset;
}
}
pub fn call(instruction: InstructionFields, thread: &mut Thread) {}
pub fn call_native(instruction: InstructionFields, thread: &mut Thread) {}
pub fn r#return(instruction: InstructionFields, thread: &mut Thread) {
let should_return_value = instruction.b_field != 0;
let return_register = instruction.c_field as usize;
let return_type = instruction.b_type;

View File

@ -2,7 +2,7 @@ use std::{sync::Arc, thread::JoinHandle};
use tracing::{info, trace};
use crate::{Chunk, DustString, Span, Value, vm::CallFrame};
use crate::{Chunk, ConcreteValue, DustString, Span, Value, vm::CallFrame};
use super::call_frame::{Pointer, Register};
@ -557,7 +557,7 @@ impl Thread {
*register = Register::Value(value);
}
pub fn get_constant(&self, constant_index: usize) -> &Value {
pub fn get_constant(&self, constant_index: usize) -> &ConcreteValue {
if cfg!(debug_assertions) {
self.chunk.constants.get(constant_index).unwrap()
} else {