1use alloc::rc::Rc;
8use alloc::string::String;
9use alloc::vec::Vec;
10
11use crate::lower::{RegConstInstr, RegDataMode, RegElemValue, RegElementMode, RegModule};
12use crate::runtime::gpu::{GpuBackend, GpuError, GpuKernelId};
13use crate::runtime::host::HostFunction;
14use crate::runtime::{RuntimeError, RuntimeErrorKind, RuntimeTrap, Value, trap};
15use crate::types::{FuncIdx, GlobalType, Limits, MemType, TableType};
16
17use crate::runtime::verify::F32_ADD_WGSL;
18
19pub const DEFAULT_OFFLOAD_THRESHOLD: usize = 1024;
23
24pub const PAGE_SIZE: usize = 65536;
26
27#[derive(Debug, Default)]
32pub struct Imports {
33 memories: Vec<MemoryProvider>,
34 globals: Vec<GlobalProvider>,
35 tables: Vec<TableProvider>,
36}
37
38impl Imports {
39 pub fn new() -> Self {
41 Self::default()
42 }
43
44 pub fn memory(mut self, module: &str, name: &str, ty: MemType) -> Self {
46 self.memories.push(MemoryProvider {
47 module: module.into(),
48 name: name.into(),
49 ty,
50 shared: None,
51 });
52 self
53 }
54
55 pub fn shared_memory(
58 mut self,
59 module: &str,
60 name: &str,
61 ty: MemType,
62 shared: alloc::rc::Rc<core::cell::RefCell<Vec<u8>>>,
63 ) -> Self {
64 self.memories.push(MemoryProvider {
65 module: module.into(),
66 name: name.into(),
67 ty,
68 shared: Some(shared),
69 });
70 self
71 }
72
73 pub fn global(mut self, module: &str, name: &str, ty: GlobalType, value: Value) -> Self {
75 self.globals.push(GlobalProvider {
76 module: module.into(),
77 name: name.into(),
78 ty,
79 value: Some(value),
80 shared: None,
81 });
82 self
83 }
84
85 pub fn shared_global(
88 mut self,
89 module: &str,
90 name: &str,
91 ty: GlobalType,
92 shared: alloc::rc::Rc<core::cell::Cell<Value>>,
93 ) -> Self {
94 self.globals.push(GlobalProvider {
95 module: module.into(),
96 name: name.into(),
97 ty,
98 value: None,
99 shared: Some(shared),
100 });
101 self
102 }
103
104 pub fn table(mut self, module: &str, name: &str, ty: TableType) -> Self {
106 self.tables.push(TableProvider {
107 module: module.into(),
108 name: name.into(),
109 ty,
110 shared: None,
111 });
112 self
113 }
114
115 pub fn shared_table(
118 mut self,
119 module: &str,
120 name: &str,
121 ty: TableType,
122 shared: alloc::rc::Rc<core::cell::RefCell<crate::runtime::Table>>,
123 ) -> Self {
124 self.tables.push(TableProvider {
125 module: module.into(),
126 name: name.into(),
127 ty,
128 shared: Some(shared),
129 });
130 self
131 }
132}
133
134#[derive(Debug)]
136struct MemoryProvider {
137 module: String,
138 name: String,
139 ty: MemType,
140 shared: Option<alloc::rc::Rc<core::cell::RefCell<Vec<u8>>>>,
142}
143
144#[derive(Debug)]
146struct GlobalProvider {
147 module: String,
148 name: String,
149 ty: GlobalType,
150 value: Option<Value>,
151 shared: Option<alloc::rc::Rc<core::cell::Cell<Value>>>,
153}
154
155#[derive(Debug)]
157struct TableProvider {
158 module: String,
159 name: String,
160 ty: TableType,
161 shared: Option<alloc::rc::Rc<core::cell::RefCell<crate::runtime::Table>>>,
163}
164
165fn table_null(ty: TableType) -> Value {
167 match ty.elem {
168 crate::types::RefType::ExternRef => Value::ExternRef(None),
169 _ => Value::FuncRef(None),
170 }
171}
172
173fn try_alloc_n<T: Clone>(n: usize, value: T) -> Option<Vec<T>> {
176 let mut vec = Vec::new();
177 vec.try_reserve(n).ok()?;
178 vec.resize(n, value);
179 Some(vec)
180}
181
182fn limits_match(provided: &Limits, declared: &Limits) -> bool {
185 if provided.min < declared.min {
186 return false;
187 }
188 match (provided.max, declared.max) {
189 (_, None) => true,
190 (Some(provided), Some(declared)) => provided <= declared,
191 (None, Some(_)) => false,
192 }
193}
194
195fn allocation_failed(what: &'static str) -> RuntimeError {
196 RuntimeError {
197 kind: RuntimeErrorKind::ResourceLimitExceeded { what },
198 }
199}
200
201fn unknown_import(module: &str, name: &str) -> RuntimeError {
202 RuntimeError {
203 kind: RuntimeErrorKind::UnknownImport {
204 module: module.into(),
205 name: name.into(),
206 },
207 }
208}
209
210fn import_type_mismatch(module: &str, name: &str) -> RuntimeError {
211 RuntimeError {
212 kind: RuntimeErrorKind::ImportTypeMismatch {
213 module: module.into(),
214 name: name.into(),
215 },
216 }
217}
218
219fn global_type_matches(provided: &GlobalType, declared: &GlobalType) -> bool {
224 use crate::types::{HeapType, Mutability, RefType, ValType};
225
226 fn valtype_is_subtype(provided: &ValType, declared: &ValType) -> bool {
227 match (provided, declared) {
228 (ValType::Ref(provided), ValType::Ref(declared)) => match (provided, declared) {
229 (RefType::FuncRef, RefType::FuncRef) | (RefType::ExternRef, RefType::ExternRef) => {
230 true
231 }
232 (
233 RefType::Typed {
234 nullable: provided_nullable,
235 heap: provided_heap,
236 },
237 RefType::Typed {
238 nullable: declared_nullable,
239 heap: declared_heap,
240 },
241 ) => {
242 (!*provided_nullable || *declared_nullable)
243 && provided_heap.is_subtype_of(*declared_heap)
244 }
245 (RefType::Typed { heap, .. }, RefType::FuncRef) => {
246 heap.is_subtype_of(HeapType::Func)
247 }
248 (RefType::Typed { heap, .. }, RefType::ExternRef) => *heap == HeapType::Extern,
249 _ => false,
250 },
251 (provided, declared) => provided == declared,
252 }
253 }
254
255 match (provided.mutability, declared.mutability) {
256 (Mutability::Const, Mutability::Const) => {
257 valtype_is_subtype(&provided.val_type, &declared.val_type)
258 }
259 (Mutability::Var, Mutability::Var) => provided.val_type == declared.val_type,
260 _ => false,
261 }
262}
263
264pub type LinkGroup = alloc::collections::BTreeMap<
267 u32,
268 (
269 alloc::rc::Rc<crate::lower::RegModule>,
270 alloc::rc::Rc<core::cell::RefCell<Store>>,
271 ),
272>;
273
274#[derive(Debug)]
279pub struct Store {
280 memories: Vec<alloc::rc::Rc<core::cell::RefCell<Vec<u8>>>>,
281 memory_types: Vec<MemType>,
282 globals: Vec<alloc::rc::Rc<core::cell::Cell<Value>>>,
283 global_types: Vec<GlobalType>,
284 tables: Vec<alloc::rc::Rc<core::cell::RefCell<crate::runtime::Table>>>,
285 table_types: Vec<TableType>,
286 elements: core::cell::RefCell<Vec<Option<Vec<Value>>>>,
289 data: core::cell::RefCell<Vec<Option<Vec<u8>>>>,
292 exports: Vec<crate::lower::RegExport>,
294 instance_id: u32,
296 fuel: core::cell::Cell<Option<u64>>,
300 pending_start: Option<crate::types::FuncIdx>,
303 link_group: Option<alloc::rc::Rc<core::cell::RefCell<LinkGroup>>>,
305 gpu: core::cell::RefCell<Option<alloc::boxed::Box<dyn GpuBackend>>>,
307 offload_threshold: usize,
309 offload_kernels: alloc::collections::BTreeMap<&'static str, GpuKernelId>,
311 imported_funcs: Vec<crate::lower::RegImport>,
313 host_funcs: Vec<Option<core::cell::RefCell<HostFunction>>>,
315 imported_memories: Vec<alloc::rc::Rc<core::cell::RefCell<Vec<u8>>>>,
316 imported_memory_types: Vec<MemType>,
317 imported_global_values: Vec<alloc::rc::Rc<core::cell::Cell<Value>>>,
318 imported_global_types: Vec<GlobalType>,
319 imported_tables: Vec<alloc::rc::Rc<core::cell::RefCell<crate::runtime::Table>>>,
320 imported_table_types: Vec<TableType>,
321 imported_memory_count: u32,
322 imported_global_count: u32,
323 imported_table_count: u32,
324}
325
326impl Store {
327 pub fn instantiate(module: &RegModule) -> Result<Self, RuntimeError> {
331 Self::instantiate_with_imports(module, &Imports::new())
332 }
333
334 pub fn instantiate_linked(
338 module: &Rc<RegModule>,
339 imports: &Imports,
340 group: &alloc::rc::Rc<core::cell::RefCell<LinkGroup>>,
341 instance_id: u32,
342 ) -> Result<alloc::rc::Rc<core::cell::RefCell<Store>>, RuntimeError> {
343 let (mut store, error) = Self::instantiate_internal(module, imports, instance_id)?;
344 store.link_group = Some(group.clone());
345 let store = alloc::rc::Rc::new(core::cell::RefCell::new(store));
346 group
350 .borrow_mut()
351 .insert(instance_id, (module.clone(), store.clone()));
352 match error {
353 Some(error) => Err(error),
354 None => Ok(store),
355 }
356 }
357
358 pub fn instantiate_with_imports(
363 module: &RegModule,
364 imports: &Imports,
365 ) -> Result<Self, RuntimeError> {
366 let (store, error) = Self::instantiate_internal(module, imports, 0)?;
367 match error {
368 Some(error) => Err(error),
369 None => Ok(store),
370 }
371 }
372
373 fn instantiate_internal(
378 module: &RegModule,
379 imports: &Imports,
380 instance_id: u32,
381 ) -> Result<(Self, Option<RuntimeError>), RuntimeError> {
382 let mut imported_memories = Vec::with_capacity(module.imported_memories.len());
384 let mut imported_memory_types = Vec::with_capacity(module.imported_memories.len());
385 for declared in &module.imported_memories {
386 let provider = imports
387 .memories
388 .iter()
389 .find(|provider| {
390 provider.module == declared.module && provider.name == declared.name
391 })
392 .ok_or_else(|| unknown_import(&declared.module, &declared.name))?;
393 let provided_limits = match &provider.shared {
396 Some(shared) => Limits {
397 min: (shared.borrow().len() / PAGE_SIZE) as u32,
398 max: provider.ty.limits.max,
399 },
400 None => provider.ty.limits,
401 };
402 if !limits_match(&provided_limits, &declared.ty.limits) {
403 return Err(import_type_mismatch(&declared.module, &declared.name));
404 }
405 let entity = match provider.shared.clone() {
406 Some(shared) => shared,
407 None => {
408 let bytes = try_alloc_n(provider.ty.limits.min as usize * PAGE_SIZE, 0u8)
409 .ok_or_else(|| allocation_failed("imported memory"))?;
410 alloc::rc::Rc::new(core::cell::RefCell::new(bytes))
411 }
412 };
413 imported_memories.push(entity);
414 imported_memory_types.push(provider.ty);
415 }
416
417 let mut imported_global_values = Vec::with_capacity(module.imported_globals.len());
419 let mut imported_global_types = Vec::with_capacity(module.imported_globals.len());
420 for declared in &module.imported_globals {
421 let provider = imports
422 .globals
423 .iter()
424 .find(|provider| {
425 provider.module == declared.module && provider.name == declared.name
426 })
427 .ok_or_else(|| unknown_import(&declared.module, &declared.name))?;
428 if !global_type_matches(&provider.ty, &declared.ty) {
429 return Err(import_type_mismatch(&declared.module, &declared.name));
430 }
431 let entity = match (&provider.shared, provider.value) {
432 (Some(shared), _) => shared.clone(),
433 (None, Some(value)) => {
434 if value.val_type() != declared.ty.val_type {
435 return Err(import_type_mismatch(&declared.module, &declared.name));
436 }
437 alloc::rc::Rc::new(core::cell::Cell::new(value))
438 }
439 (None, None) => unreachable!("global provider sets value or shared"),
440 };
441 imported_global_values.push(entity);
442 imported_global_types.push(provider.ty);
443 }
444
445 let mut imported_tables = Vec::with_capacity(module.imported_tables.len());
447 let mut imported_table_types = Vec::with_capacity(module.imported_tables.len());
448 for declared in &module.imported_tables {
449 let provider = imports
450 .tables
451 .iter()
452 .find(|provider| {
453 provider.module == declared.module && provider.name == declared.name
454 })
455 .ok_or_else(|| unknown_import(&declared.module, &declared.name))?;
456 let provided_limits = match &provider.shared {
457 Some(shared) => Limits {
458 min: shared.borrow().len(),
459 max: provider.ty.limits.max,
460 },
461 None => provider.ty.limits,
462 };
463 if provider.ty.elem != declared.ty.elem
464 || !limits_match(&provided_limits, &declared.ty.limits)
465 {
466 return Err(import_type_mismatch(&declared.module, &declared.name));
467 }
468 let entity = match provider.shared.clone() {
469 Some(shared) => shared,
470 None => {
471 let values = crate::runtime::Table::new(
472 provider.ty.clone(),
473 table_null(provider.ty.clone()),
474 )
475 .ok_or_else(|| allocation_failed("imported table"))?;
476 alloc::rc::Rc::new(core::cell::RefCell::new(values))
477 }
478 };
479 imported_tables.push(entity);
480 imported_table_types.push(provider.ty.clone());
481 }
482
483 let imported_globals_plain: Vec<Value> = imported_global_values
484 .iter()
485 .map(|cell| cell.get())
486 .collect();
487 let mut globals_plain: Vec<Value> = Vec::with_capacity(module.globals.len());
488 let mut globals: Vec<alloc::rc::Rc<core::cell::Cell<Value>>> =
489 Vec::with_capacity(module.globals.len());
490 let mut global_types = Vec::with_capacity(module.globals.len());
491 for global in &module.globals {
492 let value = eval_const(
493 &global.init,
494 &globals_plain,
495 &imported_globals_plain,
496 module.imported_global_count,
497 instance_id,
498 )?;
499 globals_plain.push(value);
500 globals.push(alloc::rc::Rc::new(core::cell::Cell::new(value)));
501 global_types.push(crate::types::GlobalType {
502 val_type: global.ty,
503 mutability: if global.mutable {
504 crate::types::Mutability::Var
505 } else {
506 crate::types::Mutability::Const
507 },
508 });
509 }
510
511 let mut memories = Vec::with_capacity(module.memories.len());
512 for mem in &module.memories {
513 let bytes = try_alloc_n(mem.limits.min as usize * PAGE_SIZE, 0u8)
514 .ok_or_else(|| allocation_failed("memory"))?;
515 memories.push(alloc::rc::Rc::new(core::cell::RefCell::new(bytes)));
516 }
517 let memory_types = module.memories.clone();
518
519 let mut tables = Vec::with_capacity(module.tables.len());
520 for table in &module.tables {
521 let fill = match &table.init {
522 Some(init) => {
523 let lowered =
524 crate::lower::lower_const_expr(init, 0).map_err(|_error| RuntimeError {
525 kind: RuntimeErrorKind::InvalidConstExpr,
526 })?;
527 eval_const(
528 &lowered,
529 &globals_plain,
530 &imported_globals_plain,
531 module.imported_global_count,
532 instance_id,
533 )?
534 }
535 None => table_null(table.clone()),
536 };
537 let values = crate::runtime::Table::new(table.clone(), fill)
538 .ok_or_else(|| allocation_failed("table"))?;
539 tables.push(alloc::rc::Rc::new(core::cell::RefCell::new(values)));
540 }
541 let table_types = module.tables.clone();
542
543 let mut store = Self {
544 memories,
545 memory_types,
546 globals,
547 global_types,
548 tables,
549 table_types,
550 elements: core::cell::RefCell::new(alloc::vec![None; module.elements.len()]),
551 data: core::cell::RefCell::new(Vec::new()),
552 exports: module.exports.clone(),
553 instance_id,
554 fuel: core::cell::Cell::new(None),
555 pending_start: None,
556 link_group: None,
557 gpu: core::cell::RefCell::new(None),
558 offload_threshold: DEFAULT_OFFLOAD_THRESHOLD,
559 offload_kernels: alloc::collections::BTreeMap::new(),
560 imported_funcs: module.imported_funcs.clone(),
561 host_funcs: (0..module.imported_funcs.len()).map(|_| None).collect(),
562 imported_memories,
563 imported_memory_types,
564 imported_global_values,
565 imported_global_types,
566 imported_tables,
567 imported_table_types,
568 imported_memory_count: module.imported_memory_count,
569 imported_global_count: module.imported_global_count,
570 imported_table_count: module.imported_table_count,
571 };
572
573 let mut instantiation_error: Option<RuntimeError> = None;
574
575 for (idx, segment) in module.elements.iter().enumerate() {
580 if instantiation_error.is_some() {
581 break;
582 }
583 let values: Vec<Value> = segment
584 .values
585 .iter()
586 .map(|value| match *value {
587 RegElemValue::FuncRef(func) => Value::FuncRef(Some((instance_id, func.0))),
588 RegElemValue::GlobalGet(global) => {
589 let idx = global.0 as usize;
590 if idx < store.imported_global_count as usize {
591 store
592 .imported_global_values
593 .get(idx)
594 .expect("imported globals resolved")
595 .get()
596 } else {
597 store
598 .globals
599 .get(idx - store.imported_global_count as usize)
600 .expect("defined globals initialized")
601 .get()
602 }
603 }
604 RegElemValue::Null => Value::FuncRef(None),
605 })
606 .collect();
607 match &segment.mode {
608 RegElementMode::Active { table, offset } => {
609 let offset = match eval_const(
610 offset,
611 &globals_plain,
612 &imported_globals_plain,
613 store.imported_global_count,
614 store.instance_id,
615 ) {
616 Ok(offset) => offset,
617 Err(error) => {
618 instantiation_error = Some(error);
619 break;
620 }
621 };
622 let Value::I32(offset) = offset else {
623 instantiation_error = Some(RuntimeError {
624 kind: RuntimeErrorKind::InvalidConstExpr,
625 });
626 break;
627 };
628 let applied = store
629 .with_table_mut(table.0, |target| {
630 if target.write_slice(offset as u32, &values) {
631 Ok(())
632 } else {
633 Err(trap(RuntimeTrap::OutOfBoundsTableAccess))
634 }
635 })
636 .ok_or(RuntimeError {
637 kind: RuntimeErrorKind::UnknownTable { table: table.0 },
638 })
639 .and_then(|result| result);
640 if let Err(error) = applied {
641 instantiation_error = Some(error);
642 break;
643 }
644 }
645 RegElementMode::Passive => {
646 store.elements.borrow_mut()[idx] = Some(values);
647 }
648 RegElementMode::Dropped => {}
649 }
650 }
651
652 for segment in &module.data {
653 if instantiation_error.is_some() {
654 break;
655 }
656 match &segment.mode {
657 RegDataMode::Active { memory, offset } => {
658 let offset = match eval_const(
659 offset,
660 &globals_plain,
661 &imported_globals_plain,
662 store.imported_global_count,
663 store.instance_id,
664 ) {
665 Ok(offset) => offset,
666 Err(error) => {
667 instantiation_error = Some(error);
668 break;
669 }
670 };
671 let Value::I32(offset) = offset else {
672 instantiation_error = Some(RuntimeError {
673 kind: RuntimeErrorKind::InvalidConstExpr,
674 });
675 break;
676 };
677 let applied = store
678 .with_memory_mut(memory.0, |mem| {
679 let start = offset as usize;
680 let Some(end) = start.checked_add(segment.bytes.len()) else {
681 return Err(trap(RuntimeTrap::OutOfBoundsMemoryAccess));
682 };
683 if end > mem.len() {
684 return Err(trap(RuntimeTrap::OutOfBoundsMemoryAccess));
685 }
686 mem[start..end].copy_from_slice(&segment.bytes);
687 Ok(())
688 })
689 .ok_or(RuntimeError {
690 kind: RuntimeErrorKind::UnknownMemory { memory: memory.0 },
691 })
692 .and_then(|result| result);
693 if let Err(error) = applied {
694 instantiation_error = Some(error);
695 break;
696 }
697 store.data.borrow_mut().push(None);
698 }
699 RegDataMode::Passive => {
700 store.data.borrow_mut().push(Some(segment.bytes.clone()));
701 }
702 }
703 }
704
705 if instantiation_error.is_none() {
709 store.pending_start = module.start;
710 }
711
712 Ok((store, instantiation_error))
713 }
714
715 pub fn run_start(&mut self, module: &RegModule) -> Result<(), RuntimeError> {
718 let Some(start_idx) = self.pending_start.take() else {
719 return Ok(());
720 };
721 if start_idx.0 < module.imported_func_count {
722 self.call_host(start_idx.0, &[])?;
723 return Ok(());
724 }
725 let func = module
726 .funcs
727 .iter()
728 .find(|func| func.idx == start_idx)
729 .ok_or(RuntimeError {
730 kind: RuntimeErrorKind::UnknownFunction { func: start_idx.0 },
731 })?;
732 crate::runtime::execute_func_in(Some(module), Some(self), func, &[], 0)?;
733 Ok(())
734 }
735
736 pub fn shared_memory(&self, idx: u32) -> Option<alloc::rc::Rc<core::cell::RefCell<Vec<u8>>>> {
743 let idx = idx as usize;
744 if idx < self.imported_memory_count as usize {
745 return self.imported_memories.get(idx).cloned();
746 }
747 self.memories
748 .get(idx - self.imported_memory_count as usize)
749 .cloned()
750 }
751
752 pub(crate) fn memory_type(&self, idx: u32) -> Option<&MemType> {
754 let idx = idx as usize;
755 if idx < self.imported_memory_count as usize {
756 return self.imported_memory_types.get(idx);
757 }
758 self.memory_types
759 .get(idx - self.imported_memory_count as usize)
760 }
761
762 pub(crate) fn global(&self, idx: u32) -> Option<Value> {
764 self.shared_global(idx).map(|cell| cell.get())
765 }
766
767 pub(crate) fn set_global(&self, idx: u32, value: Value) -> Option<()> {
769 let cell = self.shared_global(idx)?;
770 cell.set(value);
771 Some(())
772 }
773
774 pub(crate) fn shared_global(&self, idx: u32) -> Option<alloc::rc::Rc<core::cell::Cell<Value>>> {
776 let idx = idx as usize;
777 if idx < self.imported_global_count as usize {
778 return self.imported_global_values.get(idx).cloned();
779 }
780 self.globals
781 .get(idx - self.imported_global_count as usize)
782 .cloned()
783 }
784
785 pub(crate) fn global_type(&self, idx: u32) -> Option<&GlobalType> {
787 let idx = idx as usize;
788 if idx < self.imported_global_count as usize {
789 return self.imported_global_types.get(idx);
790 }
791 self.global_types
792 .get(idx - self.imported_global_count as usize)
793 }
794
795 pub(crate) fn with_table<R>(
797 &self,
798 idx: u32,
799 f: impl FnOnce(&crate::runtime::Table) -> R,
800 ) -> Option<R> {
801 let shared = self.shared_table(idx)?;
802 let table = shared.borrow();
803 Some(f(&table))
804 }
805
806 pub(crate) fn with_table_mut<R>(
808 &self,
809 idx: u32,
810 f: impl FnOnce(&mut crate::runtime::Table) -> R,
811 ) -> Option<R> {
812 let shared = self.shared_table(idx)?;
813 let mut table = shared.borrow_mut();
814 Some(f(&mut table))
815 }
816
817 pub(crate) fn shared_table(
819 &self,
820 idx: u32,
821 ) -> Option<alloc::rc::Rc<core::cell::RefCell<crate::runtime::Table>>> {
822 let idx = idx as usize;
823 if idx < self.imported_table_count as usize {
824 return self.imported_tables.get(idx).cloned();
825 }
826 self.tables
827 .get(idx - self.imported_table_count as usize)
828 .cloned()
829 }
830
831 pub(crate) fn table_type(&self, idx: u32) -> Option<&TableType> {
833 let idx = idx as usize;
834 if idx < self.imported_table_count as usize {
835 return self.imported_table_types.get(idx);
836 }
837 self.table_types
838 .get(idx - self.imported_table_count as usize)
839 }
840
841 pub fn export_func(&self, name: &str) -> Option<FuncIdx> {
843 self.exports.iter().find_map(|export| match export.desc {
844 crate::lower::RegExportDesc::Func(idx) if export.name == name => Some(idx),
845 _ => None,
846 })
847 }
848
849 pub fn export_memory(
851 &self,
852 name: &str,
853 ) -> Option<(MemType, alloc::rc::Rc<core::cell::RefCell<Vec<u8>>>)> {
854 let idx = self.exports.iter().find_map(|export| match export.desc {
855 crate::lower::RegExportDesc::Mem(idx) if export.name == name => Some(idx),
856 _ => None,
857 })?;
858 Some((*self.memory_type(idx.0)?, self.shared_memory(idx.0)?))
859 }
860
861 pub fn export_global(
863 &self,
864 name: &str,
865 ) -> Option<(GlobalType, alloc::rc::Rc<core::cell::Cell<Value>>)> {
866 let idx = self.exports.iter().find_map(|export| match export.desc {
867 crate::lower::RegExportDesc::Global(idx) if export.name == name => Some(idx),
868 _ => None,
869 })?;
870 Some((*self.global_type(idx.0)?, self.shared_global(idx.0)?))
871 }
872
873 pub fn export_table(
875 &self,
876 name: &str,
877 ) -> Option<(
878 TableType,
879 alloc::rc::Rc<core::cell::RefCell<crate::runtime::Table>>,
880 )> {
881 let idx = self.exports.iter().find_map(|export| match export.desc {
882 crate::lower::RegExportDesc::Table(idx) if export.name == name => Some(idx),
883 _ => None,
884 })?;
885 Some((self.table_type(idx.0)?.clone(), self.shared_table(idx.0)?))
886 }
887
888 pub(crate) fn with_elem<R>(
890 &self,
891 idx: u32,
892 f: impl FnOnce(Option<&Vec<Value>>) -> R,
893 ) -> Option<R> {
894 let elements = self.elements.borrow();
895 let slot = elements.get(idx as usize)?;
896 Some(f(slot.as_ref()))
897 }
898
899 pub(crate) fn drop_elem(&self, idx: u32) -> Option<()> {
901 let mut elements = self.elements.borrow_mut();
902 let slot = elements.get_mut(idx as usize)?;
903 *slot = None;
904 Some(())
905 }
906
907 pub(crate) fn with_data<R>(
909 &self,
910 idx: u32,
911 f: impl FnOnce(Option<&Vec<u8>>) -> R,
912 ) -> Option<R> {
913 let data = self.data.borrow();
914 let slot = data.get(idx as usize)?;
915 Some(f(slot.as_ref()))
916 }
917
918 pub(crate) fn drop_data(&self, idx: u32) -> Option<()> {
920 let mut data = self.data.borrow_mut();
921 let slot = data.get_mut(idx as usize)?;
922 *slot = None;
923 Some(())
924 }
925
926 pub fn get_global(&self, idx: u32) -> Option<Value> {
928 self.global(idx)
929 }
930
931 pub fn with_memory<R>(&self, idx: u32, f: impl FnOnce(&[u8]) -> R) -> Option<R> {
935 let shared = self.shared_memory(idx)?;
936 let mem = shared.borrow();
937 Some(f(&mem))
938 }
939
940 pub fn with_memory_mut<R>(&self, idx: u32, f: impl FnOnce(&mut [u8]) -> R) -> Option<R> {
943 let shared = self.shared_memory(idx)?;
944 let mut mem = shared.borrow_mut();
945 Some(f(&mut mem))
946 }
947
948 pub fn set_gpu(&mut self, backend: alloc::boxed::Box<dyn GpuBackend>) {
950 *self.gpu.borrow_mut() = Some(backend);
951 }
952
953 pub fn clear_gpu(&mut self) {
955 *self.gpu.borrow_mut() = None;
956 }
957
958 pub fn has_gpu(&self) -> bool {
960 self.gpu.borrow().is_some()
961 }
962
963 pub fn with_gpu_mut<R>(&self, f: impl FnOnce(&mut dyn GpuBackend) -> R) -> Option<R> {
965 let mut gpu = self.gpu.borrow_mut();
966 let backend = gpu.as_mut()?;
967 Some(f(&mut **backend))
968 }
969
970 pub fn register_host_func(
976 &mut self,
977 module: &str,
978 name: &str,
979 func: HostFunction,
980 ) -> Result<(), RuntimeError> {
981 let Some(pos) = self
982 .imported_funcs
983 .iter()
984 .position(|import| import.module == module && import.name == name)
985 else {
986 return Err(RuntimeError {
987 kind: RuntimeErrorKind::UnknownImport {
988 module: module.into(),
989 name: name.into(),
990 },
991 });
992 };
993 if func.ty() != &self.imported_funcs[pos].ty {
994 return Err(RuntimeError {
995 kind: RuntimeErrorKind::ImportTypeMismatch {
996 module: module.into(),
997 name: name.into(),
998 },
999 });
1000 }
1001 self.host_funcs[pos] = Some(core::cell::RefCell::new(func));
1002 Ok(())
1003 }
1004
1005 pub(crate) fn instance_id(&self) -> u32 {
1007 self.instance_id
1008 }
1009
1010 pub fn set_fuel(&self, fuel: Option<u64>) {
1017 self.fuel.set(fuel);
1018 }
1019
1020 pub fn fuel(&self) -> Option<u64> {
1022 self.fuel.get()
1023 }
1024
1025 pub(crate) fn charge_fuel(&self) -> bool {
1027 let Some(remaining) = self.fuel.get() else {
1028 return true;
1029 };
1030 if remaining == 0 {
1031 return false;
1032 }
1033 self.fuel.set(Some(remaining - 1));
1034 true
1035 }
1036
1037 pub(crate) fn link_group(&self) -> Option<&alloc::rc::Rc<core::cell::RefCell<LinkGroup>>> {
1039 self.link_group.as_ref()
1040 }
1041
1042 pub(crate) fn call_host(
1045 &self,
1046 func_idx: u32,
1047 args: &[Value],
1048 ) -> Result<Vec<Value>, RuntimeError> {
1049 if self
1050 .host_funcs
1051 .get(func_idx as usize)
1052 .and_then(Option::as_ref)
1053 .is_none()
1054 {
1055 let (module, name) = self
1056 .imported_func(func_idx)
1057 .map(|import| (import.module.clone(), import.name.clone()))
1058 .unwrap_or_default();
1059 return Err(RuntimeError {
1060 kind: RuntimeErrorKind::UnknownImport { module, name },
1061 });
1062 }
1063 self.host_funcs[func_idx as usize]
1064 .as_ref()
1065 .expect("registration checked above")
1066 .borrow_mut()
1067 .call(args)
1068 }
1069
1070 pub(crate) fn imported_func(&self, func_idx: u32) -> Option<&crate::lower::RegImport> {
1072 self.imported_funcs.get(func_idx as usize)
1073 }
1074
1075 pub fn set_offload_threshold(&mut self, threshold: usize) {
1078 self.offload_threshold = threshold;
1079 }
1080
1081 pub fn f32_add_region(
1087 &mut self,
1088 a_ptr: u32,
1089 b_ptr: u32,
1090 out_ptr: u32,
1091 count: usize,
1092 ) -> Result<(), RuntimeError> {
1093 let byte_len = count
1094 .checked_mul(4)
1095 .ok_or(trap(RuntimeTrap::OutOfBoundsMemoryAccess))?;
1096 let mem_len = self.with_memory(0, |mem| mem.len()).ok_or(RuntimeError {
1097 kind: RuntimeErrorKind::UnknownMemory { memory: 0 },
1098 })? as u64;
1099 for ptr in [a_ptr, b_ptr, out_ptr] {
1100 let end = ptr as u64 + byte_len as u64;
1101 if end > mem_len {
1102 return Err(trap(RuntimeTrap::OutOfBoundsMemoryAccess));
1103 }
1104 }
1105
1106 if !self.has_gpu() || count < self.offload_threshold {
1107 self.with_memory_mut(0, |mem| {
1109 for i in 0..count {
1110 let at = a_ptr as usize + i * 4;
1111 let bt = b_ptr as usize + i * 4;
1112 let ot = out_ptr as usize + i * 4;
1113 let lhs =
1114 f32::from_le_bytes(mem[at..at + 4].try_into().expect("width checked"));
1115 let rhs =
1116 f32::from_le_bytes(mem[bt..bt + 4].try_into().expect("width checked"));
1117 mem[ot..ot + 4].copy_from_slice(&(lhs + rhs).to_le_bytes());
1118 }
1119 })
1120 .expect("memory 0 length checked above");
1121 return Ok(());
1122 }
1123
1124 let kernel = match self.offload_kernels.get("f32_add") {
1126 Some(&kernel) => kernel,
1127 None => {
1128 let kernel = self
1129 .with_gpu_mut(|gpu| gpu.compile("vadd", F32_ADD_WGSL))
1130 .expect("gpu checked above")
1131 .map_err(runtime_gpu_error)?;
1132 self.offload_kernels.insert("f32_add", kernel);
1133 kernel
1134 }
1135 };
1136
1137 let mem = self.shared_memory(0).ok_or(RuntimeError {
1138 kind: RuntimeErrorKind::UnknownMemory { memory: 0 },
1139 })?;
1140 let (a_bytes, b_bytes) = {
1141 let mem = mem.borrow();
1142 (
1143 mem[a_ptr as usize..a_ptr as usize + byte_len].to_vec(),
1144 mem[b_ptr as usize..b_ptr as usize + byte_len].to_vec(),
1145 )
1146 };
1147 let buf_a = self
1148 .with_gpu_mut(|gpu| gpu.create_buffer(&a_bytes))
1149 .expect("gpu checked above")
1150 .map_err(runtime_gpu_error)?;
1151 let buf_b = self
1152 .with_gpu_mut(|gpu| gpu.create_buffer(&b_bytes))
1153 .expect("gpu checked above")
1154 .map_err(runtime_gpu_error)?;
1155 let buf_out = self
1156 .with_gpu_mut(|gpu| gpu.create_buffer_uninit(byte_len))
1157 .expect("gpu checked above")
1158 .map_err(runtime_gpu_error)?;
1159 let workgroups = [count.div_ceil(256) as u32, 1, 1];
1160 self.with_gpu_mut(|gpu| {
1161 gpu.dispatch_verified(kernel, &[buf_a, buf_b, buf_out], workgroups, [256, 1, 1])
1162 })
1163 .expect("gpu checked above")
1164 .map_err(runtime_gpu_error)?;
1165 let result = self
1166 .with_gpu_mut(|gpu| gpu.read_buffer(buf_out))
1167 .expect("gpu checked above")
1168 .map_err(runtime_gpu_error)?;
1169 mem.borrow_mut()[out_ptr as usize..out_ptr as usize + byte_len]
1170 .copy_from_slice(&result[..byte_len]);
1171 Ok(())
1172 }
1173}
1174
1175fn runtime_gpu_error(error: GpuError) -> RuntimeError {
1177 RuntimeError {
1178 kind: RuntimeErrorKind::Gpu(error),
1179 }
1180}
1181
1182fn eval_const(
1185 expr: &[RegConstInstr],
1186 globals: &[Value],
1187 imported_globals: &[Value],
1188 imported_global_count: u32,
1189 instance_id: u32,
1190) -> Result<Value, RuntimeError> {
1191 let mut stack: Vec<Value> = Vec::new();
1192 for instr in expr {
1193 match *instr {
1194 RegConstInstr::I32Const(value) => stack.push(Value::I32(value)),
1195 RegConstInstr::I64Const(value) => stack.push(Value::I64(value)),
1196 RegConstInstr::F32Const(bits) => stack.push(Value::F32(f32::from_bits(bits))),
1197 RegConstInstr::F64Const(bits) => stack.push(Value::F64(f64::from_bits(bits))),
1198 RegConstInstr::RefNull => stack.push(Value::FuncRef(None)),
1199 RegConstInstr::RefFunc(func) => {
1200 stack.push(Value::FuncRef(Some((instance_id, func.0))));
1201 }
1202 RegConstInstr::GlobalGet(global) => {
1203 let idx = global.0 as usize;
1204 let value = if idx < imported_global_count as usize {
1205 imported_globals.get(idx).copied().ok_or(RuntimeError {
1206 kind: RuntimeErrorKind::UnknownGlobal { global: global.0 },
1207 })?
1208 } else {
1209 globals
1210 .get(idx - imported_global_count as usize)
1211 .copied()
1212 .ok_or(RuntimeError {
1213 kind: RuntimeErrorKind::UnknownGlobal { global: global.0 },
1214 })?
1215 };
1216 stack.push(value);
1217 }
1218 RegConstInstr::I32Add => {
1219 let (lhs, rhs) = pop_i32_pair(&mut stack)?;
1220 stack.push(Value::I32(lhs.wrapping_add(rhs)));
1221 }
1222 RegConstInstr::I32Sub => {
1223 let (lhs, rhs) = pop_i32_pair(&mut stack)?;
1224 stack.push(Value::I32(lhs.wrapping_sub(rhs)));
1225 }
1226 RegConstInstr::I32Mul => {
1227 let (lhs, rhs) = pop_i32_pair(&mut stack)?;
1228 stack.push(Value::I32(lhs.wrapping_mul(rhs)));
1229 }
1230 RegConstInstr::I64Add => {
1231 let (lhs, rhs) = pop_i64_pair(&mut stack)?;
1232 stack.push(Value::I64(lhs.wrapping_add(rhs)));
1233 }
1234 RegConstInstr::I64Sub => {
1235 let (lhs, rhs) = pop_i64_pair(&mut stack)?;
1236 stack.push(Value::I64(lhs.wrapping_sub(rhs)));
1237 }
1238 RegConstInstr::I64Mul => {
1239 let (lhs, rhs) = pop_i64_pair(&mut stack)?;
1240 stack.push(Value::I64(lhs.wrapping_mul(rhs)));
1241 }
1242 }
1243 }
1244
1245 if stack.len() != 1 {
1246 return Err(RuntimeError {
1247 kind: RuntimeErrorKind::InvalidConstExpr,
1248 });
1249 }
1250 Ok(stack[0])
1251}
1252
1253fn pop_i32_pair(stack: &mut Vec<Value>) -> Result<(i32, i32), RuntimeError> {
1254 let (Some(Value::I32(rhs)), Some(Value::I32(lhs))) = (stack.pop(), stack.pop()) else {
1255 return Err(RuntimeError {
1256 kind: RuntimeErrorKind::InvalidConstExpr,
1257 });
1258 };
1259 Ok((lhs, rhs))
1260}
1261
1262fn pop_i64_pair(stack: &mut Vec<Value>) -> Result<(i64, i64), RuntimeError> {
1263 let (Some(Value::I64(rhs)), Some(Value::I64(lhs))) = (stack.pop(), stack.pop()) else {
1264 return Err(RuntimeError {
1265 kind: RuntimeErrorKind::InvalidConstExpr,
1266 });
1267 };
1268 Ok((lhs, rhs))
1269}