From ae35a3df33f4a04c9db96306e1e32df05920fff9 Mon Sep 17 00:00:00 2001 From: pufferfish101007 <50246616+pufferfish101007@users.noreply.github.com> Date: Tue, 11 Aug 2026 19:49:17 +0100 Subject: [PATCH 1/9] work towards different thread tables for each target --- src/instructions/control/stop_all.rs | 67 +++++++--- .../event/poll_waiting_threads.rs | 7 +- src/instructions/hq/yield.rs | 24 ++-- src/wasm.rs | 2 +- src/wasm/func.rs | 10 ++ src/wasm/mem_layout.rs | 4 +- src/wasm/project.rs | 12 +- src/wasm/registries.rs | 2 +- src/wasm/registries/globals.rs | 35 ++++++ src/wasm/registries/tables.rs | 116 +++++++++++------- 10 files changed, 185 insertions(+), 94 deletions(-) diff --git a/src/instructions/control/stop_all.rs b/src/instructions/control/stop_all.rs index 3b2ca327..6dc6540a 100644 --- a/src/instructions/control/stop_all.rs +++ b/src/instructions/control/stop_all.rs @@ -1,31 +1,62 @@ -use wasm_encoder::{ConstExpr, HeapType}; +use wasm_encoder::HeapType; use super::super::prelude::*; use crate::instructions_test; -use crate::wasm::{GlobalExportable, GlobalMutable, ThreadsTable}; +use crate::wasm::StepTarget; -pub fn wasm(func: &StepFunc, _inputs: Rc<[IrType]>) -> HQResult> { - let threads_count = func.registries().globals().register( - "threads_count".into(), - ( - ValType::I32, - ConstExpr::i32_const(0), - GlobalMutable(true), - GlobalExportable(true), - ), - )?; - - let threads_table = func.registries().tables().register::()?; - let thread_struct_type = func.registries().types().thread_struct_type()?; - - Ok(wasm![ +fn clear_thread( + threads_count: u32, + threads_table: u32, + thread_struct_type: u32, +) -> Vec { + wasm![ I32Const(0), #LazyGlobalSet(threads_count), I32Const(0), RefNull(HeapType::Concrete(thread_struct_type)), TableSize(threads_table), TableFill(threads_table), - ]) + ] +} + +pub fn wasm(func: &StepFunc, _inputs: Rc<[IrType]>) -> HQResult> { + let thread_struct_type = func.registries().types().thread_struct_type()?; + let total_threads_count = func.registries().globals().threads_count()?; + let num_sprites = func.costume_names().len() as u32; + + Ok(wasm![ + I32Const(0), + #LazyGlobalSet(total_threads_count), + ] + .into_iter() + .chain(clear_thread( + func.registries() + .globals() + .target_threads_count(StepTarget::Stage)?, + func.registries() + .tables() + .threads_table(StepTarget::Stage, func.registries().types())?, + thread_struct_type, + )) + .chain( + (0..num_sprites) + .map(|n| { + let step_target = StepTarget::Sprite(n); + Ok(clear_thread( + func.registries() + .globals() + .target_threads_count(step_target)?, + func.registries() + .tables() + .threads_table(step_target, func.registries().types())?, + thread_struct_type, + )) + }) + .collect::>>()? + .into_iter() + .flatten(), + ) + .collect()) } pub fn acceptable_inputs() -> HQResult> { diff --git a/src/instructions/event/poll_waiting_threads.rs b/src/instructions/event/poll_waiting_threads.rs index 3d8c4b6d..da78001b 100644 --- a/src/instructions/event/poll_waiting_threads.rs +++ b/src/instructions/event/poll_waiting_threads.rs @@ -7,7 +7,7 @@ use wasm_encoder::{BlockType as WasmBlockType, FieldType, HeapType, StorageType}; use super::super::prelude::*; -use crate::wasm::{StepFunc, ThreadsTable}; +use crate::wasm::StepFunc; pub fn wasm(func: &StepFunc, _inputs: Rc<[IrType]>) -> HQResult> { let i32_array_type = func @@ -35,7 +35,10 @@ pub fn wasm(func: &StepFunc, _inputs: Rc<[IrType]>) -> HQResult()?; + let threads_table = func + .registries() + .tables() + .threads_table(func.target(), func.registries().types())?; Ok(wasm![ LocalGet(1), // this should never have additional function arguments so this is fine diff --git a/src/instructions/hq/yield.rs b/src/instructions/hq/yield.rs index 6f04d8de..9c9e0e64 100644 --- a/src/instructions/hq/yield.rs +++ b/src/instructions/hq/yield.rs @@ -1,9 +1,9 @@ -use wasm_encoder::{BlockType, ConstExpr, HeapType}; +use wasm_encoder::{BlockType, HeapType}; use super::super::prelude::*; use crate::instructions_test; use crate::ir::{Step, StepIndex}; -use crate::wasm::{GlobalExportable, GlobalMutable, StepFunc, ThreadsTable}; +use crate::wasm::StepFunc; #[derive(Debug, Clone)] pub enum YieldMode { @@ -55,19 +55,14 @@ pub fn wasm( _inputs: Rc<[IrType]>, Fields { mode: yield_mode }: &Fields, ) -> HQResult> { - let threads_count = func.registries().globals().register( - "threads_count".into(), - ( - ValType::I32, - ConstExpr::i32_const(0), - GlobalMutable(true), - GlobalExportable(true), - ), - )?; + let threads_count = func.registries().globals().threads_count()?; Ok(match yield_mode { YieldMode::None => { - let threads_table = func.registries().tables().register::()?; + let threads_table = func + .registries() + .tables() + .threads_table(func.target(), func.registries().types())?; let thread_struct_ty = func.registries().types().thread_struct_type()?; let stack_array_ty = func.registries().types().stack_array_type()?; let stack_struct_ty = func.registries().types().stack_struct_type()?; @@ -146,7 +141,10 @@ pub fn wasm( func.compile_inner_step(Rc::clone(step))? } YieldMode::Schedule(step_index) => { - let threads_table = func.registries().tables().register::()?; + let threads_table = func + .registries() + .tables() + .threads_table(func.target(), func.registries().types())?; let thread_struct_ty = func.registries().types().thread_struct_type()?; let local = func.local(ValType::Ref(RefType { nullable: false, diff --git a/src/wasm.rs b/src/wasm.rs index 0300dd28..2597e1b8 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -11,5 +11,5 @@ pub use flags::WasmFlags; pub use func::{Instruction as InternalInstruction, StepFunc, StepTarget}; pub use project::{FinishedWasm, WasmProject}; pub use registries::{ - GlobalExportable, GlobalMutable, Registries, StepsTable, StringsTable, ThreadsTable, + GlobalExportable, GlobalMutable, Registries, StringsTable, }; diff --git a/src/wasm/func.rs b/src/wasm/func.rs index 44cfc4f2..b3675747 100644 --- a/src/wasm/func.rs +++ b/src/wasm/func.rs @@ -213,6 +213,16 @@ pub enum StepTarget { Sprite(u32), } +impl StepTarget { + #[must_use] + pub fn suffix_id(&self) -> Cow<'_, str> { + match self { + Self::Stage => "_stage".into(), + Self::Sprite(id) => format!("_{id}").into(), + } + } +} + /// representation of a step's function #[derive(Clone)] pub struct StepFunc { diff --git a/src/wasm/mem_layout.rs b/src/wasm/mem_layout.rs index df8b098f..a25873f2 100644 --- a/src/wasm/mem_layout.rs +++ b/src/wasm/mem_layout.rs @@ -100,8 +100,8 @@ memory_layout! { PEN_DOWN: i8 /// non-zero if sprite is visible, 0 otherwise (i8) VISIBLE: i8 - /// bytes 58-59 padding - _PADDING: i16 + /// sprite layer - 0 is bottom (not including stage! as that is always lowest) + LAYER: i16 /// current costume number, 0-indexed (i32) COSTUME: i32 /// sprite size, where default is 100(%) (f64) diff --git a/src/wasm/project.rs b/src/wasm/project.rs index b3ecb3bf..396894af 100644 --- a/src/wasm/project.rs +++ b/src/wasm/project.rs @@ -8,7 +8,7 @@ use wasm_encoder::{ }; use wasm_gen::wasm; -use super::{ExternalEnvironment, GlobalExportable, GlobalMutable, Registries}; +use super::{ExternalEnvironment, Registries}; use crate::ir::{Event, IrProject, IrType, StepIndex}; use crate::prelude::*; use crate::wasm::registries::functions::static_functions::{ @@ -407,15 +407,7 @@ impl WasmProject { N: TryFrom, >::Error: fmt::Debug, { - self.registries().globals().register( - "threads_count".into(), - ( - ValType::I32, - ConstExpr::i32_const(0), - GlobalMutable(true), - GlobalExportable(true), - ), - ) + self.registries().globals().threads_count() } #[expect(clippy::needless_pass_by_value, reason = "annoying to borrow a box")] diff --git a/src/wasm/registries.rs b/src/wasm/registries.rs index b12a1ff5..13e5f801 100644 --- a/src/wasm/registries.rs +++ b/src/wasm/registries.rs @@ -11,7 +11,7 @@ pub use functions::{ExternalFunctionRegistry, StaticFunctionRegistry}; pub use globals::{GlobalExportable, GlobalMutable, GlobalRegistry}; pub use lists::ListRegistry; pub use strings::{StringRegistry, TabledStringRegistry}; -pub use tables::{StepsTable, StringsTable, TableRegistry, ThreadsTable}; +pub use tables::{StringsTable, TableRegistry}; pub use targets::SpriteRegistry; pub use types::TypeRegistry; pub use variables::VariableRegistry; diff --git a/src/wasm/registries/globals.rs b/src/wasm/registries/globals.rs index 77111ae2..aac749c0 100644 --- a/src/wasm/registries/globals.rs +++ b/src/wasm/registries/globals.rs @@ -1,9 +1,11 @@ use core::ops::Deref; +use core::fmt::Display; use wasm_encoder::{ConstExpr, ExportKind, ExportSection, GlobalSection, GlobalType, ValType}; use crate::prelude::*; use crate::registry::MapRegistry; +use crate::wasm::StepTarget; #[derive(Copy, Clone, Debug)] pub struct GlobalMutable(pub bool); @@ -29,6 +31,39 @@ pub type GlobalRegistry = MapRegistry, (ValType, ConstExpr, GlobalMutable, GlobalExportable)>; impl GlobalRegistry { + fn threads_count_with_id(&self, id: S) -> HQResult + where + N: TryFrom, + >::Error: fmt::Debug, + S: Display, + { + self.register( + format!("threads_count{id}").into(), + ( + ValType::I32, + ConstExpr::i32_const(0), + GlobalMutable(true), + GlobalExportable(true), + ), + ) + } + + pub fn threads_count(&self) -> HQResult + where + N: TryFrom, + >::Error: fmt::Debug, + { + self.threads_count_with_id("") + } + + pub fn target_threads_count(&self, target: StepTarget) -> HQResult + where + N: TryFrom, + >::Error: fmt::Debug, + { + self.threads_count_with_id(target.suffix_id()) + } + pub fn finish( self, globals: &mut GlobalSection, diff --git a/src/wasm/registries/tables.rs b/src/wasm/registries/tables.rs index d9158277..fc729975 100644 --- a/src/wasm/registries/tables.rs +++ b/src/wasm/registries/tables.rs @@ -3,6 +3,8 @@ use wasm_encoder::{ }; use crate::prelude::*; +use crate::wasm::StepTarget; +use crate::wasm::registries::TypeRegistry; #[derive(Clone, Debug)] pub struct TableOptions { @@ -22,6 +24,26 @@ impl RegistryType for TableRegistrar { pub type TableRegistry = NamedRegistry; impl TableRegistry { + pub fn threads_table(&self, target: StepTarget, types: &Rc) -> HQResult + where + N: TryFrom, + >::Error: fmt::Debug, + { + self.register_dyn( + format!("threads{}", target.suffix_id()).into(), + TableOptions { + element_type: RefType { + nullable: true, + heap_type: HeapType::Concrete(types.thread_struct_type()?), + }, + min: 0, + max: None, + init: None, + export_name: Some("threads"), + }, + ) + } + pub fn finish(self, tables: &mut TableSection, exports: &mut ExportSection) { for ( _key, @@ -73,51 +95,51 @@ impl NamedRegistryItem for StringsTable { }; } -pub struct StepsTable; -impl NamedRegistryItem for StepsTable { - const VALUE: TableOptions = TableOptions { - element_type: RefType::FUNCREF, - min: 0, - max: None, - init: None, - export_name: None, - }; -} -impl NamedRegistryItemOverride for StepsTable { - fn r#override(step_count: u64) -> TableOptions { - TableOptions { - element_type: RefType::FUNCREF, - min: step_count, - max: Some(step_count), - init: None, - export_name: None, - } - } -} +// pub struct StepsTable; +// impl NamedRegistryItem for StepsTable { +// const VALUE: TableOptions = TableOptions { +// element_type: RefType::FUNCREF, +// min: 0, +// max: None, +// init: None, +// export_name: None, +// }; +// } +// impl NamedRegistryItemOverride for StepsTable { +// fn r#override(step_count: u64) -> TableOptions { +// TableOptions { +// element_type: RefType::FUNCREF, +// min: step_count, +// max: Some(step_count), +// init: None, +// export_name: None, +// } +// } +// } -pub struct ThreadsTable; -impl NamedRegistryItem for ThreadsTable { - const VALUE: TableOptions = TableOptions { - element_type: RefType::ARRAYREF, - min: 0, - max: None, - init: None, - export_name: Some("threads"), - }; -} -impl NamedRegistryItemOverride for ThreadsTable { - fn r#override(stack_struct_ty: u32) -> TableOptions { - // todo: if we don't need any stacks (i.e. no non-warped procedure, no broadcast & wait), - // revert to old behaviour and just store funcrefs (noop for null). - TableOptions { - element_type: RefType { - nullable: true, - heap_type: HeapType::Concrete(stack_struct_ty), - }, - min: 0, - max: None, - init: None, - export_name: Some("threads"), - } - } -} +// pub struct ThreadsTable; +// impl NamedRegistryItem for ThreadsTable { +// const VALUE: TableOptions = TableOptions { +// element_type: RefType::ARRAYREF, +// min: 0, +// max: None, +// init: None, +// export_name: Some("threads"), +// }; +// } +// impl NamedRegistryItemOverride for ThreadsTable { +// fn r#override(stack_struct_ty: u32) -> TableOptions { +// // todo: if we don't need any stacks (i.e. no non-warped procedure, no broadcast & wait), +// // revert to old behaviour and just store funcrefs (noop for null). +// TableOptions { +// element_type: RefType { +// nullable: true, +// heap_type: HeapType::Concrete(stack_struct_ty), +// }, +// min: 0, +// max: None, +// init: None, +// export_name: Some("threads"), +// } +// } +// } From 18dfea49d8601faee6a2b2a8e19158ee4a7507f3 Mon Sep 17 00:00:00 2001 From: pufferfish101007 <50246616+pufferfish101007@users.noreply.github.com> Date: Fri, 14 Aug 2026 20:44:06 +0100 Subject: [PATCH 2/9] progress? --- src/wasm/project.rs | 18 ++--- .../registries/functions/spawn_threads.rs | 76 +++++++++++-------- src/wasm/registries/globals.rs | 57 ++++++++++---- src/wasm/registries/types.rs | 76 +++++++++++++++---- 4 files changed, 153 insertions(+), 74 deletions(-) diff --git a/src/wasm/project.rs b/src/wasm/project.rs index 396894af..e84f518d 100644 --- a/src/wasm/project.rs +++ b/src/wasm/project.rs @@ -14,7 +14,7 @@ use crate::prelude::*; use crate::wasm::registries::functions::static_functions::{ MarkWaitingFlag, SpawnNewThread, SpawnThreadInStack, }; -use crate::wasm::{StepFunc, StringsTable, ThreadsTable, WasmFlags}; +use crate::wasm::{StepFunc, StepTarget, StringsTable, WasmFlags}; /// A respresentation of a WASM representation of a project. Cannot be created directly; /// use `TryFrom`. @@ -139,7 +139,7 @@ impl WasmProject { self.registries() .static_functions() .register_override::(( - self.registries().types().step_func_type()?, + self.registries().types().step_func()?, self.registries().types().stack_struct_type()?, self.registries().types().stack_array_type()?, self.registries().types().thread_struct_type()?, @@ -149,7 +149,7 @@ impl WasmProject { self.registries() .static_functions() .register_override::(( - self.registries().types().step_func_type()?, + self.registries().types().step_func()?, self.registries().types().stack_struct_type()?, self.registries().types().stack_array_type()?, self.registries().types().thread_struct_type()?, @@ -202,12 +202,6 @@ impl WasmProject { function_index: self.imported_func_count()? + functions.len() - 1, }; - self.registries() - .tables() - .register_override::( - self.registries().types().thread_struct_type()?, - )?; - elements.declared(Elements::Functions( (self.imported_func_count()? + self.static_func_count()? ..self.imported_func_count()? @@ -374,12 +368,12 @@ impl WasmProject { Ok(()) } - fn threads_table_index(&self) -> HQResult + fn threads_table_index(&self, target: StepTarget) -> HQResult where N: TryFrom, >::Error: fmt::Debug, { - self.registries().tables().register::() + self.registries().tables().threads_table(target, self.registries().types()) } fn spawn_new_thread_func(&self) -> HQResult @@ -620,7 +614,7 @@ impl WasmProject { ), ]); - let step_func_ty = self.registries().types().step_func_type()?; + let step_func_ty = self.registries().types().step_func()?; let stack_array_ty = self.registries().types().stack_array_type()?; let instructions = wasm![ diff --git a/src/wasm/registries/functions/spawn_threads.rs b/src/wasm/registries/functions/spawn_threads.rs index b10185c2..f8fa5a27 100644 --- a/src/wasm/registries/functions/spawn_threads.rs +++ b/src/wasm/registries/functions/spawn_threads.rs @@ -127,16 +127,17 @@ impl NamedRegistryItemOverride /// Spawn a new thread with the provided step function. This does not call it /// immediately, instead leaving that for the scheduler or calling function to do so. /// -/// Takes 2 parameters: -/// - step funcref - the step to spawn +/// Takes 3 parameters: +/// - step funcref - the step to spawn /// - ref null struct - the stack struct to spawn it with +/// - i32 - the index of the sprite to spawn a thread for, or -1 for the stage /// /// Override with: /// - u32 - the index of the step func type /// - u32 - the index of the stack struct type /// - u32 - the index of the stack array type /// - u32 - the index of the thread struct type -/// - u32 - the index of the threads table +/// - u32 - the index of the threads array pub struct SpawnNewThread; impl NamedRegistryItem for SpawnNewThread { const VALUE: MaybeStaticFunction = MaybeStaticFunction { @@ -144,10 +145,25 @@ impl NamedRegistryItem for SpawnNewThread { maybe_populate: || None, }; } -pub type SpawnNewThreadOverride = (u32, u32, u32, u32, u32); +pub struct SpawnNewThreadOverride { + func_ty: u32, + stack_struct_ty: u32, + stack_array_ty: u32, + thread_struct_ty: u32, + threads_array_index: u32, + threads_array_ty: u32, +} + impl NamedRegistryItemOverride for SpawnNewThread { fn r#override( - (func_ty, stack_struct_ty, stack_array_ty, thread_struct_ty, threads_table_index): SpawnNewThreadOverride, + SpawnNewThreadOverride { + func_ty, + stack_struct_ty, + stack_array_ty, + thread_struct_ty, + threads_array_index, + threads_array_ty, + }: SpawnNewThreadOverride, ) -> MaybeStaticFunction { MaybeStaticFunction { static_function: Some(StaticFunction { @@ -166,31 +182,31 @@ impl NamedRegistryItemOverride for }), ]), returns: Box::from([]), - locals: Box::from([]), - instructions: (wasm_const![ - I32Const(1), - LocalGet(0), - LocalGet(1), - StructNew(stack_struct_ty), - // todo: play around with initial size of stack array - RefNull(HeapType::Concrete(stack_struct_ty)), - RefNull(HeapType::Concrete(stack_struct_ty)), - RefNull(HeapType::Concrete(stack_struct_ty)), - RefNull(HeapType::Concrete(stack_struct_ty)), - RefNull(HeapType::Concrete(stack_struct_ty)), - RefNull(HeapType::Concrete(stack_struct_ty)), - RefNull(HeapType::Concrete(stack_struct_ty)), - ArrayNewFixed { - array_size: 8, - array_type_index: stack_array_ty, - }, - StructNew(thread_struct_ty), - I32Const(1), - TableGrow(threads_table_index), - Drop, - End, - ] as &[_]) - .into(), + locals: Box::from([ValType::Ref(RefType { + nullable: false, + heap_type: HeapType::Concrete(stack_array_ty), + })]), + instructions: { + const STACK_ARRAY_LOCAL: u32 = 3; + (wasm_const![ + // TODO get + I32Const(1), // stack size + // todo: play around with initial size of stack array + I32Const(8), + ArrayNewDefault(stack_array_ty), + LocalTee(STACK_ARRAY_LOCAL), + StructNew(thread_struct_ty), + I32Const(0), // index 0 into stack array + I32Const(1), // stack size + LocalGet(0), // step func + LocalGet(1), // stack struct param + StructNew(stack_struct_ty), // stack struct + ArraySet(stack_array_ty), // set 0th element of stack array to stack struct + ArraySet(threads_array_ty), + End, + ] as &[_]) + .into() + }, }), maybe_populate: || None, } diff --git a/src/wasm/registries/globals.rs b/src/wasm/registries/globals.rs index aac749c0..ef117a9e 100644 --- a/src/wasm/registries/globals.rs +++ b/src/wasm/registries/globals.rs @@ -1,11 +1,13 @@ use core::ops::Deref; -use core::fmt::Display; -use wasm_encoder::{ConstExpr, ExportKind, ExportSection, GlobalSection, GlobalType, ValType}; +use wasm_encoder::{ + ConstExpr, ExportKind, ExportSection, GlobalSection, GlobalType, Instruction, + RefType, ValType, +}; use crate::prelude::*; use crate::registry::MapRegistry; -use crate::wasm::StepTarget; +use crate::wasm::registries::TypeRegistry; #[derive(Copy, Clone, Debug)] pub struct GlobalMutable(pub bool); @@ -31,14 +33,13 @@ pub type GlobalRegistry = MapRegistry, (ValType, ConstExpr, GlobalMutable, GlobalExportable)>; impl GlobalRegistry { - fn threads_count_with_id(&self, id: S) -> HQResult + pub fn threads_count(&self) -> HQResult where N: TryFrom, >::Error: fmt::Debug, - S: Display, { self.register( - format!("threads_count{id}").into(), + "threads_count".into(), ( ValType::I32, ConstExpr::i32_const(0), @@ -48,20 +49,44 @@ impl GlobalRegistry { ) } - pub fn threads_count(&self) -> HQResult + // threadss isn't a typo here - using the Haskell convention of adding extra s's to + // the end of identifiers for nested lists + pub fn threadss(&self, types: &Rc, num_sprites: u32) -> HQResult where N: TryFrom, >::Error: fmt::Debug, { - self.threads_count_with_id("") - } - - pub fn target_threads_count(&self, target: StepTarget) -> HQResult - where - N: TryFrom, - >::Error: fmt::Debug, - { - self.threads_count_with_id(target.suffix_id()) + let array_array_type = types.thread_list_array_type()?; + let array_type = types.thread_array_type()?; + self.register( + "threadss".into(), + ( + ValType::Ref(RefType { + nullable: false, + heap_type: wasm_encoder::HeapType::Concrete(array_array_type), + }), + ConstExpr::extended( + (0..num_sprites) + .map(|i| { + [ + Instruction::I32Const(i as i32), + Instruction::I32Const(0), + Instruction::ArrayNewFixed { + array_type_index: array_type, + array_size: 0, + }, + ] + }) + .flatten() + .chain([Instruction::ArrayNewFixed { + array_type_index: array_array_type, + array_size: num_sprites, + }]), + ), // TODO: initialise properly + GlobalMutable(true), + GlobalExportable(false), + ), + ) } pub fn finish( diff --git a/src/wasm/registries/types.rs b/src/wasm/registries/types.rs index 4cee021d..2e7c045a 100644 --- a/src/wasm/registries/types.rs +++ b/src/wasm/registries/types.rs @@ -49,17 +49,37 @@ impl TypeRegistry { }, }); - pub fn step_func_type(&self) -> HQResult { + pub fn step_func(&self) -> HQResult { self.function(vec![ValType::I32, Self::STRUCT_REF], vec![]) } + pub fn dyn_array_container(&self, field: ValType) -> HQResult { + let arr_type = self.array(StorageType::Val(field), true)?; + self.struct_(vec![FieldType { + element_type: Self::ref_storage(arr_type, false), + mutable: true, + }]) + } + + pub fn ref_(heap_type: u32, nullable: bool) -> RefType { + RefType { + nullable, + heap_type: HeapType::Concrete(heap_type), + } + } + + pub fn ref_val(heap_type: u32, nullable: bool) -> ValType { + ValType::Ref(Self::ref_(heap_type, nullable)) + } + + pub fn ref_storage(heap_type: u32, nullable: bool) -> StorageType { + StorageType::Val(Self::ref_val(heap_type, nullable)) + } + pub fn stack_struct_type(&self) -> HQResult { self.struct_(vec![ FieldType { - element_type: StorageType::Val(ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(self.step_func_type()?), - })), + element_type: Self::ref_storage(self.step_func()?, false), mutable: true, }, FieldType { @@ -70,13 +90,7 @@ impl TypeRegistry { } pub fn stack_array_type(&self) -> HQResult { - self.array( - StorageType::Val(ValType::Ref(RefType { - nullable: true, - heap_type: HeapType::Concrete(self.stack_struct_type()?), - })), - true, - ) + self.array(Self::ref_storage(self.stack_struct_type()?, true), true) } pub fn thread_struct_type(&self) -> HQResult { @@ -86,15 +100,45 @@ impl TypeRegistry { mutable: true, }, FieldType { - element_type: StorageType::Val(ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(self.stack_array_type()?), - })), + element_type: Self::ref_storage(self.stack_array_type()?, false), + mutable: true, + }, + ]) + } + + pub fn thread_array_type(&self) -> HQResult { + self.array(Self::ref_storage(self.thread_struct_type()?, true), true) + } + + pub fn thread_list_struct_type(&self) -> HQResult { + self.struct_(vec![ + // target index + FieldType { + element_type: StorageType::Val(ValType::I32), + mutable: true, + }, + // number of threads + FieldType { + element_type: StorageType::Val(ValType::I32), + mutable: true, + }, + FieldType { + element_type: Self::ref_storage(self.thread_array_type()?, false), mutable: true, }, ]) } + pub fn thread_list_array_type(&self) -> HQResult { + self.array( + StorageType::Val(ValType::Ref(RefType { + nullable: true, + heap_type: HeapType::Concrete(self.thread_list_struct_type()?), + })), + true, + ) + } + pub fn proc_arg_struct_type( &self, arg_vars: &core::cell::Ref<'_, Vec>, From 0c0d9d96a35b089e6c82a937b5719c3a1ad1d4fa Mon Sep 17 00:00:00 2001 From: pufferfish101007 <50246616+pufferfish10107@users.noreply.github.com> Date: Sun, 16 Aug 2026 20:24:40 +0100 Subject: [PATCH 3/9] start working on types in the type system --- src/registry.rs | 43 ++++++++------ src/wasm/registries/functions.rs | 1 + src/wasm/registries/types.rs | 97 +++++++++++++++++++++++++++++--- 3 files changed, 116 insertions(+), 25 deletions(-) diff --git a/src/registry.rs b/src/registry.rs index 69f8312b..29a6da20 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -2,6 +2,13 @@ use core::hash::Hash; use crate::prelude::*; +pub trait RegistryResult: TryFrom {} + +impl RegistryResult for N +where + N: TryFrom, +{} + #[derive(Clone)] pub struct MapRegistry(RefCell>) where @@ -70,6 +77,10 @@ pub trait RegistryType { type Value; } +pub trait CompTimeRegistrand { + fn register(registry: &R) -> HQResult; +} + pub trait Registry: Sized + RegistryType { fn registry(&self) -> &RefCell>; @@ -79,8 +90,7 @@ pub trait Registry: Sized + RegistryType { /// the casting logic in here. fn register(&self, key: Self::Key, value: Self::Value) -> HQResult where - N: TryFrom, - >::Error: fmt::Debug, + N: RegistryResult, { self.registry() .try_borrow_mut() @@ -98,8 +108,7 @@ pub trait Registry: Sized + RegistryType { fn register_override(&self, key: Self::Key, value: Self::Value) -> HQResult where - N: TryFrom, - >::Error: fmt::Debug, + N: RegistryResult, { self.registry() .try_borrow_mut() @@ -115,6 +124,14 @@ pub trait Registry: Sized + RegistryType { .map_err(|_| make_hq_bug!("registry item index out of bounds")) } + fn register_comp(&self) -> HQResult + where + R: CompTimeRegistrand, + N: RegistryResult + { + R::register(self) + } + // TODO: register_override_ifexists or similar - for things like mark_waiting_flag, // which need to be overriden if they are registered, but don't actually need to be // registered always. @@ -123,8 +140,7 @@ pub trait Registry: Sized + RegistryType { pub trait RegistryDefault: Registry { fn register_default(&self, key: Self::Key) -> HQResult where - N: TryFrom, - >::Error: fmt::Debug, + N: RegistryResult, { self.register(key, Self::Value::default()) } @@ -234,8 +250,7 @@ where /// Registers a `NamedRegistryItem` using its key function and its `const VALUE` pub fn register(&self) -> HQResult where - N: TryFrom, - >::Error: fmt::Debug, + N: RegistryResult, T: NamedRegistryItem, { self.0.register(R::name::(), T::VALUE) @@ -245,8 +260,7 @@ where /// `Registry` pub fn register_dyn(&self, key: R::Key, value: R::Value) -> HQResult where - N: TryFrom, - >::Error: fmt::Debug, + N: RegistryResult, { self.0.register(key, value) } @@ -255,8 +269,7 @@ where /// `register_override` on the underlying `Registry` pub fn register_dyn_override(&self, key: R::Key, value: R::Value) -> HQResult where - N: TryFrom, - >::Error: fmt::Debug, + N: RegistryResult, { self.0.register_override(key, value) } @@ -265,8 +278,7 @@ where /// associated with the corresponding `NamedRegistryItemOverride` pub fn register_override(&self, override_arg: A) -> HQResult where - N: TryFrom, - >::Error: fmt::Debug, + N: RegistryResult, T: NamedRegistryItem + NamedRegistryItemOverride, { self.0 @@ -277,8 +289,7 @@ where /// types associated with the corresponding `TryNamedRegistryItemOverride` pub fn try_register_override(&self, override_arg: A) -> HQResult where - N: TryFrom, - >::Error: fmt::Debug, + N: RegistryResult, T: NamedRegistryItem + TryNamedRegistryItemOverride, { self.0 diff --git a/src/wasm/registries/functions.rs b/src/wasm/registries/functions.rs index 72f487b4..93bd0cb9 100644 --- a/src/wasm/registries/functions.rs +++ b/src/wasm/registries/functions.rs @@ -1,5 +1,6 @@ #![allow(clippy::cast_possible_wrap, reason = "can't use try_into in const")] +mod dyn_array; mod mark_waiting_flag; mod pen_colour; mod spawn_threads; diff --git a/src/wasm/registries/types.rs b/src/wasm/registries/types.rs index 2e7c045a..a88f3474 100644 --- a/src/wasm/registries/types.rs +++ b/src/wasm/registries/types.rs @@ -2,19 +2,21 @@ use wasm_encoder::{ AbstractHeapType, FieldType, HeapType, RefType, StorageType, TypeSection, ValType, }; +use core::marker::PhantomData; + use crate::ir::RcVar; use crate::prelude::*; -use crate::registry::SetRegistry; +use crate::registry::{CompTimeRegistrand, RegistryResult, SetRegistry}; use crate::wasm::WasmProject; #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum WasmType { +pub enum CompoundType { Function(Vec, Vec), Array(StorageType, bool), Struct(Vec), } -pub type TypeRegistry = SetRegistry; +pub type TypeRegistry = SetRegistry; impl TypeRegistry { pub fn function(&self, params: Vec, returns: Vec) -> HQResult @@ -22,7 +24,7 @@ impl TypeRegistry { N: TryFrom, >::Error: fmt::Debug, { - self.register_default(WasmType::Function(params, returns)) + self.register_default(CompoundType::Function(params, returns)) } pub fn array(&self, elem_type: StorageType, mutable: bool) -> HQResult @@ -30,7 +32,7 @@ impl TypeRegistry { N: TryFrom, >::Error: fmt::Debug, { - self.register_default(WasmType::Array(elem_type, mutable)) + self.register_default(CompoundType::Array(elem_type, mutable)) } pub fn struct_(&self, fields: Vec) -> HQResult @@ -38,7 +40,7 @@ impl TypeRegistry { N: TryFrom, >::Error: fmt::Debug, { - self.register_default(WasmType::Struct(fields)) + self.register_default(CompoundType::Struct(fields)) } pub const STRUCT_REF: ValType = ValType::Ref(RefType { @@ -161,10 +163,87 @@ impl TypeRegistry { pub fn finish(self, types: &mut TypeSection) { for ty in self.registry().take().keys().cloned() { match ty { - WasmType::Function(params, results) => types.ty().function(params, results), - WasmType::Array(elem_type, mutable) => types.ty().array(&elem_type, mutable), - WasmType::Struct(fields) => types.ty().struct_(fields), + CompoundType::Function(params, results) => types.ty().function(params, results), + CompoundType::Array(elem_type, mutable) => types.ty().array(&elem_type, mutable), + CompoundType::Struct(fields) => types.ty().struct_(fields), } } } } + +pub trait THeapType: CompTimeRegistrand {} +impl THeapType for T where T: CompTimeRegistrand {} + +pub trait TRefType { + type HeapType: THeapType; + const NULLABLE: bool; +} + +pub struct TNullable(PhantomData); + +pub struct TNonNullable(PhantomData); + +impl TRefType for TNullable where T: THeapType { + type HeapType = T; + const NULLABLE: bool = true; +} + +impl TRefType for TNonNullable where T: THeapType { + type HeapType = T; + const NULLABLE: bool = true; +} + +pub trait TValType { + fn val_type(types: &TypeRegistry) -> ValType; +} + +pub struct DynArray(PhantomData); + +impl CompTimeRegistrand for DynArray where T: TRefType { + fn register(types: &TypeRegistry) -> HQResult { + types.dyn_array_container( + TypeRegistry::ref_val( + T::HeapType::register(types)?, + T::NULLABLE, + ) + ) + } +} + +pub trait TFieldType { + type ValType: TValType; + const MUTABLE: bool; +} + +trait CompTypeList { + fn fields(types: &TypeRegistry) -> Vec; +} + +impl CompTypeList for () { + fn fields(_: &TypeRegistry) -> Vec { + vec![] + } +} + +impl CompTypeList for (Head, Tail) where Head: CompTypeList, Tail: TFieldType { + fn fields(types: &TypeRegistry) -> Vec { + let mut fields = Head::fields(types); + fields.push( + FieldType { + mutable: Tail::MUTABLE, + element_type: StorageType::Val(Tail::ValType::val_type(types)), + } + ); + fields + } +} + +struct TStruct(PhantomData); + +impl CompTimeRegistrand for TStruct where Fields: CompTypeList { + fn register(types: &TypeRegistry) -> HQResult { + types.struct_( + Fields::fields(types) + ) + } +} From 09d1df671ce3c8cfd518e04be90822c492afc726 Mon Sep 17 00:00:00 2001 From: pufferfish101007 <50246616+pufferfish10107@users.noreply.github.com> Date: Wed, 19 Aug 2026 17:23:41 +0100 Subject: [PATCH 4/9] more type system types and start implementing dyn_array funcs --- src/wasm/registries/functions/dyn_array.rs | 108 +++++++++++++++++++++ src/wasm/registries/types.rs | 74 ++++++++++---- 2 files changed, 163 insertions(+), 19 deletions(-) create mode 100644 src/wasm/registries/functions/dyn_array.rs diff --git a/src/wasm/registries/functions/dyn_array.rs b/src/wasm/registries/functions/dyn_array.rs new file mode 100644 index 00000000..70f73deb --- /dev/null +++ b/src/wasm/registries/functions/dyn_array.rs @@ -0,0 +1,108 @@ + +use wasm_encoder::{BlockType as WasmBlockType, ValType}; +use wasm_gen::wasm_const; + +use core::marker::PhantomData; + +use super::{MaybeStaticFunction, StaticFunction}; +use crate::{prelude::*, wasm::registries::{TypeRegistry, types::{TDynArray, TDynArrayField, TValType}}}; + +/// Pushes an element to a dynamic (resizeable) array +/// +/// Takes 2 parameters: +/// ref dynamic_array - the array +/// t - the element +pub struct DynArrayPush(PhantomData); +impl NamedRegistryItem for DynArrayPush { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + maybe_populate: || None, + }; +} + +pub struct DynArrayPushOverride { + types: Rc, +} + +impl TryNamedRegistryItemOverride + for DynArrayPush +{ + fn try_override( + DynArrayPushOverride { types }: DynArrayPushOverride, + ) -> HQResult { + let struct_type = types.register_comp::, u32>()?; + let array_type = types.register_comp::, u32>()?; + Ok(MaybeStaticFunction { + static_function: Some(StaticFunction { + export: None, + instructions: Box::from(wasm_const![ + LocalGet(0), + StructGet { + struct_type_index: struct_type, + field_index: 0, + }, + ArrayLen, + LocalGet(0), + StructGet { + struct_type_index: struct_type, + field_index: 1, + }, + LocalTee(2), + I32Eq, + If(WasmBlockType::Empty), + LocalGet(2), + I32Const(1), + I32Shl, + ArrayNewDefault(array_type), // dest + LocalTee(3), + I32Const(0), // dest index + LocalGet(0), + StructGet { + struct_type_index: struct_type, + field_index: 0, + }, // src + I32Const(0), // src index + LocalGet(2), // length + ArrayCopy { + array_type_index_dst: array_type, + array_type_index_src: array_type, + }, + LocalGet(0), + LocalGet(3), + StructSet { + struct_type_index: struct_type, + field_index: 0, + }, + End, + LocalGet(0), + StructGet { + struct_type_index: struct_type, + field_index: 0, + }, + LocalGet(2), + LocalGet(1), + ArraySet(array_type), + LocalGet(0), + LocalGet(2), + I32Const(1), + I32Add, + StructSet { + struct_type_index: struct_type, + field_index: 1, + }, + ] as &[_]), + params: Box::from([ + TypeRegistry::ref_val(struct_type, false), + T::val_type(&types)?, + ]), + returns: Box::from([]), + locals: Box::from([ + ValType::I32, + TypeRegistry::ref_val(array_type, false), + ]), + }), + maybe_populate: || None, + }) + } +} + diff --git a/src/wasm/registries/types.rs b/src/wasm/registries/types.rs index a88f3474..856a122a 100644 --- a/src/wasm/registries/types.rs +++ b/src/wasm/registries/types.rs @@ -187,6 +187,7 @@ impl TRefType for TNullable where T: THeapType { type HeapType = T; const NULLABLE: bool = true; } +impl TDefaultable for TNullable {} impl TRefType for TNonNullable where T: THeapType { type HeapType = T; @@ -194,56 +195,91 @@ impl TRefType for TNonNullable where T: THeapType { } pub trait TValType { - fn val_type(types: &TypeRegistry) -> ValType; + fn val_type(types: &TypeRegistry) -> HQResult; } -pub struct DynArray(PhantomData); +impl TValType for T where T: TRefType { + fn val_type(types: &TypeRegistry) -> HQResult { + Ok(TypeRegistry::ref_val( + T::HeapType::register(types)?, + T::NULLABLE, + )) + } +} -impl CompTimeRegistrand for DynArray where T: TRefType { - fn register(types: &TypeRegistry) -> HQResult { - types.dyn_array_container( - TypeRegistry::ref_val( - T::HeapType::register(types)?, - T::NULLABLE, - ) - ) +pub struct TI32; + +impl TValType for TI32 { + fn val_type(types: &TypeRegistry) -> HQResult { + Ok(ValType::I32) } } +impl TDefaultable for TI32 {} pub trait TFieldType { type ValType: TValType; const MUTABLE: bool; } +pub struct TMutField(PhantomData); +pub struct TConstField(PhantomData); + +impl TFieldType for TMutField { + type ValType = T; + const MUTABLE: bool = true; +} + +impl TFieldType for TConstField { + type ValType = T; + const MUTABLE: bool = false; +} + +pub trait TDefaultable: TValType {} + + trait CompTypeList { - fn fields(types: &TypeRegistry) -> Vec; + fn fields(types: &TypeRegistry) -> HQResult>; } impl CompTypeList for () { - fn fields(_: &TypeRegistry) -> Vec { - vec![] + fn fields(_: &TypeRegistry) -> HQResult> { + Ok(vec![]) } } impl CompTypeList for (Head, Tail) where Head: CompTypeList, Tail: TFieldType { - fn fields(types: &TypeRegistry) -> Vec { - let mut fields = Head::fields(types); + fn fields(types: &TypeRegistry) -> HQResult> { + let mut fields = Head::fields(types)?; fields.push( FieldType { mutable: Tail::MUTABLE, - element_type: StorageType::Val(Tail::ValType::val_type(types)), + element_type: StorageType::Val(Tail::ValType::val_type(types)?), } ); - fields + Ok(fields) } } -struct TStruct(PhantomData); +pub struct TStruct(PhantomData); impl CompTimeRegistrand for TStruct where Fields: CompTypeList { fn register(types: &TypeRegistry) -> HQResult { types.struct_( - Fields::fields(types) + Fields::fields(types)? ) } } + +pub struct TArray(PhantomData); + +impl CompTimeRegistrand for TArray { + fn register(types: &TypeRegistry) -> HQResult { + types.array( + StorageType::Val(Field::ValType::val_type(types)?), + Field::MUTABLE, + ) + } +} + +pub type TDynArrayField = TArray>; +pub type TDynArray = TStruct<(((), TMutField>>), TMutField)>; From b2faa61fc1f295445e703e757d5b286a0aa39fdf Mon Sep 17 00:00:00 2001 From: pufferfish101007 <50246616+pufferfish101007@users.noreply.github.com> Date: Wed, 19 Aug 2026 23:52:10 +0100 Subject: [PATCH 5/9] rewrite `SpawnNewThreadInStack` with new dyn arrays + threadss array global --- src/instructions/hq/yield.rs | 3 +- src/instructions/procedures/call_nonwarp.rs | 3 +- src/registry.rs | 7 +- src/wasm.rs | 4 +- src/wasm/mem_layout.rs | 2 +- src/wasm/project.rs | 13 +- src/wasm/registries/functions.rs | 2 +- src/wasm/registries/functions/dyn_array.rs | 173 +++++++++--- .../registries/functions/spawn_threads.rs | 161 ++++++----- src/wasm/registries/globals.rs | 13 +- src/wasm/registries/tables.rs | 3 +- src/wasm/registries/types.rs | 253 +++++++++--------- 12 files changed, 365 insertions(+), 272 deletions(-) diff --git a/src/instructions/hq/yield.rs b/src/instructions/hq/yield.rs index 9c9e0e64..b2f65d24 100644 --- a/src/instructions/hq/yield.rs +++ b/src/instructions/hq/yield.rs @@ -4,6 +4,7 @@ use super::super::prelude::*; use crate::instructions_test; use crate::ir::{Step, StepIndex}; use crate::wasm::StepFunc; +use crate::wasm::registries::types::TStepFunc; #[derive(Debug, Clone)] pub enum YieldMode { @@ -75,7 +76,7 @@ pub fn wasm( heap_type: HeapType::Concrete(stack_struct_ty), }))?; let i32_local = func.local(ValType::I32)?; - let step_func_ty = func.registries().types().step_func_type()?; + let step_func_ty = func.registries().types().register_comp::()?; func.free_local(thread_struct_local)?; func.free_local(stack_struct_local)?; func.free_local(i32_local)?; diff --git a/src/instructions/procedures/call_nonwarp.rs b/src/instructions/procedures/call_nonwarp.rs index 54347734..231d7f90 100644 --- a/src/instructions/procedures/call_nonwarp.rs +++ b/src/instructions/procedures/call_nonwarp.rs @@ -4,6 +4,7 @@ use super::super::prelude::*; use crate::instructions_test; use crate::ir::{Proc, StepIndex}; use crate::wasm::registries::functions::static_functions::SpawnThreadInStack; +use crate::wasm::registries::types::TStepFunc; use crate::wasm::{StepFunc, WasmProject}; #[derive(Clone, Debug)] @@ -102,7 +103,7 @@ pub fn wasm( LocalGet((func.params().len() - 2).try_into().map_err(|_| make_hq_bug!("local index out of bounds"))?), LocalGet(arg_struct_local), #LazyNonWarpedProcRef(Rc::clone(proc)), - ReturnCallRef(func.registries().types().step_func_type()?) + ReturnCallRef(func.registries().types().register_comp::()?) ]); Ok(wasm) diff --git a/src/registry.rs b/src/registry.rs index 29a6da20..1b992bc2 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -4,10 +4,7 @@ use crate::prelude::*; pub trait RegistryResult: TryFrom {} -impl RegistryResult for N -where - N: TryFrom, -{} +impl RegistryResult for N where N: TryFrom {} #[derive(Clone)] pub struct MapRegistry(RefCell>) @@ -127,7 +124,7 @@ pub trait Registry: Sized + RegistryType { fn register_comp(&self) -> HQResult where R: CompTimeRegistrand, - N: RegistryResult + N: RegistryResult, { R::register(self) } diff --git a/src/wasm.rs b/src/wasm.rs index 2597e1b8..d1727632 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -10,6 +10,4 @@ pub use external::ExternalEnvironment; pub use flags::WasmFlags; pub use func::{Instruction as InternalInstruction, StepFunc, StepTarget}; pub use project::{FinishedWasm, WasmProject}; -pub use registries::{ - GlobalExportable, GlobalMutable, Registries, StringsTable, -}; +pub use registries::{GlobalExportable, GlobalMutable, Registries, StringsTable}; diff --git a/src/wasm/mem_layout.rs b/src/wasm/mem_layout.rs index a25873f2..11693863 100644 --- a/src/wasm/mem_layout.rs +++ b/src/wasm/mem_layout.rs @@ -100,7 +100,7 @@ memory_layout! { PEN_DOWN: i8 /// non-zero if sprite is visible, 0 otherwise (i8) VISIBLE: i8 - /// sprite layer - 0 is bottom (not including stage! as that is always lowest) + /// sprite layer - 0 is bottom (not including stage! as that is always lowest) (i16) LAYER: i16 /// current costume number, 0-indexed (i32) COSTUME: i32 diff --git a/src/wasm/project.rs b/src/wasm/project.rs index e84f518d..fae3c2d7 100644 --- a/src/wasm/project.rs +++ b/src/wasm/project.rs @@ -14,6 +14,7 @@ use crate::prelude::*; use crate::wasm::registries::functions::static_functions::{ MarkWaitingFlag, SpawnNewThread, SpawnThreadInStack, }; +use crate::wasm::registries::types::{TStackStruct, TStepFunc}; use crate::wasm::{StepFunc, StepTarget, StringsTable, WasmFlags}; /// A respresentation of a WASM representation of a project. Cannot be created directly; @@ -139,8 +140,8 @@ impl WasmProject { self.registries() .static_functions() .register_override::(( - self.registries().types().step_func()?, - self.registries().types().stack_struct_type()?, + self.registries().types().register_comp::()?, + self.registries().types().register_comp::()?, self.registries().types().stack_array_type()?, self.registries().types().thread_struct_type()?, self.threads_table_index()?, @@ -149,8 +150,8 @@ impl WasmProject { self.registries() .static_functions() .register_override::(( - self.registries().types().step_func()?, - self.registries().types().stack_struct_type()?, + self.registries().types().register_comp::()?, + self.registries().types().register_comp::()?, self.registries().types().stack_array_type()?, self.registries().types().thread_struct_type()?, self.threads_table_index()?, @@ -373,7 +374,9 @@ impl WasmProject { N: TryFrom, >::Error: fmt::Debug, { - self.registries().tables().threads_table(target, self.registries().types()) + self.registries() + .tables() + .threads_table(target, self.registries().types()) } fn spawn_new_thread_func(&self) -> HQResult diff --git a/src/wasm/registries/functions.rs b/src/wasm/registries/functions.rs index 93bd0cb9..1a4e57b7 100644 --- a/src/wasm/registries/functions.rs +++ b/src/wasm/registries/functions.rs @@ -110,6 +110,6 @@ pub mod static_functions { pub use super::mark_waiting_flag::MarkWaitingFlag; pub use super::pen_colour::{UpdatePenColorFromHSV, UpdatePenColorFromRGB}; pub use super::spawn_threads::{ - SpawnNewThread, SpawnNewThreadOverride, SpawnThreadInStack, SpawnThreadInStackOverride, + SpawnNewThread, SpawnNewThreadOverride, SpawnThreadInStack, }; } diff --git a/src/wasm/registries/functions/dyn_array.rs b/src/wasm/registries/functions/dyn_array.rs index 70f73deb..6d174814 100644 --- a/src/wasm/registries/functions/dyn_array.rs +++ b/src/wasm/registries/functions/dyn_array.rs @@ -1,16 +1,21 @@ +use core::marker::PhantomData; use wasm_encoder::{BlockType as WasmBlockType, ValType}; use wasm_gen::wasm_const; -use core::marker::PhantomData; - use super::{MaybeStaticFunction, StaticFunction}; -use crate::{prelude::*, wasm::registries::{TypeRegistry, types::{TDynArray, TDynArrayField, TValType}}}; +use crate::prelude::*; +use crate::wasm::registries::TypeRegistry; +use crate::wasm::registries::types::{TDynArray, TDynArrayField, TNonNullable, TValType}; + +pub struct DynArrayFuncOverride { + types: Rc, +} /// Pushes an element to a dynamic (resizeable) array /// /// Takes 2 parameters: -/// ref dynamic_array - the array +/// ref dynamic_array - the dynamic array struct (obtained from `TDynArray` for `T: TValType`) /// t - the element pub struct DynArrayPush(PhantomData); impl NamedRegistryItem for DynArrayPush { @@ -20,15 +25,11 @@ impl NamedRegistryItem for DynArrayPush { }; } -pub struct DynArrayPushOverride { - types: Rc, -} - -impl TryNamedRegistryItemOverride +impl TryNamedRegistryItemOverride for DynArrayPush { fn try_override( - DynArrayPushOverride { types }: DynArrayPushOverride, + DynArrayFuncOverride { types }: DynArrayFuncOverride, ) -> HQResult { let struct_type = types.register_comp::, u32>()?; let array_type = types.register_comp::, u32>()?; @@ -50,29 +51,29 @@ impl TryNamedRegistryItemOverride TryNamedRegistryItemOverride>>::val_type(&types)?, T::val_type(&types)?, ]), returns: Box::from([]), locals: Box::from([ ValType::I32, - TypeRegistry::ref_val(array_type, false), + >>::val_type(&types)?, ]), }), maybe_populate: || None, @@ -106,3 +107,105 @@ impl TryNamedRegistryItemOverride - the dynamic array struct (obtained from `TDynArray` for `T: TValType`) +/// i32 - the index +/// +/// Returns t +pub struct DynArrayGet(PhantomData); +impl NamedRegistryItem for DynArrayGet { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + maybe_populate: || None, + }; +} + +impl TryNamedRegistryItemOverride + for DynArrayGet +{ + fn try_override( + DynArrayFuncOverride { types }: DynArrayFuncOverride, + ) -> HQResult { + let struct_type = types.register_comp::, u32>()?; + let array_type = types.register_comp::, u32>()?; + Ok(MaybeStaticFunction { + static_function: Some(StaticFunction { + export: None, + instructions: Box::from(wasm_const![ + LocalGet(0), + StructGet { + struct_type_index: struct_type, + field_index: 0, + }, + LocalGet(1), + ArrayGet(array_type), + ] as &[_]), + params: Box::from([ + >>::val_type(&types)?, + ValType::I32, + ]), + returns: Box::from([T::val_type(&types)?]), + locals: Box::from([]), + }), + maybe_populate: || None, + }) + } +} + +/// Pops the last element from a dynamic (resizeable) array +/// +/// Takes 1 parameters: +/// ref dynamic_array - the dynamic array struct (obtained from `TDynArray` for `T: TValType`) +/// +/// Returns t +pub struct DynArrayPop(PhantomData); +impl NamedRegistryItem for DynArrayPop { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + maybe_populate: || None, + }; +} + +impl TryNamedRegistryItemOverride + for DynArrayPop +{ + fn try_override( + DynArrayFuncOverride { types }: DynArrayFuncOverride, + ) -> HQResult { + let struct_type = types.register_comp::, u32>()?; + let array_type = types.register_comp::, u32>()?; + Ok(MaybeStaticFunction { + static_function: Some(StaticFunction { + export: None, + instructions: Box::from(wasm_const![ + LocalGet(0), + StructGet { + struct_type_index: struct_type, + field_index: 0, + }, + LocalGet(0), + StructGet { + struct_type_index: struct_type, + field_index: 1, + }, + I32Const(1), + I32Sub, + LocalTee(1), + ArrayGet(array_type), + LocalGet(0), + LocalGet(1), + StructSet { + struct_type_index: struct_type, + field_index: 1, + }, + ] as &[_]), + params: Box::from([>>::val_type(&types)?]), + returns: Box::from([T::val_type(&types)?]), + locals: Box::from([ValType::I32]), + }), + maybe_populate: || None, + }) + } +} diff --git a/src/wasm/registries/functions/spawn_threads.rs b/src/wasm/registries/functions/spawn_threads.rs index f8fa5a27..d68ce24f 100644 --- a/src/wasm/registries/functions/spawn_threads.rs +++ b/src/wasm/registries/functions/spawn_threads.rs @@ -1,24 +1,38 @@ -use wasm_encoder::{AbstractHeapType, HeapType, RefType, ValType}; +use wasm_encoder::{ + AbstractHeapType, BlockType as WasmBlockType, HeapType, MemArg, RefType, ValType, +}; use wasm_gen::wasm_const; use super::{MaybeStaticFunction, StaticFunction}; use crate::prelude::*; +use crate::wasm::mem_layout; +use crate::wasm::registries::functions::dyn_array::{DynArrayGet, DynArrayPop, DynArrayPush}; +use crate::wasm::registries::types::{ + THeapType, TNonNullable, TNullable, TStackArray, TStackStruct, TStepFunc, TStructRef, + TTargetThreadArray, TValType, +}; +use crate::wasm::registries::{GlobalRegistry, StaticFunctionRegistry, TypeRegistry}; + +pub struct SpawnThreadFuncOverride { + types: Rc, + globals: Rc, + static_functions: Rc, + num_sprites: u32, + imported_func_count: u32, +} /// Spawns a new thread in the same stack (i.e. a thread that yields back to the current -/// thread once it completes.) +/// thread once it completes.) The step that is provided to return to will be written into +/// the current stack frame, and the new thread's step is added to the top of the current +/// frame with the provided struct argument so that that will run until completion before +/// yielding to the provided next step. /// /// Takes 4 parameters: +/// - i32 - the index of the calling target /// - i32 - the current thread index /// - step funcref - the step to spawn /// - structref - the structref to pass to the step being spawned /// - step funcref - the step to return to after -/// -/// Override with: -/// - u32 - the index of the step func type -/// - u32 - the index of the stack struct type -/// - u32 - the index of the stack array type -/// - u32 - the index of the thread struct type -/// - u32 - the index of the threads table pub struct SpawnThreadInStack; impl NamedRegistryItem for SpawnThreadInStack { const VALUE: MaybeStaticFunction = MaybeStaticFunction { @@ -26,101 +40,80 @@ impl NamedRegistryItem for SpawnThreadInStack { maybe_populate: || None, }; } -pub type SpawnThreadInStackOverride = (u32, u32, u32, u32, u32); -impl NamedRegistryItemOverride +impl TryNamedRegistryItemOverride for SpawnThreadInStack { - fn r#override( - (func_ty, stack_struct_type, stack_array_type, thread_struct_type, threads_table): SpawnThreadInStackOverride, - ) -> MaybeStaticFunction { - MaybeStaticFunction { + fn try_override( + SpawnThreadFuncOverride { + types, + globals, + static_functions, + num_sprites, + imported_func_count, + }: SpawnThreadFuncOverride, + ) -> HQResult { + let stack_struct_type = types.register_comp::()?; + let target_threads_type = types.register_comp::()?; + let target_threads_global = globals.threadss(&types, num_sprites)?; + let dyn_array_push = static_functions.register::, u32>()?; + type StackStruct = TNullable; + Ok(MaybeStaticFunction { static_function: Some(StaticFunction { export: None, instructions: Box::from(wasm_const![ - LocalGet(1), - LocalGet(2), - StructNew(stack_struct_type), - LocalSet(4), LocalGet(0), - TableGet(threads_table), - RefAsNonNull, + I32Eqz, // if this is not the stage, we need to find its layer + If(WasmBlockType::Empty), + LocalGet(0), + I32Const(mem_layout::sprite::BLOCK_SIZE as i32), + I32Mul, + I32Load16U(MemArg { + offset: (mem_layout::stage::BLOCK_SIZE + mem_layout::sprite::LAYER) as u64, + align: 1, + memory_index: 0, + }), + LocalSet(0), // local 0 is now index of sprite in + End, + GlobalGet(target_threads_global), + LocalGet(0), + ArrayGet(target_threads_type), + LocalGet(1), + Call( + imported_func_count + + static_functions + .register::>, u32>()? + ), LocalTee(5), - StructGet { - struct_type_index: thread_struct_type, - field_index: 1, - }, + Call( + imported_func_count + + static_functions.register::, u32>()? + ), + Drop, LocalGet(5), - StructGet { - struct_type_index: thread_struct_type, - field_index: 0, - }, LocalGet(4), - // todo: consider the case where we need to resize the array - ArraySet(stack_array_type), - LocalGet(5), - StructGet { - struct_type_index: thread_struct_type, - field_index: 1, - }, + RefNull(TStackStruct::heap_type(&types)?), + StructNew(stack_struct_type), + Call(imported_func_count + dyn_array_push), // TODO: this will do unnecessary bounds checks. Just mutate the last element. LocalGet(5), - StructGet { - struct_type_index: thread_struct_type, - field_index: 0, - }, - I32Const(1), - I32Sub, - ArrayGet(stack_array_type), + LocalGet(2), LocalGet(3), - StructSet { - struct_type_index: stack_struct_type, - field_index: 0, - }, - LocalGet(5), - LocalGet(5), - StructGet { - struct_type_index: thread_struct_type, - field_index: 0, - }, - I32Const(1), - I32Add, - StructSet { - struct_type_index: thread_struct_type, - field_index: 0, - }, - End + StructNew(stack_struct_type), + Call(imported_func_count + dyn_array_push), ] as &[_]), params: Box::from([ ValType::I32, - ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(func_ty), - }), - ValType::Ref(RefType { - nullable: true, - heap_type: wasm_encoder::HeapType::Abstract { - shared: false, - ty: AbstractHeapType::Struct, - }, - }), - ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(func_ty), - }), + ValType::I32, + >::val_type(&types)?, + >::val_type(&types)?, + >::val_type(&types)?, ]), returns: Box::from([]), locals: Box::from([ - ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(stack_struct_type), - }), - ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(thread_struct_type), - }), + >::val_type(&types)?, ]), }), maybe_populate: || None, - } + }) } } diff --git a/src/wasm/registries/globals.rs b/src/wasm/registries/globals.rs index ef117a9e..e64d064c 100644 --- a/src/wasm/registries/globals.rs +++ b/src/wasm/registries/globals.rs @@ -1,13 +1,13 @@ use core::ops::Deref; use wasm_encoder::{ - ConstExpr, ExportKind, ExportSection, GlobalSection, GlobalType, Instruction, - RefType, ValType, + ConstExpr, ExportKind, ExportSection, GlobalSection, GlobalType, Instruction, ValType, }; use crate::prelude::*; use crate::registry::MapRegistry; use crate::wasm::registries::TypeRegistry; +use crate::wasm::registries::types::{TNonNullable, TTargetThreadArray, TThreadArray, TValType}; #[derive(Copy, Clone, Debug)] pub struct GlobalMutable(pub bool); @@ -56,15 +56,12 @@ impl GlobalRegistry { N: TryFrom, >::Error: fmt::Debug, { - let array_array_type = types.thread_list_array_type()?; - let array_type = types.thread_array_type()?; + let array_array_type = types.register_comp::()?; + let array_type = types.register_comp::()?; self.register( "threadss".into(), ( - ValType::Ref(RefType { - nullable: false, - heap_type: wasm_encoder::HeapType::Concrete(array_array_type), - }), + >::val_type(&types)?, ConstExpr::extended( (0..num_sprites) .map(|i| { diff --git a/src/wasm/registries/tables.rs b/src/wasm/registries/tables.rs index fc729975..1d639df0 100644 --- a/src/wasm/registries/tables.rs +++ b/src/wasm/registries/tables.rs @@ -5,6 +5,7 @@ use wasm_encoder::{ use crate::prelude::*; use crate::wasm::StepTarget; use crate::wasm::registries::TypeRegistry; +use crate::wasm::registries::types::TThreadStruct; #[derive(Clone, Debug)] pub struct TableOptions { @@ -34,7 +35,7 @@ impl TableRegistry { TableOptions { element_type: RefType { nullable: true, - heap_type: HeapType::Concrete(types.thread_struct_type()?), + heap_type: HeapType::Concrete(types.register_comp::()?), }, min: 0, max: None, diff --git a/src/wasm/registries/types.rs b/src/wasm/registries/types.rs index 856a122a..8ae3ffc9 100644 --- a/src/wasm/registries/types.rs +++ b/src/wasm/registries/types.rs @@ -1,9 +1,9 @@ +use core::marker::PhantomData; + use wasm_encoder::{ AbstractHeapType, FieldType, HeapType, RefType, StorageType, TypeSection, ValType, }; -use core::marker::PhantomData; - use crate::ir::RcVar; use crate::prelude::*; use crate::registry::{CompTimeRegistrand, RegistryResult, SetRegistry}; @@ -21,24 +21,21 @@ pub type TypeRegistry = SetRegistry; impl TypeRegistry { pub fn function(&self, params: Vec, returns: Vec) -> HQResult where - N: TryFrom, - >::Error: fmt::Debug, + N: RegistryResult, { self.register_default(CompoundType::Function(params, returns)) } pub fn array(&self, elem_type: StorageType, mutable: bool) -> HQResult where - N: TryFrom, - >::Error: fmt::Debug, + N: RegistryResult, { self.register_default(CompoundType::Array(elem_type, mutable)) } pub fn struct_(&self, fields: Vec) -> HQResult where - N: TryFrom, - >::Error: fmt::Debug, + N: RegistryResult, { self.register_default(CompoundType::Struct(fields)) } @@ -51,96 +48,6 @@ impl TypeRegistry { }, }); - pub fn step_func(&self) -> HQResult { - self.function(vec![ValType::I32, Self::STRUCT_REF], vec![]) - } - - pub fn dyn_array_container(&self, field: ValType) -> HQResult { - let arr_type = self.array(StorageType::Val(field), true)?; - self.struct_(vec![FieldType { - element_type: Self::ref_storage(arr_type, false), - mutable: true, - }]) - } - - pub fn ref_(heap_type: u32, nullable: bool) -> RefType { - RefType { - nullable, - heap_type: HeapType::Concrete(heap_type), - } - } - - pub fn ref_val(heap_type: u32, nullable: bool) -> ValType { - ValType::Ref(Self::ref_(heap_type, nullable)) - } - - pub fn ref_storage(heap_type: u32, nullable: bool) -> StorageType { - StorageType::Val(Self::ref_val(heap_type, nullable)) - } - - pub fn stack_struct_type(&self) -> HQResult { - self.struct_(vec![ - FieldType { - element_type: Self::ref_storage(self.step_func()?, false), - mutable: true, - }, - FieldType { - element_type: StorageType::Val(Self::STRUCT_REF), - mutable: false, - }, - ]) - } - - pub fn stack_array_type(&self) -> HQResult { - self.array(Self::ref_storage(self.stack_struct_type()?, true), true) - } - - pub fn thread_struct_type(&self) -> HQResult { - self.struct_(vec![ - FieldType { - element_type: StorageType::Val(ValType::I32), - mutable: true, - }, - FieldType { - element_type: Self::ref_storage(self.stack_array_type()?, false), - mutable: true, - }, - ]) - } - - pub fn thread_array_type(&self) -> HQResult { - self.array(Self::ref_storage(self.thread_struct_type()?, true), true) - } - - pub fn thread_list_struct_type(&self) -> HQResult { - self.struct_(vec![ - // target index - FieldType { - element_type: StorageType::Val(ValType::I32), - mutable: true, - }, - // number of threads - FieldType { - element_type: StorageType::Val(ValType::I32), - mutable: true, - }, - FieldType { - element_type: Self::ref_storage(self.thread_array_type()?, false), - mutable: true, - }, - ]) - } - - pub fn thread_list_array_type(&self) -> HQResult { - self.array( - StorageType::Val(ValType::Ref(RefType { - nullable: true, - heap_type: HeapType::Concrete(self.thread_list_struct_type()?), - })), - true, - ) - } - pub fn proc_arg_struct_type( &self, arg_vars: &core::cell::Ref<'_, Vec>, @@ -171,25 +78,56 @@ impl TypeRegistry { } } -pub trait THeapType: CompTimeRegistrand {} -impl THeapType for T where T: CompTimeRegistrand {} +pub trait THeapType { + fn heap_type(types: &TypeRegistry) -> HQResult; +} + +impl THeapType for T +where + T: CompTimeRegistrand, +{ + fn heap_type(types: &TypeRegistry) -> HQResult { + Ok(HeapType::Concrete(types.register_comp::()?)) + } +} + +pub struct TStructRef; +impl THeapType for TStructRef { + fn heap_type(_types: &TypeRegistry) -> HQResult { + Ok(HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Struct, + }) + } +} pub trait TRefType { type HeapType: THeapType; const NULLABLE: bool; + + fn ref_type(types: &TypeRegistry) -> HQResult { + Ok(RefType { + nullable: Self::NULLABLE, + heap_type: Self::HeapType::heap_type(types)?, + }) + } } pub struct TNullable(PhantomData); - -pub struct TNonNullable(PhantomData); - -impl TRefType for TNullable where T: THeapType { +impl TRefType for TNullable +where + T: THeapType, +{ type HeapType = T; const NULLABLE: bool = true; } impl TDefaultable for TNullable {} -impl TRefType for TNonNullable where T: THeapType { +pub struct TNonNullable(PhantomData); +impl TRefType for TNonNullable +where + T: THeapType, +{ type HeapType = T; const NULLABLE: bool = true; } @@ -198,19 +136,19 @@ pub trait TValType { fn val_type(types: &TypeRegistry) -> HQResult; } -impl TValType for T where T: TRefType { +impl TValType for T +where + T: TRefType, +{ fn val_type(types: &TypeRegistry) -> HQResult { - Ok(TypeRegistry::ref_val( - T::HeapType::register(types)?, - T::NULLABLE, - )) + Ok(ValType::Ref(T::ref_type(types)?)) } } pub struct TI32; impl TValType for TI32 { - fn val_type(types: &TypeRegistry) -> HQResult { + fn val_type(_types: &TypeRegistry) -> HQResult { Ok(ValType::I32) } } @@ -219,6 +157,13 @@ impl TDefaultable for TI32 {} pub trait TFieldType { type ValType: TValType; const MUTABLE: bool; + + fn field_type(types: &TypeRegistry) -> HQResult { + Ok(FieldType { + element_type: StorageType::Val(Self::ValType::val_type(types)?), + mutable: Self::MUTABLE, + }) + } } pub struct TMutField(PhantomData); @@ -227,7 +172,7 @@ pub struct TConstField(PhantomData); impl TFieldType for TMutField { type ValType = T; const MUTABLE: bool = true; -} +} impl TFieldType for TConstField { type ValType = T; @@ -236,37 +181,36 @@ impl TFieldType for TConstField { pub trait TDefaultable: TValType {} - -trait CompTypeList { +trait TFieldList { fn fields(types: &TypeRegistry) -> HQResult>; } -impl CompTypeList for () { +impl TFieldList for () { fn fields(_: &TypeRegistry) -> HQResult> { Ok(vec![]) } } -impl CompTypeList for (Head, Tail) where Head: CompTypeList, Tail: TFieldType { +impl TFieldList for (Head, Tail) +where + Head: TFieldList, + Tail: TFieldType, +{ fn fields(types: &TypeRegistry) -> HQResult> { let mut fields = Head::fields(types)?; - fields.push( - FieldType { - mutable: Tail::MUTABLE, - element_type: StorageType::Val(Tail::ValType::val_type(types)?), - } - ); + fields.push(Tail::field_type(types)?); Ok(fields) } } pub struct TStruct(PhantomData); -impl CompTimeRegistrand for TStruct where Fields: CompTypeList { +impl CompTimeRegistrand for TStruct +where + Fields: TFieldList, +{ fn register(types: &TypeRegistry) -> HQResult { - types.struct_( - Fields::fields(types)? - ) + types.struct_(Fields::fields(types)?) } } @@ -281,5 +225,60 @@ impl CompTimeRegistrand for TArray } } +trait TValTypeList { + fn val_types(types: &TypeRegistry) -> HQResult>; +} + +impl TValTypeList for () { + fn val_types(_: &TypeRegistry) -> HQResult> { + Ok(vec![]) + } +} + +impl TValTypeList for (Head, Tail) +where + Head: TValTypeList, + Tail: TValType, +{ + fn val_types(types: &TypeRegistry) -> HQResult> { + let mut val_types = Head::val_types(types)?; + val_types.push(Tail::val_type(types)?); + Ok(val_types) + } +} + +pub struct TFunc(PhantomData, PhantomData); + +impl CompTimeRegistrand for TFunc +where + Params: TValTypeList, + Result: TValTypeList, +{ + fn register(types: &TypeRegistry) -> HQResult { + types.function(Params::val_types(types)?, Result::val_types(types)?) + } +} + +pub type TStepFunc = TFunc<(((), TI32), TNullable), ()>; + pub type TDynArrayField = TArray>; -pub type TDynArray = TStruct<(((), TMutField>>), TMutField)>; +pub type TDynArray = TStruct<( + ((), TMutField>>), + TMutField, +)>; + +pub type TStackStruct = TStruct<( + ((), TMutField>), + TConstField>, +)>; + +pub type TStackArray = TDynArray>; + +pub type TThreadArray = TDynArray>; + +pub type TTargetThreadsStruct = TStruct<( + (((), TMutField), TMutField), + TMutField>, +)>; + +pub type TTargetThreadArray = TArray>>; From 435604eac434919ddb8b28c5790b7f032d482ebd Mon Sep 17 00:00:00 2001 From: pufferfish101007 <50246616+pufferfish101007@users.noreply.github.com> Date: Thu, 20 Aug 2026 22:34:55 +0100 Subject: [PATCH 6/9] finish thread spawning routines, start cleaning up other references --- src/wasm/project.rs | 72 ++++----- src/wasm/registries.rs | 9 +- src/wasm/registries/functions.rs | 7 +- src/wasm/registries/functions/dyn_array.rs | 67 ++++++-- .../registries/functions/spawn_threads.rs | 144 +++++++++--------- src/wasm/registries/globals.rs | 2 +- src/wasm/registries/tables.rs | 27 +--- 7 files changed, 179 insertions(+), 149 deletions(-) diff --git a/src/wasm/project.rs b/src/wasm/project.rs index fae3c2d7..a88a829a 100644 --- a/src/wasm/project.rs +++ b/src/wasm/project.rs @@ -12,9 +12,9 @@ use super::{ExternalEnvironment, Registries}; use crate::ir::{Event, IrProject, IrType, StepIndex}; use crate::prelude::*; use crate::wasm::registries::functions::static_functions::{ - MarkWaitingFlag, SpawnNewThread, SpawnThreadInStack, + MarkWaitingFlag, SpawnNewThread, SpawnThreadFuncOverride, SpawnThreadInStack, }; -use crate::wasm::registries::types::{TStackStruct, TStepFunc}; +use crate::wasm::registries::types::{TStackArray, TStackStruct, TStepFunc}; use crate::wasm::{StepFunc, StepTarget, StringsTable, WasmFlags}; /// A respresentation of a WASM representation of a project. Cannot be created directly; @@ -137,25 +137,23 @@ impl WasmProject { .clone() .finish(&mut imports, self.registries().types())?; + let spawn_thread_func_override = SpawnThreadFuncOverride { + types: Rc::clone(self.registries().types()), + static_functions: Rc::clone(self.registries().static_functions()), + globals: Rc::clone(self.registries().globals()), + num_sprites: self.costume_names().len() as u32, + imported_func_count: self.imported_func_count()?, + }; + self.registries() .static_functions() - .register_override::(( - self.registries().types().register_comp::()?, - self.registries().types().register_comp::()?, - self.registries().types().stack_array_type()?, - self.registries().types().thread_struct_type()?, - self.threads_table_index()?, - ))?; + .try_register_override::( + spawn_thread_func_override.clone(), + )?; self.registries() .static_functions() - .register_override::(( - self.registries().types().register_comp::()?, - self.registries().types().register_comp::()?, - self.registries().types().stack_array_type()?, - self.registries().types().thread_struct_type()?, - self.threads_table_index()?, - ))?; + .try_register_override::(spawn_thread_func_override)?; self.registries() .static_functions() @@ -166,7 +164,7 @@ impl WasmProject { }], )?)?; - self.registries().static_functions().clone().finish( + Rc::unwrap_or_clone(self.registries().static_functions().clone()).finish( &mut functions, &mut exports, &mut codes, @@ -183,7 +181,7 @@ impl WasmProject { self.threads_count_global()?, self.spawn_new_thread_func()?, self.spawn_thread_in_stack_func()?, - self.threads_table_index()?, + self.threadss_global()?, self.imported_func_count()?, self.static_func_count()?, self.imported_global_count()?, @@ -276,7 +274,7 @@ impl WasmProject { exports.export("memory", ExportKind::Memory, 0); - self.registries().globals().clone().finish( + Rc::unwrap_or_clone(self.registries().globals().clone()).finish( &mut globals, &mut exports, self.imported_global_count()?, @@ -369,16 +367,6 @@ impl WasmProject { Ok(()) } - fn threads_table_index(&self, target: StepTarget) -> HQResult - where - N: TryFrom, - >::Error: fmt::Debug, - { - self.registries() - .tables() - .threads_table(target, self.registries().types()) - } - fn spawn_new_thread_func(&self) -> HQResult where N: TryFrom, @@ -407,6 +395,16 @@ impl WasmProject { self.registries().globals().threads_count() } + fn threadss_global(&self) -> HQResult + where + N: TryFrom, + >::Error: fmt::Debug, + { + self.registries() + .globals() + .threadss(self.registries().types(), self.costume_names().len() as u32) + } + #[expect(clippy::needless_pass_by_value, reason = "annoying to borrow a box")] fn finish_event( &self, @@ -444,7 +442,7 @@ impl WasmProject { self.threads_count_global()?, self.spawn_new_thread_func()?, self.spawn_thread_in_stack_func()?, - self.threads_table_index()?, + self.threadss_global()?, self.imported_func_count()?, self.static_func_count()?, self.imported_global_count()?, @@ -467,7 +465,7 @@ impl WasmProject { self.threads_count_global()?, self.spawn_new_thread_func()?, self.spawn_thread_in_stack_func()?, - self.threads_table_index()?, + self.threadss_global()?, self.imported_func_count()?, self.static_func_count()?, self.imported_global_count()?, @@ -566,7 +564,7 @@ impl WasmProject { self.threads_count_global()?, self.spawn_new_thread_func()?, self.spawn_thread_in_stack_func()?, - self.threads_table_index()?, + self.threadss_global()?, self.imported_func_count()?, self.static_func_count()?, self.imported_global_count()?, @@ -596,8 +594,14 @@ impl WasmProject { codes: &mut CodeSection, exports: &mut ExportSection, ) -> HQResult<()> { - let thread_struct_type = self.registries().types().thread_struct_type()?; - let stack_struct_ty = self.registries().types().stack_struct_type()?; + let thread_struct_type = self + .registries() + .types() + .register_comp::()?; + let stack_struct_ty = self + .registries() + .types() + .register_comp::()?; let mut tick_func = Function::new(vec![ (2, ValType::I32), diff --git a/src/wasm/registries.rs b/src/wasm/registries.rs index 13e5f801..376f6520 100644 --- a/src/wasm/registries.rs +++ b/src/wasm/registries.rs @@ -22,7 +22,7 @@ pub struct Registries { strings: Rc, tabled_strings: Rc, external_functions: ExternalFunctionRegistry, - static_functions: StaticFunctionRegistry, + static_functions: Rc, types: Rc, tables: TableRegistry, globals: Rc, @@ -39,6 +39,7 @@ impl Default for Registries { let types = Rc::new(TypeRegistry::default()); let variables = VariableRegistry::new(&globals, &strings, &tabled_strings); let lists = ListRegistry::new(&globals, &types, &strings, &tabled_strings); + let static_functions = Rc::new(StaticFunctionRegistry::default()); Self { globals, variables, @@ -48,7 +49,7 @@ impl Default for Registries { tables: TableRegistry::default(), types, sprites: SpriteRegistry::default(), - static_functions: StaticFunctionRegistry::default(), + static_functions, lists, } } @@ -67,7 +68,7 @@ impl Registries { &self.external_functions } - pub const fn static_functions(&self) -> &StaticFunctionRegistry { + pub const fn static_functions(&self) -> &Rc { &self.static_functions } @@ -79,7 +80,7 @@ impl Registries { &self.tables } - pub fn globals(&self) -> &GlobalRegistry { + pub fn globals(&self) -> &Rc { &self.globals } diff --git a/src/wasm/registries/functions.rs b/src/wasm/registries/functions.rs index 1a4e57b7..e7e32383 100644 --- a/src/wasm/registries/functions.rs +++ b/src/wasm/registries/functions.rs @@ -107,9 +107,10 @@ impl StaticFunctionRegistry { } pub mod static_functions { + pub use super::dyn_array::{ + DynArrayFuncOverride, DynArrayGet, DynArrayNew, DynArrayPop, DynArrayPush, + }; pub use super::mark_waiting_flag::MarkWaitingFlag; pub use super::pen_colour::{UpdatePenColorFromHSV, UpdatePenColorFromRGB}; - pub use super::spawn_threads::{ - SpawnNewThread, SpawnNewThreadOverride, SpawnThreadInStack, - }; + pub use super::spawn_threads::{SpawnNewThread, SpawnThreadFuncOverride, SpawnThreadInStack}; } diff --git a/src/wasm/registries/functions/dyn_array.rs b/src/wasm/registries/functions/dyn_array.rs index 6d174814..fa13ffe7 100644 --- a/src/wasm/registries/functions/dyn_array.rs +++ b/src/wasm/registries/functions/dyn_array.rs @@ -6,27 +6,30 @@ use wasm_gen::wasm_const; use super::{MaybeStaticFunction, StaticFunction}; use crate::prelude::*; use crate::wasm::registries::TypeRegistry; -use crate::wasm::registries::types::{TDynArray, TDynArrayField, TNonNullable, TValType}; +use crate::wasm::registries::types::{ + TDefaultable, TDynArray, TDynArrayField, TNonNullable, TValType, +}; +#[derive(Clone)] pub struct DynArrayFuncOverride { - types: Rc, + pub types: Rc, } /// Pushes an element to a dynamic (resizeable) array /// /// Takes 2 parameters: -/// ref dynamic_array - the dynamic array struct (obtained from `TDynArray` for `T: TValType`) +/// ref dynamic_array - the dynamic array struct (obtained from `TDynArray` for `T: TDefaultable`) /// t - the element pub struct DynArrayPush(PhantomData); -impl NamedRegistryItem for DynArrayPush { +impl NamedRegistryItem for DynArrayPush { const VALUE: MaybeStaticFunction = MaybeStaticFunction { static_function: None, maybe_populate: || None, }; } -impl TryNamedRegistryItemOverride - for DynArrayPush +impl + TryNamedRegistryItemOverride for DynArrayPush { fn try_override( DynArrayFuncOverride { types }: DynArrayFuncOverride, @@ -110,19 +113,19 @@ impl TryNamedRegistryItemOverride - the dynamic array struct (obtained from `TDynArray` for `T: TValType`) +/// ref dynamic_array - the dynamic array struct (obtained from `TDynArray` for `T: TDefaultable`) /// i32 - the index /// /// Returns t pub struct DynArrayGet(PhantomData); -impl NamedRegistryItem for DynArrayGet { +impl NamedRegistryItem for DynArrayGet { const VALUE: MaybeStaticFunction = MaybeStaticFunction { static_function: None, maybe_populate: || None, }; } -impl TryNamedRegistryItemOverride +impl TryNamedRegistryItemOverride for DynArrayGet { fn try_override( @@ -157,18 +160,18 @@ impl TryNamedRegistryItemOverride - the dynamic array struct (obtained from `TDynArray` for `T: TValType`) +/// ref dynamic_array - the dynamic array struct (obtained from `TDynArray` for `T: TDefaultable`) /// /// Returns t pub struct DynArrayPop(PhantomData); -impl NamedRegistryItem for DynArrayPop { +impl NamedRegistryItem for DynArrayPop { const VALUE: MaybeStaticFunction = MaybeStaticFunction { static_function: None, maybe_populate: || None, }; } -impl TryNamedRegistryItemOverride +impl TryNamedRegistryItemOverride for DynArrayPop { fn try_override( @@ -209,3 +212,43 @@ impl TryNamedRegistryItemOverride +pub struct DynArrayNew(PhantomData); +impl NamedRegistryItem for DynArrayNew { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + maybe_populate: || None, + }; +} + +impl TryNamedRegistryItemOverride + for DynArrayNew +{ + fn try_override( + DynArrayFuncOverride { types }: DynArrayFuncOverride, + ) -> HQResult { + let struct_type = types.register_comp::, u32>()?; + let array_type = types.register_comp::, u32>()?; + Ok(MaybeStaticFunction { + static_function: Some(StaticFunction { + export: None, + instructions: Box::from(wasm_const![ + LocalGet(0), + ArrayNewDefault(array_type), + I32Const(0), + StructNew(struct_type), + ] as &[_]), + params: Box::from([ValType::I32]), + returns: Box::from([>>::val_type(&types)?]), + locals: Box::from([]), + }), + maybe_populate: || None, + }) + } +} diff --git a/src/wasm/registries/functions/spawn_threads.rs b/src/wasm/registries/functions/spawn_threads.rs index d68ce24f..f7301df8 100644 --- a/src/wasm/registries/functions/spawn_threads.rs +++ b/src/wasm/registries/functions/spawn_threads.rs @@ -1,26 +1,29 @@ -use wasm_encoder::{ - AbstractHeapType, BlockType as WasmBlockType, HeapType, MemArg, RefType, ValType, -}; +use wasm_encoder::{BlockType as WasmBlockType, MemArg, ValType}; use wasm_gen::wasm_const; use super::{MaybeStaticFunction, StaticFunction}; use crate::prelude::*; use crate::wasm::mem_layout; -use crate::wasm::registries::functions::dyn_array::{DynArrayGet, DynArrayPop, DynArrayPush}; +use crate::wasm::registries::functions::dyn_array::{ + DynArrayGet, DynArrayNew, DynArrayPop, DynArrayPush, +}; use crate::wasm::registries::types::{ - THeapType, TNonNullable, TNullable, TStackArray, TStackStruct, TStepFunc, TStructRef, - TTargetThreadArray, TValType, + THeapType, TNonNullable, TNullable, TStackArray, TStackStruct, TStepFunc, TTargetThreadArray, + TThreadArray, TValType, }; use crate::wasm::registries::{GlobalRegistry, StaticFunctionRegistry, TypeRegistry}; +#[derive(Clone)] pub struct SpawnThreadFuncOverride { - types: Rc, - globals: Rc, - static_functions: Rc, - num_sprites: u32, - imported_func_count: u32, + pub types: Rc, + pub globals: Rc, + pub static_functions: Rc, + pub num_sprites: u32, + pub imported_func_count: u32, } +type StackStructRef = TNullable; + /// Spawns a new thread in the same stack (i.e. a thread that yields back to the current /// thread once it completes.) The step that is provided to return to will be written into /// the current stack frame, and the new thread's step is added to the top of the current @@ -55,8 +58,7 @@ impl TryNamedRegistryItemOverride let stack_struct_type = types.register_comp::()?; let target_threads_type = types.register_comp::()?; let target_threads_global = globals.threadss(&types, num_sprites)?; - let dyn_array_push = static_functions.register::, u32>()?; - type StackStruct = TNullable; + let dyn_array_push = static_functions.register::, u32>()?; Ok(MaybeStaticFunction { static_function: Some(StaticFunction { export: None, @@ -86,7 +88,7 @@ impl TryNamedRegistryItemOverride LocalTee(5), Call( imported_func_count - + static_functions.register::, u32>()? + + static_functions.register::, u32>()? ), Drop, LocalGet(5), @@ -104,13 +106,11 @@ impl TryNamedRegistryItemOverride ValType::I32, ValType::I32, >::val_type(&types)?, - >::val_type(&types)?, + StackStructRef::val_type(&types)?, >::val_type(&types)?, ]), returns: Box::from([]), - locals: Box::from([ - >::val_type(&types)?, - ]), + locals: Box::from([>::val_type(&types)?]), }), maybe_populate: || None, }) @@ -121,9 +121,9 @@ impl TryNamedRegistryItemOverride /// immediately, instead leaving that for the scheduler or calling function to do so. /// /// Takes 3 parameters: +/// - i32 - the index of the target to spawn a thread for /// - step funcref - the step to spawn /// - ref null struct - the stack struct to spawn it with -/// - i32 - the index of the sprite to spawn a thread for, or -1 for the stage /// /// Override with: /// - u32 - the index of the step func type @@ -138,70 +138,76 @@ impl NamedRegistryItem for SpawnNewThread { maybe_populate: || None, }; } -pub struct SpawnNewThreadOverride { - func_ty: u32, - stack_struct_ty: u32, - stack_array_ty: u32, - thread_struct_ty: u32, - threads_array_index: u32, - threads_array_ty: u32, -} -impl NamedRegistryItemOverride for SpawnNewThread { - fn r#override( - SpawnNewThreadOverride { - func_ty, - stack_struct_ty, - stack_array_ty, - thread_struct_ty, - threads_array_index, - threads_array_ty, - }: SpawnNewThreadOverride, - ) -> MaybeStaticFunction { - MaybeStaticFunction { +impl TryNamedRegistryItemOverride for SpawnNewThread { + fn try_override( + SpawnThreadFuncOverride { + types, + globals, + static_functions, + num_sprites, + imported_func_count, + }: SpawnThreadFuncOverride, + ) -> HQResult { + let stack_struct_type = types.register_comp::()?; + let target_threads_type = types.register_comp::()?; + let target_threads_global = globals.threadss(&types, num_sprites)?; + Ok(MaybeStaticFunction { static_function: Some(StaticFunction { export: None, params: Box::from([ - ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(func_ty), - }), - ValType::Ref(RefType { - nullable: true, - heap_type: wasm_encoder::HeapType::Abstract { - shared: false, - ty: AbstractHeapType::Struct, - }, - }), + ValType::I32, + >::val_type(&types)?, + StackStructRef::val_type(&types)?, ]), returns: Box::from([]), - locals: Box::from([ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(stack_array_ty), - })]), + locals: Box::from([>::val_type(&types)?]), instructions: { - const STACK_ARRAY_LOCAL: u32 = 3; (wasm_const![ - // TODO get - I32Const(1), // stack size - // todo: play around with initial size of stack array + LocalGet(0), + I32Eqz, // if this is not the stage, we need to find its layer + If(WasmBlockType::Empty), + LocalGet(0), + I32Const(mem_layout::sprite::BLOCK_SIZE as i32), + I32Mul, + I32Load16U(MemArg { + offset: (mem_layout::stage::BLOCK_SIZE + mem_layout::sprite::LAYER) + as u64, + align: 1, + memory_index: 0, + }), + LocalSet(0), // local 0 is now index of sprite in + End, + GlobalGet(target_threads_global), + LocalGet(0), + ArrayGet(target_threads_type), I32Const(8), - ArrayNewDefault(stack_array_ty), - LocalTee(STACK_ARRAY_LOCAL), - StructNew(thread_struct_ty), - I32Const(0), // index 0 into stack array - I32Const(1), // stack size - LocalGet(0), // step func - LocalGet(1), // stack struct param - StructNew(stack_struct_ty), // stack struct - ArraySet(stack_array_ty), // set 0th element of stack array to stack struct - ArraySet(threads_array_ty), + Call( + imported_func_count + + static_functions + .register::, u32>()? + ), + LocalTee(3), + LocalGet(1), + LocalGet(2), + StructNew(stack_struct_type), + Call( + imported_func_count + + static_functions + .register::, u32>()? + ), + LocalGet(3), + Call( + imported_func_count + + static_functions + .register::>, u32>()? + ), End, ] as &[_]) .into() }, }), maybe_populate: || None, - } + }) } } diff --git a/src/wasm/registries/globals.rs b/src/wasm/registries/globals.rs index e64d064c..4babf3bc 100644 --- a/src/wasm/registries/globals.rs +++ b/src/wasm/registries/globals.rs @@ -63,7 +63,7 @@ impl GlobalRegistry { ( >::val_type(&types)?, ConstExpr::extended( - (0..num_sprites) + (0..=num_sprites) // stage + sprites .map(|i| { [ Instruction::I32Const(i as i32), diff --git a/src/wasm/registries/tables.rs b/src/wasm/registries/tables.rs index 1d639df0..eacd01b4 100644 --- a/src/wasm/registries/tables.rs +++ b/src/wasm/registries/tables.rs @@ -1,11 +1,6 @@ -use wasm_encoder::{ - ConstExpr, ExportKind, ExportSection, HeapType, RefType, TableSection, TableType, -}; +use wasm_encoder::{ConstExpr, ExportKind, ExportSection, RefType, TableSection, TableType}; use crate::prelude::*; -use crate::wasm::StepTarget; -use crate::wasm::registries::TypeRegistry; -use crate::wasm::registries::types::TThreadStruct; #[derive(Clone, Debug)] pub struct TableOptions { @@ -25,26 +20,6 @@ impl RegistryType for TableRegistrar { pub type TableRegistry = NamedRegistry; impl TableRegistry { - pub fn threads_table(&self, target: StepTarget, types: &Rc) -> HQResult - where - N: TryFrom, - >::Error: fmt::Debug, - { - self.register_dyn( - format!("threads{}", target.suffix_id()).into(), - TableOptions { - element_type: RefType { - nullable: true, - heap_type: HeapType::Concrete(types.register_comp::()?), - }, - min: 0, - max: None, - init: None, - export_name: Some("threads"), - }, - ) - } - pub fn finish(self, tables: &mut TableSection, exports: &mut ExportSection) { for ( _key, From 02599e969e167b6d1206e5d834108ebda290e347 Mon Sep 17 00:00:00 2001 From: pufferfish101007 <50246616+pufferfish101007@users.noreply.github.com> Date: Sun, 23 Aug 2026 21:33:37 +0100 Subject: [PATCH 7/9] rewrite more functions and instructions to use new thread layout --- src/instructions/control/stop_all.rs | 66 ++++---- .../event/poll_waiting_threads.rs | 102 ++++++------ src/instructions/procedures/argument.rs | 4 +- src/wasm/func.rs | 8 +- src/wasm/project.rs | 154 ++++++++++-------- src/wasm/registries/functions.rs | 3 +- src/wasm/registries/functions/dyn_array.rs | 130 +++++++++++++++ .../registries/functions/spawn_threads.rs | 11 +- src/wasm/registries/types.rs | 2 +- 9 files changed, 315 insertions(+), 165 deletions(-) diff --git a/src/instructions/control/stop_all.rs b/src/instructions/control/stop_all.rs index 6dc6540a..441d2547 100644 --- a/src/instructions/control/stop_all.rs +++ b/src/instructions/control/stop_all.rs @@ -2,7 +2,8 @@ use wasm_encoder::HeapType; use super::super::prelude::*; use crate::instructions_test; -use crate::wasm::StepTarget; +use crate::wasm::registries::functions::static_functions::DynArrayClear; +use crate::wasm::registries::types::{TNullable, TTargetThreadArray, TThreadArray}; fn clear_thread( threads_count: u32, @@ -20,43 +21,42 @@ fn clear_thread( } pub fn wasm(func: &StepFunc, _inputs: Rc<[IrType]>) -> HQResult> { - let thread_struct_type = func.registries().types().thread_struct_type()?; + let local_target_counter = func.local(ValType::I32)?; + func.free_local(local_target_counter)?; + let threadss_global = func + .registries() + .globals() + .threadss(func.registries().types(), func.costume_names().len() as u32)?; let total_threads_count = func.registries().globals().threads_count()?; - let num_sprites = func.costume_names().len() as u32; + let num_targets = 1 + func.costume_names().len() as i32; + let array_type = func + .registries() + .types() + .register_comp::()?; + let dyn_array_clear = func + .registries() + .static_functions() + .register::>, _>()?; Ok(wasm![ I32Const(0), #LazyGlobalSet(total_threads_count), - ] - .into_iter() - .chain(clear_thread( - func.registries() - .globals() - .target_threads_count(StepTarget::Stage)?, - func.registries() - .tables() - .threads_table(StepTarget::Stage, func.registries().types())?, - thread_struct_type, - )) - .chain( - (0..num_sprites) - .map(|n| { - let step_target = StepTarget::Sprite(n); - Ok(clear_thread( - func.registries() - .globals() - .target_threads_count(step_target)?, - func.registries() - .tables() - .threads_table(step_target, func.registries().types())?, - thread_struct_type, - )) - }) - .collect::>>()? - .into_iter() - .flatten(), - ) - .collect()) + I32Const(0), + LocalSet(local_target_counter), + Loop(wasm_encoder::BlockType::Empty), + #LazyGlobalGet(threadss_global), + LocalGet(local_target_counter), + ArrayGet(array_type), + #StaticFunctionCall(dyn_array_clear), + LocalGet(local_target_counter), + I32Const(1), + I32Add, + LocalTee(local_target_counter), + I32Const(num_targets), + I32LtS, + BrIf(0), + End, + ]) } pub fn acceptable_inputs() -> HQResult> { diff --git a/src/instructions/event/poll_waiting_threads.rs b/src/instructions/event/poll_waiting_threads.rs index da78001b..0d6df6c4 100644 --- a/src/instructions/event/poll_waiting_threads.rs +++ b/src/instructions/event/poll_waiting_threads.rs @@ -4,48 +4,48 @@ //! //! Returns 1 if still waiting on any threads, 0 otherwise. -use wasm_encoder::{BlockType as WasmBlockType, FieldType, HeapType, StorageType}; +use wasm_encoder::BlockType as WasmBlockType; use super::super::prelude::*; use crate::wasm::StepFunc; +use crate::wasm::registries::functions::static_functions::DynArrayLen; +use crate::wasm::registries::types::{ + TArray, TConstField, THeapType, TMutField, TNonNullable, TNullable, TStackArray, TStackStruct, + TStruct, TValType, +}; + +type TWaitingThreadArray = TArray>>; +type TPollStruct = TStruct<((), TConstField>)>; pub fn wasm(func: &StepFunc, _inputs: Rc<[IrType]>) -> HQResult> { - let i32_array_type = func - .registries() - .types() - .array(StorageType::Val(ValType::I32), true)?; - let poll_struct_type = func.registries().types().struct_(vec![FieldType { - mutable: false, - element_type: StorageType::Val(ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(i32_array_type), - })), - }])?; - - let arr_local = func.local(ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(i32_array_type), - }))?; + let types = Rc::clone(func.registries().types()); + + let thread_array_type = types.register_comp::()?; + let poll_struct_type = types.register_comp::()?; + + let arr_local = func.local(>::val_type(&types)?)?; func.free_local(arr_local)?; let arr_len_local = func.local(ValType::I32)?; let i_local = func.local(ValType::I32)?; + let stack_local = func.local(>::val_type(&types)?)?; let wait_local = func.local(ValType::I32)?; func.free_local(arr_len_local)?; + func.free_local(stack_local)?; func.free_local(i_local)?; func.free_local(wait_local)?; - let threads_table = func + let dyn_array_len = func .registries() - .tables() - .threads_table(func.target(), func.registries().types())?; + .static_functions() + .register::>, _>()?; Ok(wasm![ - LocalGet(1), // this should never have additional function arguments so this is fine - RefCastNonNull(HeapType::Concrete(poll_struct_type)), + LocalGet(1), // this step should never have additional function arguments so this is fine + RefCastNonNull(TPollStruct::heap_type(&types)?), StructGet { struct_type_index: poll_struct_type, - field_index: 0 + field_index: 0, }, LocalTee(arr_local), ArrayLen, @@ -54,35 +54,37 @@ pub fn wasm(func: &StepFunc, _inputs: Rc<[IrType]>) -> HQResult`. @@ -143,6 +147,7 @@ impl WasmProject { globals: Rc::clone(self.registries().globals()), num_sprites: self.costume_names().len() as u32, imported_func_count: self.imported_func_count()?, + imported_global_count: self.imported_global_count()?, }; self.registries() @@ -594,92 +599,101 @@ impl WasmProject { codes: &mut CodeSection, exports: &mut ExportSection, ) -> HQResult<()> { - let thread_struct_type = self - .registries() - .types() - .register_comp::()?; - let stack_struct_ty = self - .registries() - .types() - .register_comp::()?; + let types = Rc::clone(self.registries().types()); let mut tick_func = Function::new(vec![ - (2, ValType::I32), - ( - 1, - ValType::Ref(RefType { - nullable: true, - heap_type: HeapType::Concrete(thread_struct_type), - }), - ), - ( - 1, - ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(stack_struct_ty), - }), - ), + (3, ValType::I32), + (1, >::val_type(&types)?), + (1, >::val_type(&types)?), + (1, >::val_type(&types)?), ]); - let step_func_ty = self.registries().types().step_func()?; - let stack_array_ty = self.registries().types().stack_array_type()?; + let stack_struct_type = types.register_comp::()?; + let target_thread_struct_type = types.register_comp::()?; + let target_threads_array_type = types.register_comp::()?; + let step_func_ty = types.register_comp::()?; + + let threadss_global = self.threadss_global()?; + + let targets_num = 1 + self.costume_names().len() as i32; + + hq_assert!(targets_num > 0); + + const LOCAL_TARGET_INDEX: u32 = 0; + const LOCAL_STACK_INDEX: u32 = 1; + const LOCAL_THREADS_NUM: u32 = 2; + const LOCAL_THREAD_LIST: u32 = 3; + const LOCAL_THREAD: u32 = 4; + const LOCAL_STEP: u32 = 5; let instructions = wasm![ - TableSize(self.threads_table_index()?), - LocalTee(1), - I32Eqz, - BrIf(0), Loop(WasmBlockType::Empty), - LocalGet(0), - LocalGet(0), - TableGet(self.threads_table_index()?), - LocalTee(2), - RefIsNull, - If(WasmBlockType::Empty), - LocalGet(0), - I32Const(1), - I32Add, - LocalTee(0), - LocalGet(1), - I32LtS, - If(WasmBlockType::Empty), - Br(2), - Else, - Return, - End, - End, - LocalGet(2), - RefAsNonNull, + #LazyGlobalGet(threadss_global), + LocalGet(LOCAL_TARGET_INDEX), + ArrayGet(target_threads_array_type), StructGet { - struct_type_index: thread_struct_type, - field_index: 1 + struct_type_index: target_thread_struct_type, + field_index: 1, }, - LocalGet(2), + LocalTee(LOCAL_THREAD_LIST), + #StaticFunctionCall( + self.registries() + .static_functions() + .register::>, u32>()? + ), + LocalTee(LOCAL_THREADS_NUM), + I32Eqz, + BrIf(0), + I32Const(0), + LocalSet(LOCAL_STACK_INDEX), + Loop(WasmBlockType::Empty), + LocalGet(LOCAL_THREAD_LIST), + LocalGet(LOCAL_STACK_INDEX), + #StaticFunctionCall( + self.registries() + .static_functions() + .register::>, u32>()? + ), RefAsNonNull, - StructGet { - struct_type_index: thread_struct_type, - field_index: 0 - }, + LocalTee(LOCAL_THREAD), + LocalGet(LOCAL_THREAD), + #StaticFunctionCall( + self.registries() + .static_functions() + .register::>, u32>()? + ), I32Const(1), I32Sub, - ArrayGet(stack_array_ty), + #StaticFunctionCall( + self.registries() + .static_functions() + .register::>, u32>()? + ), + LocalTee(LOCAL_STEP), RefAsNonNull, - LocalTee(3), StructGet { - struct_type_index: stack_struct_ty, - field_index: 1 + struct_type_index: stack_struct_type, + field_index: 1, }, LocalGet(3), StructGet { - struct_type_index: stack_struct_ty, - field_index: 0 + struct_type_index: stack_struct_type, + field_index: 0, }, CallRef(step_func_ty), - LocalGet(0), + LocalGet(LOCAL_STACK_INDEX), I32Const(1), I32Add, - LocalTee(0), - LocalGet(1), + LocalTee(LOCAL_STACK_INDEX), + LocalGet(LOCAL_THREADS_NUM), + I32LtS, + BrIf(0), + End, + LocalGet(LOCAL_TARGET_INDEX), + I32Const(1), + I32Add, + LocalTee(LOCAL_TARGET_INDEX), + I32Const(targets_num), I32LtS, BrIf(0), End, @@ -691,7 +705,7 @@ impl WasmProject { self.threads_count_global()?, self.spawn_new_thread_func()?, self.spawn_thread_in_stack_func()?, - self.threads_table_index()?, + self.threadss_global()?, self.imported_func_count()?, self.static_func_count()?, self.imported_global_count()?, @@ -700,7 +714,7 @@ impl WasmProject { } } tick_func.instruction(&Instruction::End); - funcs.function(self.registries().types().function(vec![], vec![])?); + funcs.function(types.register_comp::, _>()?); codes.function(&tick_func); exports.export( "tick", diff --git a/src/wasm/registries/functions.rs b/src/wasm/registries/functions.rs index e7e32383..15940774 100644 --- a/src/wasm/registries/functions.rs +++ b/src/wasm/registries/functions.rs @@ -108,7 +108,8 @@ impl StaticFunctionRegistry { pub mod static_functions { pub use super::dyn_array::{ - DynArrayFuncOverride, DynArrayGet, DynArrayNew, DynArrayPop, DynArrayPush, + DynArrayClear, DynArrayFuncOverride, DynArrayGet, DynArrayLen, DynArrayNew, DynArrayPop, + DynArrayPush, }; pub use super::mark_waiting_flag::MarkWaitingFlag; pub use super::pen_colour::{UpdatePenColorFromHSV, UpdatePenColorFromRGB}; diff --git a/src/wasm/registries/functions/dyn_array.rs b/src/wasm/registries/functions/dyn_array.rs index fa13ffe7..2ab31b27 100644 --- a/src/wasm/registries/functions/dyn_array.rs +++ b/src/wasm/registries/functions/dyn_array.rs @@ -157,6 +157,54 @@ impl TryNamedRegistryItemOverride - the dynamic array struct (obtained from `TDynArray` for `T: TDefaultable`) +/// i32 - the index +/// t - the element +pub struct DynArraySet(PhantomData); +impl NamedRegistryItem for DynArraySet { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + maybe_populate: || None, + }; +} + +impl TryNamedRegistryItemOverride + for DynArraySet +{ + fn try_override( + DynArrayFuncOverride { types }: DynArrayFuncOverride, + ) -> HQResult { + let struct_type = types.register_comp::, u32>()?; + let array_type = types.register_comp::, u32>()?; + Ok(MaybeStaticFunction { + static_function: Some(StaticFunction { + export: None, + instructions: Box::from(wasm_const![ + LocalGet(0), + StructGet { + struct_type_index: struct_type, + field_index: 0, + }, + LocalGet(1), + LocalGet(2), + ArrayGet(array_type), + ] as &[_]), + params: Box::from([ + >>::val_type(&types)?, + ValType::I32, + T::val_type(&types)?, + ]), + returns: Box::from([T::val_type(&types)?]), + locals: Box::from([]), + }), + maybe_populate: || None, + }) + } +} + /// Pops the last element from a dynamic (resizeable) array /// /// Takes 1 parameters: @@ -252,3 +300,85 @@ impl TryNamedRegistryItemOverride - the dynamic array +/// +/// Returns i32 +pub struct DynArrayLen(PhantomData); +impl NamedRegistryItem for DynArrayLen { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + maybe_populate: || None, + }; +} + +impl TryNamedRegistryItemOverride + for DynArrayLen +{ + fn try_override( + DynArrayFuncOverride { types }: DynArrayFuncOverride, + ) -> HQResult { + let struct_type = types.register_comp::, u32>()?; + Ok(MaybeStaticFunction { + static_function: Some(StaticFunction { + export: None, + instructions: Box::from(wasm_const![ + LocalGet(0), + StructGet { + struct_type_index: struct_type, + field_index: 1 + }, + ] as &[_]), + params: Box::from([>>::val_type(&types)?]), + returns: Box::from([ValType::I32]), + locals: Box::from([]), + }), + maybe_populate: || None, + }) + } +} + +/// Clears the given dynamic array to length 0 (but doesn't actually drop any of the elements) +/// +/// Takes 1 parameters: +/// ref dynamic_array - the dynamic array +pub struct DynArrayClear(PhantomData); +impl NamedRegistryItem for DynArrayClear { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + maybe_populate: || None, + }; +} + +impl TryNamedRegistryItemOverride + for DynArrayClear +{ + fn try_override( + DynArrayFuncOverride { types }: DynArrayFuncOverride, + ) -> HQResult { + let struct_type = types.register_comp::, u32>()?; + Ok(MaybeStaticFunction { + static_function: Some(StaticFunction { + export: None, + instructions: Box::from(wasm_const![ + LocalGet(0), + I32Const(0), + StructSet { + struct_type_index: struct_type, + field_index: 1 + }, + ] as &[_]), + params: Box::from([>>::val_type(&types)?]), + returns: Box::from([ValType::I32]), + locals: Box::from([]), + }), + maybe_populate: || None, + }) + } +} + + + diff --git a/src/wasm/registries/functions/spawn_threads.rs b/src/wasm/registries/functions/spawn_threads.rs index f7301df8..cc6bfb30 100644 --- a/src/wasm/registries/functions/spawn_threads.rs +++ b/src/wasm/registries/functions/spawn_threads.rs @@ -20,6 +20,7 @@ pub struct SpawnThreadFuncOverride { pub static_functions: Rc, pub num_sprites: u32, pub imported_func_count: u32, + pub imported_global_count: u32, } type StackStructRef = TNullable; @@ -53,11 +54,12 @@ impl TryNamedRegistryItemOverride static_functions, num_sprites, imported_func_count, + imported_global_count, }: SpawnThreadFuncOverride, ) -> HQResult { let stack_struct_type = types.register_comp::()?; let target_threads_type = types.register_comp::()?; - let target_threads_global = globals.threadss(&types, num_sprites)?; + let target_threads_global: u32 = globals.threadss(&types, num_sprites)?; let dyn_array_push = static_functions.register::, u32>()?; Ok(MaybeStaticFunction { static_function: Some(StaticFunction { @@ -76,7 +78,7 @@ impl TryNamedRegistryItemOverride }), LocalSet(0), // local 0 is now index of sprite in End, - GlobalGet(target_threads_global), + GlobalGet(imported_global_count + target_threads_global), LocalGet(0), ArrayGet(target_threads_type), LocalGet(1), @@ -147,11 +149,12 @@ impl TryNamedRegistryItemOverride static_functions, num_sprites, imported_func_count, + imported_global_count, }: SpawnThreadFuncOverride, ) -> HQResult { let stack_struct_type = types.register_comp::()?; let target_threads_type = types.register_comp::()?; - let target_threads_global = globals.threadss(&types, num_sprites)?; + let target_threads_global: u32 = globals.threadss(&types, num_sprites)?; Ok(MaybeStaticFunction { static_function: Some(StaticFunction { export: None, @@ -178,7 +181,7 @@ impl TryNamedRegistryItemOverride }), LocalSet(0), // local 0 is now index of sprite in End, - GlobalGet(target_threads_global), + GlobalGet(imported_global_count + target_threads_global), LocalGet(0), ArrayGet(target_threads_type), I32Const(8), diff --git a/src/wasm/registries/types.rs b/src/wasm/registries/types.rs index 8ae3ffc9..14f00c61 100644 --- a/src/wasm/registries/types.rs +++ b/src/wasm/registries/types.rs @@ -277,7 +277,7 @@ pub type TStackArray = TDynArray>; pub type TThreadArray = TDynArray>; pub type TTargetThreadsStruct = TStruct<( - (((), TMutField), TMutField), + ((), TMutField), TMutField>, )>; From 30ec8732c48a9f55e975654f5190848ce303d306 Mon Sep 17 00:00:00 2001 From: pufferfish101007 <50246616+pufferfish101007@users.noreply.github.com> Date: Sun, 23 Aug 2026 23:23:45 +0100 Subject: [PATCH 8/9] remove a layer of indirection in thread referencing --- .../registries/functions/spawn_threads.rs | 68 +++++-------------- src/wasm/registries/types.rs | 2 +- 2 files changed, 18 insertions(+), 52 deletions(-) diff --git a/src/wasm/registries/functions/spawn_threads.rs b/src/wasm/registries/functions/spawn_threads.rs index cc6bfb30..5dc084fa 100644 --- a/src/wasm/registries/functions/spawn_threads.rs +++ b/src/wasm/registries/functions/spawn_threads.rs @@ -1,14 +1,12 @@ -use wasm_encoder::{BlockType as WasmBlockType, MemArg, ValType}; +use wasm_encoder::{BlockType as WasmBlockType, HeapType, MemArg, ValType}; use wasm_gen::wasm_const; use super::{MaybeStaticFunction, StaticFunction}; use crate::prelude::*; use crate::wasm::mem_layout; -use crate::wasm::registries::functions::dyn_array::{ - DynArrayGet, DynArrayNew, DynArrayPop, DynArrayPush, -}; +use crate::wasm::registries::functions::dyn_array::{DynArrayNew, DynArrayPop, DynArrayPush}; use crate::wasm::registries::types::{ - THeapType, TNonNullable, TNullable, TStackArray, TStackStruct, TStepFunc, TTargetThreadArray, + TNonNullable, TNullable, TStackArray, TStackStruct, TStepFunc, TTargetThreadArray, TThreadArray, TValType, }; use crate::wasm::registries::{GlobalRegistry, StaticFunctionRegistry, TypeRegistry}; @@ -32,11 +30,10 @@ type StackStructRef = TNullable; /// yielding to the provided next step. /// /// Takes 4 parameters: -/// - i32 - the index of the calling target -/// - i32 - the current thread index -/// - step funcref - the step to spawn +/// - ref stack_array - the current stack +/// - ref step_func - the step to spawn /// - structref - the structref to pass to the step being spawned -/// - step funcref - the step to return to after +/// - ref step_func - the step to return to after pub struct SpawnThreadInStack; impl NamedRegistryItem for SpawnThreadInStack { const VALUE: MaybeStaticFunction = MaybeStaticFunction { @@ -50,69 +47,45 @@ impl TryNamedRegistryItemOverride fn try_override( SpawnThreadFuncOverride { types, - globals, static_functions, - num_sprites, imported_func_count, - imported_global_count, + .. }: SpawnThreadFuncOverride, ) -> HQResult { let stack_struct_type = types.register_comp::()?; - let target_threads_type = types.register_comp::()?; - let target_threads_global: u32 = globals.threadss(&types, num_sprites)?; let dyn_array_push = static_functions.register::, u32>()?; Ok(MaybeStaticFunction { static_function: Some(StaticFunction { export: None, instructions: Box::from(wasm_const![ LocalGet(0), - I32Eqz, // if this is not the stage, we need to find its layer - If(WasmBlockType::Empty), - LocalGet(0), - I32Const(mem_layout::sprite::BLOCK_SIZE as i32), - I32Mul, - I32Load16U(MemArg { - offset: (mem_layout::stage::BLOCK_SIZE + mem_layout::sprite::LAYER) as u64, - align: 1, - memory_index: 0, - }), - LocalSet(0), // local 0 is now index of sprite in - End, - GlobalGet(imported_global_count + target_threads_global), - LocalGet(0), - ArrayGet(target_threads_type), - LocalGet(1), - Call( - imported_func_count - + static_functions - .register::>, u32>()? - ), - LocalTee(5), Call( imported_func_count + static_functions.register::, u32>()? ), Drop, - LocalGet(5), - LocalGet(4), - RefNull(TStackStruct::heap_type(&types)?), + LocalGet(0), + LocalGet(3), + RefNull(HeapType::Abstract { + shared: false, + ty: wasm_encoder::AbstractHeapType::Struct + }), StructNew(stack_struct_type), Call(imported_func_count + dyn_array_push), // TODO: this will do unnecessary bounds checks. Just mutate the last element. - LocalGet(5), + LocalGet(0), + LocalGet(1), LocalGet(2), - LocalGet(3), StructNew(stack_struct_type), Call(imported_func_count + dyn_array_push), ] as &[_]), params: Box::from([ - ValType::I32, - ValType::I32, + >::val_type(&types)?, >::val_type(&types)?, StackStructRef::val_type(&types)?, >::val_type(&types)?, ]), returns: Box::from([]), - locals: Box::from([>::val_type(&types)?]), + locals: Box::from([]), }), maybe_populate: || None, }) @@ -126,13 +99,6 @@ impl TryNamedRegistryItemOverride /// - i32 - the index of the target to spawn a thread for /// - step funcref - the step to spawn /// - ref null struct - the stack struct to spawn it with -/// -/// Override with: -/// - u32 - the index of the step func type -/// - u32 - the index of the stack struct type -/// - u32 - the index of the stack array type -/// - u32 - the index of the thread struct type -/// - u32 - the index of the threads array pub struct SpawnNewThread; impl NamedRegistryItem for SpawnNewThread { const VALUE: MaybeStaticFunction = MaybeStaticFunction { diff --git a/src/wasm/registries/types.rs b/src/wasm/registries/types.rs index 14f00c61..8afd5ba1 100644 --- a/src/wasm/registries/types.rs +++ b/src/wasm/registries/types.rs @@ -259,7 +259,7 @@ where } } -pub type TStepFunc = TFunc<(((), TI32), TNullable), ()>; +pub type TStepFunc = TFunc<(((), TNonNullable), TNullable), ()>; pub type TDynArrayField = TArray>; pub type TDynArray = TStruct<( From c5c2071e4cd66fe3c55a786841445ed0bc2a5297 Mon Sep 17 00:00:00 2001 From: pufferfish101007 <50246616+pufferfish101007@users.noreply.github.com> Date: Fri, 4 Sep 2026 20:41:57 +0100 Subject: [PATCH 9/9] finally a typesafe representation of mutually recursive types? --- Cargo.toml | 6 +- rust-toolchain.toml | 2 +- src/ir/blocks/special.rs | 2 +- src/lib.rs | 7 +- src/wasm/project.rs | 8 +- src/wasm/registries/functions/dyn_array.rs | 32 +- .../registries/functions/spawn_threads.rs | 16 +- src/wasm/registries/globals.rs | 4 +- src/wasm/registries/types.rs | 595 +++++++++++++++--- 9 files changed, 546 insertions(+), 126 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b7826d25..b351c1ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ publish = false serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } serde_json = { version = "1.0.150", default-features = false, features = ["alloc"] } enum-field-getter = { path = "enum-field-getter" } -wasm-encoder = "0.245.1" +wasm-encoder = "0.258.0" indexmap = { version = "2.14.0", default-features = false } hashers = "1.0.1" uuid = { version = "1.23.3", default-features = false, features = ["v4", "js"] } @@ -22,8 +22,8 @@ wasm-gen = { path = "wasm-gen", version = "0.2.0" } petgraph = { version = "0.8.1", default-features = false, features = ["stable_graph"] } [dev-dependencies] -wasmparser = { git = "https://github.com/pufferfish101007/wasm-tools.git", rev = "4e9ffc0" } -wasmprinter = "0.245.1" +wasmparser = "0.258.0" +wasmprinter = "0.258.0" [target.'cfg(not(target_family = "wasm"))'.dev-dependencies] # ezno-checker = { git = "https://github.com/kaleidawave/ezno.git", rev = "96d5058bdbb0cde924be008ca1e5a67fe39f46b9" } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index a4540fe6..42ba26da 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2026-08-07" +channel = "nightly-2026-08-31" targets = [ "wasm32-unknown-unknown" ] \ No newline at end of file diff --git a/src/ir/blocks/special.rs b/src/ir/blocks/special.rs index c0b27b7c..e9acf8f0 100644 --- a/src/ir/blocks/special.rs +++ b/src/ir/blocks/special.rs @@ -106,7 +106,7 @@ pub fn from_special_block( 9 => { let hex = (*SHORTHAND_HEX_COLOUR_REGEX).replace(value, "$1$1$2$2$3$3"); if let Some(captures) = (*HEX_COLOUR_REGEX).captures(&hex) { - if let box [r, g, b] = (1..4) + if let deref!([r, g, b]) = (1..4) .map(|i| &captures[i]) .map(|capture| { u8::from_str_radix(capture, 16) diff --git a/src/lib.rs b/src/lib.rs index f752682d..6de882e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,14 @@ #![feature(stmt_expr_attributes)] // used in error.rs for panic mode #![feature(associated_type_defaults)] // used in registry.rs for default key type for NamedRegistry -#![feature(box_patterns)] // used in ir/blocks/special.rs to match Box<[_]> as array +#![feature(deref_patterns)] // used in ir/blocks/special.rs to match Box<[_]> as array #![feature(iterator_try_reduce)] // used in instructions/input_switcher.rs for building return type #![feature(try_find)] // used in ir/proc.rs for finding prototype/def blocks #![feature(arbitrary_self_types)] // used in ir/types.rs to take `&mut Rc` as self type for `TypeStack` +#![feature(macro_metavar_expr_concat)] +#![feature(macro_metavar_expr)] +#![feature(impl_restriction)] +#![feature(min_specialization)] + #![doc(html_logo_url = "https://hyperquark.edgecompute.app/logo.png")] #![doc(html_favicon_url = "https://hyperquark.edgecompute.app/favicon.ico")] #![warn(clippy::cargo, clippy::nursery, clippy::pedantic)] diff --git a/src/wasm/project.rs b/src/wasm/project.rs index 3bec2820..51d135ce 100644 --- a/src/wasm/project.rs +++ b/src/wasm/project.rs @@ -17,7 +17,7 @@ use crate::wasm::registries::functions::static_functions::{ }; use crate::wasm::registries::types::{ TFunc, TNonNullable, TNullable, TStackArray, TStackStruct, TStepFunc, TTargetThreadArray, - TTargetThreadsStruct, TThreadArray, TValType, + TTargetThreadsStruct, TThreadArray, TType, }; use crate::wasm::{StepFunc, StringsTable, WasmFlags}; @@ -603,9 +603,9 @@ impl WasmProject { let mut tick_func = Function::new(vec![ (3, ValType::I32), - (1, >::val_type(&types)?), - (1, >::val_type(&types)?), - (1, >::val_type(&types)?), + (1, >::ty(&types)?), + (1, >::ty(&types)?), + (1, >::ty(&types)?), ]); let stack_struct_type = types.register_comp::()?; diff --git a/src/wasm/registries/functions/dyn_array.rs b/src/wasm/registries/functions/dyn_array.rs index 2ab31b27..0ea0cdea 100644 --- a/src/wasm/registries/functions/dyn_array.rs +++ b/src/wasm/registries/functions/dyn_array.rs @@ -7,7 +7,7 @@ use super::{MaybeStaticFunction, StaticFunction}; use crate::prelude::*; use crate::wasm::registries::TypeRegistry; use crate::wasm::registries::types::{ - TDefaultable, TDynArray, TDynArrayField, TNonNullable, TValType, + TDefaultable, TDynArray, TDynArrayField, TNonNullable, TType, }; #[derive(Clone)] @@ -21,14 +21,14 @@ pub struct DynArrayFuncOverride { /// ref dynamic_array - the dynamic array struct (obtained from `TDynArray` for `T: TDefaultable`) /// t - the element pub struct DynArrayPush(PhantomData); -impl NamedRegistryItem for DynArrayPush { +impl + TDefaultable> NamedRegistryItem for DynArrayPush { const VALUE: MaybeStaticFunction = MaybeStaticFunction { static_function: None, maybe_populate: || None, }; } -impl +impl + TDefaultable> TryNamedRegistryItemOverride for DynArrayPush { fn try_override( @@ -96,13 +96,13 @@ impl }, ] as &[_]), params: Box::from([ - >>::val_type(&types)?, - T::val_type(&types)?, + >>::ty(&types)?, + T::ty(&types)?, ]), returns: Box::from([]), locals: Box::from([ ValType::I32, - >>::val_type(&types)?, + >>::ty(&types)?, ]), }), maybe_populate: || None, @@ -146,10 +146,10 @@ impl TryNamedRegistryItemOverride>>::val_type(&types)?, + >>::ty(&types)?, ValType::I32, ]), - returns: Box::from([T::val_type(&types)?]), + returns: Box::from([T::ty(&types)?]), locals: Box::from([]), }), maybe_populate: || None, @@ -193,11 +193,11 @@ impl TryNamedRegistryItemOverride>>::val_type(&types)?, + >>::ty(&types)?, ValType::I32, - T::val_type(&types)?, + T::ty(&types)?, ]), - returns: Box::from([T::val_type(&types)?]), + returns: Box::from([T::ty(&types)?]), locals: Box::from([]), }), maybe_populate: || None, @@ -252,8 +252,8 @@ impl TryNamedRegistryItemOverride>>::val_type(&types)?]), - returns: Box::from([T::val_type(&types)?]), + params: Box::from([>>::ty(&types)?]), + returns: Box::from([T::ty(&types)?]), locals: Box::from([ValType::I32]), }), maybe_populate: || None, @@ -293,7 +293,7 @@ impl TryNamedRegistryItemOverride>>::val_type(&types)?]), + returns: Box::from([>>::ty(&types)?]), locals: Box::from([]), }), maybe_populate: || None, @@ -332,7 +332,7 @@ impl TryNamedRegistryItemOverride>>::val_type(&types)?]), + params: Box::from([>>::ty(&types)?]), returns: Box::from([ValType::I32]), locals: Box::from([]), }), @@ -371,7 +371,7 @@ impl TryNamedRegistryItemOverride>>::val_type(&types)?]), + params: Box::from([>>::ty(&types)?]), returns: Box::from([ValType::I32]), locals: Box::from([]), }), diff --git a/src/wasm/registries/functions/spawn_threads.rs b/src/wasm/registries/functions/spawn_threads.rs index 5dc084fa..264ebcc9 100644 --- a/src/wasm/registries/functions/spawn_threads.rs +++ b/src/wasm/registries/functions/spawn_threads.rs @@ -7,7 +7,7 @@ use crate::wasm::mem_layout; use crate::wasm::registries::functions::dyn_array::{DynArrayNew, DynArrayPop, DynArrayPush}; use crate::wasm::registries::types::{ TNonNullable, TNullable, TStackArray, TStackStruct, TStepFunc, TTargetThreadArray, - TThreadArray, TValType, + TThreadArray, TType, }; use crate::wasm::registries::{GlobalRegistry, StaticFunctionRegistry, TypeRegistry}; @@ -79,10 +79,10 @@ impl TryNamedRegistryItemOverride Call(imported_func_count + dyn_array_push), ] as &[_]), params: Box::from([ - >::val_type(&types)?, - >::val_type(&types)?, - StackStructRef::val_type(&types)?, - >::val_type(&types)?, + >::ty(&types)?, + >::ty(&types)?, + StackStructRef::ty(&types)?, + >::ty(&types)?, ]), returns: Box::from([]), locals: Box::from([]), @@ -126,11 +126,11 @@ impl TryNamedRegistryItemOverride export: None, params: Box::from([ ValType::I32, - >::val_type(&types)?, - StackStructRef::val_type(&types)?, + >::ty(&types)?, + StackStructRef::ty(&types)?, ]), returns: Box::from([]), - locals: Box::from([>::val_type(&types)?]), + locals: Box::from([>::ty(&types)?]), instructions: { (wasm_const![ LocalGet(0), diff --git a/src/wasm/registries/globals.rs b/src/wasm/registries/globals.rs index 4babf3bc..c06dd5e8 100644 --- a/src/wasm/registries/globals.rs +++ b/src/wasm/registries/globals.rs @@ -7,7 +7,7 @@ use wasm_encoder::{ use crate::prelude::*; use crate::registry::MapRegistry; use crate::wasm::registries::TypeRegistry; -use crate::wasm::registries::types::{TNonNullable, TTargetThreadArray, TThreadArray, TValType}; +use crate::wasm::registries::types::{TNonNullable, TTargetThreadArray, TThreadArray, TType}; #[derive(Copy, Clone, Debug)] pub struct GlobalMutable(pub bool); @@ -61,7 +61,7 @@ impl GlobalRegistry { self.register( "threadss".into(), ( - >::val_type(&types)?, + as TType>::ty(&types)?, ConstExpr::extended( (0..=num_sprites) // stage + sprites .map(|i| { diff --git a/src/wasm/registries/types.rs b/src/wasm/registries/types.rs index 8afd5ba1..046c2f23 100644 --- a/src/wasm/registries/types.rs +++ b/src/wasm/registries/types.rs @@ -78,207 +78,622 @@ impl TypeRegistry { } } -pub trait THeapType { - fn heap_type(types: &TypeRegistry) -> HQResult; +trait TypeRegisteringInfo { + fn types(&self) -> &TypeRegistry; } -impl THeapType for T +#[derive(Clone)] +struct RecGroupInfo { + types: Rc, + rec_group_start: u32, +} + +impl<'a> TypeRegisteringInfo for RecGroupInfo { + fn types(&self) -> &TypeRegistry { + &self.types + } +} + +impl TypeRegisteringInfo for TypeRegistry { + fn types(&self) -> &TypeRegistry { + &self + } +} + +pub impl(self) trait TRecGroupType { + fn rec_group_ty(registering_info: &I) -> HQResult; +} + +impl TRecGroupType for T +where + T: TRecGroupType, + I: TypeRegisteringInfo, +{ + default fn rec_group_ty(types: &I) -> HQResult { + Ok(HeapType::Concrete(T::rec_group_ty(types)?)) + } +} + +pub trait TType { + fn ty(types: &TypeRegistry) -> HQResult; +} + +impl TType for U +where + U: TRecGroupType, +{ + fn ty(types: &TypeRegistry) -> HQResult { + U::rec_group_ty(types) + } +} + +impl CompTimeRegistrand for T +where + T: TType, +{ + fn register(types: &TypeRegistry) -> HQResult { + T::ty(types) + } +} + +trait HasTypeDependencies { + type Dependencies: TypeList; + type RecGroupDependencies: TypeList; +} + +trait TypeList { + type Head; + type Tail: TypeList; + + type Concat: TypeList; +} +trait RegTypeList: TypeList { + fn register_each(types: &I) -> HQResult<()>; +} + +impl TypeList for () { + type Head = (); + type Tail = (); + + type Concat = Other; +} + +impl RegTypeList for () { + fn register_each(_types: &I) -> HQResult<()> { + Ok(()) + } +} + +impl TypeList for ((HeadT, Head),) { + type Head = (HeadT, Head); + type Tail = (); + + type Concat = ((HeadT, Head), Other); +} + +impl TypeList for ((HeadT, Head), Tail) +where + Tail: TypeList, +{ + type Head = (HeadT, Head); + type Tail = Tail; + + type Concat = ((HeadT, Head), Tail::Concat); +} + +impl RegTypeList for ((HeadT, Head), Tail) where - T: CompTimeRegistrand, + I: TypeRegisteringInfo, + Head: TRecGroupType, + Tail: RegTypeList, { - fn heap_type(types: &TypeRegistry) -> HQResult { - Ok(HeapType::Concrete(types.register_comp::()?)) + fn register_each(types: &I) -> HQResult<()> { + Head::rec_group_ty(types)?; + Tail::register_each(types) } } +// impl<'a, T, U> TRecGroupType for U +// where +// U: TType, +// { +// fn rec_group_ty(registering_info: &RecGroupInfo) -> HQResult { +// >::ty(registering_info.types) +// } +// } + pub struct TStructRef; -impl THeapType for TStructRef { - fn heap_type(_types: &TypeRegistry) -> HQResult { +impl TRecGroupType for TStructRef { + fn rec_group_ty(_types: &I) -> HQResult { + panic!("this shouldn't be called ever!!! evil!!!") + } +} +impl TRecGroupType for TStructRef { + fn rec_group_ty(_types: &I) -> HQResult { Ok(HeapType::Abstract { shared: false, ty: AbstractHeapType::Struct, }) } } +impl HasTypeDependencies for TStructRef { + type Dependencies = (); + type RecGroupDependencies = (); +} pub trait TRefType { - type HeapType: THeapType; + type HeapType; const NULLABLE: bool; +} - fn ref_type(types: &TypeRegistry) -> HQResult { +impl TRecGroupType for T +where + T: TRefType, + T::HeapType: TRecGroupType, + I: TypeRegisteringInfo, +{ + fn rec_group_ty(types: &I) -> HQResult { Ok(RefType { - nullable: Self::NULLABLE, - heap_type: Self::HeapType::heap_type(types)?, + nullable: T::NULLABLE, + heap_type: T::HeapType::rec_group_ty(types)?, }) } } -pub struct TNullable(PhantomData); -impl TRefType for TNullable +impl HasTypeDependencies for T where - T: THeapType, + T: TRefType, + T::HeapType: HasTypeDependencies, { - type HeapType = T; - const NULLABLE: bool = true; + type Dependencies = >::Dependencies; + type RecGroupDependencies = + >::RecGroupDependencies; } -impl TDefaultable for TNullable {} - -pub struct TNonNullable(PhantomData); -impl TRefType for TNonNullable +impl HasTypeDependencies for T where - T: THeapType, + T: TRefType, + T::HeapType: HasTypeDependencies, { + type Dependencies = >::Dependencies; + type RecGroupDependencies = + >::RecGroupDependencies; +} + +pub struct TNullable(PhantomData); +impl TRefType for TNullable { type HeapType = T; const NULLABLE: bool = true; } +impl TDefaultable for TNullable {} -pub trait TValType { - fn val_type(types: &TypeRegistry) -> HQResult; +pub struct TNonNullable(PhantomData); +impl TRefType for TNonNullable { + type HeapType = T; + const NULLABLE: bool = false; } -impl TValType for T +impl TRecGroupType for T where T: TRefType, + T::HeapType: TRecGroupType, + I: TypeRegisteringInfo, { - fn val_type(types: &TypeRegistry) -> HQResult { - Ok(ValType::Ref(T::ref_type(types)?)) + fn rec_group_ty(types: &I) -> HQResult { + Ok(ValType::Ref( + >::rec_group_ty(types)?, + )) } } pub struct TI32; -impl TValType for TI32 { - fn val_type(_types: &TypeRegistry) -> HQResult { +impl TRecGroupType for TI32 { + fn rec_group_ty(_types: &I) -> HQResult { Ok(ValType::I32) } } impl TDefaultable for TI32 {} +impl HasTypeDependencies for TI32 { + type Dependencies = (); + type RecGroupDependencies = (); +} pub trait TFieldType { - type ValType: TValType; + type ValType; const MUTABLE: bool; +} - fn field_type(types: &TypeRegistry) -> HQResult { +impl TRecGroupType for T +where + T: TFieldType, + I: TypeRegisteringInfo, + T::ValType: TRecGroupType, +{ + fn rec_group_ty(types: &I) -> HQResult { Ok(FieldType { - element_type: StorageType::Val(Self::ValType::val_type(types)?), - mutable: Self::MUTABLE, + element_type: StorageType::Val(T::ValType::rec_group_ty(types)?), + mutable: T::MUTABLE, }) } } +impl HasTypeDependencies for T +where + T: TFieldType, + T::ValType: HasTypeDependencies, +{ + type Dependencies = >::Dependencies; + type RecGroupDependencies = >::RecGroupDependencies; +} + pub struct TMutField(PhantomData); pub struct TConstField(PhantomData); -impl TFieldType for TMutField { +impl TFieldType for TMutField { type ValType = T; const MUTABLE: bool = true; } -impl TFieldType for TConstField { +impl TFieldType for TConstField { type ValType = T; const MUTABLE: bool = false; } -pub trait TDefaultable: TValType {} +pub trait TDefaultable {} -trait TFieldList { - fn fields(types: &TypeRegistry) -> HQResult>; -} - -impl TFieldList for () { - fn fields(_: &TypeRegistry) -> HQResult> { +impl TRecGroupType, I> for () { + fn rec_group_ty(_types: &I) -> HQResult> { Ok(vec![]) } } -impl TFieldList for (Head, Tail) +impl TRecGroupType, I> for (Head, Tail) where - Head: TFieldList, - Tail: TFieldType, + I: TypeRegisteringInfo, + Head: TRecGroupType, + Tail: TRecGroupType, I>, { - fn fields(types: &TypeRegistry) -> HQResult> { - let mut fields = Head::fields(types)?; - fields.push(Tail::field_type(types)?); - Ok(fields) + fn rec_group_ty(types: &I) -> HQResult> { + let mut tys = vec![Head::rec_group_ty(types)?]; + tys.extend(Tail::rec_group_ty(types)?); + Ok(tys) } } pub struct TStruct(PhantomData); -impl CompTimeRegistrand for TStruct +impl TRecGroupType for TStruct where - Fields: TFieldList, + I: TypeRegisteringInfo, + Fields: TRecGroupType, I>, { - fn register(types: &TypeRegistry) -> HQResult { - types.struct_(Fields::fields(types)?) + fn rec_group_ty(types: &I) -> HQResult { + types.types().struct_(Fields::rec_group_ty(types)?) } } +struct TTypeListMarker(PhantomData); + +impl HasTypeDependencies> for () { + type Dependencies = (); + type RecGroupDependencies = (); +} + +impl HasTypeDependencies> for (Head, Tail) +where + Head: HasTypeDependencies, + Head::Dependencies: TypeList, + Head::RecGroupDependencies: TypeList, + Tail: HasTypeDependencies>, + Tail::Dependencies: TypeList, + Tail::RecGroupDependencies: TypeList, +{ + type Dependencies = <>::Dependencies as TypeList>::Concat< + >>::Dependencies, + >; + type RecGroupDependencies = + <>::RecGroupDependencies as TypeList>::Concat< + >>::RecGroupDependencies, + >; +} + +trait CompoundTypeDependencies { + type Dependencies: TypeList; + type RecGroupDependencies: TypeList; +} + +impl CompoundTypeDependencies for TStruct +where + Fields: TRecGroupType, TypeRegistry> + + HasTypeDependencies>, + Fields::Dependencies: TypeList, +{ + type Dependencies = <((HeapType, Self),) as TypeList>::Concat; + + type RecGroupDependencies = (); +} + +impl CompoundTypeDependencies for TStruct +where + Fields: TRecGroupType, RecGroupInfo> + + HasTypeDependencies>, + Fields::RecGroupDependencies: TypeList, +{ + type Dependencies = Fields::Dependencies; + + type RecGroupDependencies = + <((HeapType, Self),) as TypeList>::Concat; +} + +impl HasTypeDependencies for TStruct +where + Fields: TRecGroupType, RecGroupInfo> + + HasTypeDependencies>, + Self: CompoundTypeDependencies< + Fields, + >>::RecGroupDependencies, + >, +{ + type Dependencies = + >::Dependencies; + + type RecGroupDependencies = >::RecGroupDependencies; +} + pub struct TArray(PhantomData); -impl CompTimeRegistrand for TArray { - fn register(types: &TypeRegistry) -> HQResult { - types.array( - StorageType::Val(Field::ValType::val_type(types)?), +impl TRecGroupType for TArray +where + I: TypeRegisteringInfo, + Field: TFieldType, + Field::ValType: TRecGroupType, +{ + fn rec_group_ty(types: &I) -> HQResult { + types.types().array( + StorageType::Val(Field::ValType::rec_group_ty(types)?), Field::MUTABLE, ) } } -trait TValTypeList { - fn val_types(types: &TypeRegistry) -> HQResult>; +impl CompoundTypeDependencies for TArray +where + Field: TFieldType //TRecGroupType + + HasTypeDependencies, + Field::ValType: TType, +{ + type Dependencies = <((HeapType, Self),) as TypeList>::Concat; + + type RecGroupDependencies = (); } -impl TValTypeList for () { - fn val_types(_: &TypeRegistry) -> HQResult> { - Ok(vec![]) - } +impl CompoundTypeDependencies for TArray +where + (Head, Tail): TypeList, + Field: TFieldType + HasTypeDependencies, +{ + type Dependencies = Field::Dependencies; + + type RecGroupDependencies = + <((HeapType, Self),) as TypeList>::Concat; } -impl TValTypeList for (Head, Tail) +impl HasTypeDependencies for TArray where - Head: TValTypeList, - Tail: TValType, + Field: TFieldType + HasTypeDependencies, + Self: CompoundTypeDependencies< + Field, + >::RecGroupDependencies, + >, { - fn val_types(types: &TypeRegistry) -> HQResult> { - let mut val_types = Head::val_types(types)?; - val_types.push(Tail::val_type(types)?); - Ok(val_types) - } + type Dependencies = + >::Dependencies; + + type RecGroupDependencies = >::RecGroupDependencies; } pub struct TFunc(PhantomData, PhantomData); -impl CompTimeRegistrand for TFunc +impl TRecGroupType for TFunc where - Params: TValTypeList, - Result: TValTypeList, + I: TypeRegisteringInfo, + Params: TRecGroupType, I>, + Result: TRecGroupType, I>, { - fn register(types: &TypeRegistry) -> HQResult { - types.function(Params::val_types(types)?, Result::val_types(types)?) + fn rec_group_ty(types: &I) -> HQResult { + types + .types() + .function(Params::rec_group_ty(types)?, Result::rec_group_ty(types)?) + } +} + +impl CompoundTypeDependencies<(Params, Results), ()> for TFunc +where + Params: + TRecGroupType, RecGroupInfo> + HasTypeDependencies>, + Results: + TRecGroupType, RecGroupInfo> + HasTypeDependencies>, +{ + type Dependencies = + <<((HeapType, Self),) as TypeList>::Concat as TypeList>::Concat< + Results::Dependencies, + >; + + type RecGroupDependencies = (); +} + +impl CompoundTypeDependencies<(Params, Results), (Head, Tail)> + for TFunc +where + Params: + TRecGroupType, RecGroupInfo> + HasTypeDependencies>, + Results: + TRecGroupType, RecGroupInfo> + HasTypeDependencies>, +{ + type Dependencies = ::Concat; + + type RecGroupDependencies = <<((HeapType, Self),) as TypeList>::Concat< + Params::RecGroupDependencies, + > as TypeList>::Concat; +} + +impl HasTypeDependencies for TFunc +where + Params: + TRecGroupType, RecGroupInfo> + HasTypeDependencies>, + Results: + TRecGroupType, RecGroupInfo> + HasTypeDependencies>, + Self: CompoundTypeDependencies< + (Params, Results), + <>>::RecGroupDependencies as TypeList>::Concat<>>::RecGroupDependencies>, + >, +{ + type Dependencies = + >>::RecGroupDependencies as TypeList>::Concat<>>::RecGroupDependencies>, + >>::Dependencies; + type RecGroupDependencies = + >>::RecGroupDependencies as TypeList>::Concat<>>::RecGroupDependencies>, + >>::RecGroupDependencies; +} + +macro_rules! rec_group { + ( + $rec_group_name:ident { + $($name:ident = $typename:ident{$($typeparams:tt)+};)+ + } + ) => { + macro_rules! ${ concat($rec_group_name, _sub_rec_group_types) } { + ( + ${concat($rec_group_name, _sub_rec_group_types)}!($$($$macro_args:tt)+) + ) => { + ${concat($rec_group_name, _sub_rec_group_types)}!($$($$macro_args)+) + }; + ( + $$ty:ident{$$({$$($$params:tt)+}),+} + ) => { + $$ty< + $$( + ${concat($rec_group_name, _sub_rec_group_types)}!( + $$($$params)+ + ) + ),+ + > + }; + $( + ($name) => { + TRecGroupItem<${ index() }> + }; + )+ + ($$ty:ident) => { + $$ty + }; + (()) => {()}; + ( + ({$$($$first:tt)+},) + ) => { + ( + ${concat($rec_group_name, _sub_rec_group_types)}!( + $$($$first)+ + ), + () + ) + }; + ( + ({$$($$first:tt)+}, $$({$$($$rest:tt)+}),+ $$(,)?) + ) => { + ( + ${concat($rec_group_name, _sub_rec_group_types)}!( + $$($$first)+ + ), + ${concat($rec_group_name, _sub_rec_group_types)}!( + ($$({$$($$rest)+},)+) + ) + ) + }; + } + + fn ${ concat($rec_group_name, _register_deps) }(types: &TypeRegistry) -> HQResult<()> { + $( + <${concat($name, Type)} as HasTypeDependencies>::Dependencies::register_each(types)?; + )+ + Ok(()) + } + + $( + type ${concat($name, Type)} = ${ concat($rec_group_name, _sub_rec_group_types) }!( + $typename{$($typeparams)+} + ); + + pub struct $name; + + impl CompTimeRegistrand for $name { + fn register(types: &TypeRegistry) -> HQResult { + ${ concat($rec_group_name, _register_deps) }(types)?; + hq_todo!() + // let rec_group_info = RecGroupInfo { + // types, + // rec_group_start: types.registry().len() as u32, + // }; + // $( + // $name::rec_group_ty(&rec_group_info)?; + // ) + + } + } + )+ } } -pub type TStepFunc = TFunc<(((), TNonNullable), TNullable), ()>; +pub struct TRecGroupItem; + +impl HasTypeDependencies for TRecGroupItem { + type Dependencies = (); + type RecGroupDependencies = ((HeapType, Self), ()); +} + +impl<'a, const I: u32> TRecGroupType for TRecGroupItem { + fn rec_group_ty(registering_info: &RecGroupInfo) -> HQResult { + Ok(HeapType::Concrete(registering_info.rec_group_start + I)) + } +} + +rec_group! { + rec_grp { + TStepFunc = TFunc{ + {( + {TNonNullable{{TStackArray}}}, + {TNullable{{TStructRef}}}, + )}, + {()} + }; + TStackStruct = TStruct{{( + {TMutField{{TNonNullable{{TStepFunc}}}}}, + {TConstField{{TNullable{{TStructRef}}}}}, + )}}; + TStackDynArrayField = TArray{{TMutField{{TNullable{{TStackStruct}}}}}}; + TStackArray = TDynArray{{TNullable{{TStackStruct}}}}; + } +} pub type TDynArrayField = TArray>; pub type TDynArray = TStruct<( - ((), TMutField>>), - TMutField, + TMutField>>, + (TMutField, ()), )>; -pub type TStackStruct = TStruct<( - ((), TMutField>), - TConstField>, -)>; - -pub type TStackArray = TDynArray>; - pub type TThreadArray = TDynArray>; -pub type TTargetThreadsStruct = TStruct<( - ((), TMutField), - TMutField>, -)>; +pub type TTargetThreadsStruct = + TStruct<(TMutField, (TMutField>, ()))>; pub type TTargetThreadArray = TArray>>;