1
0

Add thread spawning and a random number generator

This commit is contained in:
Jeff 2025-01-09 19:56:36 -05:00
parent 5caad00f65
commit de426d814a
18 changed files with 326 additions and 147 deletions

10
Cargo.lock generated
View File

@ -269,6 +269,15 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]]
name = "crossbeam-channel"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.8.5" version = "0.8.5"
@ -335,6 +344,7 @@ dependencies = [
"annotate-snippets", "annotate-snippets",
"colored", "colored",
"criterion", "criterion",
"crossbeam-channel",
"getrandom", "getrandom",
"rand", "rand",
"serde", "serde",

View File

@ -12,7 +12,7 @@ version.workspace = true
annotate-snippets = "0.11.4" annotate-snippets = "0.11.4"
colored = "2.1.0" colored = "2.1.0"
rand = "0.8.5" rand = "0.8.5"
serde = { version = "1.0.203", features = ["derive"] } serde = { version = "1.0.203", features = ["derive", "rc"] }
serde_json = "1.0.117" serde_json = "1.0.117"
getrandom = { version = "0.2", features = [ getrandom = { version = "0.2", features = [
"js", "js",
@ -21,6 +21,7 @@ smartstring = { version = "1.0.1", features = [
"serde", "serde",
], default-features = false } ], default-features = false }
tracing = "0.1.41" tracing = "0.1.41"
crossbeam-channel = "0.5.14"
[dev-dependencies] [dev-dependencies]
criterion = { version = "0.3.4", features = ["html_reports"] } criterion = { version = "0.3.4", features = ["html_reports"] }
@ -33,6 +34,10 @@ harness = false
name = "fibonacci" name = "fibonacci"
harness = false harness = false
[[bench]]
name = "threads"
harness = false
[[test]] [[test]]
name = "logic_and" name = "logic_and"
path = "tests/logic/and.rs" path = "tests/logic/and.rs"

View File

@ -0,0 +1,31 @@
use std::time::Duration;
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use dust_lang::run;
const SOURCE: &str = r#"
let mut i = 0
while i < 1_000 {
i += 1
spawn(
fn () { random_int(0, 10); }
)
}
"#;
fn threads(source: &str) {
run(source).unwrap();
}
fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("threads");
group.measurement_time(Duration::from_secs(15));
group.bench_function("threads", |b| b.iter(|| threads(black_box(SOURCE))));
group.finish();
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@ -23,6 +23,7 @@ pub use scope::Scope;
use std::fmt::{self, Debug, Display, Formatter, Write as FmtWrite}; use std::fmt::{self, Debug, Display, Formatter, Write as FmtWrite};
use std::io::Write; use std::io::Write;
use std::sync::Arc;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -40,7 +41,7 @@ pub struct Chunk {
pub(crate) positions: Vec<Span>, pub(crate) positions: Vec<Span>,
pub(crate) constants: Vec<Value>, pub(crate) constants: Vec<Value>,
pub(crate) locals: Vec<Local>, pub(crate) locals: Vec<Local>,
pub(crate) prototypes: Vec<Chunk>, pub(crate) prototypes: Vec<Arc<Chunk>>,
pub(crate) register_count: usize, pub(crate) register_count: usize,
pub(crate) prototype_index: u8, pub(crate) prototype_index: u8,
@ -55,7 +56,7 @@ impl Chunk {
positions: impl Into<Vec<Span>>, positions: impl Into<Vec<Span>>,
constants: impl Into<Vec<Value>>, constants: impl Into<Vec<Value>>,
locals: impl Into<Vec<Local>>, locals: impl Into<Vec<Local>>,
prototypes: Vec<Chunk>, prototypes: impl IntoIterator<Item = Chunk>,
) -> Self { ) -> Self {
Self { Self {
name, name,
@ -64,7 +65,7 @@ impl Chunk {
positions: positions.into(), positions: positions.into(),
constants: constants.into(), constants: constants.into(),
locals: locals.into(), locals: locals.into(),
prototypes, prototypes: prototypes.into_iter().map(Arc::new).collect(),
register_count: 0, register_count: 0,
prototype_index: 0, prototype_index: 0,
} }

View File

@ -28,7 +28,7 @@ use parse_rule::{ParseRule, Precedence};
use tracing::{Level, debug, info, span}; use tracing::{Level, debug, info, span};
use type_checks::{check_math_type, check_math_types}; use type_checks::{check_math_type, check_math_types};
use std::mem::replace; use std::{mem::replace, sync::Arc};
use optimize::control_flow_register_consolidation; use optimize::control_flow_register_consolidation;
@ -96,7 +96,7 @@ pub struct Compiler<'src> {
/// Prototypes that have been compiled. These are assigned to the chunk when /// Prototypes that have been compiled. These are assigned to the chunk when
/// [`Compiler::finish`] is called. /// [`Compiler::finish`] is called.
prototypes: Vec<Chunk>, prototypes: Vec<Arc<Chunk>>,
/// Maximum stack size required by the chunk. This is assigned to the chunk when /// Maximum stack size required by the chunk. This is assigned to the chunk when
/// [`Compiler::finish`] is called. /// [`Compiler::finish`] is called.
@ -1645,7 +1645,7 @@ impl<'src> Compiler<'src> {
let chunk = function_compiler.finish(); let chunk = function_compiler.finish();
let destination = self.next_register(); let destination = self.next_register();
self.prototypes.push(chunk); self.prototypes.push(Arc::new(chunk));
if let Some(identifier) = identifier { if let Some(identifier) = identifier {
self.declare_local( self.declare_local(

View File

@ -5,7 +5,7 @@
//! - [`Lexer`], which lexes the input a token at a time //! - [`Lexer`], which lexes the input a token at a time
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{dust_error::AnnotatedError, CompileError, DustError, Span, Token}; use crate::{CompileError, DustError, Span, Token, dust_error::AnnotatedError};
/// Lexes the input and returns a vector of tokens and their positions. /// Lexes the input and returns a vector of tokens and their positions.
/// ///
@ -83,7 +83,7 @@ impl<'src> Lexer<'src> {
self.skip_whitespace(); self.skip_whitespace();
let (token, span) = if let Some(character) = self.peek_char() { let (token, span) = if let Some(character) = self.peek_char() {
let lexer = LexRule::from(&character).lexer; let lexer = LexRule::from(&character).lex_action;
lexer(self)? lexer(self)?
} else { } else {
@ -613,92 +613,92 @@ impl<'src> Lexer<'src> {
} }
} }
type LexerFn<'src> = fn(&mut Lexer<'src>) -> Result<(Token<'src>, Span), LexError>; type LexAction<'src> = fn(&mut Lexer<'src>) -> Result<(Token<'src>, Span), LexError>;
pub struct LexRule<'src> { pub struct LexRule<'src> {
lexer: LexerFn<'src>, lex_action: LexAction<'src>,
} }
impl From<&char> for LexRule<'_> { impl From<&char> for LexRule<'_> {
fn from(char: &char) -> Self { fn from(char: &char) -> Self {
match char { match char {
'0'..='9' => LexRule { '0'..='9' => LexRule {
lexer: Lexer::lex_numeric, lex_action: Lexer::lex_numeric,
}, },
char if char.is_alphabetic() => LexRule { char if char.is_alphabetic() => LexRule {
lexer: Lexer::lex_keyword_or_identifier, lex_action: Lexer::lex_keyword_or_identifier,
}, },
'"' => LexRule { '"' => LexRule {
lexer: Lexer::lex_string, lex_action: Lexer::lex_string,
}, },
'\'' => LexRule { '\'' => LexRule {
lexer: Lexer::lex_char, lex_action: Lexer::lex_char,
}, },
'+' => LexRule { '+' => LexRule {
lexer: Lexer::lex_plus, lex_action: Lexer::lex_plus,
}, },
'-' => LexRule { '-' => LexRule {
lexer: Lexer::lex_minus, lex_action: Lexer::lex_minus,
}, },
'*' => LexRule { '*' => LexRule {
lexer: Lexer::lex_star, lex_action: Lexer::lex_star,
}, },
'/' => LexRule { '/' => LexRule {
lexer: Lexer::lex_slash, lex_action: Lexer::lex_slash,
}, },
'%' => LexRule { '%' => LexRule {
lexer: Lexer::lex_percent, lex_action: Lexer::lex_percent,
}, },
'!' => LexRule { '!' => LexRule {
lexer: Lexer::lex_exclamation_mark, lex_action: Lexer::lex_exclamation_mark,
}, },
'=' => LexRule { '=' => LexRule {
lexer: Lexer::lex_equal, lex_action: Lexer::lex_equal,
}, },
'<' => LexRule { '<' => LexRule {
lexer: Lexer::lex_less_than, lex_action: Lexer::lex_less_than,
}, },
'>' => LexRule { '>' => LexRule {
lexer: Lexer::lex_greater_than, lex_action: Lexer::lex_greater_than,
}, },
'&' => LexRule { '&' => LexRule {
lexer: Lexer::lex_ampersand, lex_action: Lexer::lex_ampersand,
}, },
'|' => LexRule { '|' => LexRule {
lexer: Lexer::lex_pipe, lex_action: Lexer::lex_pipe,
}, },
'(' => LexRule { '(' => LexRule {
lexer: Lexer::lex_left_parenthesis, lex_action: Lexer::lex_left_parenthesis,
}, },
')' => LexRule { ')' => LexRule {
lexer: Lexer::lex_right_parenthesis, lex_action: Lexer::lex_right_parenthesis,
}, },
'[' => LexRule { '[' => LexRule {
lexer: Lexer::lex_left_bracket, lex_action: Lexer::lex_left_bracket,
}, },
']' => LexRule { ']' => LexRule {
lexer: Lexer::lex_right_bracket, lex_action: Lexer::lex_right_bracket,
}, },
'{' => LexRule { '{' => LexRule {
lexer: Lexer::lex_left_brace, lex_action: Lexer::lex_left_brace,
}, },
'}' => LexRule { '}' => LexRule {
lexer: Lexer::lex_right_brace, lex_action: Lexer::lex_right_brace,
}, },
';' => LexRule { ';' => LexRule {
lexer: Lexer::lex_semicolon, lex_action: Lexer::lex_semicolon,
}, },
':' => LexRule { ':' => LexRule {
lexer: Lexer::lex_colon, lex_action: Lexer::lex_colon,
}, },
',' => LexRule { ',' => LexRule {
lexer: Lexer::lex_comma, lex_action: Lexer::lex_comma,
}, },
'.' => LexRule { '.' => LexRule {
lexer: Lexer::lex_dot, lex_action: Lexer::lex_dot,
}, },
_ => LexRule { _ => LexRule {
lexer: Lexer::lex_unexpected, lex_action: Lexer::lex_unexpected,
}, },
} }
} }

View File

@ -2,7 +2,7 @@ use std::{ops::Range, panic};
use crate::vm::ThreadData; use crate::vm::ThreadData;
pub fn panic(data: &mut ThreadData, _: Option<u8>, argument_range: Range<u8>) -> bool { pub fn panic(data: &mut ThreadData, _: u8, argument_range: Range<u8>) -> bool {
let position = data.current_position(); let position = data.current_position();
let mut message = format!("Dust panic at {position}!"); let mut message = format!("Dust panic at {position}!");

View File

@ -1,17 +1,12 @@
use std::io::{stdin, stdout, Write}; use std::io::{Write, stdin, stdout};
use std::ops::Range; use std::ops::Range;
use crate::{ use crate::{
vm::{get_next_action, Register, ThreadData},
ConcreteValue, Value, ConcreteValue, Value,
vm::{Register, ThreadData, get_next_action},
}; };
pub fn read_line( pub fn read_line(data: &mut ThreadData, destination: u8, _argument_range: Range<u8>) -> bool {
data: &mut ThreadData,
destination: Option<u8>,
_argument_range: Range<u8>,
) -> bool {
let destination = destination.unwrap();
let mut buffer = String::new(); let mut buffer = String::new();
if stdin().read_line(&mut buffer).is_ok() { if stdin().read_line(&mut buffer).is_ok() {
@ -29,7 +24,7 @@ pub fn read_line(
false false
} }
pub fn write(data: &mut ThreadData, _destination: Option<u8>, argument_range: Range<u8>) -> bool { pub fn write(data: &mut ThreadData, _: u8, argument_range: Range<u8>) -> bool {
let mut stdout = stdout(); let mut stdout = stdout();
for register_index in argument_range { for register_index in argument_range {
@ -45,11 +40,7 @@ pub fn write(data: &mut ThreadData, _destination: Option<u8>, argument_range: Ra
false false
} }
pub fn write_line( pub fn write_line(data: &mut ThreadData, _: u8, argument_range: Range<u8>) -> bool {
data: &mut ThreadData,
_destination: Option<u8>,
argument_range: Range<u8>,
) -> bool {
let mut stdout = stdout().lock(); let mut stdout = stdout().lock();
for register_index in argument_range { for register_index in argument_range {

View File

@ -4,7 +4,9 @@
//! itself or that are more efficient to implement in Rust. //! itself or that are more efficient to implement in Rust.
mod assert; mod assert;
mod io; mod io;
mod random;
mod string; mod string;
mod thread;
use std::{ use std::{
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
@ -15,7 +17,7 @@ use std::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{vm::ThreadData, AnnotatedError, FunctionType, Span, Type}; use crate::{AnnotatedError, FunctionType, Span, Type, vm::ThreadData};
macro_rules! define_native_function { macro_rules! define_native_function {
($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => { ($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
@ -33,7 +35,7 @@ macro_rules! define_native_function {
pub fn call( pub fn call(
&self, &self,
data: &mut ThreadData, data: &mut ThreadData,
destination: Option<u8>, destination: u8,
argument_range: Range<u8>, argument_range: Range<u8>,
) -> bool { ) -> bool {
match self { match self {
@ -244,11 +246,42 @@ define_native_function! {
return_type: Type::None return_type: Type::None
}, },
io::write_line io::write_line
) ),
// // Random // // Random
// (Random, 58_u8, "random", true), (
// (RandomInRange, 59_u8, "random_in_range", true) RandomInteger,
58,
"random_int",
FunctionType {
type_parameters: Vec::with_capacity(0),
value_parameters: vec![(0, Type::Integer), (1, Type::Integer)],
return_type: Type::Integer
},
random::random_int
),
// Thread
(
Spawn,
60,
"spawn",
FunctionType {
type_parameters: Vec::with_capacity(0),
value_parameters: vec![
(
0,
Type::Function(Box::new(FunctionType {
type_parameters: Vec::with_capacity(0),
value_parameters: Vec::with_capacity(0),
return_type: Type::Any
}))
)
],
return_type: Type::None
},
thread::spawn
)
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]

View File

@ -0,0 +1,40 @@
use std::ops::Range;
use rand::Rng;
use crate::{
Value,
vm::{Register, ThreadData, get_next_action},
};
pub fn random_int(data: &mut ThreadData, destination: u8, argument_range: Range<u8>) -> bool {
let mut argument_range_iter = argument_range.into_iter();
let (min, max) = {
let mut min = None;
loop {
let register_index = argument_range_iter
.next()
.unwrap_or_else(|| panic!("No argument was passed to \"random_int\""));
let value_option = data.open_register_allow_empty_unchecked(register_index);
if let Some(argument) = value_option {
if let Some(integer) = argument.as_integer() {
if min.is_none() {
min = Some(integer);
} else {
break (min, integer);
}
}
}
}
};
let random_integer = rand::thread_rng().gen_range(min.unwrap()..max);
data.set_register(destination, Register::Value(Value::integer(random_integer)));
data.next_action = get_next_action(data);
false
}

View File

@ -1,18 +1,13 @@
use std::ops::Range; use std::ops::Range;
use crate::{ use crate::{
vm::{get_next_action, Register, ThreadData},
ConcreteValue, Value, ConcreteValue, Value,
vm::{Register, ThreadData, get_next_action},
}; };
pub fn to_string( pub fn to_string(data: &mut ThreadData, destination: u8, argument_range: Range<u8>) -> bool {
data: &mut ThreadData,
destination: Option<u8>,
argument_range: Range<u8>,
) -> bool {
let argument_value = data.open_register_unchecked(argument_range.start); let argument_value = data.open_register_unchecked(argument_range.start);
let argument_string = argument_value.display(data); let argument_string = argument_value.display(data);
let destination = destination.unwrap();
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string))); let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
data.set_register(destination, register); data.set_register(destination, register);

View File

@ -0,0 +1,64 @@
use std::{
ops::Range,
thread::{Builder, JoinHandle},
};
use tracing::{Level, info, span};
use crate::{
DustString,
vm::{Thread, ThreadData, get_next_action},
};
fn start_thread(data: &mut ThreadData, argument_range: Range<u8>) -> JoinHandle<()> {
let mut argument_range_iter = argument_range.into_iter();
let function_argument = {
loop {
let register_index = argument_range_iter
.next()
.unwrap_or_else(|| panic!("No argument was passed to \"spawn\""));
let value_option = data.open_register_allow_empty_unchecked(register_index);
if let Some(argument) = value_option {
break argument;
}
}
};
let function = function_argument.as_function().unwrap();
let prototype_index = function.prototype_index as usize;
let current_call = data.call_stack.last_unchecked();
let prototype = current_call.chunk.prototypes[prototype_index].clone();
info!(
"Spawning thread for \"{}\"",
function
.name
.as_ref()
.cloned()
.unwrap_or_else(|| DustString::from("anonymous"))
);
let thread_name = prototype
.name
.as_ref()
.map(|name| name.to_string())
.unwrap_or_else(|| "anonymous".to_string());
let mut thread = Thread::new(prototype);
Builder::new()
.name(thread_name)
.spawn(move || {
let span = span!(Level::INFO, "Spawned thread");
let _enter = span.enter();
thread.run();
})
.expect("Critical VM Error: Failed to spawn thread")
}
pub fn spawn(data: &mut ThreadData, _: u8, argument_range: Range<u8>) -> bool {
let _ = start_thread(data, argument_range);
data.next_action = get_next_action(data);
false
}

View File

@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
use std::fmt::{self, Debug, Display, Formatter}; use std::fmt::{self, Debug, Display, Formatter};
use crate::{vm::ThreadData, Type}; use crate::{Type, vm::ThreadData};
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum Value { pub enum Value {
@ -50,9 +50,9 @@ impl Value {
Value::Concrete(ConcreteValue::String(string.into())) Value::Concrete(ConcreteValue::String(string.into()))
} }
pub fn as_boolean(&self) -> Option<&bool> { pub fn as_boolean(&self) -> Option<bool> {
if let Value::Concrete(ConcreteValue::Boolean(value)) = self { if let Value::Concrete(ConcreteValue::Boolean(boolean)) = self {
Some(value) Some(*boolean)
} else { } else {
None None
} }
@ -66,6 +66,14 @@ impl Value {
} }
} }
pub fn as_integer(&self) -> Option<i64> {
if let Value::Concrete(ConcreteValue::Integer(integer)) = self {
Some(*integer)
} else {
None
}
}
pub fn as_string(&self) -> Option<&DustString> { pub fn as_string(&self) -> Option<&DustString> {
if let Value::Concrete(ConcreteValue::String(value)) = self { if let Value::Concrete(ConcreteValue::String(value)) = self {
Some(value) Some(value)
@ -74,6 +82,14 @@ impl Value {
} }
} }
pub fn is_string(&self) -> bool {
matches!(self, Value::Concrete(ConcreteValue::String(_)))
}
pub fn is_function(&self) -> bool {
matches!(self, Value::Function(_))
}
pub fn r#type(&self) -> Type { pub fn r#type(&self) -> Type {
match self { match self {
Value::Concrete(concrete_value) => concrete_value.r#type(), Value::Concrete(concrete_value) => concrete_value.r#type(),

View File

@ -1,29 +1,34 @@
use std::fmt::{self, Debug, Display, Formatter}; use std::{
fmt::{self, Debug, Display, Formatter},
sync::Arc,
};
use crate::{Chunk, DustString}; use crate::{Chunk, DustString};
use super::Register; use super::Register;
#[derive(Debug)] #[derive(Debug)]
pub struct FunctionCall<'a> { pub struct FunctionCall {
pub chunk: &'a Chunk, pub chunk: Arc<Chunk>,
pub ip: usize, pub ip: usize,
pub return_register: u8, pub return_register: u8,
pub registers: Vec<Register>, pub registers: Vec<Register>,
} }
impl<'a> FunctionCall<'a> { impl FunctionCall {
pub fn new(chunk: &'a Chunk, return_register: u8) -> Self { pub fn new(chunk: Arc<Chunk>, return_register: u8) -> Self {
let register_count = chunk.register_count;
Self { Self {
chunk, chunk,
ip: 0, ip: 0,
return_register, return_register,
registers: vec![Register::Empty; chunk.register_count], registers: vec![Register::Empty; register_count],
} }
} }
} }
impl Display for FunctionCall<'_> { impl Display for FunctionCall {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!( write!(
f, f,

View File

@ -6,19 +6,20 @@ mod thread;
use std::{ use std::{
fmt::{self, Debug, Display, Formatter}, fmt::{self, Debug, Display, Formatter},
sync::mpsc, sync::Arc,
thread::spawn, thread::Builder,
}; };
pub use function_call::FunctionCall; pub use function_call::FunctionCall;
pub(crate) use run_action::get_next_action;
pub use run_action::RunAction; pub use run_action::RunAction;
pub(crate) use run_action::get_next_action;
pub use stack::Stack; pub use stack::Stack;
pub use thread::{Thread, ThreadData}; pub use thread::{Thread, ThreadData};
use tracing::{span, Level}; use crossbeam_channel::bounded;
use tracing::{Level, span};
use crate::{compile, Chunk, DustError, Value}; use crate::{Chunk, DustError, Value, compile};
pub fn run(source: &str) -> Result<Option<Value>, DustError> { pub fn run(source: &str) -> Result<Option<Value>, DustError> {
let chunk = compile(source)?; let chunk = compile(source)?;
@ -28,41 +29,31 @@ pub fn run(source: &str) -> Result<Option<Value>, DustError> {
} }
pub struct Vm { pub struct Vm {
threads: Vec<Thread>, main_chunk: Chunk,
} }
impl Vm { impl Vm {
pub fn new(chunk: Chunk) -> Self { pub fn new(main_chunk: Chunk) -> Self {
let threads = vec![Thread::new(chunk)]; Self { main_chunk }
debug_assert_eq!(1, threads.capacity());
Self { threads }
} }
pub fn run(mut self) -> Option<Value> { pub fn run(self) -> Option<Value> {
let span = span!(Level::INFO, "Run"); let span = span!(Level::INFO, "Run");
let _enter = span.enter(); let _enter = span.enter();
let thread_name = self
.main_chunk
.name
.as_ref()
.map(|name| name.to_string())
.unwrap_or_else(|| "anonymous".to_string());
let mut main_thread = Thread::new(Arc::new(self.main_chunk));
let (tx, rx) = bounded(1);
let _ = Builder::new().name(thread_name).spawn(move || {
let value_option = main_thread.run();
let _ = tx.send(value_option);
});
if self.threads.len() == 1 { rx.recv().unwrap()
return self.threads[0].run();
}
let (tx, rx) = mpsc::channel();
for mut thread in self.threads {
let tx = tx.clone();
spawn(move || {
let return_value = thread.run();
if let Some(value) = return_value {
tx.send(value).unwrap();
}
});
}
rx.into_iter().last()
} }
} }

View File

@ -1,16 +1,16 @@
use tracing::trace; use tracing::trace;
use crate::{ use crate::{
AbstractList, Argument, ConcreteValue, Instruction, Type, Value,
instruction::{ instruction::{
Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadBoolean, Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadBoolean,
LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point, LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
Return, SetLocal, Subtract, Test, TestSet, Return, SetLocal, Subtract, Test, TestSet,
}, },
vm::FunctionCall, vm::FunctionCall,
AbstractList, Argument, ConcreteValue, Instruction, Type, Value,
}; };
use super::{thread::ThreadData, Pointer, Register}; use super::{Pointer, Register, thread::ThreadData};
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct RunAction { pub struct RunAction {
@ -525,14 +525,14 @@ pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
let current_call = data.call_stack.last_unchecked(); let current_call = data.call_stack.last_unchecked();
let first_argument_register = return_register - argument_count; let first_argument_register = return_register - argument_count;
let prototype = if is_recursive { let prototype = if is_recursive {
current_call.chunk current_call.chunk.clone()
} else { } else {
let function = data let function = data
.open_register_unchecked(function_register) .open_register_unchecked(function_register)
.as_function() .as_function()
.unwrap(); .unwrap();
&current_call.chunk.prototypes[function.prototype_index as usize] current_call.chunk.prototypes[function.prototype_index as usize].clone()
}; };
let mut next_call = FunctionCall::new(prototype, return_register); let mut next_call = FunctionCall::new(prototype, return_register);
let mut argument_index = 0; let mut argument_index = 0;
@ -565,7 +565,7 @@ pub fn call_native(instruction: Instruction, data: &mut ThreadData) -> bool {
let first_argument_index = destination - argument_count; let first_argument_index = destination - argument_count;
let argument_range = first_argument_index..destination; let argument_range = first_argument_index..destination;
function.call(data, Some(destination), argument_range) function.call(data, destination, argument_range)
} }
pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool {

View File

@ -124,7 +124,7 @@ impl<T: Debug> Debug for Stack<T> {
} }
} }
impl Display for Stack<FunctionCall<'_>> { impl Display for Stack<FunctionCall> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
writeln!(f, "----- DUST CALL STACK -----")?; writeln!(f, "----- DUST CALL STACK -----")?;

View File

@ -1,17 +1,17 @@
use std::mem::replace; use std::{mem::replace, sync::Arc, thread::JoinHandle};
use tracing::{info, trace}; use tracing::{info, trace};
use crate::{vm::FunctionCall, Argument, Chunk, DustString, Span, Value}; use crate::{Argument, Chunk, DustString, Span, Value, vm::FunctionCall};
use super::{Pointer, Register, RunAction, Stack}; use super::{Pointer, Register, RunAction, Stack};
pub struct Thread { pub struct Thread {
chunk: Chunk, chunk: Arc<Chunk>,
} }
impl Thread { impl Thread {
pub fn new(chunk: Chunk) -> Self { pub fn new(chunk: Arc<Chunk>) -> Self {
Thread { chunk } Thread { chunk }
} }
@ -25,7 +25,7 @@ impl Thread {
); );
let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1); let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1);
let main_call = FunctionCall::new(&self.chunk, 0); let main_call = FunctionCall::new(self.chunk.clone(), 0);
call_stack.push(main_call); call_stack.push(main_call);
@ -34,6 +34,7 @@ impl Thread {
call_stack, call_stack,
next_action: first_action, next_action: first_action,
return_value_index: None, return_value_index: None,
spawned_threads: Vec::new(),
}; };
loop { loop {
@ -45,7 +46,7 @@ impl Thread {
); );
if should_end { if should_end {
let return_value = if let Some(register_index) = thread_data.return_value_index { let value_option = if let Some(register_index) = thread_data.return_value_index {
let value = let value =
thread_data.empty_register_or_clone_constant_unchecked(register_index); thread_data.empty_register_or_clone_constant_unchecked(register_index);
@ -54,20 +55,28 @@ impl Thread {
None None
}; };
return return_value; thread_data
.spawned_threads
.into_iter()
.for_each(|join_handle| {
let _ = join_handle.join();
});
return value_option;
} }
} }
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ThreadData<'a> { pub struct ThreadData {
pub call_stack: Stack<FunctionCall<'a>>, pub call_stack: Stack<FunctionCall>,
pub next_action: RunAction, pub next_action: RunAction,
pub return_value_index: Option<u8>, pub return_value_index: Option<u8>,
pub spawned_threads: Vec<JoinHandle<()>>,
} }
impl ThreadData<'_> { impl ThreadData {
pub fn current_position(&self) -> Span { pub fn current_position(&self) -> Span {
let current_call = self.call_stack.last_unchecked(); let current_call = self.call_stack.last_unchecked();
@ -75,7 +84,7 @@ impl ThreadData<'_> {
} }
pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value { pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value {
trace!("Follow pointer {pointer}"); trace!("Follow {pointer}");
match pointer { match pointer {
Pointer::Register(register_index) => self.open_register_unchecked(register_index), Pointer::Register(register_index) => self.open_register_unchecked(register_index),
@ -97,7 +106,7 @@ impl ThreadData<'_> {
} }
pub fn get_register_unchecked(&self, register_index: u8) -> &Register { pub fn get_register_unchecked(&self, register_index: u8) -> &Register {
trace!("Get register R{register_index}"); trace!("Get R{register_index}");
let register_index = register_index as usize; let register_index = register_index as usize;
@ -133,37 +142,25 @@ impl ThreadData<'_> {
} }
}; };
trace!("Open R{register_index} to {register}");
match register { match register {
Register::Value(value) => { Register::Value(value) => value,
trace!("Register R{register_index} opened to value {value}"); Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer),
value
}
Register::Pointer(pointer) => {
trace!("Open register R{register_index} opened to pointer {pointer}");
self.follow_pointer_unchecked(*pointer)
}
Register::Empty => panic!("VM Error: Register {register_index} is empty"), Register::Empty => panic!("VM Error: Register {register_index} is empty"),
} }
} }
pub fn open_register_allow_empty_unchecked(&self, register_index: u8) -> Option<&Value> { pub fn open_register_allow_empty_unchecked(&self, register_index: u8) -> Option<&Value> {
trace!("Open register R{register_index}"); trace!("Open R{register_index}");
let register = self.get_register_unchecked(register_index); let register = self.get_register_unchecked(register_index);
trace!("Open R{register_index} to {register}");
match register { match register {
Register::Value(value) => { Register::Value(value) => Some(value),
trace!("Register R{register_index} openned to value {value}"); Register::Pointer(pointer) => Some(self.follow_pointer_unchecked(*pointer)),
Some(value)
}
Register::Pointer(pointer) => {
trace!("Open register R{register_index} openned to pointer {pointer}");
Some(self.follow_pointer_unchecked(*pointer))
}
Register::Empty => None, Register::Empty => None,
} }
} }
@ -260,7 +257,7 @@ impl ThreadData<'_> {
pub fn get_local_register(&self, local_index: u8) -> u8 { pub fn get_local_register(&self, local_index: u8) -> u8 {
let local_index = local_index as usize; let local_index = local_index as usize;
let chunk = self.call_stack.last_unchecked().chunk; let chunk = &self.call_stack.last_unchecked().chunk;
assert!( assert!(
local_index < chunk.locals.len(), local_index < chunk.locals.len(),