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/instructions/control/stop_all.rs b/src/instructions/control/stop_all.rs index 3b2ca327..441d2547 100644 --- a/src/instructions/control/stop_all.rs +++ b/src/instructions/control/stop_all.rs @@ -1,30 +1,61 @@ -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::registries::functions::static_functions::DynArrayClear; +use crate::wasm::registries::types::{TNullable, TTargetThreadArray, TThreadArray}; -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 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_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), + 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, ]) } diff --git a/src/instructions/event/poll_waiting_threads.rs b/src/instructions/event/poll_waiting_threads.rs index 3d8c4b6d..0d6df6c4 100644 --- a/src/instructions/event/poll_waiting_threads.rs +++ b/src/instructions/event/poll_waiting_threads.rs @@ -4,45 +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, ThreadsTable}; +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.registries().tables().register::()?; + let dyn_array_len = func + .registries() + .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, @@ -51,35 +54,37 @@ pub fn wasm(func: &StepFunc, _inputs: Rc<[IrType]>) -> HQResult, 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()?; @@ -80,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)?; @@ -146,7 +142,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/instructions/procedures/argument.rs b/src/instructions/procedures/argument.rs index be757b32..375cf37f 100644 --- a/src/instructions/procedures/argument.rs +++ b/src/instructions/procedures/argument.rs @@ -2,7 +2,7 @@ use wasm_encoder::{AbstractHeapType, HeapType}; use super::super::prelude::*; use crate::ir::RcVar; -use crate::wasm::registries::types::WasmType; +use crate::wasm::registries::types::CompoundType; use crate::wasm::{StepFunc, WasmProject}; #[derive(Clone, Debug)] @@ -72,7 +72,7 @@ pub fn wasm( .proc_arg_struct_type(&(**arg_vars).borrow())?; let registries = func.registries(); let type_registry = registries.types().registry().borrow(); - let WasmType::Struct(struct_type_fields) = type_registry + let CompoundType::Struct(struct_type_fields) = type_registry .get_index(struct_type_index as usize) .ok_or_else(|| make_hq_bug!("type index not found in type registry"))? .0 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/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/registry.rs b/src/registry.rs index 69f8312b..1b992bc2 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -2,6 +2,10 @@ 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 +74,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 +87,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 +105,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 +121,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 +137,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 +247,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 +257,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 +266,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 +275,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 +286,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.rs b/src/wasm.rs index 0300dd28..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, StepsTable, StringsTable, ThreadsTable, -}; +pub use registries::{GlobalExportable, GlobalMutable, Registries, StringsTable}; diff --git a/src/wasm/func.rs b/src/wasm/func.rs index 44cfc4f2..c248edda 100644 --- a/src/wasm/func.rs +++ b/src/wasm/func.rs @@ -34,7 +34,7 @@ impl Instruction { threads_count_global: u32, spawn_new_thread_func: u32, spawn_thread_in_stack_func: u32, - threads_table: u32, + threadss_global: u32, imported_func_count: u32, static_func_count: u32, imported_global_count: u32, @@ -129,7 +129,7 @@ impl Instruction { j.try_into() .map_err(|_| make_hq_bug!("index out of bounds"))?, ), - WInstruction::TableSize(threads_table), + // WInstruction::TableSize(threads_table), WInstruction::ArraySet(i32_array_type), WInstruction::RefFunc(i + imported_func_count + static_func_count), WInstruction::RefNull(HeapType::Abstract { @@ -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 { @@ -395,7 +405,7 @@ impl StepFunc { threads_count_global: u32, spawn_new_thread_func: u32, spawn_thread_in_stack_func: u32, - threads_table: u32, + threadss_global: u32, imported_func_count: u32, static_func_count: u32, imported_global_count: u32, @@ -408,7 +418,7 @@ impl StepFunc { threads_count_global, spawn_new_thread_func, spawn_thread_in_stack_func, - threads_table, + threadss_global, imported_func_count, static_func_count, imported_global_count, diff --git a/src/wasm/mem_layout.rs b/src/wasm/mem_layout.rs index df8b098f..11693863 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) (i16) + 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..51d135ce 100644 --- a/src/wasm/project.rs +++ b/src/wasm/project.rs @@ -8,13 +8,18 @@ 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::{ - MarkWaitingFlag, SpawnNewThread, SpawnThreadInStack, + DynArrayGet, DynArrayLen, MarkWaitingFlag, SpawnNewThread, SpawnThreadFuncOverride, + SpawnThreadInStack, }; -use crate::wasm::{StepFunc, StringsTable, ThreadsTable, WasmFlags}; +use crate::wasm::registries::types::{ + TFunc, TNonNullable, TNullable, TStackArray, TStackStruct, TStepFunc, TTargetThreadArray, + TTargetThreadsStruct, TThreadArray, TType, +}; +use crate::wasm::{StepFunc, StringsTable, WasmFlags}; /// A respresentation of a WASM representation of a project. Cannot be created directly; /// use `TryFrom`. @@ -136,25 +141,24 @@ 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()?, + imported_global_count: self.imported_global_count()?, + }; + self.registries() .static_functions() - .register_override::(( - self.registries().types().step_func_type()?, - self.registries().types().stack_struct_type()?, - 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().step_func_type()?, - self.registries().types().stack_struct_type()?, - 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() @@ -165,7 +169,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, @@ -182,7 +186,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()?, @@ -202,12 +206,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()? @@ -281,7 +279,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()?, @@ -374,14 +372,6 @@ impl WasmProject { Ok(()) } - fn threads_table_index(&self) -> HQResult - where - N: TryFrom, - >::Error: fmt::Debug, - { - self.registries().tables().register::() - } - fn spawn_new_thread_func(&self) -> HQResult where N: TryFrom, @@ -407,15 +397,17 @@ 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() + } + + 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")] @@ -455,7 +447,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()?, @@ -478,7 +470,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()?, @@ -577,7 +569,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()?, @@ -607,86 +599,101 @@ 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 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, >::ty(&types)?), + (1, >::ty(&types)?), + (1, >::ty(&types)?), ]); - let step_func_ty = self.registries().types().step_func_type()?; - 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, @@ -698,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()?, @@ -707,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.rs b/src/wasm/registries.rs index b12a1ff5..376f6520 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; @@ -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 72f487b4..15940774 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; @@ -106,9 +107,11 @@ impl StaticFunctionRegistry { } pub mod static_functions { + pub use super::dyn_array::{ + DynArrayClear, DynArrayFuncOverride, DynArrayGet, DynArrayLen, 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, SpawnThreadInStackOverride, - }; + 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 new file mode 100644 index 00000000..0ea0cdea --- /dev/null +++ b/src/wasm/registries/functions/dyn_array.rs @@ -0,0 +1,384 @@ +use core::marker::PhantomData; + +use wasm_encoder::{BlockType as WasmBlockType, ValType}; +use wasm_gen::wasm_const; + +use super::{MaybeStaticFunction, StaticFunction}; +use crate::prelude::*; +use crate::wasm::registries::TypeRegistry; +use crate::wasm::registries::types::{ + TDefaultable, TDynArray, TDynArrayField, TNonNullable, TType, +}; + +#[derive(Clone)] +pub struct DynArrayFuncOverride { + 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: TDefaultable`) +/// t - the element +pub struct DynArrayPush(PhantomData); +impl + TDefaultable> NamedRegistryItem for DynArrayPush { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + maybe_populate: || None, + }; +} + +impl + TDefaultable> + TryNamedRegistryItemOverride for DynArrayPush +{ + 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, + }, + 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([ + >>::ty(&types)?, + T::ty(&types)?, + ]), + returns: Box::from([]), + locals: Box::from([ + ValType::I32, + >>::ty(&types)?, + ]), + }), + maybe_populate: || None, + }) + } +} + +/// Gets an element of a dynamic (resizeable) array +/// +/// Takes 2 parameters: +/// 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 { + 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([ + >>::ty(&types)?, + ValType::I32, + ]), + returns: Box::from([T::ty(&types)?]), + locals: Box::from([]), + }), + maybe_populate: || None, + }) + } +} + +/// Sets an element of a dynamic (resizeable) array +/// +/// Takes 3 parameters: +/// ref dynamic_array - 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([ + >>::ty(&types)?, + ValType::I32, + T::ty(&types)?, + ]), + returns: Box::from([T::ty(&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: TDefaultable`) +/// +/// 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([>>::ty(&types)?]), + returns: Box::from([T::ty(&types)?]), + locals: Box::from([ValType::I32]), + }), + maybe_populate: || None, + }) + } +} + +/// Creates a new dynamic (resizeable) array of the given capacity +/// +/// Takes 1 parameters: +/// i32 - the initial capacity of the array to create +/// +/// Returns ref dynamic_array +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([>>::ty(&types)?]), + locals: Box::from([]), + }), + maybe_populate: || None, + }) + } +} + +/// Returns the length (not capacity) of the given dynamic array +/// +/// Takes 1 parameters: +/// ref dynamic_array - 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([>>::ty(&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([>>::ty(&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 b10185c2..264ebcc9 100644 --- a/src/wasm/registries/functions/spawn_threads.rs +++ b/src/wasm/registries/functions/spawn_threads.rs @@ -1,24 +1,39 @@ -use wasm_encoder::{AbstractHeapType, HeapType, RefType, 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::{DynArrayNew, DynArrayPop, DynArrayPush}; +use crate::wasm::registries::types::{ + TNonNullable, TNullable, TStackArray, TStackStruct, TStepFunc, TTargetThreadArray, + TThreadArray, TType, +}; +use crate::wasm::registries::{GlobalRegistry, StaticFunctionRegistry, TypeRegistry}; + +#[derive(Clone)] +pub struct SpawnThreadFuncOverride { + pub types: Rc, + pub globals: Rc, + pub static_functions: Rc, + pub num_sprites: u32, + pub imported_func_count: u32, + pub imported_global_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.) +/// 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 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 -/// -/// 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 +/// - ref step_func - the step to return to after pub struct SpawnThreadInStack; impl NamedRegistryItem for SpawnThreadInStack { const VALUE: MaybeStaticFunction = MaybeStaticFunction { @@ -26,117 +41,64 @@ 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, + static_functions, + imported_func_count, + .. + }: SpawnThreadFuncOverride, + ) -> HQResult { + let stack_struct_type = types.register_comp::()?; + let dyn_array_push = static_functions.register::, u32>()?; + Ok(MaybeStaticFunction { static_function: Some(StaticFunction { export: None, instructions: Box::from(wasm_const![ + LocalGet(0), + Call( + imported_func_count + + static_functions.register::, u32>()? + ), + Drop, + 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(0), LocalGet(1), LocalGet(2), StructNew(stack_struct_type), - LocalSet(4), - LocalGet(0), - TableGet(threads_table), - RefAsNonNull, - LocalTee(5), - StructGet { - struct_type_index: thread_struct_type, - field_index: 1, - }, - 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, - }, - LocalGet(5), - StructGet { - struct_type_index: thread_struct_type, - field_index: 0, - }, - I32Const(1), - I32Sub, - ArrayGet(stack_array_type), - 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 + 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), - }), + >::ty(&types)?, + >::ty(&types)?, + StackStructRef::ty(&types)?, + >::ty(&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), - }), - ]), + locals: Box::from([]), }), maybe_populate: || None, - } + }) } } /// 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: +/// - 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 table pub struct SpawnNewThread; impl NamedRegistryItem for SpawnNewThread { const VALUE: MaybeStaticFunction = MaybeStaticFunction { @@ -144,55 +106,77 @@ impl NamedRegistryItem for SpawnNewThread { maybe_populate: || None, }; } -pub type SpawnNewThreadOverride = (u32, u32, u32, u32, u32); -impl NamedRegistryItemOverride for SpawnNewThread { - fn r#override( - (func_ty, stack_struct_ty, stack_array_ty, thread_struct_ty, threads_table_index): SpawnNewThreadOverride, - ) -> MaybeStaticFunction { - MaybeStaticFunction { + +impl TryNamedRegistryItemOverride for SpawnNewThread { + 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)?; + 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, + >::ty(&types)?, + StackStructRef::ty(&types)?, ]), 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([>::ty(&types)?]), + instructions: { + (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), + I32Const(8), + 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 77111ae2..c06dd5e8 100644 --- a/src/wasm/registries/globals.rs +++ b/src/wasm/registries/globals.rs @@ -1,9 +1,13 @@ use core::ops::Deref; -use wasm_encoder::{ConstExpr, ExportKind, ExportSection, GlobalSection, GlobalType, ValType}; +use wasm_encoder::{ + 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, TType}; #[derive(Copy, Clone, Debug)] pub struct GlobalMutable(pub bool); @@ -29,6 +33,59 @@ pub type GlobalRegistry = MapRegistry, (ValType, ConstExpr, GlobalMutable, GlobalExportable)>; impl GlobalRegistry { + pub fn threads_count(&self) -> HQResult + where + N: TryFrom, + >::Error: fmt::Debug, + { + self.register( + "threads_count".into(), + ( + ValType::I32, + ConstExpr::i32_const(0), + GlobalMutable(true), + GlobalExportable(true), + ), + ) + } + + // 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, + { + let array_array_type = types.register_comp::()?; + let array_type = types.register_comp::()?; + self.register( + "threadss".into(), + ( + as TType>::ty(&types)?, + ConstExpr::extended( + (0..=num_sprites) // stage + 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( self, globals: &mut GlobalSection, diff --git a/src/wasm/registries/tables.rs b/src/wasm/registries/tables.rs index d9158277..eacd01b4 100644 --- a/src/wasm/registries/tables.rs +++ b/src/wasm/registries/tables.rs @@ -1,6 +1,4 @@ -use wasm_encoder::{ - ConstExpr, ExportKind, ExportSection, HeapType, RefType, TableSection, TableType, -}; +use wasm_encoder::{ConstExpr, ExportKind, ExportSection, RefType, TableSection, TableType}; use crate::prelude::*; @@ -73,51 +71,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"), +// } +// } +// } diff --git a/src/wasm/registries/types.rs b/src/wasm/registries/types.rs index 4cee021d..046c2f23 100644 --- a/src/wasm/registries/types.rs +++ b/src/wasm/registries/types.rs @@ -1,44 +1,43 @@ +use core::marker::PhantomData; + use wasm_encoder::{ AbstractHeapType, FieldType, HeapType, RefType, StorageType, TypeSection, ValType, }; 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 where - N: TryFrom, - >::Error: fmt::Debug, + N: RegistryResult, { - self.register_default(WasmType::Function(params, returns)) + 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(WasmType::Array(elem_type, mutable)) + 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(WasmType::Struct(fields)) + self.register_default(CompoundType::Struct(fields)) } pub const STRUCT_REF: ValType = ValType::Ref(RefType { @@ -49,52 +48,6 @@ impl TypeRegistry { }, }); - pub fn step_func_type(&self) -> HQResult { - self.function(vec![ValType::I32, Self::STRUCT_REF], vec![]) - } - - 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()?), - })), - mutable: true, - }, - FieldType { - element_type: StorageType::Val(Self::STRUCT_REF), - mutable: false, - }, - ]) - } - - 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, - ) - } - - pub fn thread_struct_type(&self) -> HQResult { - self.struct_(vec![ - FieldType { - element_type: StorageType::Val(ValType::I32), - mutable: true, - }, - FieldType { - element_type: StorageType::Val(ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(self.stack_array_type()?), - })), - mutable: true, - }, - ]) - } - pub fn proc_arg_struct_type( &self, arg_vars: &core::cell::Ref<'_, Vec>, @@ -117,10 +70,630 @@ 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), } } } } + +trait TypeRegisteringInfo { + fn types(&self) -> &TypeRegistry; +} + +#[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 + I: TypeRegisteringInfo, + Head: TRecGroupType, + Tail: RegTypeList, +{ + 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 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; + const NULLABLE: bool; +} + +impl TRecGroupType for T +where + T: TRefType, + T::HeapType: TRecGroupType, + I: TypeRegisteringInfo, +{ + fn rec_group_ty(types: &I) -> HQResult { + Ok(RefType { + nullable: T::NULLABLE, + heap_type: T::HeapType::rec_group_ty(types)?, + }) + } +} + +impl HasTypeDependencies for T +where + T: TRefType, + T::HeapType: HasTypeDependencies, +{ + type Dependencies = >::Dependencies; + type RecGroupDependencies = + >::RecGroupDependencies; +} +impl HasTypeDependencies for T +where + 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 struct TNonNullable(PhantomData); +impl TRefType for TNonNullable { + type HeapType = T; + const NULLABLE: bool = false; +} + +impl TRecGroupType for T +where + T: TRefType, + T::HeapType: TRecGroupType, + I: TypeRegisteringInfo, +{ + fn rec_group_ty(types: &I) -> HQResult { + Ok(ValType::Ref( + >::rec_group_ty(types)?, + )) + } +} + +pub struct TI32; + +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; + const MUTABLE: bool; +} + +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(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 { + type ValType = T; + const MUTABLE: bool = true; +} + +impl TFieldType for TConstField { + type ValType = T; + const MUTABLE: bool = false; +} + +pub trait TDefaultable {} + +impl TRecGroupType, I> for () { + fn rec_group_ty(_types: &I) -> HQResult> { + Ok(vec![]) + } +} + +impl TRecGroupType, I> for (Head, Tail) +where + I: TypeRegisteringInfo, + Head: TRecGroupType, + Tail: TRecGroupType, I>, +{ + 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 TRecGroupType for TStruct +where + I: TypeRegisteringInfo, + Fields: TRecGroupType, I>, +{ + 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 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, + ) + } +} + +impl CompoundTypeDependencies for TArray +where + Field: TFieldType //TRecGroupType + + HasTypeDependencies, + Field::ValType: TType, +{ + type Dependencies = <((HeapType, Self),) as TypeList>::Concat; + + type RecGroupDependencies = (); +} + +impl CompoundTypeDependencies for TArray +where + (Head, Tail): TypeList, + Field: TFieldType + HasTypeDependencies, +{ + type Dependencies = Field::Dependencies; + + type RecGroupDependencies = + <((HeapType, Self),) as TypeList>::Concat; +} + +impl HasTypeDependencies for TArray +where + Field: TFieldType + HasTypeDependencies, + Self: CompoundTypeDependencies< + Field, + >::RecGroupDependencies, + >, +{ + type Dependencies = + >::Dependencies; + + type RecGroupDependencies = >::RecGroupDependencies; +} + +pub struct TFunc(PhantomData, PhantomData); + +impl TRecGroupType for TFunc +where + I: TypeRegisteringInfo, + Params: TRecGroupType, I>, + Result: TRecGroupType, I>, +{ + 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 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, ()), +)>; + +pub type TThreadArray = TDynArray>; + +pub type TTargetThreadsStruct = + TStruct<(TMutField, (TMutField>, ()))>; + +pub type TTargetThreadArray = TArray>>;