1#![cfg_attr(feature = "gc", allow(irrefutable_let_patterns))]
6
7use crate::vmcontext::{VMFuncRef, VMTableDefinition};
8use crate::{GcStore, SendSyncPtr, Store, VMGcRef};
9use anyhow::{bail, ensure, format_err, Error, Result};
10use sptr::Strict;
11use std::ops::Range;
12use std::ptr::{self, NonNull};
13use wasmtime_environ::{
14 TablePlan, Trap, WasmHeapType, WasmRefType, FUNCREF_INIT_BIT, FUNCREF_MASK,
15};
16
17pub enum TableElement {
22 FuncRef(*mut VMFuncRef),
24
25 GcRef(Option<VMGcRef>),
27
28 UninitFunc,
33}
34
35#[derive(Copy, Clone, PartialEq, Eq, Debug)]
36pub enum TableElementType {
37 Func,
38 GcRef,
39}
40
41impl TableElementType {
42 fn matches(&self, val: &TableElement) -> bool {
43 match (val, self) {
44 (TableElement::FuncRef(_), TableElementType::Func) => true,
45 (TableElement::GcRef(_), TableElementType::GcRef) => true,
46 _ => false,
47 }
48 }
49}
50
51unsafe impl Send for TableElement where VMGcRef: Send {}
54unsafe impl Sync for TableElement where VMGcRef: Sync {}
55
56impl TableElement {
57 pub(crate) unsafe fn into_func_ref_asserting_initialized(self) -> *mut VMFuncRef {
69 match self {
70 Self::FuncRef(e) => e,
71 Self::UninitFunc => panic!("Uninitialized table element value outside of table slot"),
72 Self::GcRef(_) => panic!("GC reference is not a function reference"),
73 }
74 }
75
76 pub(crate) fn is_uninit(&self) -> bool {
79 match self {
80 Self::UninitFunc => true,
81 _ => false,
82 }
83 }
84}
85
86impl From<*mut VMFuncRef> for TableElement {
87 fn from(f: *mut VMFuncRef) -> TableElement {
88 TableElement::FuncRef(f)
89 }
90}
91
92impl From<Option<VMGcRef>> for TableElement {
93 fn from(x: Option<VMGcRef>) -> TableElement {
94 TableElement::GcRef(x)
95 }
96}
97
98impl From<VMGcRef> for TableElement {
99 fn from(x: VMGcRef) -> TableElement {
100 TableElement::GcRef(Some(x))
101 }
102}
103
104#[derive(Copy, Clone)]
105#[repr(transparent)]
106struct TaggedFuncRef(*mut VMFuncRef);
107
108impl TaggedFuncRef {
109 const UNINIT: TaggedFuncRef = TaggedFuncRef(ptr::null_mut());
110
111 fn from(ptr: *mut VMFuncRef) -> Self {
114 let masked = Strict::map_addr(ptr, |a| a | FUNCREF_INIT_BIT);
115 TaggedFuncRef(masked)
116 }
117
118 fn into_table_element(self) -> TableElement {
121 let ptr = self.0;
122 if ptr.is_null() {
123 TableElement::UninitFunc
124 } else {
125 let unmasked = Strict::map_addr(ptr, |a| a & FUNCREF_MASK);
126 TableElement::FuncRef(unmasked)
127 }
128 }
129}
130
131pub type FuncTableElem = Option<SendSyncPtr<VMFuncRef>>;
132
133pub enum StaticTable {
134 Func(StaticFuncTable),
135 GcRef(StaticGcRefTable),
136}
137
138impl From<StaticFuncTable> for StaticTable {
139 fn from(value: StaticFuncTable) -> Self {
140 Self::Func(value)
141 }
142}
143
144impl From<StaticGcRefTable> for StaticTable {
145 fn from(value: StaticGcRefTable) -> Self {
146 Self::GcRef(value)
147 }
148}
149
150pub struct StaticFuncTable {
151 data: SendSyncPtr<[FuncTableElem]>,
154 size: u32,
156}
157
158pub struct StaticGcRefTable {
159 data: SendSyncPtr<[Option<VMGcRef>]>,
162 size: u32,
164}
165
166pub enum DynamicTable {
167 Func(DynamicFuncTable),
168 GcRef(DynamicGcRefTable),
169}
170
171impl From<DynamicFuncTable> for DynamicTable {
172 fn from(value: DynamicFuncTable) -> Self {
173 Self::Func(value)
174 }
175}
176
177impl From<DynamicGcRefTable> for DynamicTable {
178 fn from(value: DynamicGcRefTable) -> Self {
179 Self::GcRef(value)
180 }
181}
182
183pub struct DynamicFuncTable {
184 elements: Vec<FuncTableElem>,
187 maximum: Option<u32>,
189}
190
191pub struct DynamicGcRefTable {
192 elements: Vec<Option<VMGcRef>>,
195 maximum: Option<u32>,
197}
198
199pub enum Table {
201 Static(StaticTable),
204 Dynamic(DynamicTable),
207}
208
209impl From<StaticTable> for Table {
210 fn from(value: StaticTable) -> Self {
211 Self::Static(value)
212 }
213}
214
215impl From<StaticFuncTable> for Table {
216 fn from(value: StaticFuncTable) -> Self {
217 let t: StaticTable = value.into();
218 t.into()
219 }
220}
221
222impl From<StaticGcRefTable> for Table {
223 fn from(value: StaticGcRefTable) -> Self {
224 let t: StaticTable = value.into();
225 t.into()
226 }
227}
228
229impl From<DynamicTable> for Table {
230 fn from(value: DynamicTable) -> Self {
231 Self::Dynamic(value)
232 }
233}
234
235impl From<DynamicFuncTable> for Table {
236 fn from(value: DynamicFuncTable) -> Self {
237 let t: DynamicTable = value.into();
238 t.into()
239 }
240}
241
242impl From<DynamicGcRefTable> for Table {
243 fn from(value: DynamicGcRefTable) -> Self {
244 let t: DynamicTable = value.into();
245 t.into()
246 }
247}
248
249fn wasm_to_table_type(ty: WasmRefType) -> TableElementType {
250 match ty.heap_type {
251 WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
252 TableElementType::Func
253 }
254 WasmHeapType::Extern | WasmHeapType::Any | WasmHeapType::I31 | WasmHeapType::None => {
255 TableElementType::GcRef
256 }
257 }
258}
259
260impl Table {
261 pub fn new_dynamic(plan: &TablePlan, store: &mut dyn Store) -> Result<Self> {
263 Self::limit_new(plan, store)?;
264 match wasm_to_table_type(plan.table.wasm_ty) {
265 TableElementType::Func => Ok(Self::from(DynamicFuncTable {
266 elements: vec![None; usize::try_from(plan.table.minimum).unwrap()],
267 maximum: plan.table.maximum,
268 })),
269 TableElementType::GcRef => Ok(Self::from(DynamicGcRefTable {
270 elements: (0..usize::try_from(plan.table.minimum).unwrap())
271 .map(|_| None)
272 .collect(),
273 maximum: plan.table.maximum,
274 })),
275 }
276 }
277
278 pub unsafe fn new_static(
280 plan: &TablePlan,
281 data: SendSyncPtr<[u8]>,
282 store: &mut dyn Store,
283 ) -> Result<Self> {
284 Self::limit_new(plan, store)?;
285
286 let size = plan.table.minimum;
287 let max = plan
288 .table
289 .maximum
290 .map_or(usize::MAX, |x| usize::try_from(x).unwrap());
291
292 match wasm_to_table_type(plan.table.wasm_ty) {
293 TableElementType::Func => {
294 let len = {
295 let data = data.as_non_null().as_ref();
296 let (before, data, after) = data.align_to::<FuncTableElem>();
297 assert!(before.is_empty());
298 assert!(after.is_empty());
299 data.len()
300 };
301 ensure!(
302 usize::try_from(plan.table.minimum).unwrap() <= len,
303 "initial table size of {} exceeds the pooling allocator's \
304 configured maximum table size of {len} elements",
305 plan.table.minimum,
306 );
307 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts(
308 data.as_non_null().cast::<FuncTableElem>(),
309 std::cmp::min(len, max),
310 ));
311 Ok(Self::from(StaticFuncTable { data, size }))
312 }
313 TableElementType::GcRef => {
314 let len = {
315 let data = data.as_non_null().as_ref();
316 let (before, data, after) = data.align_to::<Option<VMGcRef>>();
317 assert!(before.is_empty());
318 assert!(after.is_empty());
319 data.len()
320 };
321 ensure!(
322 usize::try_from(plan.table.minimum).unwrap() <= len,
323 "initial table size of {} exceeds the pooling allocator's \
324 configured maximum table size of {len} elements",
325 plan.table.minimum,
326 );
327 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts(
328 data.as_non_null().cast::<Option<VMGcRef>>(),
329 std::cmp::min(len, max),
330 ));
331 Ok(Self::from(StaticGcRefTable { data, size }))
332 }
333 }
334 }
335
336 fn limit_new(plan: &TablePlan, store: &mut dyn Store) -> Result<()> {
337 if !store.table_growing(0, plan.table.minimum, plan.table.maximum)? {
338 bail!(
339 "table minimum size of {} elements exceeds table limits",
340 plan.table.minimum
341 );
342 }
343 Ok(())
344 }
345
346 pub fn element_type(&self) -> TableElementType {
348 match self {
349 Table::Static(StaticTable::Func(_)) | Table::Dynamic(DynamicTable::Func(_)) => {
350 TableElementType::Func
351 }
352 Table::Static(StaticTable::GcRef(_)) | Table::Dynamic(DynamicTable::GcRef(_)) => {
353 TableElementType::GcRef
354 }
355 }
356 }
357
358 #[cfg(feature = "pooling-allocator")]
360 pub(crate) fn is_static(&self) -> bool {
361 matches!(self, Table::Static(_))
362 }
363
364 pub fn size(&self) -> u32 {
366 match self {
367 Table::Static(StaticTable::Func(StaticFuncTable { size, .. })) => *size,
368 Table::Static(StaticTable::GcRef(StaticGcRefTable { size, .. })) => *size,
369 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => {
370 elements.len().try_into().unwrap()
371 }
372 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
373 elements.len().try_into().unwrap()
374 }
375 }
376 }
377
378 pub fn maximum(&self) -> Option<u32> {
385 match self {
386 Table::Static(StaticTable::Func(StaticFuncTable { data, .. })) => {
387 Some(u32::try_from(data.len()).unwrap())
388 }
389 Table::Static(StaticTable::GcRef(StaticGcRefTable { data, .. })) => {
390 Some(u32::try_from(data.len()).unwrap())
391 }
392 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { maximum, .. })) => *maximum,
393 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { maximum, .. })) => *maximum,
394 }
395 }
396
397 pub fn init_func(
403 &mut self,
404 dst: u32,
405 items: impl ExactSizeIterator<Item = *mut VMFuncRef>,
406 ) -> Result<(), Trap> {
407 let dst = usize::try_from(dst).map_err(|_| Trap::TableOutOfBounds)?;
408
409 let elements = self
410 .funcrefs_mut()
411 .get_mut(dst..)
412 .and_then(|s| s.get_mut(..items.len()))
413 .ok_or(Trap::TableOutOfBounds)?;
414
415 for (item, slot) in items.zip(elements) {
416 *slot = TaggedFuncRef::from(item);
417 }
418 Ok(())
419 }
420
421 pub fn init_gc_refs(
425 &mut self,
426 dst: u32,
427 items: impl ExactSizeIterator<Item = Option<VMGcRef>>,
428 ) -> Result<(), Trap> {
429 let dst = usize::try_from(dst).map_err(|_| Trap::TableOutOfBounds)?;
430
431 let elements = self
432 .gc_refs_mut()
433 .get_mut(dst..)
434 .and_then(|s| s.get_mut(..items.len()))
435 .ok_or(Trap::TableOutOfBounds)?;
436
437 for (item, slot) in items.zip(elements) {
438 *slot = item;
439 }
440 Ok(())
441 }
442
443 pub fn fill(
451 &mut self,
452 gc_store: &mut GcStore,
453 dst: u32,
454 val: TableElement,
455 len: u32,
456 ) -> Result<(), Trap> {
457 let start = dst as usize;
458 let end = start
459 .checked_add(len as usize)
460 .ok_or_else(|| Trap::TableOutOfBounds)?;
461
462 if end > self.size() as usize {
463 return Err(Trap::TableOutOfBounds);
464 }
465
466 match val {
467 TableElement::FuncRef(f) => {
468 self.funcrefs_mut()[start..end].fill(TaggedFuncRef::from(f));
469 }
470 TableElement::GcRef(r) => {
471 for slot in &mut self.gc_refs_mut()[start..end] {
473 gc_store.write_gc_ref(slot, r.as_ref());
474 }
475
476 if let Some(r) = r {
479 gc_store.drop_gc_ref(r);
480 }
481 }
482 TableElement::UninitFunc => {
483 self.funcrefs_mut()[start..end].fill(TaggedFuncRef::UNINIT);
484 }
485 }
486
487 Ok(())
488 }
489
490 pub unsafe fn grow(
511 &mut self,
512 delta: u32,
513 init_value: TableElement,
514 store: &mut dyn Store,
515 ) -> Result<Option<u32>, Error> {
516 let old_size = self.size();
517
518 if delta == 0 {
521 return Ok(Some(old_size));
522 }
523
524 let new_size = match old_size.checked_add(delta) {
525 Some(s) => s,
526 None => {
527 store.table_grow_failed(format_err!("overflow calculating new table size"))?;
528 return Ok(None);
529 }
530 };
531
532 if !store.table_growing(old_size, new_size, self.maximum())? {
533 return Ok(None);
534 }
535
536 if let Some(max) = self.maximum() {
540 if new_size > max {
541 store.table_grow_failed(format_err!("Table maximum size exceeded"))?;
542 return Ok(None);
543 }
544 }
545
546 debug_assert!(self.type_matches(&init_value));
547
548 match self {
550 Table::Static(StaticTable::Func(StaticFuncTable { data, size })) => {
551 unsafe {
552 debug_assert!(data.as_ref()[*size as usize..new_size as usize]
553 .iter()
554 .all(|x| x.is_none()));
555 }
556 *size = new_size;
557 }
558 Table::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => {
559 unsafe {
560 debug_assert!(data.as_ref()[*size as usize..new_size as usize]
561 .iter()
562 .all(|x| x.is_none()));
563 }
564 *size = new_size;
565 }
566
567 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => {
575 elements.resize(usize::try_from(new_size).unwrap(), None);
576 }
577 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
578 elements.resize_with(usize::try_from(new_size).unwrap(), || None);
579 }
580 }
581
582 self.fill(store.gc_store(), old_size, init_value, delta)
583 .expect("table should not be out of bounds");
584
585 Ok(Some(old_size))
586 }
587
588 pub fn get(&self, gc_store: &mut GcStore, index: u32) -> Option<TableElement> {
592 let index = usize::try_from(index).ok()?;
593 match self.element_type() {
594 TableElementType::Func => self
595 .funcrefs()
596 .get(index)
597 .copied()
598 .map(|e| e.into_table_element()),
599 TableElementType::GcRef => self.gc_refs().get(index).map(|r| {
600 let r = r.as_ref().map(|r| gc_store.clone_gc_ref(r));
601 TableElement::GcRef(r)
602 }),
603 }
604 }
605
606 pub fn set(&mut self, index: u32, elem: TableElement) -> Result<(), ()> {
617 let index = usize::try_from(index).map_err(|_| ())?;
618 match elem {
619 TableElement::FuncRef(f) => {
620 *self.funcrefs_mut().get_mut(index).ok_or(())? = TaggedFuncRef::from(f);
621 }
622 TableElement::UninitFunc => {
623 *self.funcrefs_mut().get_mut(index).ok_or(())? = TaggedFuncRef::UNINIT;
624 }
625 TableElement::GcRef(e) => {
626 *self.gc_refs_mut().get_mut(index).ok_or(())? = e;
627 }
628 }
629 Ok(())
630 }
631
632 pub unsafe fn copy(
639 gc_store: &mut GcStore,
640 dst_table: *mut Self,
641 src_table: *mut Self,
642 dst_index: u32,
643 src_index: u32,
644 len: u32,
645 ) -> Result<(), Trap> {
646 if src_index
649 .checked_add(len)
650 .map_or(true, |n| n > (*src_table).size())
651 || dst_index
652 .checked_add(len)
653 .map_or(true, |m| m > (*dst_table).size())
654 {
655 return Err(Trap::TableOutOfBounds);
656 }
657
658 debug_assert!(
659 (*dst_table).element_type() == (*src_table).element_type(),
660 "table element type mismatch"
661 );
662
663 let src_range = src_index as usize..src_index as usize + len as usize;
664 let dst_range = dst_index as usize..dst_index as usize + len as usize;
665
666 if ptr::eq(dst_table, src_table) {
668 (*dst_table).copy_elements_within(gc_store, dst_range, src_range);
669 } else {
670 Self::copy_elements(gc_store, &mut *dst_table, &*src_table, dst_range, src_range);
671 }
672
673 Ok(())
674 }
675
676 pub fn vmtable(&mut self) -> VMTableDefinition {
678 match self {
679 Table::Static(StaticTable::Func(StaticFuncTable { data, size })) => VMTableDefinition {
680 base: data.as_ptr().cast(),
681 current_elements: *size,
682 },
683 Table::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => {
684 VMTableDefinition {
685 base: data.as_ptr().cast(),
686 current_elements: *size,
687 }
688 }
689 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => {
690 VMTableDefinition {
691 base: elements.as_mut_ptr().cast(),
692 current_elements: elements.len().try_into().unwrap(),
693 }
694 }
695 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
696 VMTableDefinition {
697 base: elements.as_mut_ptr().cast(),
698 current_elements: elements.len().try_into().unwrap(),
699 }
700 }
701 }
702 }
703
704 fn type_matches(&self, val: &TableElement) -> bool {
705 self.element_type().matches(val)
706 }
707
708 fn funcrefs(&self) -> &[TaggedFuncRef] {
709 assert_eq!(self.element_type(), TableElementType::Func);
710 match self {
711 Self::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => unsafe {
712 std::slice::from_raw_parts(elements.as_ptr().cast(), elements.len())
713 },
714 Self::Static(StaticTable::Func(StaticFuncTable { data, size })) => unsafe {
715 std::slice::from_raw_parts(data.as_ptr().cast(), usize::try_from(*size).unwrap())
716 },
717 _ => unreachable!(),
718 }
719 }
720
721 fn funcrefs_mut(&mut self) -> &mut [TaggedFuncRef] {
722 assert_eq!(self.element_type(), TableElementType::Func);
723 match self {
724 Self::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => unsafe {
725 std::slice::from_raw_parts_mut(elements.as_mut_ptr().cast(), elements.len())
726 },
727 Self::Static(StaticTable::Func(StaticFuncTable { data, size })) => unsafe {
728 std::slice::from_raw_parts_mut(
729 data.as_ptr().cast(),
730 usize::try_from(*size).unwrap(),
731 )
732 },
733 _ => unreachable!(),
734 }
735 }
736
737 fn gc_refs(&self) -> &[Option<VMGcRef>] {
738 assert_eq!(self.element_type(), TableElementType::GcRef);
739 match self {
740 Self::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => elements,
741 Self::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => unsafe {
742 &data.as_non_null().as_ref()[..usize::try_from(*size).unwrap()]
743 },
744 _ => unreachable!(),
745 }
746 }
747
748 pub fn gc_refs_mut(&mut self) -> &mut [Option<VMGcRef>] {
752 assert_eq!(self.element_type(), TableElementType::GcRef);
753 match self {
754 Self::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => elements,
755 Self::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => unsafe {
756 &mut data.as_non_null().as_mut()[..usize::try_from(*size).unwrap()]
757 },
758 _ => unreachable!(),
759 }
760 }
761
762 fn copy_elements(
763 gc_store: &mut GcStore,
764 dst_table: &mut Self,
765 src_table: &Self,
766 dst_range: Range<usize>,
767 src_range: Range<usize>,
768 ) {
769 debug_assert!(!ptr::eq(dst_table, src_table));
771
772 let ty = dst_table.element_type();
773
774 match ty {
775 TableElementType::Func => {
776 dst_table.funcrefs_mut()[dst_range]
778 .copy_from_slice(&src_table.funcrefs()[src_range]);
779 }
780 TableElementType::GcRef => {
781 assert_eq!(
782 dst_range.end - dst_range.start,
783 src_range.end - src_range.start
784 );
785 assert!(dst_range.end <= dst_table.gc_refs().len());
786 assert!(src_range.end <= src_table.gc_refs().len());
787 for (dst, src) in dst_range.zip(src_range) {
788 gc_store.write_gc_ref(
789 &mut dst_table.gc_refs_mut()[dst],
790 src_table.gc_refs()[src].as_ref(),
791 );
792 }
793 }
794 }
795 }
796
797 fn copy_elements_within(
798 &mut self,
799 gc_store: &mut GcStore,
800 dst_range: Range<usize>,
801 src_range: Range<usize>,
802 ) {
803 assert_eq!(
804 dst_range.end - dst_range.start,
805 src_range.end - src_range.start
806 );
807
808 if src_range.start == dst_range.start {
810 return;
811 }
812
813 let ty = self.element_type();
814 match ty {
815 TableElementType::Func => {
816 self.funcrefs_mut().copy_within(src_range, dst_range.start);
818 }
819 TableElementType::GcRef => {
820 let elements = self.gc_refs_mut();
823 if dst_range.start < src_range.start {
824 for (d, s) in dst_range.zip(src_range) {
825 let (ds, ss) = elements.split_at_mut(s);
826 let dst = &mut ds[d];
827 let src = ss[0].as_ref();
828 gc_store.write_gc_ref(dst, src);
829 }
830 } else {
831 for (s, d) in src_range.rev().zip(dst_range.rev()) {
832 let (ss, ds) = elements.split_at_mut(d);
833 let dst = &mut ds[0];
834 let src = ss[s].as_ref();
835 gc_store.write_gc_ref(dst, src);
836 }
837 }
838 }
839 }
840 }
841}
842
843impl Default for Table {
845 fn default() -> Self {
846 Self::from(StaticFuncTable {
847 data: SendSyncPtr::new(NonNull::from(&mut [])),
848 size: 0,
849 })
850 }
851}