diff --git a/Cargo.toml b/Cargo.toml index b7826d25..d4232684 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.259.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 = { git = "https://github.com/pufferfish101007/wasm-tools.git", rev = "184f8f5" } +wasmprinter = "0.259.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..7c910d00 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2026-08-07" +channel = "nightly-2026-09-10" targets = [ "wasm32-unknown-unknown" ] \ No newline at end of file diff --git a/src/instructions.rs b/src/instructions.rs index b3afad52..d3bb44dc 100644 --- a/src/instructions.rs +++ b/src/instructions.rs @@ -26,6 +26,8 @@ mod pen; mod procedures; mod sensing; +pub use event::poll_waiting_threads::{TPollStruct, TWaitingThreadArray}; + #[macro_use] mod tests; diff --git a/src/instructions/control/stop_all.rs b/src/instructions/control/stop_all.rs index 3b2ca327..502ba4dd 100644 --- a/src/instructions/control/stop_all.rs +++ b/src/instructions/control/stop_all.rs @@ -1,30 +1,67 @@ -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, TStackArray, TTargetThreadArray, TTargetThreadsStruct, TThreadArray, TType, +}; -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), + StructGet { + struct_type_index: TTargetThreadsStruct::ty(func.registries().types())?, + field_index: 1, + }, + #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/control/wait.rs b/src/instructions/control/wait.rs index 18ab64a5..98762f65 100644 --- a/src/instructions/control/wait.rs +++ b/src/instructions/control/wait.rs @@ -4,6 +4,7 @@ use super::super::prelude::*; use crate::instructions_test; use crate::ir::StepIndex; use crate::wasm::registries::functions::static_functions::SpawnThreadInStack; +use crate::wasm::registries::types::{TStepFunc, TType}; use crate::wasm::{GlobalExportable, GlobalMutable, StepFunc}; #[derive(Clone, Debug)] @@ -62,6 +63,8 @@ pub fn wasm( ), )?; + let step_func_ty = TStepFunc::ty(func.registries().types())?; + Ok( if t1.contains(IrType::FloatNeg) { wasm![ @@ -79,9 +82,11 @@ pub fn wasm( LocalSet(struct_local), LocalGet((func.params().len() - 2).try_into().map_err(|_| make_hq_bug!("local index out of bounds"))?), #LazyStepRef(*poll_step), + RefCastNonNull(step_func_ty), LocalGet(struct_local), RefCastNullable(HeapType::Abstract { shared: false, ty: AbstractHeapType::Struct }), #LazyStepRef(*next_step), + RefCastNonNull(step_func_ty), #StaticFunctionCall(spawn_thread_in_stack_func), ] ).collect() @@ -111,6 +116,7 @@ mod test { use super::super::super::tests::*; use super::*; use crate::wasm::registries::TypeRegistry; + use crate::wasm::registries::types::{TNonNullable, TStackArray, TType}; use crate::wasm::{StepTarget, WasmFlags, WasmProject}; #[test] @@ -122,13 +128,16 @@ mod test { pub fn make_fields() -> Fields { Fields { poll_step: StepIndex(0), - next_step: StepIndex(1), + next_step: StepIndex(0), } } pub fn setup_project(wasm_proj: &WasmProject, flags: WasmFlags) { let step_func = StepFunc::new_with_types( - Box::from([ValType::I32, TypeRegistry::STRUCT_REF]), + Box::from([ + >::ty(wasm_proj.registries().types()).unwrap(), + TypeRegistry::STRUCT_REF, + ]), Box::from([]), wasm_proj.registries(), flags, diff --git a/src/instructions/event/broadcast_and_wait.rs b/src/instructions/event/broadcast_and_wait.rs index 3923b3fc..b7f18f23 100644 --- a/src/instructions/event/broadcast_and_wait.rs +++ b/src/instructions/event/broadcast_and_wait.rs @@ -1,9 +1,9 @@ -use wasm_encoder::{HeapType, StorageType}; - use super::super::prelude::*; +use crate::instructions::TWaitingThreadArray; use crate::instructions_test; use crate::ir::StepIndex; use crate::wasm::StepFunc; +use crate::wasm::registries::types::{TNonNullable, TType}; #[derive(Clone, Debug)] pub struct Fields { @@ -35,14 +35,9 @@ pub fn wasm( next_step, }: &Fields, ) -> HQResult> { - let i32_array_type = func - .registries() - .types() - .array(StorageType::Val(ValType::I32), true)?; - let arr_local = func.local(ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(i32_array_type), - }))?; + let arr_local = func.local(>::ty( + func.registries().types(), + )?)?; func.free_local(arr_local)?; Ok(wasm![ @@ -74,6 +69,7 @@ mod test { use super::*; use crate::instructions::tests::assert_valid_json; use crate::wasm::registries::TypeRegistry; + use crate::wasm::registries::types::{TNonNullable, TStackArray, TType}; use crate::wasm::{StepTarget, WasmFlags, WasmProject}; #[test] @@ -92,7 +88,10 @@ mod test { pub fn setup_project(wasm_proj: &WasmProject, flags: WasmFlags) { let step_func = StepFunc::new_with_types( - Box::from([ValType::I32, TypeRegistry::STRUCT_REF]), + Box::from([ + >::ty(wasm_proj.registries().types()).unwrap(), + TypeRegistry::STRUCT_REF, + ]), Box::from([]), wasm_proj.registries(), flags, diff --git a/src/instructions/event/poll_waiting_threads.rs b/src/instructions/event/poll_waiting_threads.rs index 3d8c4b6d..07c20d9a 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, TMutField, TNonNullable, TNullable, TStackArray, TStackStruct, TStruct, + TType, +}; + +pub type TWaitingThreadArray = TArray>>; +pub 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(>::ty(&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(>::ty(&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::ty(&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), - ), - )?; - Ok(match yield_mode { YieldMode::None => { - let threads_table = func.registries().tables().register::()?; - 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()?; - let thread_struct_local = func.local(ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(thread_struct_ty), - }))?; - let stack_struct_local = func.local(ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(stack_struct_ty), - }))?; - let i32_local = func.local(ValType::I32)?; - let step_func_ty = func.registries().types().step_func_type()?; - func.free_local(thread_struct_local)?; - func.free_local(stack_struct_local)?; - func.free_local(i32_local)?; + let static_functions = Rc::clone(func.registries().static_functions()); + let types = Rc::clone(func.registries().types()); + + let pop_stack = + static_functions.register::>, _>()?; + let stack_len = + static_functions.register::>, _>()?; + let stack_get = + static_functions.register::>, _>()?; + + let stack_array_ty = >>::ty(&types)?; + let step_struct_ty = TStackStruct::ty(&types)?; + + let stack_local = (func.params().len() - 2) as u32; + + let stack_len_local = func.local(ValType::I32)?; + let step_struct_local = func.local(>::ty(&types)?)?; + func.free_local(step_struct_local)?; + func.free_local(stack_len_local)?; wasm![ - LocalGet(0), - TableGet(threads_table), - RefAsNonNull, - LocalTee(thread_struct_local), - StructGet { struct_type_index: thread_struct_ty, field_index: 0 }, - I32Const(1), - I32Sub, - LocalTee(i32_local), + LocalGet(stack_local), + RefCastNonNull(stack_array_ty), + #StaticFunctionCall(pop_stack), + Drop, + LocalGet(stack_local), + RefCastNonNull(stack_array_ty), + #StaticFunctionCall(stack_len), + LocalTee(stack_len_local), I32Eqz, If(BlockType::Empty), - #LazyGlobalGet(threads_count), - I32Const(1), - I32Sub, - #LazyGlobalSet(threads_count), - LocalGet(0), - RefNull(HeapType::Concrete(thread_struct_ty)), - TableSet(threads_table), + // Empty stack cleanup (if it happens at all) will happen in scheduler, not here. Return, Else, - LocalGet(thread_struct_local), - LocalGet(i32_local), - StructSet { - struct_type_index: thread_struct_ty, - field_index: 0, - }, - LocalGet(thread_struct_local), - StructGet { - struct_type_index: thread_struct_ty, - field_index: 1, - }, - LocalGet(i32_local), + LocalGet(stack_local), + LocalGet(stack_local), + LocalGet(stack_local), + RefCastNonNull(stack_array_ty), + LocalGet(stack_len_local), I32Const(1), I32Sub, - ArrayGet(stack_array_ty), + #StaticFunctionCall(stack_get), RefAsNonNull, - LocalSet(stack_struct_local), - LocalGet((func.params().len() - 2).try_into().map_err(|_| make_hq_bug!("local index out of bounds"))?), - LocalGet(stack_struct_local), + LocalTee(step_struct_local), StructGet { - struct_type_index: stack_struct_ty, + struct_type_index: step_struct_ty, field_index: 1, }, - LocalGet(stack_struct_local), + LocalGet(step_struct_local), StructGet { - struct_type_index: stack_struct_ty, + struct_type_index: step_struct_ty, field_index: 0, }, - ReturnCallRef(step_func_ty), + ReturnCallRef(TStepFunc::ty(&types)?), End, ] } @@ -146,30 +127,32 @@ pub fn wasm( func.compile_inner_step(Rc::clone(step))? } YieldMode::Schedule(step_index) => { - let threads_table = func.registries().tables().register::()?; - let thread_struct_ty = func.registries().types().thread_struct_type()?; - let local = func.local(ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(thread_struct_ty), - }))?; - func.free_local(local)?; - let stack_array_ty = func.registries().types().stack_array_type()?; - let stack_struct_ty = func.registries().types().stack_struct_type()?; + let static_functions = Rc::clone(func.registries().static_functions()); + let types = Rc::clone(func.registries().types()); + + let stack_array_ty = >>::ty(&types)?; + let step_func_ty = TStepFunc::ty(&types)?; + + let stack_local = (func.params().len() - 2) as u32; + + let stack_len = + static_functions.register::>, _>()?; + let stack_get = + static_functions.register::>, _>()?; wasm![ - LocalGet(0), - TableGet(threads_table), - RefAsNonNull, - LocalTee(local), - StructGet { struct_type_index: thread_struct_ty, field_index: 1 }, - LocalGet(local), - StructGet { struct_type_index: thread_struct_ty, field_index: 0 }, + LocalGet(stack_local), + RefCastNonNull(stack_array_ty), + LocalGet(stack_local), + RefCastNonNull(stack_array_ty), + #StaticFunctionCall(stack_len), I32Const(1), I32Sub, - ArrayGet(stack_array_ty), + #StaticFunctionCall(stack_get), RefAsNonNull, #LazyStepRef(*step_index), - StructSet { struct_type_index: stack_struct_ty, field_index: 0 }, + RefCastNonNull(step_func_ty), + StructSet { struct_type_index: TStackStruct::ty(&types)?, field_index: 0 }, Return ] } diff --git a/src/instructions/procedures/argument.rs b/src/instructions/procedures/argument.rs index be757b32..f794ba5e 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, RegistryItem}; use crate::wasm::{StepFunc, WasmProject}; #[derive(Clone, Debug)] @@ -72,10 +72,11 @@ 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 - .get_index(struct_type_index as usize) - .ok_or_else(|| make_hq_bug!("type index not found in type registry"))? - .0 + let RegistryItem::Type(CompoundType::Struct(struct_type_fields)) = // TODO: do we need to consider the possibility of rec group items? + type_registry + .get_index(struct_type_index as usize) + .ok_or_else(|| make_hq_bug!("type index not found in type registry"))? + .0 else { hq_bug!("struct type was not a struct type") }; diff --git a/src/instructions/procedures/call_nonwarp.rs b/src/instructions/procedures/call_nonwarp.rs index 54347734..abbc3038 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, TType}; use crate::wasm::{StepFunc, WasmProject}; #[derive(Clone, Debug)] @@ -35,6 +36,8 @@ pub fn wasm( hq_bug!("nonwarped_specific_proc didn't exist for call_nonwarp") }; + let step_func_ty = TStepFunc::ty(func.registries().types())?; + let arg_struct_type = func.registries().types().struct_( (*nonwarped_specific_proc.arg_vars()) .borrow() @@ -96,13 +99,16 @@ pub fn wasm( LocalSet(arg_struct_local), LocalGet((func.params().len() - 2).try_into().map_err(|_| make_hq_bug!("local index out of bounds"))?), #LazyNonWarpedProcRef(Rc::clone(proc)), + RefCastNonNull(step_func_ty), LocalGet(arg_struct_local), #LazyStepRef(*next_step), + RefCastNonNull(step_func_ty), #StaticFunctionCall(spawn_thread_in_stack), 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()?) + RefCastNonNull(step_func_ty), + ReturnCallRef(func.registries().types().register_comp::()?) ]); Ok(wasm) @@ -140,6 +146,7 @@ mod test { use super::super::super::tests::*; use super::*; use crate::ir::{PartialStep, StepIndex}; + use crate::wasm::registries::types::{TNonNullable, TStackArray, TType}; #[test] fn fields_display_is_valid_json() { @@ -150,7 +157,7 @@ mod test { pub fn test_project_setup(wasm_proj: &WasmProject, flags: crate::wasm::WasmFlags) { let proc_step_func = StepFunc::new_with_types( Box::from([ - ValType::I32, + >::ty(wasm_proj.registries().types()).unwrap(), crate::wasm::registries::TypeRegistry::STRUCT_REF, ]), vec![].into(), diff --git a/src/instructions/procedures/call_warp.rs b/src/instructions/procedures/call_warp.rs index d5579f8f..ac4866a0 100644 --- a/src/instructions/procedures/call_warp.rs +++ b/src/instructions/procedures/call_warp.rs @@ -112,6 +112,7 @@ mod test { use super::super::super::tests::*; use super::*; use crate::ir::{PartialStep, StepIndex}; + use crate::wasm::registries::types::{TNonNullable, TStackArray, TType}; #[test] fn fields_display_is_valid_json() { @@ -129,7 +130,7 @@ mod test { .iter() .map(|ty| WasmProject::ir_type_to_wasm(*ty)) .chain([ - ValType::I32, + >::ty(wasm_proj.registries().types()).unwrap(), crate::wasm::registries::TypeRegistry::STRUCT_REF, ]) .collect(), diff --git a/src/instructions/sensing/askandwait.rs b/src/instructions/sensing/askandwait.rs index 5c2f4a95..17b41e87 100644 --- a/src/instructions/sensing/askandwait.rs +++ b/src/instructions/sensing/askandwait.rs @@ -4,6 +4,7 @@ use super::super::prelude::*; use crate::ir::StepIndex; use crate::wasm::StepFunc; use crate::wasm::registries::functions::static_functions::{MarkWaitingFlag, SpawnThreadInStack}; +use crate::wasm::registries::types::{TStepFunc, TType}; #[derive(Clone, Debug)] pub struct Fields { @@ -39,7 +40,10 @@ pub fn wasm( let struct_valtype = ValType::Ref(RefType { nullable: false, heap_type: HeapType::Concrete(i8_struct_type), - }); + }); // TODO: rust-type-ify these types + + let step_func_ty = TStepFunc::ty(func.registries().types())?; + let struct_local = func.local(struct_valtype)?; func.free_local(struct_local)?; @@ -66,9 +70,11 @@ pub fn wasm( .map_err(|_| make_hq_bug!("local index out of bounds"))? ), #LazyStepRef(*poll_step), + RefCastNonNull(step_func_ty), StructNewDefault(i8_struct_type), LocalTee(struct_local), #LazyStepRef(*next_step), + RefCastNonNull(step_func_ty), #StaticFunctionCall(spawn_thread_func), LocalGet(struct_local), Call(queue_ask), @@ -98,6 +104,7 @@ mod test { use super::super::super::tests::*; use super::*; use crate::wasm::registries::TypeRegistry; + use crate::wasm::registries::types::{TNonNullable, TStackArray, TType}; use crate::wasm::{StepTarget, WasmFlags, WasmProject}; #[test] @@ -115,7 +122,10 @@ mod test { pub fn setup_project(wasm_proj: &WasmProject, flags: WasmFlags) { let step_func = StepFunc::new_with_types( - Box::from([ValType::I32, TypeRegistry::STRUCT_REF]), + Box::from([ + >::ty(wasm_proj.registries().types()).unwrap(), + TypeRegistry::STRUCT_REF, + ]), Box::from([]), wasm_proj.registries(), flags, diff --git a/src/instructions/tests.rs b/src/instructions/tests.rs index 00cd5a7d..12922588 100644 --- a/src/instructions/tests.rs +++ b/src/instructions/tests.rs @@ -81,7 +81,7 @@ macro_rules! instructions_test { $($setup(&mut proj, flags());)? let output_type_result = output_type(types.clone().into(), $(&$fields)?); let registries = Rc::new(Registries::default()); - let step_func = StepFunc::new(Rc::clone(®istries), flags(), StepTarget::Sprite(0), 0, Rc::clone(proj.costume_names())); + let step_func = StepFunc::new(Rc::clone(®istries), flags(), StepTarget::Sprite(0), 0, Rc::clone(proj.costume_names())).unwrap(); let wasm_result = wasm(&step_func, types); match (output_type_result.clone(), wasm_result.clone()) { (Err(..), Ok(..)) | (Ok(..), Err(..)) => panic!("output_type result doesn't match wasm result for type(s) {:?}:\noutput_type: {:?},\nwasm: {:?}", ($($($type_arg,)*)?), output_type_result, wasm_result), @@ -228,12 +228,11 @@ pub use test_util::*; #[cfg(test)] mod test_util { - use wasm_encoder::ValType; - use crate::instructions::IrOpcode; use crate::ir::{IrType, ReturnType, Step, StepContext, Target}; use crate::prelude::*; use crate::wasm::registries::TypeRegistry; + use crate::wasm::registries::types::{TNonNullable, TStackArray, TType}; use crate::wasm::{InternalInstruction, StepFunc, StepTarget, WasmFlags, WasmProject}; pub fn make_target() -> Rc { @@ -289,7 +288,7 @@ mod test_util { .iter() .copied() .map(WasmProject::ir_type_to_wasm) - .chain([ValType::I32, TypeRegistry::STRUCT_REF]) + .chain([>::ty(registries.types())?, TypeRegistry::STRUCT_REF]) .collect::>(); let result = match output_type { ReturnType::Singleton(output) => vec![WasmProject::ir_type_to_wasm(output)], @@ -324,13 +323,16 @@ mod test_util { let wasm_bytes = proj.finish().unwrap().wasm_bytes; + std::fs::write("./wasm.wasm", &wasm_bytes); + println!("{}", wasmprinter::print_bytes(wasm_bytes.clone()).unwrap()); wasmparser::validate(&wasm_bytes).map_err(|err| { make_hq_bug!( - "invalid wasm module with types {:?}. Original error message: {}", + "invalid wasm module with types {:?}. Original error message: {}. At offset {}", types, - err.message() + err.message(), + err.offset() ) })?; @@ -359,7 +361,10 @@ mod test_util { .iter() .copied() .map(WasmProject::ir_type_to_wasm) - .chain([ValType::I32, TypeRegistry::STRUCT_REF]) + .chain([ + >::ty(registries.types())?, + TypeRegistry::STRUCT_REF, + ]) .collect::>(); let result = vec![]; let step_func = StepFunc::new_with_types( 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/ir/types.rs b/src/ir/types.rs index b8cb5230..38027307 100644 --- a/src/ir/types.rs +++ b/src/ir/types.rs @@ -105,7 +105,6 @@ impl Type { .find(|&ty| ty.contains(self)) } - #[must_use] pub fn base_types(self) -> Box> { if self.is_none() { return Box::new(core::iter::empty()); diff --git a/src/lib.rs b/src/lib.rs index f752682d..208d1a43 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(specialization)] +#![feature(const_type_name)] +#![feature(generic_const_exprs)] #![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..e81c78d2 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,12 @@ pub trait RegistryType { type Value; } +pub trait CompTimeRegistrand { + type Receiver: core::ops::Deref; + + fn register(registry: &Self::Receiver) -> HQResult; +} + pub trait Registry: Sized + RegistryType { fn registry(&self) -> &RefCell>; @@ -79,8 +89,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 +107,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 +123,14 @@ pub trait Registry: Sized + RegistryType { .map_err(|_| make_hq_bug!("registry item index out of bounds")) } + fn register_comp(self: &R::Receiver) -> 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 +139,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,19 +249,25 @@ 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, + { + let (key, value) = Self::registration::(); + self.0.register(key, value) + } + + pub fn registration() -> (R::Key, R::Value) + where T: NamedRegistryItem, { - self.0.register(R::name::(), T::VALUE) + (R::name::(), T::VALUE) } /// Registers a runtime key-value pair; just calls `register` on the underlying /// `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 +276,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 +285,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 +296,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..79b34576 100644 --- a/src/wasm/func.rs +++ b/src/wasm/func.rs @@ -8,10 +8,13 @@ use wasm_encoder::{ use wasm_gen::wasm; use super::{Registries, WasmFlags, WasmProject}; -use crate::instructions::{IrOpcode, wrap_instructions}; +use crate::instructions::{IrOpcode, TPollStruct, TWaitingThreadArray, wrap_instructions}; use crate::ir::{Event, PartialStep, Proc, RcVar, Step, StepIndex}; use crate::prelude::*; use crate::wasm::registries::TypeRegistry; +use crate::wasm::registries::types::{ + TArray, TConstField, TMutField, TNonNullable, TStackArray, TStepFunc, TStruct, TType, +}; #[derive(Clone, Debug)] pub enum Instruction { @@ -34,18 +37,14 @@ 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, + steps: &Rc>>, ) -> HQResult]>> { Ok(match self { Self::Immediate(instr) => Box::from([instr.clone()]), - #[cfg(test)] - Self::LazyStepRef(_step) => Box::from([WInstruction::RefFunc( - imported_func_count + static_func_count, - )]), - #[cfg(not(test))] Self::LazyStepRef(step_index) => Box::from([WInstruction::RefFunc( imported_func_count + static_func_count @@ -56,20 +55,34 @@ impl Instruction { let broadcast_indices = events .get(&Event::Broadcast(broadcast.clone())) .cloned() - .unwrap_or_default(); + .unwrap_or_default() + .into_iter() + .map(|i| { + HQResult::Ok(( + i, + steps + .try_borrow()? + .get(i as usize) + .ok_or_else(|| make_hq_bug!("step index out of bounds"))? + .target_index() as i32, + )) + }) + .collect::>>()?; // todo: these should begin execution in the same step, I think, possibly immediately? broadcast_indices .iter() - .flat_map(|&i| { + .flat_map(|&(i, target)| { [ + WInstruction::I32Const(target), WInstruction::RefFunc(i + imported_func_count + static_func_count), WInstruction::RefNull(HeapType::Abstract { shared: false, ty: AbstractHeapType::Struct, }), WInstruction::Call(spawn_new_thread_func + imported_func_count), + WInstruction::Drop, ] }) .chain([ @@ -84,19 +97,25 @@ impl Instruction { .collect() } Self::LazyBroadcastSpawnAndWait((broadcast, poll_step, next_step, arr_local)) => { - let broadcast_indices = events + let broadcast_and_target_indices = events .get(&Event::Broadcast(broadcast.clone())) .cloned() - .unwrap_or_default(); - - let i32_array_type = types.array(StorageType::Val(ValType::I32), true)?; - let thread_poll_struct = types.struct_(vec![FieldType { - element_type: StorageType::Val(ValType::Ref(RefType { - nullable: false, - heap_type: HeapType::Concrete(i32_array_type), - })), - mutable: false, - }])?; + .unwrap_or_default() + .into_iter() + .map(|i| { + HQResult::Ok(( + i, + steps + .try_borrow()? + .get(i as usize) + .ok_or_else(|| make_hq_bug!("step index out of bounds"))? + .target_index(), + )) + }) + .collect::>>()?; + let waiting_thread_array_type = TWaitingThreadArray::ty(types)?; + let poll_struct_type = TPollStruct::ty(types)?; + let step_func_type = TStepFunc::ty(types)?; let poll_step_index: u32 = poll_step .0 @@ -108,35 +127,31 @@ impl Instruction { .try_into() .map_err(|_| make_hq_bug!("next_step index out of bounds"))?; - let broadcast_num = i32::try_from(broadcast_indices.len()) + let broadcast_num = i32::try_from(broadcast_and_target_indices.len()) .map_err(|_| make_hq_bug!("indices len out of bounds"))?; [ WInstruction::I32Const(broadcast_num), - WInstruction::ArrayNewDefault(i32_array_type), + WInstruction::ArrayNewDefault(waiting_thread_array_type), WInstruction::LocalSet(*arr_local), ] .into_iter() .chain( - broadcast_indices + broadcast_and_target_indices .iter() - .enumerate() - .map(|(j, &i)| { + .map(|&(i, target)| { // todo: should these should begin execution in the same step? Ok([ WInstruction::LocalGet(*arr_local), - WInstruction::I32Const( - j.try_into() - .map_err(|_| make_hq_bug!("index out of bounds"))?, - ), - WInstruction::TableSize(threads_table), - WInstruction::ArraySet(i32_array_type), + WInstruction::I32Const(target as i32), WInstruction::RefFunc(i + imported_func_count + static_func_count), + WInstruction::RefCastNonNull(step_func_type), WInstruction::RefNull(HeapType::Abstract { shared: false, ty: AbstractHeapType::Struct, }), WInstruction::Call(spawn_new_thread_func + imported_func_count), + WInstruction::ArraySet(waiting_thread_array_type), ]) }) .collect::>>()? @@ -151,11 +166,13 @@ impl Instruction { WInstruction::RefFunc( poll_step_index + imported_func_count + static_func_count, ), + WInstruction::RefCastNonNull(step_func_type), WInstruction::LocalGet(*arr_local), - WInstruction::StructNew(thread_poll_struct), + WInstruction::StructNew(poll_struct_type), WInstruction::RefFunc( next_step_index + imported_func_count + static_func_count, ), + WInstruction::RefCastNonNull(step_func_type), WInstruction::Call(spawn_thread_in_stack_func + imported_func_count), ]) .collect() @@ -213,6 +230,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 { @@ -264,19 +291,21 @@ impl StepFunc { } /// creates a new step function, with one paramter - #[must_use] pub fn new( registries: Rc, flags: WasmFlags, target: StepTarget, target_index: u32, costume_names: Rc>>>, - ) -> Self { - Self { + ) -> HQResult { + Ok(Self { locals: RefCell::new(vec![]), available_locals: RefCell::new(BTreeMap::new()), instructions: RefCell::new(vec![]), - params: Box::new([ValType::I32, TypeRegistry::STRUCT_REF]), + params: Box::new([ + >::ty(registries.types())?, + TypeRegistry::STRUCT_REF, + ]), output: Box::new([]), registries, flags, @@ -284,7 +313,7 @@ impl StepFunc { target, target_index, costume_names, - } + }) } /// creates a new step function with the specified amount of paramters. @@ -395,10 +424,11 @@ 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, + steps: &Rc>>, ) -> HQResult<()> { let mut func = Function::new_with_locals_types(self.locals.take()); for instruction in self.instructions().take() { @@ -408,10 +438,11 @@ 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, + steps, )? { func.instruction(&real_instruction); } @@ -467,10 +498,16 @@ impl StepFunc { .borrow() .iter() .map(|var| WasmProject::ir_type_to_wasm(*var.possible_types())) - .chain([ValType::I32, TypeRegistry::STRUCT_REF]) + .chain([ + >::ty(registries.types())?, + TypeRegistry::STRUCT_REF, + ]) .collect() } else { - Box::from([ValType::I32, TypeRegistry::STRUCT_REF]) + Box::from([ + >::ty(registries.types())?, + TypeRegistry::STRUCT_REF, + ]) }; let outputs = if step.try_borrow()?.context().warp { (*proc_context.ret_vars) @@ -491,7 +528,7 @@ impl StepFunc { costume_names, ) } else { - Self::new(registries, flags, target, target_index, costume_names) + Self::new(registries, flags, target, target_index, costume_names)? }; if let Some(ref proc_context) = step.try_borrow()?.context().proc_context && !step.try_borrow()?.context().warp 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..ec6d248c 100644 --- a/src/wasm/project.rs +++ b/src/wasm/project.rs @@ -2,19 +2,21 @@ use itertools::Itertools; use wasm_bindgen::prelude::*; use wasm_encoder::{ AbstractHeapType, BlockType as WasmBlockType, CodeSection, ConstExpr, DataCountSection, - DataSection, ElementSection, Elements, ExportKind, ExportSection, FieldType, Function, - FunctionSection, GlobalSection, HeapType, ImportSection, Instruction, MemorySection, - MemoryType, Module, RefType, StartSection, StorageType, TableSection, TypeSection, ValType, + DataSection, ElementSection, Elements, ExportKind, ExportSection, Function, FunctionSection, + GlobalSection, HeapType, ImportSection, Instruction, MemorySection, MemoryType, Module, + RefType, StartSection, TableSection, TypeSection, ValType, }; 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::TypeRegistry; use crate::wasm::registries::functions::static_functions::{ - MarkWaitingFlag, SpawnNewThread, SpawnThreadInStack, + SpawnNewThread, SpawnThreadInStack, Tick, UnreachableDbg, }; -use crate::wasm::{StepFunc, StringsTable, ThreadsTable, WasmFlags}; +use crate::wasm::registries::types::{TStepFunc, TType}; +use crate::wasm::{StepFunc, StringsTable, WasmFlags}; /// A respresentation of a WASM representation of a project. Cannot be created directly; /// use `TryFrom`. @@ -138,40 +140,26 @@ impl WasmProject { 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()?, - ))?; - + .register::()?; 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()?, - ))?; - + .register::()?; self.registries() .static_functions() - .register_override::(self.registries().types().struct_( - vec![FieldType { - element_type: StorageType::I8, - mutable: true, - }], - )?)?; - - self.registries().static_functions().clone().finish( - &mut functions, - &mut exports, - &mut codes, - self.registries.types(), - self.imported_func_count()?, - )?; + .register::()?; // required for events finishing + self.registries() + .static_functions() + .register::()?; // required for broadcasts + + let static_func_count = + Rc::unwrap_or_clone(Rc::clone(self.registries().static_functions())).finish( + &self, + &mut functions, + &mut exports, + &mut codes, + self.registries.types(), + self.imported_func_count()?, + )?; for step_func in self.steps().try_borrow()?.iter().cloned() { step_func.finish( @@ -182,18 +170,22 @@ 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()?, + static_func_count, self.imported_global_count()?, + self.steps(), )?; } - self.tick_func(&mut functions, &mut codes, &mut exports)?; - - self.finish_events(&mut functions, &mut codes, &mut exports)?; - - self.unreachable_dbg_func(&mut functions, &mut codes, &mut exports)?; + self.finish_events( + &mut functions, + &mut codes, + &mut exports, + static_func_count, + self.steps(), + self.registries().types(), + )?; codes.function(&start_func); functions.function(self.registries().types().function(vec![], vec![])?); @@ -202,16 +194,10 @@ 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()? + static_func_count ..self.imported_func_count()? - + self.static_func_count()? + + static_func_count + u32::try_from(self.steps().try_borrow()?.len()) .map_err(|_| make_hq_bug!("steps len out of bounds"))?) .collect(), @@ -281,12 +267,12 @@ 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()?, self.imported_func_count()?, - self.static_func_count()?, + static_func_count, ); module @@ -324,7 +310,7 @@ impl WasmProject { }) } - fn imported_func_count(&self) -> HQResult { + pub fn imported_func_count(&self) -> HQResult { self.registries() .external_functions() .registry() @@ -334,17 +320,17 @@ impl WasmProject { .map_err(|_| make_hq_bug!("external function map len out of bounds")) } - fn static_func_count(&self) -> HQResult { - self.registries() - .static_functions() - .registry() - .try_borrow()? - .len() - .try_into() - .map_err(|_| make_hq_bug!("static function map len out of bounds")) - } - - fn imported_global_count(&self) -> HQResult { + // pub fn static_func_count(&self) -> HQResult { + // self.registries() + // .static_functions() + // .registry() + // .try_borrow()? + // .len() + // .try_into() + // .map_err(|_| make_hq_bug!("static function map len out of bounds")) + // } + + pub fn imported_global_count(&self) -> HQResult { self.registries() .strings() .registry() @@ -354,68 +340,42 @@ impl WasmProject { .map_err(|_| make_hq_bug!("string registry len out of bounds")) } - fn unreachable_dbg_func( - &self, - functions: &mut FunctionSection, - codes: &mut CodeSection, - exports: &mut ExportSection, - ) -> HQResult<()> { - let mut func = Function::new(vec![]); - func.instruction(&Instruction::Unreachable); - func.instruction(&Instruction::End); - codes.function(&func); - functions.function(self.registries().types().function(vec![], vec![])?); - exports.export( - "unreachable_dbg", - ExportKind::Func, - self.imported_func_count()? + functions.len() - 1, - ); - - Ok(()) - } - - fn threads_table_index(&self) -> HQResult + pub fn spawn_new_thread_func(&self) -> HQResult where N: TryFrom, >::Error: fmt::Debug, { - self.registries().tables().register::() + self.registries() + .static_functions() + .register::() } - fn spawn_new_thread_func(&self) -> HQResult + pub fn spawn_thread_in_stack_func(&self) -> HQResult where N: TryFrom, >::Error: fmt::Debug, { self.registries() .static_functions() - .register::() + .register::() } - fn spawn_thread_in_stack_func(&self) -> HQResult + pub fn threads_count_global(&self) -> HQResult where N: TryFrom, >::Error: fmt::Debug, { - self.registries() - .static_functions() - .register::() + self.registries().globals().threads_count() } - fn threads_count_global(&self) -> HQResult + pub fn threadss_global(&self) -> HQResult where 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() + .threadss(self.registries().types(), self.costume_names().len() as u32) } #[expect(clippy::needless_pass_by_value, reason = "annoying to borrow a box")] @@ -426,6 +386,9 @@ impl WasmProject { funcs: &mut FunctionSection, codes: &mut CodeSection, exports: &mut ExportSection, + static_func_count: u32, + steps: &Rc>>, + types: &Rc, ) -> HQResult { let mut func = Function::new(vec![]); @@ -437,12 +400,21 @@ impl WasmProject { .iter() .map(|&i| { Ok(wasm![ - RefFunc(i + self.imported_func_count()? + self.static_func_count()?), + I32Const( + steps + .try_borrow()? + .get(i as usize) + .ok_or_else(|| make_hq_bug!("step index out of bounds"))? + .target_index() as i32 + ), + RefFunc(i + self.imported_func_count()? + static_func_count), + RefCastNonNull(TStepFunc::ty(types)?), RefNull(HeapType::Abstract { shared: false, ty: AbstractHeapType::Struct }), #StaticFunctionCall(spawn_new_thread), + Drop, ]) }) .flatten_ok() @@ -455,10 +427,11 @@ 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()?, + static_func_count, self.imported_global_count()?, + self.steps(), )? { func.instruction(&real_instruction); } @@ -478,10 +451,11 @@ 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()?, + static_func_count, self.imported_global_count()?, + self.steps(), )? { func.instruction(&real_instruction); } @@ -504,6 +478,9 @@ impl WasmProject { funcs: &mut FunctionSection, codes: &mut CodeSection, exports: &mut ExportSection, + static_func_count: u32, + steps: &Rc>>, + types: &Rc, ) -> HQResult<()> { let event_funcs = self .events @@ -523,6 +500,9 @@ impl WasmProject { funcs, codes, exports, + static_func_count, + steps, + types, )?, ))) }) @@ -577,10 +557,11 @@ 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()?, + static_func_count, self.imported_global_count()?, + self.steps(), )? { sprite_clicked_func.instruction(&real_instruction); } @@ -601,122 +582,6 @@ impl WasmProject { Ok(()) } - fn tick_func( - &self, - funcs: &mut FunctionSection, - 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 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), - }), - ), - ]); - - let step_func_ty = self.registries().types().step_func_type()?; - let stack_array_ty = self.registries().types().stack_array_type()?; - - 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, - StructGet { - struct_type_index: thread_struct_type, - field_index: 1 - }, - LocalGet(2), - RefAsNonNull, - StructGet { - struct_type_index: thread_struct_type, - field_index: 0 - }, - I32Const(1), - I32Sub, - ArrayGet(stack_array_ty), - RefAsNonNull, - LocalTee(3), - StructGet { - struct_type_index: stack_struct_ty, - field_index: 1 - }, - LocalGet(3), - StructGet { - struct_type_index: stack_struct_ty, - field_index: 0 - }, - CallRef(step_func_ty), - LocalGet(0), - I32Const(1), - I32Add, - LocalTee(0), - LocalGet(1), - I32LtS, - BrIf(0), - End, - ]; - for instr in instructions { - for real_instruction in instr.eval( - &self.events, - self.registries().types(), - self.threads_count_global()?, - self.spawn_new_thread_func()?, - self.spawn_thread_in_stack_func()?, - self.threads_table_index()?, - self.imported_func_count()?, - self.static_func_count()?, - self.imported_global_count()?, - )? { - tick_func.instruction(&real_instruction); - } - } - tick_func.instruction(&Instruction::End); - funcs.function(self.registries().types().function(vec![], vec![])?); - codes.function(&tick_func); - exports.export( - "tick", - ExportKind::Func, - funcs.len() + self.imported_func_count()? - 1, - ); - Ok(()) - } - pub fn from_ir( ir_project: &Rc, _ssa_token: crate::optimisation::SSAToken, diff --git a/src/wasm/registries.rs b/src/wasm/registries.rs index b12a1ff5..0900e046 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 const fn globals(&self) -> &Rc { &self.globals } diff --git a/src/wasm/registries/functions.rs b/src/wasm/registries/functions.rs index 72f487b4..7ba31d63 100644 --- a/src/wasm/registries/functions.rs +++ b/src/wasm/registries/functions.rs @@ -1,14 +1,18 @@ #![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; +mod tick; +mod unreachable_dbg; use wasm_encoder::{ CodeSection, EntityType, ExportKind, ExportSection, Function, FunctionSection, ImportSection, Instruction as WInstruction, ValType, }; +use super::super::WasmProject; use super::TypeRegistry; use crate::prelude::*; use crate::registry::{MapRegistry, Registry}; @@ -47,7 +51,11 @@ pub struct StaticFunction { #[derive(Clone)] pub struct MaybeStaticFunction { pub static_function: Option, - pub maybe_populate: fn() -> Option, + pub maybe_populate: fn( + &WasmProject, + &IndexMap, MaybeStaticFunction>, + ) -> HQResult>, + pub register_deps: fn() -> Vec<(Box, MaybeStaticFunction)>, } pub struct StaticFunctionRegistrar; @@ -57,21 +65,42 @@ impl RegistryType for StaticFunctionRegistrar { pub type StaticFunctionRegistry = NamedRegistry; impl StaticFunctionRegistry { + /// Finishes the registry. Returns the final size of the registry. pub fn finish( self, + wasm_proj: &WasmProject, functions: &mut FunctionSection, exports: &mut ExportSection, codes: &mut CodeSection, type_registry: &TypeRegistry, imported_func_count: u32, - ) -> HQResult<()> { + ) -> HQResult { + let mut num_funcs = self.registry().borrow().len(); + let mut to_register = vec![]; + loop { + for (_name, MaybeStaticFunction { register_deps, .. }) in + self.registry().borrow().iter() + { + to_register.extend(register_deps()); + } + for (key, val) in core::mem::take(&mut to_register) { + self.register_dyn::(key, val)?; + } + let new_num_funcs = self.registry().borrow().len(); + if new_num_funcs == num_funcs { + break; + } + num_funcs = new_num_funcs; + } + let registry = self.registry().take(); for ( _name, MaybeStaticFunction { static_function, maybe_populate, + .. }, - ) in self.registry().take() + ) in ®istry { let Some(StaticFunction { instructions, @@ -79,7 +108,9 @@ impl StaticFunctionRegistry { returns, locals, export, - }) = static_function.map_or_else(maybe_populate, Some) + }) = static_function + .clone() + .map_or_else(|| maybe_populate(wasm_proj, ®istry), |sf| Ok(Some(sf)))? else { hq_bug!( "static functions must either be overriden, or have a non-None maybe_populate \ @@ -101,14 +132,18 @@ impl StaticFunctionRegistry { ); } } - Ok(()) + Ok(num_funcs as u32) } } 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, SpawnThreadInStack}; + pub use super::tick::Tick; + pub use super::unreachable_dbg::UnreachableDbg; } diff --git a/src/wasm/registries/functions/dyn_array.rs b/src/wasm/registries/functions/dyn_array.rs new file mode 100644 index 00000000..d0a558e1 --- /dev/null +++ b/src/wasm/registries/functions/dyn_array.rs @@ -0,0 +1,316 @@ +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, + register_deps: || vec![], + maybe_populate: |proj, _| { + let types = Rc::clone(proj.registries().types()); + let struct_type = types.register_comp::, u32>()?; + let array_type = types.register_comp::, u32>()?; + Ok(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, + }, + End + ] as &[_]), + params: Box::from([>>::ty(&types)?, T::ty(&types)?]), + returns: Box::from([]), + locals: Box::from([ValType::I32, >>::ty(&types)?]), + })) + }, + }; +} + +/// 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 + TDefaultable> NamedRegistryItem for DynArrayGet { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + register_deps: || vec![], + maybe_populate: |proj, _| { + let types = Rc::clone(proj.registries().types()); + let struct_type = types.register_comp::, u32>()?; + let array_type = types.register_comp::, u32>()?; + Ok(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), + End, + ] as &[_]), + params: Box::from([>>::ty(&types)?, ValType::I32]), + returns: Box::from([T::ty(&types)?]), + locals: Box::from([]), + })) + }, + }; +} + +/// 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 + TDefaultable> NamedRegistryItem for DynArraySet { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + register_deps: || vec![], + maybe_populate: |proj, _| { + let types = Rc::clone(proj.registries().types()); + let struct_type = types.register_comp::, u32>()?; + let array_type = types.register_comp::, u32>()?; + Ok(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), + End, + ] as &[_]), + params: Box::from([ + >>::ty(&types)?, + ValType::I32, + T::ty(&types)?, + ]), + returns: Box::from([T::ty(&types)?]), + locals: Box::from([]), + })) + }, + }; +} + +/// Pops the last element from a dynamic (resizeable) array +/// +/// Takes 1 parameter: +/// ref `dynamic_array` - the dynamic array struct (obtained from `TDynArray` for `T: TDefaultable`) +/// +/// Returns t +pub struct DynArrayPop(PhantomData); +impl + TDefaultable> NamedRegistryItem for DynArrayPop { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + register_deps: || vec![], + maybe_populate: |proj, _| { + let types = Rc::clone(proj.registries().types()); + let struct_type = types.register_comp::, u32>()?; + let array_type = types.register_comp::, u32>()?; + Ok(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, + }, + End, + ] as &[_]), + params: Box::from([>>::ty(&types)?]), + returns: Box::from([T::ty(&types)?]), + locals: Box::from([ValType::I32]), + })) + }, + }; +} + +/// Creates a new dynamic (resizeable) array of the given capacity +/// +/// Takes 1 parameter: +/// i32 - the initial capacity of the array to create +/// +/// Returns ref `dynamic_array` +pub struct DynArrayNew(PhantomData); +impl + TDefaultable> NamedRegistryItem for DynArrayNew { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + register_deps: || vec![], + maybe_populate: |proj, _| { + let types = Rc::clone(proj.registries().types()); + let struct_type = types.register_comp::, u32>()?; + let array_type = types.register_comp::, u32>()?; + Ok(Some(StaticFunction { + export: None, + instructions: Box::from(wasm_const![ + LocalGet(0), + ArrayNewDefault(array_type), + I32Const(0), + StructNew(struct_type), + End, + ] as &[_]), + params: Box::from([ValType::I32]), + returns: Box::from([>>::ty(&types)?]), + locals: Box::from([]), + })) + }, + }; +} + +/// Returns the length (not capacity) of the given dynamic array +/// +/// Takes 1 parameter: +/// ref `dynamic_array` - the dynamic array +/// +/// Returns i32 +pub struct DynArrayLen(PhantomData); +impl + TDefaultable> NamedRegistryItem for DynArrayLen { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + register_deps: || vec![], + maybe_populate: |proj, _| { + let types = Rc::clone(proj.registries().types()); + let struct_type = types.register_comp::, u32>()?; + Ok(Some(StaticFunction { + export: None, + instructions: Box::from(wasm_const![ + LocalGet(0), + StructGet { + struct_type_index: struct_type, + field_index: 1 + }, + End, + ] as &[_]), + params: Box::from([>>::ty(&types)?]), + returns: Box::from([ValType::I32]), + locals: Box::from([]), + })) + }, + }; +} + +/// Clears the given dynamic array to length 0 (but doesn't actually drop any of the elements) +/// +/// Takes 1 parameter: +/// ref `dynamic_array` - the dynamic array +pub struct DynArrayClear(PhantomData); +impl + TDefaultable> NamedRegistryItem for DynArrayClear { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + register_deps: || vec![], + maybe_populate: |proj, _| { + let types = Rc::clone(proj.registries().types()); + let struct_type = types.register_comp::, u32>()?; + Ok(Some(StaticFunction { + export: None, + instructions: Box::from(wasm_const![ + LocalGet(0), + I32Const(0), + StructSet { + struct_type_index: struct_type, + field_index: 1 + }, + End, + ] as &[_]), + params: Box::from([>>::ty(&types)?]), + returns: Box::from([]), + locals: Box::from([]), + })) + }, + }; +} diff --git a/src/wasm/registries/functions/mark_waiting_flag.rs b/src/wasm/registries/functions/mark_waiting_flag.rs index f2e3751c..58a97d7f 100644 --- a/src/wasm/registries/functions/mark_waiting_flag.rs +++ b/src/wasm/registries/functions/mark_waiting_flag.rs @@ -1,4 +1,4 @@ -use wasm_encoder::{HeapType, RefType, ValType}; +use wasm_encoder::{FieldType, HeapType, RefType, StorageType, ValType}; use wasm_gen::wasm_const; use super::{MaybeStaticFunction, StaticFunction}; @@ -16,14 +16,13 @@ pub struct MarkWaitingFlag; impl NamedRegistryItem for MarkWaitingFlag { const VALUE: MaybeStaticFunction = MaybeStaticFunction { static_function: None, - maybe_populate: || None, - }; -} -pub type MarkWaitingFlagOverride = u32; -impl NamedRegistryItemOverride for MarkWaitingFlag { - fn r#override(i8_struct_ty: u32) -> MaybeStaticFunction { - MaybeStaticFunction { - static_function: Some(StaticFunction { + register_deps: || vec![], + maybe_populate: |proj, _| { + let i8_struct_ty = proj.registries().types().struct_(vec![FieldType { + element_type: StorageType::I8, + mutable: true, + }])?; + Ok(Some(StaticFunction { export: Some("mark_waiting_flag".into()), instructions: Box::from(wasm_const![ LocalGet(0), @@ -40,8 +39,7 @@ impl NamedRegistryItemOverride for })]), returns: Box::new([]), locals: Box::new([]), - }), - maybe_populate: || None, - } - } + })) + }, + }; } diff --git a/src/wasm/registries/functions/pen_colour.rs b/src/wasm/registries/functions/pen_colour.rs index 82e448df..dced8156 100644 --- a/src/wasm/registries/functions/pen_colour.rs +++ b/src/wasm/registries/functions/pen_colour.rs @@ -27,8 +27,9 @@ pub struct UpdatePenColorFromHSV; impl NamedRegistryItem for UpdatePenColorFromHSV { const VALUE: MaybeStaticFunction = MaybeStaticFunction { static_function: None, - maybe_populate: || { - Some(StaticFunction { + register_deps: || vec![], + maybe_populate: |_, _| { + Ok(Some(StaticFunction { export: None, params: Box::from([ValType::I32]), returns: Box::from([]), @@ -283,7 +284,7 @@ impl NamedRegistryItem for UpdatePenColorFromHSV { End, ] as &[_]) .into(), - }) + })) }, }; } @@ -306,8 +307,9 @@ pub struct UpdatePenColorFromRGB; impl NamedRegistryItem for UpdatePenColorFromRGB { const VALUE: MaybeStaticFunction = MaybeStaticFunction { static_function: None, - maybe_populate: || { - Some(StaticFunction { + register_deps: || vec![], + maybe_populate: |_, _| { + Ok(Some(StaticFunction { export: None, params: Box::from([ValType::I32]), returns: Box::from([]), @@ -520,7 +522,7 @@ impl NamedRegistryItem for UpdatePenColorFromRGB { End, ] as &[_]) .into(), - }) + })) }, }; } diff --git a/src/wasm/registries/functions/spawn_threads.rs b/src/wasm/registries/functions/spawn_threads.rs index b10185c2..22905ac9 100644 --- a/src/wasm/registries/functions/spawn_threads.rs +++ b/src/wasm/registries/functions/spawn_threads.rs @@ -1,198 +1,206 @@ -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::StaticFunctionRegistry; +use crate::wasm::registries::functions::StaticFunctionRegistrar; +use crate::wasm::registries::functions::dyn_array::{DynArrayNew, DynArrayPop, DynArrayPush}; +use crate::wasm::registries::types::{ + TDynArray, TNonNullable, TNullable, TStackArray, TStackStruct, TStepFunc, TStructRef, + TTargetThreadArray, TTargetThreadsStruct, TThreadArray, TType, +}; + +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 { static_function: None, - maybe_populate: || None, - }; -} -pub type SpawnThreadInStackOverride = (u32, u32, u32, u32, u32); -impl NamedRegistryItemOverride - for SpawnThreadInStack -{ - fn r#override( - (func_ty, stack_struct_type, stack_array_type, thread_struct_type, threads_table): SpawnThreadInStackOverride, - ) -> MaybeStaticFunction { - MaybeStaticFunction { - static_function: Some(StaticFunction { + register_deps: || { + vec![StaticFunctionRegistry::registration::< + DynArrayPop, + >()] + }, + maybe_populate: |proj, static_funcs| { + let imported_func_count = proj.imported_func_count()?; + + let types = Rc::clone(proj.registries().types()); + + let stack_struct_type = TStackStruct::ty(&types)?; + let stack_array_ty = >::ty(&types)?; + + let dyn_array_pop = static_funcs + .get_index_of(&StaticFunctionRegistrar::name::>()) + .ok_or_else(|| make_hq_bug!("static function dependency not registered"))? + as u32; + let dyn_array_push = static_funcs + .get_index_of(&StaticFunctionRegistrar::name::>()) + .ok_or_else(|| make_hq_bug!("static function dependency not registered"))? + as u32; + + Ok(Some(StaticFunction { export: None, instructions: Box::from(wasm_const![ + LocalGet(0), + RefCastNonNull(stack_array_ty), + Call(imported_func_count + dyn_array_pop), + Drop, + LocalGet(0), + RefCastNonNull(stack_array_ty), + 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), + RefCastNonNull(stack_array_ty), 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), + End, ] 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)?, + >::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), - }), - ]), - }), - maybe_populate: || None, - } - } + locals: Box::from([]), + })) + }, + }; } /// 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 +/// +/// Returns ref TStackArray pub struct SpawnNewThread; impl NamedRegistryItem for SpawnNewThread { const VALUE: MaybeStaticFunction = MaybeStaticFunction { static_function: None, - 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 { - static_function: Some(StaticFunction { + register_deps: || { + vec![ + StaticFunctionRegistry::registration::>(), + StaticFunctionRegistry::registration::>(), + StaticFunctionRegistry::registration::>>(), + ] + }, + maybe_populate: |proj, static_funcs| { + let types = Rc::clone(proj.registries().types()); + + let stack_struct_type = types.register_comp::()?; + let target_threads_type = types.register_comp::()?; + let target_threads_global: u32 = proj + .registries() + .globals + .threadss(&types, proj.costume_names().len() as u32)?; + + let imported_global_count = proj.imported_global_count()?; + let imported_func_count = proj.imported_func_count()?; + + Ok(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)?, + >::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(), - }), - maybe_populate: || None, - } - } + returns: Box::from([ + >::ty(&types)?, + ]), + 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) + .into(), + 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), + StructGet { + struct_type_index: TTargetThreadsStruct::ty(&types)?, + field_index: 1, + }, + I32Const(8), + Call( + imported_func_count + + (static_funcs + .get_index_of(&StaticFunctionRegistrar::name::< + DynArrayNew, + >()) + .ok_or_else(|| make_hq_bug!( + "static function dependency not registered" + ))? as u32) + ), + LocalTee(3), + LocalGet(1), + LocalGet(2), + StructNew(stack_struct_type), + Call( + imported_func_count + + (static_funcs + .get_index_of(&StaticFunctionRegistrar::name::< + DynArrayPush, + >()) + .ok_or_else(|| make_hq_bug!( + "static function dependency not registered" + ))? as u32) + ), + LocalGet(3), + RefCastNonNull(TStackArray::ty(&types)?), + Call( + imported_func_count + + (static_funcs + .get_index_of(&StaticFunctionRegistrar::name::< + DynArrayPush>, + >()) + .ok_or_else(|| make_hq_bug!( + "static function dependency not registered" + ))? as u32) + ), + LocalGet(3), + RefCastNonNull(TStackArray::ty(&types)?), + End, + ] as &[_]) + .into() + }, + })) + }, + }; } diff --git a/src/wasm/registries/functions/tick.rs b/src/wasm/registries/functions/tick.rs new file mode 100644 index 00000000..39128850 --- /dev/null +++ b/src/wasm/registries/functions/tick.rs @@ -0,0 +1,164 @@ +use wasm_encoder::{BlockType as WasmBlockType, ValType}; +use wasm_gen::wasm_const; + +use super::{MaybeStaticFunction, StaticFunction}; +use crate::prelude::*; +use crate::wasm::registries::StaticFunctionRegistry; +use crate::wasm::registries::functions::StaticFunctionRegistrar; +use crate::wasm::registries::functions::dyn_array::{DynArrayGet, DynArrayLen}; +use crate::wasm::registries::types::{ + TDynArray, TNonNullable, TNullable, TStackArray, TStackStruct, TStepFunc, TTargetThreadArray, + TTargetThreadsStruct, TThreadArray, TType, +}; + +pub struct Tick; +impl NamedRegistryItem for Tick { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + register_deps: || { + vec![ + StaticFunctionRegistry::registration::>>(), + StaticFunctionRegistry::registration::>>(), + StaticFunctionRegistry::registration::>>(), + StaticFunctionRegistry::registration::>>(), + ] + }, + maybe_populate: |proj, static_functions| { + let types = Rc::clone(proj.registries().types()); + + 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::()?; + + // this is fine to use here because strings are finished before static funcs, + // and strings are the only imported globals. + let imported_globals = proj.imported_global_count()?; + + let threadss_global = proj.threadss_global::()? + imported_globals; + + let targets_num = 1 + proj.costume_names().len() as i32; + + let imported_func_count = proj.imported_func_count()?; + + 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; + + Ok(Some(StaticFunction { + export: Some("tick".into()), + instructions: Box::from(wasm_const![ + Loop(WasmBlockType::Empty), + GlobalGet(threadss_global), + LocalGet(LOCAL_TARGET_INDEX), + ArrayGet(target_threads_array_type), + StructGet { + struct_type_index: target_thread_struct_type, + field_index: 1, + }, + LocalTee(LOCAL_THREAD_LIST), + Call( + imported_func_count + + static_functions + .get_index_of(&StaticFunctionRegistrar::name::< + DynArrayLen>, + >()) + .ok_or_else(|| make_hq_bug!( + "static function dependency not registered" + ))? as 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), + Call( + imported_func_count + + static_functions + .get_index_of(&StaticFunctionRegistrar::name::< + DynArrayGet>, + >()) + .ok_or_else(|| make_hq_bug!( + "static function dependency not registered" + ))? as u32 + ), + RefAsNonNull, + LocalTee(LOCAL_THREAD), + LocalGet(LOCAL_THREAD), + RefCastNonNull(>>::ty(&types)?), + LocalGet(LOCAL_THREAD), + RefCastNonNull(>>::ty(&types)?), + Call( + imported_func_count + + static_functions + .get_index_of(&StaticFunctionRegistrar::name::< + DynArrayLen>, + >()) + .ok_or_else(|| make_hq_bug!( + "static function dependency not registered" + ))? as u32 + ), + I32Const(1), + I32Sub, + Call( + imported_func_count + + static_functions + .get_index_of(&StaticFunctionRegistrar::name::< + DynArrayGet>, + >()) + .ok_or_else(|| make_hq_bug!( + "static function dependency not registered" + ))? as u32 + ), + RefAsNonNull, + LocalTee(LOCAL_STEP), + StructGet { + struct_type_index: stack_struct_type, + field_index: 1, + }, + LocalGet(LOCAL_STEP), + StructGet { + struct_type_index: stack_struct_type, + field_index: 0, + }, + CallRef(step_func_ty), + LocalGet(LOCAL_STACK_INDEX), + I32Const(1), + I32Add, + 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, + End, + ] as &[_]), + params: Box::new([]), + returns: Box::new([]), + locals: Box::new([ + ValType::I32, + ValType::I32, + ValType::I32, + >::ty(&types)?, + >::ty(&types)?, + >::ty(&types)?, + ]), + })) + }, + }; +} diff --git a/src/wasm/registries/functions/unreachable_dbg.rs b/src/wasm/registries/functions/unreachable_dbg.rs new file mode 100644 index 00000000..f43a2881 --- /dev/null +++ b/src/wasm/registries/functions/unreachable_dbg.rs @@ -0,0 +1,23 @@ +use wasm_encoder::{FieldType, HeapType, RefType, StorageType, ValType}; +use wasm_gen::wasm_const; + +use super::{MaybeStaticFunction, StaticFunction}; +use crate::prelude::*; + +/// Traps. Used for exposing the wasm module in devtools. +pub struct UnreachableDbg; +impl NamedRegistryItem for UnreachableDbg { + const VALUE: MaybeStaticFunction = MaybeStaticFunction { + static_function: None, + register_deps: || vec![], + maybe_populate: |_, _| { + Ok(Some(StaticFunction { + export: Some("unreachable_dbg".into()), + instructions: Box::from(wasm_const![Unreachable, End] as &[_]), + params: Box::new([]), + returns: Box::new([]), + locals: Box::new([]), + })) + }, + }; +} diff --git a/src/wasm/registries/globals.rs b/src/wasm/registries/globals.rs index 77111ae2..cc08ae5b 100644 --- a/src/wasm/registries/globals.rs +++ b/src/wasm/registries/globals.rs @@ -1,9 +1,16 @@ 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::{ + ListItem, StructSubTypes, TNonNullable, TRefType, TTargetThreadArray, TTargetThreadsStruct, + TThreadArray, TType, +}; #[derive(Copy, Clone, Debug)] pub struct GlobalMutable(pub bool); @@ -29,6 +36,61 @@ 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 = TTargetThreadArray::ty(types)?; + let target_threads_struct_type = TTargetThreadsStruct::ty(types)?; + let dyn_array_type = types.register_comp::()?; + let array_type = <<::Fields as ListItem<0>>::Get as TRefType>::HeapType::ty(types)?; + + self.register( + "threadss".into(), + ( + as TType>::ty(types)?, + ConstExpr::extended( + (0..=num_sprites) // stage + sprites + .flat_map(|i| { + [ + Instruction::I32Const(i as i32), + Instruction::I32Const(8), + Instruction::ArrayNewDefault(array_type), + Instruction::I32Const(8), + Instruction::StructNew(dyn_array_type), + Instruction::StructNew(target_threads_struct_type), + ] + }) + .chain([Instruction::ArrayNewFixed { + array_type_index: array_array_type, + array_size: num_sprites + 1, + }]), + ), // 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..89e56d26 100644 --- a/src/wasm/registries/types.rs +++ b/src/wasm/registries/types.rs @@ -1,126 +1,48 @@ -use wasm_encoder::{ - AbstractHeapType, FieldType, HeapType, RefType, StorageType, TypeSection, ValType, -}; +use wasm_encoder::HeapType; -use crate::ir::RcVar; use crate::prelude::*; -use crate::registry::SetRegistry; -use crate::wasm::WasmProject; -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum WasmType { - Function(Vec, Vec), - Array(StorageType, bool), - Struct(Vec), -} - -pub type TypeRegistry = SetRegistry; - -impl TypeRegistry { - pub fn function(&self, params: Vec, returns: Vec) -> HQResult - where - N: TryFrom, - >::Error: fmt::Debug, - { - self.register_default(WasmType::Function(params, returns)) - } - - pub fn array(&self, elem_type: StorageType, mutable: bool) -> HQResult - where - N: TryFrom, - >::Error: fmt::Debug, - { - self.register_default(WasmType::Array(elem_type, mutable)) - } - - pub fn struct_(&self, fields: Vec) -> HQResult - where - N: TryFrom, - >::Error: fmt::Debug, - { - self.register_default(WasmType::Struct(fields)) - } - - pub const STRUCT_REF: ValType = ValType::Ref(RefType { - nullable: true, - heap_type: HeapType::Abstract { - shared: false, - ty: AbstractHeapType::Struct, - }, - }); - - pub fn step_func_type(&self) -> HQResult { - self.function(vec![ValType::I32, Self::STRUCT_REF], vec![]) +mod dependencies; +mod registration; +#[macro_use] +mod rec_group; +mod registry; +mod subtypes; +#[macro_use] +mod tyfp; + +pub use rec_group::*; +pub use registration::*; +pub use registry::{CompoundType, RecGroup, RegistryItem, TypeRegistry}; +pub use subtypes::*; +pub use tyfp::*; + +rec_group! { + rec_grp { + TStepFunc = TFunc{ + {( + {TNonNullable{{TStackArray}}}, + {TNullable{{TStructRef}}}, + )}, + {()} + }; + TStackStruct = TStruct{{( + {TMutField{{TNonNullable{{TStepFunc}}}}}, + {TConstField{{TNullable{{TStructRef}}}}}, + )}}; + TStackArray = TDynArray{{TNullable{{TStackStruct}}}}; } +} - 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 type TDynArrayField = TArray>; +pub type TDynArray = TStruct<( + TMutField>>, + (TMutField, ()), +)>; - 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 type TThreadArray = TDynArray>; - 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 type TTargetThreadsStruct = + TStruct<(TMutField, (TMutField>, ()))>; - pub fn proc_arg_struct_type( - &self, - arg_vars: &core::cell::Ref<'_, Vec>, - ) -> HQResult { - self.struct_( - arg_vars - .iter() - .map(|var| { - Ok(FieldType { - mutable: false, - element_type: StorageType::Val(WasmProject::ir_type_to_wasm( - *var.possible_types(), - )), - }) - }) - .collect::>>()?, - ) - } - - 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), - } - } - } -} +pub type TTargetThreadArray = TArray>>; diff --git a/src/wasm/registries/types/dependencies.rs b/src/wasm/registries/types/dependencies.rs new file mode 100644 index 00000000..85a57858 --- /dev/null +++ b/src/wasm/registries/types/dependencies.rs @@ -0,0 +1,219 @@ +use core::marker::PhantomData; + +use wasm_encoder::{FieldType, HeapType, RefType, ValType}; + +use super::TypeRegistry; +use super::rec_group::RecGroupRegistry; +use super::registration::{ + TArray, TFieldType, TFunc, TI32, TRecGroupType, TRefType, TStruct, TStructRef, TType, +}; +use super::tyfp::List; +use crate::prelude::*; +use crate::wasm::registries::types::TRecGroupItem; + +pub trait HasTypeDependencies { + type Dependencies: List; + type RecGroupDependencies: List; +} + +impl HasTypeDependencies for TStructRef { + type Dependencies = (); + type RecGroupDependencies = (); +} + +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; +} + +impl HasTypeDependencies for TI32 { + type Dependencies = (); + type RecGroupDependencies = (); +} + +impl HasTypeDependencies for T +where + T: TFieldType, + T::ValType: HasTypeDependencies, +{ + type Dependencies = >::Dependencies; + type RecGroupDependencies = >::RecGroupDependencies; +} + +pub struct TTypeListMarker(PhantomData); + +impl HasTypeDependencies> for () { + type Dependencies = (); + type RecGroupDependencies = (); +} + +impl HasTypeDependencies> for (Head, Tail) +where + Head: HasTypeDependencies, + Head::Dependencies: List, + Head::RecGroupDependencies: List, + Tail: HasTypeDependencies>, + Tail::Dependencies: List, + Tail::RecGroupDependencies: List, +{ + type Dependencies = <>::Dependencies as List>::Concat< + >>::Dependencies, + >; + type RecGroupDependencies = + <>::RecGroupDependencies as List>::Concat< + >>::RecGroupDependencies, + >; +} + +pub trait CompoundTypeDependencies { + type Dependencies: List; + type RecGroupDependencies: List; +} + +impl CompoundTypeDependencies for TStruct +where + Fields: TRecGroupType, Rc> + + HasTypeDependencies>, + Fields::Dependencies: List, +{ + type Dependencies = <((HeapType, Self), ()) as List>::Concat; + + type RecGroupDependencies = (); +} + +impl CompoundTypeDependencies for TStruct +where + Fields: TRecGroupType, RecGroupRegistry> + + HasTypeDependencies>, + Fields::RecGroupDependencies: List, +{ + type Dependencies = Fields::Dependencies; + + type RecGroupDependencies = + <((HeapType, Self), ()) as List>::Concat; +} + +impl HasTypeDependencies for TStruct +where + Fields: TRecGroupType, RecGroupRegistry> + + HasTypeDependencies>, + Self: CompoundTypeDependencies< + Fields, + >>::RecGroupDependencies, + >, +{ + type Dependencies = + >::Dependencies; + + type RecGroupDependencies = >::RecGroupDependencies; +} + +impl CompoundTypeDependencies for TArray +where + Field: TFieldType //TRecGroupType + + HasTypeDependencies, + Field::ValType: TType, +{ + type Dependencies = <((HeapType, Self), ()) as List>::Concat; + + type RecGroupDependencies = (); +} + +impl CompoundTypeDependencies for TArray +where + (Head, Tail): List, + Field: TFieldType + HasTypeDependencies, +{ + type Dependencies = Field::Dependencies; + + type RecGroupDependencies = + <((HeapType, Self), ()) as List>::Concat; +} + +impl HasTypeDependencies for TArray +where + Field: TFieldType + HasTypeDependencies, + Self: CompoundTypeDependencies< + Field, + >::RecGroupDependencies, + >, +{ + type Dependencies = + >::Dependencies; + + type RecGroupDependencies = >::RecGroupDependencies; +} + +impl CompoundTypeDependencies<(Params, Results), ()> for TFunc +where + Params: TRecGroupType, RecGroupRegistry> + + HasTypeDependencies>, + Results: TRecGroupType, RecGroupRegistry> + + HasTypeDependencies>, +{ + type Dependencies = + <<((HeapType, Self), ()) as List>::Concat as List>::Concat< + Results::Dependencies, + >; + + type RecGroupDependencies = (); +} + +impl CompoundTypeDependencies<(Params, Results), (Head, Tail)> + for TFunc +where + Params: TRecGroupType, RecGroupRegistry> + + HasTypeDependencies>, + Results: TRecGroupType, RecGroupRegistry> + + HasTypeDependencies>, +{ + type Dependencies = ::Concat; + + type RecGroupDependencies = <<((HeapType, Self), ()) as List>::Concat< + Params::RecGroupDependencies, + > as List>::Concat; +} + +impl HasTypeDependencies for TFunc +where + Params: + TRecGroupType, RecGroupRegistry> + HasTypeDependencies>, + Results: + TRecGroupType, RecGroupRegistry> + HasTypeDependencies>, + Self: CompoundTypeDependencies< + (Params, Results), + <>>::RecGroupDependencies as List>::Concat<>>::RecGroupDependencies>, + >, +{ + type Dependencies = + >>::RecGroupDependencies as List>::Concat<>>::RecGroupDependencies>, + >>::Dependencies; + type RecGroupDependencies = + >>::RecGroupDependencies as List>::Concat<>>::RecGroupDependencies>, + >>::RecGroupDependencies; +} diff --git a/src/wasm/registries/types/rec_group.rs b/src/wasm/registries/types/rec_group.rs new file mode 100644 index 00000000..c9492263 --- /dev/null +++ b/src/wasm/registries/types/rec_group.rs @@ -0,0 +1,233 @@ +use core::marker::PhantomData; + +use wasm_encoder::HeapType; + +use super::TypeRegistry; +use super::dependencies::HasTypeDependencies; +use super::registration::TRecGroupType; +use crate::prelude::*; +use crate::registry::RegistryResult; +use crate::wasm::registries::types::{CompoundType, List, RegistryItem, TypeRegistryLike}; + +pub struct RecGroupRegistry { + pub types: Rc, + pub rec_group_start: u32, + pub main_rec_types_num: u32, + pub rec_type_deps: RefCell>, +} + +impl TypeRegistryLike for RecGroupRegistry { + fn register(&self, ty: CompoundType) -> HQResult + where + N: RegistryResult, + { + let idx = if let Some(idx) = self + .types + .registry() + .borrow() + .get_index_of(&RegistryItem::Type(ty.clone())) + { + idx + } else if let Some((idx, _)) = self + .rec_type_deps + .borrow() + .iter() + .find_position(|other| other == &&ty) + { + idx + } else { + let deps_len = self.rec_type_deps.borrow().len(); + self.rec_type_deps.borrow_mut().push(ty); + deps_len + (self.main_rec_types_num + self.rec_group_start) as usize + }; + idx.try_into() + .map_err(|_| make_hq_bug!("registry index out of bounds")) + } +} + +pub struct RecGroupMember(PhantomData<(Marker, T)>); + +impl HasTypeDependencies for RecGroupMember +where + T: HasTypeDependencies, +{ + type Dependencies = T::Dependencies; + type RecGroupDependencies = T::RecGroupDependencies; +} + +impl TRecGroupType for RecGroupMember +where + T: TRecGroupType, +{ + fn rec_group_ty(types: &RecGroupRegistry) -> HQResult { + T::rec_group_ty(types) + } +} + +pub trait RecGroupMarker { + const NAME: &str; + + type Types: List; +} + +pub trait IsRecGroupMember { + type Marker: RecGroupMarker; +} + +impl IsRecGroupMember for RecGroupMember +where + Marker: RecGroupMarker, +{ + type Marker = Marker; +} + +pub struct TRecGroupItem; + +impl HasTypeDependencies for TRecGroupItem { + type Dependencies = (); + type RecGroupDependencies = ((HeapType, Self), ()); +} + +impl TRecGroupType for TRecGroupItem { + fn rec_group_ty(registering_info: &RecGroupRegistry) -> HQResult { + Ok(HeapType::Concrete(registering_info.rec_group_start + I)) + } +} + +#[macro_export] +macro_rules! rec_group { + ( + $rec_group_name:ident { + $($name:ident = $typename:ident{$($typeparams:tt)+};)+ + } + ) => { + mod $rec_group_name { + use $crate::wasm::registries::types::*; + use $crate::wasm::registries::types::dependencies::HasTypeDependencies; + + macro_rules! sub_rec_group_types { + ( + sub_rec_group_types!($$($$macro_args:tt)+) + ) => { + sub_rec_group_types!($$($$macro_args)+) + }; + ( + $$ty:ident{$$({$$($$params:tt)+}),+} + ) => { + $$ty< + $$( + sub_rec_group_types!( + $$($$params)+ + ) + ),+ + > + }; + $( + ($name) => { + TRecGroupItem<${ index() }> + }; + )+ + ($$ty:ident) => { + $$ty + }; + (()) => {()}; + ( + ({$$($$first:tt)+},) + ) => { + ( + sub_rec_group_types!( + $$($$first)+ + ), + () + ) + }; + ( + ({$$($$first:tt)+}, $$({$$($$rest:tt)+}),+ $$(,)?) + ) => { + ( + sub_rec_group_types!( + $$($$first)+ + ), + sub_rec_group_types!( + ($$({$$($$rest)+},)+) + ) + ) + }; + } + + fn dummy_rec_group() -> Rc { + Rc::new(RecGroup { + name: stringify!($rec_group_name).into(), + types: vec![] + }) + } + + fn register_rec_group(types: &Rc) -> HQResult<()> { + if types.registry().borrow().contains_key(&RegistryItem::RecGroupItem(dummy_rec_group(), 0)) { + return Ok(()); + } + $( + <$name as HasTypeDependencies>::Dependencies::register_each(types)?; + )+ + let start_index = types.registry().borrow().len() as u32; + let rec_group_info = RecGroupRegistry { + types: Rc::clone(types), + rec_group_start: start_index, + main_rec_types_num: ${count($name)}, + rec_type_deps: RefCell::new(vec![]), + }; + let mut compound_types: Vec = vec![]; + $( + compound_types.push($name::rec_group_ty(&rec_group_info)?); + )+ + compound_types.extend(rec_group_info.rec_type_deps.take()); + let num_types = compound_types.len() as u32; + let rec_group = Rc::new(RecGroup { + name: stringify!($rec_group_name).into(), + types: compound_types, + }); + for i in 0..num_types { + types.register_default::(RegistryItem::RecGroupItem( + Rc::clone(&rec_group), + i + ))?; + } + Ok(()) + } + + pub struct Marker; + + impl RecGroupMarker for Marker { + const NAME: &str = stringify!($rec_group_name); + + type Types = ty_list!($($name),+); + } + + $( + pub type $name = RecGroupMember< + Marker, + sub_rec_group_types!( + $typename{$($typeparams)+} + ) + >; + + // type ${concat($name, CompoundTypeRecGroupDependencies)} = < + // << + // $name as $crate::wasm::registries::types::dependencies::HasTypeDependencies + // >::RecGroupDependencies as List>::Tail + // as $crate::wasm::registries::types::tyfp::Filter<$crate::wasm::registries::types::rec_group::HasCompoundTypeRegistration> + // >::Filtered; + + impl TRecGroupType> for $name { + fn rec_group_ty(types: &Rc) -> HQResult { + register_rec_group(types)?; + let rec_group_types = dummy_rec_group(); + types.register_default(RegistryItem::RecGroupItem(rec_group_types, ${index()})) + } + } + )+ + } + + pub use $rec_group_name::{$($name),+}; + } +} diff --git a/src/wasm/registries/types/registration.rs b/src/wasm/registries/types/registration.rs new file mode 100644 index 00000000..2cbc5000 --- /dev/null +++ b/src/wasm/registries/types/registration.rs @@ -0,0 +1,282 @@ +use core::marker::PhantomData; + +use wasm_encoder::{AbstractHeapType, FieldType, HeapType, RefType, StorageType, ValType}; + +use super::tyfp::List; +use super::{CompoundType, TypeRegistry}; +use crate::prelude::*; +use crate::registry::{CompTimeRegistrand, RegistryResult}; + +pub trait TypeRegistryLike { + fn register(&self, ty: CompoundType) -> HQResult + where + N: RegistryResult; +} + +impl TypeRegistryLike for Rc { + fn register(&self, ty: CompoundType) -> HQResult + where + N: RegistryResult, + { + self.register_compound_type(ty) + } +} + +// pub trait TypeRegisteringInfo { +// fn types(&self) -> &impl TypeRegistryLike; +// } + +// impl TypeRegisteringInfo for Rc { +// fn types(&self) -> &impl TypeRegistryLike { +// self +// } +// } + +pub trait TRecGroupType { + fn rec_group_ty(registering_info: &I) -> HQResult; +} + +impl TRecGroupType for T +where + T: TRecGroupType, + I: TypeRegistryLike, +{ + default fn rec_group_ty(types: &I) -> HQResult { + types.register(T::rec_group_ty(types)?) + } +} + +impl TRecGroupType for T +where + T: TRecGroupType, + I: TypeRegistryLike, +{ + default fn rec_group_ty(types: &I) -> HQResult { + Ok(HeapType::Concrete(T::rec_group_ty(types)?)) + } +} + +pub trait TType: TRecGroupType> { + fn ty(types: &Rc) -> HQResult; +} + +impl TType for U +where + U: TRecGroupType>, +{ + fn ty(types: &Rc) -> HQResult { + U::rec_group_ty(types) + } +} + +impl CompTimeRegistrand for T +where + T: TType, +{ + type Receiver = Rc; + + fn register(types: &Rc) -> HQResult { + T::ty(types) + } +} + +pub trait TDefaultable {} + +pub trait RegTypeList: List { + fn register_each(types: &I) -> HQResult<()>; +} + +impl RegTypeList for () { + fn register_each(_types: &I) -> HQResult<()> { + Ok(()) + } +} + +impl RegTypeList for ((HeadT, Head), Tail) +where + I: TypeRegistryLike, + Head: TRecGroupType, + Tail: RegTypeList, +{ + fn register_each(types: &I) -> HQResult<()> { + Head::rec_group_ty(types)?; + Tail::register_each(types) + } +} + +pub struct TStructRef; +impl TRecGroupType for TStructRef { + fn rec_group_ty(_types: &I) -> HQResult { + // generic in `T` to make const-evaluation lazy + // so that the panic is only reached if the function is used + #[expect(clippy::extra_unused_type_parameters, reason = "explained above")] + const fn error() { + const { + panic!( + "tried to register `TStructRef` as a `CompoundType`! + this implementation exists only to satisfy the trait solver.", + ) + } + } + error::<()>(); + unreachable!(); + } +} + +impl TRecGroupType for TStructRef { + fn rec_group_ty(_types: &I) -> HQResult { + Ok(HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Struct, + }) + } +} + +pub trait TRefType { + type HeapType; + const NULLABLE: bool; +} + +impl TRecGroupType for T +where + T: TRefType, + T::HeapType: TRecGroupType, + I: TypeRegistryLike, +{ + fn rec_group_ty(types: &I) -> HQResult { + Ok(RefType { + nullable: T::NULLABLE, + heap_type: T::HeapType::rec_group_ty(types)?, + }) + } +} + +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: TypeRegistryLike, +{ + 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 {} + +pub trait TFieldType { + type ValType; + const MUTABLE: bool; +} + +impl TRecGroupType for T +where + T: TFieldType, + I: TypeRegistryLike, + 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, + }) + } +} + +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; +} + +impl TRecGroupType, I> for () { + fn rec_group_ty(_types: &I) -> HQResult> { + Ok(vec![]) + } +} + +impl TRecGroupType, I> for (Head, Tail) +where + I: TypeRegistryLike, + 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: TypeRegistryLike, + Fields: TRecGroupType, I>, +{ + fn rec_group_ty(types: &I) -> HQResult { + Ok(CompoundType::Struct(Fields::rec_group_ty(types)?)) + } +} + +pub struct TArray(PhantomData); + +impl TRecGroupType for TArray +where + I: TypeRegistryLike, + Field: TFieldType, + Field::ValType: TRecGroupType, +{ + fn rec_group_ty(types: &I) -> HQResult { + Ok(CompoundType::Array( + StorageType::Val(Field::ValType::rec_group_ty(types)?), + Field::MUTABLE, + )) + } +} + +pub struct TFunc(PhantomData, PhantomData); + +impl TRecGroupType for TFunc +where + I: TypeRegistryLike, + Params: TRecGroupType, I>, + Result: TRecGroupType, I>, +{ + fn rec_group_ty(types: &I) -> HQResult { + Ok(CompoundType::Function( + Params::rec_group_ty(types)?, + Result::rec_group_ty(types)?, + )) + } +} diff --git a/src/wasm/registries/types/registry.rs b/src/wasm/registries/types/registry.rs new file mode 100644 index 00000000..a3e3e3fb --- /dev/null +++ b/src/wasm/registries/types/registry.rs @@ -0,0 +1,219 @@ +use wasm_encoder::{ + AbstractHeapType, ArrayType, CompositeInnerType, CompositeType, FieldType, FuncType, HeapType, + RefType, StorageType, StructType, SubType, TypeSection, ValType, +}; + +use crate::ir::RcVar; +use crate::prelude::*; +use crate::registry::{RegistryResult, SetRegistry}; +use crate::wasm::WasmProject; + +#[derive(Clone, Debug, Eq)] +pub struct RecGroup { + pub name: Box, + pub types: Vec, +} + +impl PartialEq for RecGroup { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} + +impl core::hash::Hash for RecGroup { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum CompoundType { + Function(Vec, Vec), + Array(StorageType, bool), + Struct(Vec), +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum RegistryItem { + Type(CompoundType), + RecGroupItem(Rc, u32), +} + +#[must_use] +pub const fn immediate_val_dep(val: &ValType) -> Option { + if let ValType::Ref(RefType { + heap_type: HeapType::Concrete(conc), + .. + }) = val + { + Some(*conc) + } else { + None + } +} + +const fn immediate_field_dep(field: &StorageType) -> Option { + match field { + StorageType::Val(val) => immediate_val_dep(val), + StorageType::I16 | StorageType::I8 => None, + } +} + +fn immediate_deps(ty: &CompoundType) -> Vec { + match ty { + CompoundType::Array(field, _) => immediate_field_dep(field).into_iter().collect(), + CompoundType::Struct(fields) => fields + .iter() + .map(|field| &field.element_type) + .filter_map(immediate_field_dep) + .collect(), + CompoundType::Function(params, results) => params + .iter() + .chain(results) + .filter_map(immediate_val_dep) + .collect(), + } +} + +fn extract_rec_group(item: RegistryItem) -> Option> { + match item { + RegistryItem::RecGroupItem(rec_group, _) => Some(Rc::clone(&rec_group)), + RegistryItem::Type(_) => None, + } +} + +fn find_compound_type_in_rec_group( + compound_type: &CompoundType, + rec_group: &Rc, +) -> Option { + rec_group + .types + .iter() + .find_position(|other| compound_type == *other) + .map(|(index, _)| index as u32) +} + +pub type TypeRegistry = SetRegistry; + +impl TypeRegistry { + pub fn register_compound_type(&self, compound_type: CompoundType) -> HQResult + where + N: RegistryResult, + { + let equivalent_in_rec_group = immediate_deps(&compound_type) + .into_iter() + .filter_map(|index| { + self.registry() + .borrow() + .get_index(index as usize) + .map(|(item, ())| item) + .cloned() + }) + .filter_map(extract_rec_group) + .find_map(|rec_group| { + find_compound_type_in_rec_group(&compound_type, &rec_group) + .map(|found| (rec_group, found)) + }); + if let Some((rec_group, found_index)) = equivalent_in_rec_group { + self.register_default(RegistryItem::RecGroupItem(rec_group, found_index)) + } else { + self.register_default(RegistryItem::Type(compound_type)) + } + } + + pub fn function(&self, params: Vec, returns: Vec) -> HQResult + where + N: RegistryResult, + { + self.register_compound_type(CompoundType::Function(params, returns)) + } + + pub fn array(&self, elem_type: StorageType, mutable: bool) -> HQResult + where + N: RegistryResult, + { + self.register_compound_type(CompoundType::Array(elem_type, mutable)) + } + + pub fn struct_(&self, fields: Vec) -> HQResult + where + N: RegistryResult, + { + self.register_compound_type(CompoundType::Struct(fields)) + } + + pub const STRUCT_REF: ValType = ValType::Ref(RefType { + nullable: true, + heap_type: HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Struct, + }, + }); + + pub fn proc_arg_struct_type( + &self, + arg_vars: &core::cell::Ref<'_, Vec>, + ) -> HQResult { + self.struct_( + arg_vars + .iter() + .map(|var| { + Ok(FieldType { + mutable: false, + element_type: StorageType::Val(WasmProject::ir_type_to_wasm( + *var.possible_types(), + )), + }) + }) + .collect::>>()?, + ) + } + + fn type_to_composite_inner(ty: CompoundType) -> CompositeInnerType { + match ty { + CompoundType::Function(params, results) => { + CompositeInnerType::Func(FuncType::new(params, results)) + } + CompoundType::Struct(fields) => CompositeInnerType::Struct(StructType { + fields: fields.into(), + }), + CompoundType::Array(element_type, mutable) => { + CompositeInnerType::Array(ArrayType(FieldType { + element_type, + mutable, + })) + } + } + } + + fn type_to_composite(ty: CompoundType) -> CompositeType { + CompositeType { + inner: Self::type_to_composite_inner(ty), + shared: false, + describes: None, + descriptor: None, + } + } + + fn type_to_subtype(ty: CompoundType) -> SubType { + SubType { + is_final: true, + supertype_idx: None, + composite_type: Self::type_to_composite(ty), + } + } + + pub fn finish(self, types: &mut TypeSection) { + for ty in self.registry().take().keys().cloned() { + match ty { + RegistryItem::Type(ty) => types.ty().subtype(&Self::type_to_subtype(ty)), + RegistryItem::RecGroupItem(rec_group, 0) => { + types + .ty() + .rec(rec_group.types.iter().cloned().map(Self::type_to_subtype)); + } + RegistryItem::RecGroupItem(_, _) => (), + } + } + } +} diff --git a/src/wasm/registries/types/subtypes.rs b/src/wasm/registries/types/subtypes.rs new file mode 100644 index 00000000..c4361958 --- /dev/null +++ b/src/wasm/registries/types/subtypes.rs @@ -0,0 +1,73 @@ +use core::marker::PhantomData; + +use crate::wasm::registries::types::{Bool, Func, List, Map, TFieldType, TFunc, TStruct}; + +pub trait FuncSubTypes { + type Params: List; + type Results: List; +} + +pub struct IsFieldType; +impl Func for IsFieldType { + type Func = FieldTypeTester; +} + +pub struct FieldTypeTester(PhantomData); + +impl Bool for FieldTypeTester { + default const BOOL: bool = false; +} + +impl Bool for FieldTypeTester<(HeadT, Head)> +where + Head: TFieldType, +{ + const BOOL: bool = true; +} + +pub struct ExtractValFromField; + +pub struct FieldValExtractor(PhantomData); + +pub trait HasValType { + type ValType; +} + +impl HasValType for FieldValExtractor { + default type ValType = !; +} + +impl HasValType for FieldValExtractor +where + T: TFieldType, +{ + type ValType = T::ValType; +} + +impl Func for ExtractValFromField { + type Func = as HasValType>::ValType; +} + +impl FuncSubTypes for TFunc +where + Params: List, + Results: List, +{ + type Params = Params; + type Results = Results; +} + +pub trait StructSubTypes { + type Fields: List; +} + +impl StructSubTypes for TStruct +where + Fields: List + Map, +{ + type Fields = Fields::Mapped; +} + +pub trait ArraySubType { + type Field; +} diff --git a/src/wasm/registries/types/tyfp.rs b/src/wasm/registries/types/tyfp.rs new file mode 100644 index 00000000..30e20f0f --- /dev/null +++ b/src/wasm/registries/types/tyfp.rs @@ -0,0 +1,164 @@ +//! Functional programming in the type system. Or something like that. + +#[macro_export] +macro_rules! ty_list { + ($first:ty, $($rest:ty),*) => {($first, ty_list!($($rest),*))}; + ($first:ty) => {($first, ())}; + () => {()}; +} + +pub trait List { + type Head; + type Tail: List; + + type Concat: List; +} + +impl List for () { + type Head = !; + type Tail = (); + + type Concat = Other; +} + +impl List for (Head, Tail) +where + Tail: List, +{ + type Head = Head; + type Tail = Tail; + + type Concat = (Head, Tail::Concat); +} + +pub trait ListLen { + const LEN: usize; +} + +impl ListLen for () { + const LEN: usize = 0; +} + +impl ListLen for (Head, Tail) +where + Tail: ListLen, +{ + const LEN: usize = Tail::LEN + 1; +} + +pub trait Reverse: List { + type Reversed: List; +} + +impl Reverse for () { + type Reversed = (); +} + +impl Reverse for (Head, Tail) +where + Tail: Reverse, +{ + type Reversed = ::Concat<(Head, ())>; +} + +pub trait Bool { + const BOOL: bool; +} + +pub trait Func { + type Func; +} + +pub trait Filter: List { + type Filtered: List; +} + +impl Filter for () { + type Filtered = (); +} + +pub trait FilterResult { + type FilterResult: List; +} + +impl FilterResult for (Head, Tail) +where + Tail: Filter, + Cond: Func, +{ + type FilterResult = (Head, Tail::Filtered); +} + +impl FilterResult for (Head, Tail) +where + Tail: Filter, + Cond: Func, +{ + type FilterResult = Tail::Filtered; +} + +impl Filter for (Head, Tail) +where + Tail: Filter, + Cond: Func, + Cond::Func: Bool, + (Head, Tail): FilterResult as Bool>::BOOL }>, +{ + type Filtered = + <(Head, Tail) as FilterResult as Bool>::BOOL }>>::FilterResult; +} + +pub trait All: List { + const ALL: bool; +} + +impl All for () { + const ALL: bool = true; +} + +impl All for (Head, Tail) +where + Cond: Func, + Cond::Func: Bool, + Tail: All, +{ + const ALL: bool = as Bool>::BOOL && >::ALL; +} + +pub trait Map: List { + type Mapped: List; +} + +impl Map for () { + type Mapped = (); +} + +impl> Map for (Head, Tail) { + type Mapped = (F::Func, Tail::Mapped); +} + +pub trait ListItem { + type Get; +} + +impl ListItem for () { + type Get = !; +} + +impl ListItem<0> for (Head, Tail) { + type Get = Head; +} + +pub struct ConstSat; + +pub trait True {} + +impl True for ConstSat {} + +impl ListItem for (Head, Tail) +where + ConstSat<{ I > 0 }>: True, + Tail: ListItem<{ I - 1 }>, +{ + type Get = Tail::Get; +}