1use crate::generators::gc_ops::stack::StackType;
4use crate::generators::gc_ops::{
5 limits::{GcOpsLimits, MAX_INLINE_CONSTRUCTION},
6 types::{CompositeType, EmitCtx, FieldType, RecGroupId, StructField, TypeId, Types, emit_new},
7};
8use mutatis::Generate;
9use serde::{Deserialize, Serialize};
10use std::collections::BTreeMap;
11use wasm_encoder::{
12 AbstractHeapType, BlockType, CodeSection, ConstExpr, EntityType, ExportKind, ExportSection,
13 Function, FunctionSection, GlobalSection, GlobalType, HeapType, ImportSection, Instruction,
14 Module, RefType, TableSection, TableType, TypeSection, ValType,
15};
16
17const STRUCT: HeapType = HeapType::Abstract {
19 shared: false,
20 ty: AbstractHeapType::Struct,
21};
22
23const ARRAY: HeapType = HeapType::Abstract {
25 shared: false,
26 ty: AbstractHeapType::Array,
27};
28
29const EQ: HeapType = HeapType::Abstract {
31 shared: false,
32 ty: AbstractHeapType::Eq,
33};
34
35const STRUCTREF: RefType = RefType {
37 nullable: true,
38 heap_type: STRUCT,
39};
40
41fn concrete(index: u32) -> RefType {
43 RefType {
44 nullable: true,
45 heap_type: HeapType::Concrete(index),
46 }
47}
48
49fn pick_type_index(indices: &[u32], raw: u32) -> Option<u32> {
52 if indices.is_empty() {
53 None
54 } else {
55 Some(indices[usize::try_from(raw).unwrap() % indices.len()])
56 }
57}
58
59fn array_element<'a>(
61 types: &'a Types,
62 encoding_order: &[TypeId],
63 type_index: u32,
64) -> Option<&'a StructField> {
65 encoding_order
66 .get(usize::try_from(type_index).unwrap())
67 .and_then(|tid| types.type_defs.get(tid))
68 .and_then(|def| match &def.composite_type {
69 CompositeType::Array(at) => Some(&at.element),
70 CompositeType::Struct(_) => None,
71 })
72}
73
74fn struct_fields<'a>(
77 types: &'a Types,
78 encoding_order: &[TypeId],
79 type_index: u32,
80) -> Option<&'a [StructField]> {
81 encoding_order
82 .get(usize::try_from(type_index).unwrap())
83 .and_then(|tid| types.type_defs.get(tid))
84 .and_then(|def| match &def.composite_type {
85 CompositeType::Struct(st) => Some(st.fields.as_slice()),
86 CompositeType::Array(_) => None,
87 })
88}
89
90fn nullable_table(element_type: RefType, size: u32) -> TableType {
92 TableType {
93 element_type,
94 minimum: u64::from(size),
95 maximum: None,
96 table64: false,
97 shared: false,
98 }
99}
100
101fn null_ref_global(globals: &mut GlobalSection, heap_type: HeapType) -> u32 {
103 let index = globals.len();
104 globals.global(
105 GlobalType {
106 val_type: ValType::Ref(RefType {
107 nullable: true,
108 heap_type,
109 }),
110 mutable: true,
111 shared: false,
112 },
113 &ConstExpr::ref_null(heap_type),
114 );
115 index
116}
117
118fn table_get(func: &mut Function, elem_index: u32, table: u32) {
120 func.instruction(&Instruction::I32Const(elem_index.cast_signed()));
121 func.instruction(&Instruction::TableGet(table));
122}
123
124fn table_set_via(func: &mut Function, tmp: u32, elem_index: u32, table: u32) {
127 func.instruction(&Instruction::LocalSet(tmp));
128 func.instruction(&Instruction::I32Const(elem_index.cast_signed()));
129 func.instruction(&Instruction::LocalGet(tmp));
130 func.instruction(&Instruction::TableSet(table));
131}
132
133fn if_non_null(func: &mut Function, tmp: u32, body: impl FnOnce(&mut Function)) {
136 func.instruction(&Instruction::LocalTee(tmp));
137 func.instruction(&Instruction::RefIsNull);
138 func.instruction(&Instruction::If(BlockType::Empty));
139 func.instruction(&Instruction::Else);
140 body(func);
141 func.instruction(&Instruction::End);
142}
143
144fn struct_get_instruction(
146 struct_type_index: u32,
147 field_index: u32,
148 field_type: FieldType,
149 unsigned: bool,
150) -> Instruction<'static> {
151 match (field_type.is_packed(), unsigned) {
152 (false, _) => Instruction::StructGet {
153 struct_type_index,
154 field_index,
155 },
156 (true, true) => Instruction::StructGetU {
157 struct_type_index,
158 field_index,
159 },
160 (true, false) => Instruction::StructGetS {
161 struct_type_index,
162 field_index,
163 },
164 }
165}
166
167fn array_get_instruction(
169 array_type_index: u32,
170 element_type: FieldType,
171 unsigned: bool,
172) -> Instruction<'static> {
173 match (element_type.is_packed(), unsigned) {
174 (false, _) => Instruction::ArrayGet(array_type_index),
175 (true, true) => Instruction::ArrayGetU(array_type_index),
176 (true, false) => Instruction::ArrayGetS(array_type_index),
177 }
178}
179
180struct HostTypes {
182 gc: u32,
184 run: u32,
186 take_refs: u32,
188 make_refs: u32,
190 take_struct: u32,
192 take_eq: u32,
194 take_i31: u32,
196 take_array: u32,
198}
199
200#[derive(Clone, Copy)]
202struct HostFuncs {
203 gc: u32,
205 take_refs: u32,
207 make_refs: u32,
209 take_struct: u32,
211 take_eq: u32,
213 take_i31: u32,
215 take_array: u32,
217 typed_base: u32,
219}
220
221impl HostFuncs {
222 fn typed(&self, dense: u32) -> u32 {
224 self.typed_base + dense
225 }
226}
227
228#[derive(Clone, Copy)]
231struct RootBanks {
232 structref: u32,
234 eqref: u32,
236 i31ref: u32,
238 arrayref: u32,
240 typed_base: u32,
242}
243
244impl RootBanks {
245 fn typed(&self, dense: u32) -> u32 {
247 self.typed_base + dense
248 }
249}
250
251#[derive(Clone, Copy)]
253struct LocalBanks {
254 extern_scratch: u32,
256 structref: u32,
258 eqref: u32,
260 i31ref: u32,
262 arrayref: u32,
264 typed_base: u32,
266 typed2_base: u32,
268 proto_base: u32,
270}
271
272impl LocalBanks {
273 fn typed(&self, dense: u32) -> u32 {
275 self.typed_base + dense
276 }
277
278 fn typed2(&self, dense: u32) -> u32 {
280 self.typed2_base + dense
281 }
282
283 fn proto(&self, dense: u32) -> u32 {
285 self.proto_base + dense
286 }
287}
288
289#[derive(Clone, Copy)]
292pub(crate) struct WasmEncodingBases {
293 funcs: HostFuncs,
295 struct_type_base: u32,
297 locals: LocalBanks,
299 globals: RootBanks,
301 tables: RootBanks,
303 array_length: u32,
305}
306
307impl WasmEncodingBases {
308 fn wasm_type(&self, dense: u32) -> u32 {
310 self.struct_type_base + dense
311 }
312
313 fn root_slots(&self, kind: RefKind) -> RootSlots {
315 let (l, g, t) = (&self.locals, &self.globals, &self.tables);
316 match kind {
317 RefKind::Extern { slot } => RootSlots {
318 local: slot,
319 global: slot,
320 table: 0,
321 tmp: l.extern_scratch,
322 },
323 RefKind::Struct => RootSlots {
324 local: l.structref,
325 global: g.structref,
326 table: t.structref,
327 tmp: l.structref,
328 },
329 RefKind::Eq => RootSlots {
330 local: l.eqref,
331 global: g.eqref,
332 table: t.eqref,
333 tmp: l.eqref,
334 },
335 RefKind::I31 => RootSlots {
336 local: l.i31ref,
337 global: g.i31ref,
338 table: t.i31ref,
339 tmp: l.i31ref,
340 },
341 RefKind::Array => RootSlots {
342 local: l.arrayref,
343 global: g.arrayref,
344 table: t.arrayref,
345 tmp: l.arrayref,
346 },
347 RefKind::Typed(d) => RootSlots {
348 local: l.typed(d),
349 global: g.typed(d),
350 table: t.typed(d),
351 tmp: l.typed(d),
352 },
353 }
354 }
355}
356
357#[derive(Clone, Copy, Debug)]
359enum RefKind {
360 Extern { slot: u32 },
362 Struct,
364 Eq,
366 I31,
368 Array,
370 Typed(u32),
372}
373
374#[derive(Clone, Copy, Debug)]
376enum Storage {
377 Local,
379 Global,
381 Table(u32),
383}
384
385#[derive(Clone, Copy, Debug)]
387enum Dir {
388 Get,
390 Set,
392}
393
394#[derive(Clone, Copy)]
396struct RootSlots {
397 local: u32,
399 global: u32,
401 table: u32,
403 tmp: u32,
405}
406
407struct LocalDecls {
409 decls: Vec<(u32, ValType)>,
411 next: u32,
413}
414
415impl LocalDecls {
416 fn new(num_params: u32) -> Self {
418 Self {
419 decls: Vec::new(),
420 next: num_params,
421 }
422 }
423
424 fn declare(&mut self, ty: ValType) -> u32 {
426 let index = self.next;
427 self.next += 1;
428 self.decls.push((1, ty));
429 index
430 }
431
432 fn declare_bank(&mut self, count: u32, ty: impl Fn(u32) -> ValType) -> u32 {
434 let base = self.next;
435 for i in 0..count {
436 self.declare(ty(i));
437 }
438 base
439 }
440}
441
442fn host_function_types(types: &mut TypeSection, num_params: u32) -> HostTypes {
444 let three_refs = vec![ValType::EXTERNREF, ValType::EXTERNREF, ValType::EXTERNREF];
445
446 let gc = types.len();
452 types.ty().function(vec![], three_refs.clone());
453
454 let run = types.len();
455 types.ty().function(
456 vec![ValType::EXTERNREF; usize::try_from(num_params).unwrap()],
457 vec![],
458 );
459
460 let take_refs = types.len();
461 types.ty().function(three_refs.clone(), vec![]);
462
463 let make_refs = types.len();
464 types.ty().function(vec![], three_refs);
465
466 let take_struct = types.len();
467 types.ty().function(vec![ValType::Ref(STRUCTREF)], vec![]);
468
469 let take_eq = types.len();
470 types
471 .ty()
472 .function(vec![ValType::Ref(RefType::EQREF)], vec![]);
473
474 let take_i31 = types.len();
477 types.ty().function(
478 vec![ValType::Ref(RefType::I31REF), ValType::I32, ValType::I32],
479 vec![],
480 );
481
482 let take_array = types.len();
483 types
484 .ty()
485 .function(vec![ValType::Ref(RefType::ARRAYREF)], vec![]);
486
487 HostTypes {
488 gc,
489 run,
490 take_refs,
491 make_refs,
492 take_struct,
493 take_eq,
494 take_i31,
495 take_array,
496 }
497}
498
499fn typed_take_types(types: &mut TypeSection, struct_type_base: u32, concrete_count: u32) -> u32 {
501 let base = struct_type_base + concrete_count;
503 for i in 0..concrete_count {
504 types
505 .ty()
506 .function(vec![ValType::Ref(concrete(struct_type_base + i))], vec![]);
507 }
508 base
509}
510
511fn encode_tables(
513 table_size: u32,
514 struct_type_base: u32,
515 concrete_count: u32,
516) -> (TableSection, RootBanks) {
517 let mut tables = TableSection::new();
518 tables.table(nullable_table(RefType::EXTERNREF, table_size));
519
520 let structref = tables.len();
521 tables.table(nullable_table(STRUCTREF, table_size));
522
523 let eqref = tables.len();
524 tables.table(nullable_table(RefType::EQREF, table_size));
525
526 let i31ref = tables.len();
527 tables.table(nullable_table(RefType::I31REF, table_size));
528
529 let arrayref = tables.len();
530 tables.table(nullable_table(RefType::ARRAYREF, table_size));
531
532 let typed_base = tables.len();
533 for i in 0..concrete_count {
534 tables.table(nullable_table(concrete(struct_type_base + i), table_size));
535 }
536
537 let banks = RootBanks {
538 structref,
539 eqref,
540 i31ref,
541 arrayref,
542 typed_base,
543 };
544 (tables, banks)
545}
546
547fn encode_globals(
549 num_globals: u32,
550 struct_type_base: u32,
551 concrete_count: u32,
552) -> (GlobalSection, RootBanks) {
553 let mut globals = GlobalSection::new();
554 for _ in 0..num_globals {
555 null_ref_global(&mut globals, HeapType::EXTERN);
556 }
557 let structref = null_ref_global(&mut globals, STRUCT);
558 let eqref = null_ref_global(&mut globals, EQ);
559 let i31ref = null_ref_global(&mut globals, HeapType::I31);
560 let arrayref = null_ref_global(&mut globals, ARRAY);
561 let typed_base = globals.len();
562 for i in 0..concrete_count {
563 null_ref_global(&mut globals, HeapType::Concrete(struct_type_base + i));
564 }
565
566 let banks = RootBanks {
567 structref,
568 eqref,
569 i31ref,
570 arrayref,
571 typed_base,
572 };
573 (globals, banks)
574}
575
576fn declare_locals(
578 num_params: u32,
579 struct_type_base: u32,
580 concrete_count: u32,
581) -> (LocalDecls, LocalBanks) {
582 let mut locals = LocalDecls::new(num_params);
583 let extern_scratch = locals.declare(ValType::EXTERNREF);
584 let structref = locals.declare(ValType::Ref(STRUCTREF));
585 let eqref = locals.declare(ValType::Ref(RefType::EQREF));
586 let i31ref = locals.declare(ValType::Ref(RefType::I31REF));
587 let arrayref = locals.declare(ValType::Ref(RefType::ARRAYREF));
588
589 let typed = |i| ValType::Ref(concrete(struct_type_base + i));
590 let typed_base = locals.declare_bank(concrete_count, typed);
591 let typed2_base = locals.declare_bank(concrete_count, typed);
592 let proto_base = locals.declare_bank(concrete_count, typed);
593
594 let banks = LocalBanks {
595 extern_scratch,
596 structref,
597 eqref,
598 i31ref,
599 arrayref,
600 typed_base,
601 typed2_base,
602 proto_base,
603 };
604 (locals, banks)
605}
606
607#[derive(Clone, Debug, Default, Serialize, Deserialize)]
610pub struct GcOps {
611 pub(crate) limits: GcOpsLimits,
613 pub(crate) ops: Vec<GcOp>,
615 pub(crate) types: Types,
617}
618
619impl GcOps {
620 pub fn to_wasm_binary(&mut self) -> Vec<u8> {
632 let mut encoding_order_grouped = Vec::with_capacity(self.types.rec_groups.len());
633 self.fixup(&mut encoding_order_grouped);
634
635 let encoding_order: Vec<TypeId> = encoding_order_grouped
638 .iter()
639 .flat_map(|(_, members)| members.iter().copied())
640 .collect();
641
642 let mut types = TypeSection::new();
643 let host_types = host_function_types(&mut types, self.limits.num_params);
644 let struct_type_base = types.len();
645 let type_ids_to_index =
646 self.encode_concrete_types(&mut types, &encoding_order_grouped, struct_type_base);
647 let concrete_count = u32::try_from(type_ids_to_index.len()).unwrap();
648 let typed_fn_type_base = typed_take_types(&mut types, struct_type_base, concrete_count);
649
650 let (imports, funcs) = self.encode_imports(
651 &host_types,
652 typed_fn_type_base,
653 struct_type_base,
654 &encoding_order,
655 );
656 let (tables, table_banks) =
657 encode_tables(self.limits.table_size, struct_type_base, concrete_count);
658 let (globals, global_banks) =
659 encode_globals(self.limits.num_globals, struct_type_base, concrete_count);
660 let (local_decls, local_banks) =
661 declare_locals(self.limits.num_params, struct_type_base, concrete_count);
662
663 let mut functions = FunctionSection::new();
665 let mut exports = ExportSection::new();
666 functions.function(host_types.run);
667 exports.export("run", ExportKind::Func, imports.len());
668
669 let bases = WasmEncodingBases {
670 funcs,
671 struct_type_base,
672 locals: local_banks,
673 globals: global_banks,
674 tables: table_banks,
675 array_length: self.limits.array_length,
676 };
677 let func = self.encode_run_body(local_decls, bases, &type_ids_to_index, &encoding_order);
678 let mut code = CodeSection::new();
679 code.function(&func);
680
681 let mut module = Module::new();
682 module
683 .section(&types)
684 .section(&imports)
685 .section(&functions)
686 .section(&tables)
687 .section(&globals)
688 .section(&exports)
689 .section(&code);
690
691 module.finish()
692 }
693
694 fn encode_concrete_types(
696 &self,
697 types: &mut TypeSection,
698 encoding_order_grouped: &[(RecGroupId, Vec<TypeId>)],
699 struct_type_base: u32,
700 ) -> BTreeMap<TypeId, u32> {
701 let mut type_ids_to_index: BTreeMap<TypeId, u32> = BTreeMap::new();
705 let mut next_idx = struct_type_base;
706 for (_, members) in encoding_order_grouped {
707 for &tid in members {
708 type_ids_to_index.insert(tid, next_idx);
709 next_idx += 1;
710 }
711 }
712
713 let encode_ty_id = |ty_id: &TypeId| -> wasm_encoder::SubType {
714 let def = &self.types.type_defs[ty_id];
715 let inner = match &def.composite_type {
716 CompositeType::Struct(st) => {
717 let fields: Box<[wasm_encoder::FieldType]> = st
718 .fields
719 .iter()
720 .map(|f| wasm_encoder::FieldType {
721 element_type: f.field_type.to_storage_type(&type_ids_to_index),
722 mutable: f.mutable,
723 })
724 .collect();
725 wasm_encoder::CompositeInnerType::Struct(wasm_encoder::StructType { fields })
726 }
727 CompositeType::Array(at) => {
728 let element = wasm_encoder::FieldType {
729 element_type: at.element.field_type.to_storage_type(&type_ids_to_index),
730 mutable: at.element.mutable,
731 };
732 wasm_encoder::CompositeInnerType::Array(wasm_encoder::ArrayType(element))
733 }
734 };
735 wasm_encoder::SubType {
736 is_final: def.is_final,
737 supertype_idxs: def
738 .supertype
739 .map(|st| type_ids_to_index[&st])
740 .into_iter()
741 .collect(),
742 composite_type: wasm_encoder::CompositeType {
743 inner,
744 shared: false,
745 describes: None,
746 descriptor: None,
747 },
748 }
749 };
750
751 for (_, group_members) in encoding_order_grouped {
752 let members: Vec<wasm_encoder::SubType> =
753 group_members.iter().map(encode_ty_id).collect();
754 types.ty().rec(members);
755 }
756
757 type_ids_to_index
758 }
759
760 fn encode_imports(
762 &self,
763 host_types: &HostTypes,
764 typed_fn_type_base: u32,
765 struct_type_base: u32,
766 encoding_order: &[TypeId],
767 ) -> (ImportSection, HostFuncs) {
768 let mut imports = ImportSection::new();
769 let mut import_func = |name: &str, type_idx: u32| -> u32 {
770 let index = imports.len();
771 imports.import("", name, EntityType::Function(type_idx));
772 index
773 };
774 let gc = import_func("gc", host_types.gc);
775 let take_refs = import_func("take_refs", host_types.take_refs);
776 let make_refs = import_func("make_refs", host_types.make_refs);
777 let take_struct = import_func("take_struct", host_types.take_struct);
778 let take_eq = import_func("take_eq", host_types.take_eq);
779 let take_i31 = import_func("take_i31", host_types.take_i31);
780 let take_array = import_func("take_array", host_types.take_array);
781
782 let typed_base = imports.len();
784 for (i, tid) in (0u32..).zip(encoding_order) {
785 let wasm_idx = struct_type_base + i;
786 let is_array = self
787 .types
788 .type_defs
789 .get(tid)
790 .map(|def| def.composite_type.is_array())
791 .unwrap_or(false);
792 let name = if is_array {
793 format!("take_array_{wasm_idx}")
794 } else {
795 format!("take_struct_{wasm_idx}")
796 };
797 imports.import("", &name, EntityType::Function(typed_fn_type_base + i));
798 }
799
800 let funcs = HostFuncs {
801 gc,
802 take_refs,
803 make_refs,
804 take_struct,
805 take_eq,
806 take_i31,
807 take_array,
808 typed_base,
809 };
810 (imports, funcs)
811 }
812
813 fn encode_run_body(
815 &self,
816 locals: LocalDecls,
817 bases: WasmEncodingBases,
818 type_ids_to_index: &BTreeMap<TypeId, u32>,
819 encoding_order: &[TypeId],
820 ) -> Function {
821 let mut inhabitable = BTreeMap::new();
822 self.types.inhabitable(&mut inhabitable);
823 let struct_ref_target = self.types.least_rank_inhabitable_struct(&inhabitable);
824
825 let proto_order = self
828 .types
829 .prototype_types(&inhabitable, MAX_INLINE_CONSTRUCTION);
830 let protos: BTreeMap<TypeId, u32> = proto_order
831 .iter()
832 .map(|tid| {
833 let dense = type_ids_to_index[tid] - bases.struct_type_base;
834 (*tid, bases.locals.proto(dense))
835 })
836 .collect();
837
838 let ctx = EmitCtx {
839 types: &self.types,
840 struct_ref_target,
841 protos: &protos,
842 type_ids_to_index,
843 bases,
844 encoding_order,
845 };
846
847 let mut func = Function::new(locals.decls);
848 func.instruction(&Instruction::Loop(BlockType::Empty));
849
850 for tid in &proto_order {
855 emit_new(*tid, &mut func, ctx);
856 func.instruction(&Instruction::LocalSet(protos[tid]));
857 }
858 for op in &self.ops {
859 op.encode(&mut func, ctx);
860 }
861 func.instruction(&Instruction::Br(0));
862 func.instruction(&Instruction::End);
863 func.instruction(&Instruction::End);
864 func
865 }
866
867 pub fn fixup(&mut self, encoding_order_grouped: &mut Vec<(RecGroupId, Vec<TypeId>)>) {
879 self.limits.fixup_limits();
880 self.types.fixup_types(&self.limits, encoding_order_grouped);
881 let encoding_order: Vec<TypeId> = encoding_order_grouped
882 .iter()
883 .flat_map(|(_, members)| members.iter().copied())
884 .collect();
885
886 let mut new_ops = Vec::with_capacity(self.ops.len());
887 let mut stack: Vec<StackType> = Vec::new();
888 let num_types = u32::try_from(self.types.type_defs.len()).unwrap();
889
890 let mut struct_type_indices = Vec::new();
893 let mut array_type_indices = Vec::new();
894 for (i, tid) in encoding_order.iter().enumerate() {
895 let i = u32::try_from(i).unwrap();
896 match self.types.type_defs.get(tid) {
897 Some(def) if def.composite_type.is_array() => array_type_indices.push(i),
898 Some(_) => struct_type_indices.push(i),
899 None => {}
900 }
901 }
902
903 let mut operand_types = Vec::new();
904 for op in &self.ops {
905 let Some(op) = op.fixup_immediates(
906 &self.limits,
907 num_types,
908 &struct_type_indices,
909 &array_type_indices,
910 ) else {
911 continue;
912 };
913 let op = StackType::fixup_cast(op, &self.types, &encoding_order);
914
915 debug_assert!(operand_types.is_empty());
916 op.operand_types(&mut operand_types);
917 for ty in operand_types.drain(..) {
918 StackType::fixup_operand(
919 ty,
920 &mut stack,
921 &mut new_ops,
922 num_types,
923 &self.types,
924 &encoding_order,
925 );
926 }
927
928 let mut result_types = Vec::new();
930 StackType::emit(op, &mut stack, &mut new_ops, num_types, &mut result_types);
931 }
932
933 for _ in 0..stack.len() {
935 new_ops.push(GcOp::Drop);
936 }
937
938 log::trace!("ops after fixup: {new_ops:#?}");
939 self.ops = new_ops;
940 }
941
942 pub fn pop(&mut self) -> bool {
947 self.ops.pop().is_some()
948 }
949}
950
951macro_rules! for_each_gc_op {
952 ( $mac:ident ) => {
953 $mac! {
954 #[operands([])]
955 #[results([ExternRef, ExternRef, ExternRef])]
956 Gc,
957
958 #[operands([])]
959 #[results([ExternRef, ExternRef, ExternRef])]
960 MakeRefs,
961
962 #[operands([Some(ExternRef), Some(ExternRef), Some(ExternRef)])]
963 #[results([])]
964 TakeRefs,
965
966 #[operands([])]
967 #[results([ExternRef])]
968 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
969 elem_index = elem_index % (limits.table_size + 1);
972 })]
973 TableGet { elem_index: u32 },
974
975 #[operands([Some(ExternRef)])]
976 #[results([])]
977 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
978 elem_index = elem_index % (limits.table_size + 1);
981 })]
982 TableSet { elem_index: u32 },
983
984 #[operands([])]
985 #[results([ExternRef])]
986 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
987 global_index = global_index.checked_rem(limits.num_globals)?;
988 })]
989 GlobalGet { global_index: u32 },
990
991 #[operands([Some(ExternRef)])]
992 #[results([])]
993 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
994 global_index = global_index.checked_rem(limits.num_globals)?;
995 })]
996 GlobalSet { global_index: u32 },
997
998 #[operands([])]
999 #[results([ExternRef])]
1000 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1001 local_index = local_index.checked_rem(limits.num_params)?;
1002 })]
1003 LocalGet { local_index: u32 },
1004
1005 #[operands([Some(ExternRef)])]
1006 #[results([])]
1007 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1008 local_index = local_index.checked_rem(limits.num_params)?;
1009 })]
1010 LocalSet { local_index: u32 },
1011
1012 #[operands([])]
1013 #[results([Struct(Some(type_index))])]
1014 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1015 type_index = pick_type_index(struct_type_indices, type_index)?;
1016 })]
1017 StructNew { type_index: u32 },
1018
1019 #[operands([])]
1020 #[results([Struct(Some(type_index))])]
1021 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1022 type_index = pick_type_index(struct_type_indices, type_index)?;
1023 })]
1024 StructNewDefault { type_index: u32 },
1025
1026 #[operands([Some(Struct(None))])]
1027 #[results([])]
1028 TakeStructCall,
1029
1030 #[operands([Some(Struct(Some(type_index)))])]
1031 #[results([])]
1032 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1033 type_index = pick_type_index(struct_type_indices, type_index)?;
1034 })]
1035 TakeTypedStructCall { type_index: u32 },
1036
1037 #[operands([Some(Struct(None))])]
1038 #[results([])]
1039 StructLocalSet,
1040
1041 #[operands([])]
1042 #[results([Struct(None)])]
1043 StructLocalGet,
1044
1045 #[operands([Some(Struct(Some(type_index)))])]
1046 #[results([])]
1047 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1048 type_index = pick_type_index(struct_type_indices, type_index)?;
1049 })]
1050 TypedStructLocalSet { type_index: u32 },
1051
1052 #[operands([])]
1053 #[results([Struct(Some(type_index))])]
1054 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1055 type_index = pick_type_index(struct_type_indices, type_index)?;
1056 })]
1057 TypedStructLocalGet { type_index: u32 },
1058
1059 #[operands([Some(Struct(None))])]
1060 #[results([])]
1061 StructGlobalSet,
1062
1063 #[operands([])]
1064 #[results([Struct(None)])]
1065 StructGlobalGet,
1066
1067 #[operands([Some(Struct(Some(type_index)))])]
1068 #[results([])]
1069 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1070 type_index = pick_type_index(struct_type_indices, type_index)?;
1071 })]
1072 TypedStructGlobalSet { type_index: u32 },
1073
1074 #[operands([])]
1075 #[results([Struct(Some(type_index))])]
1076 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1077 type_index = pick_type_index(struct_type_indices, type_index)?;
1078 })]
1079 TypedStructGlobalGet { type_index: u32 },
1080
1081 #[operands([Some(Struct(None))])]
1082 #[results([])]
1083 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1084 elem_index = elem_index % (limits.table_size + 1);
1087 })]
1088 StructTableSet { elem_index: u32 },
1089
1090 #[operands([])]
1091 #[results([Struct(None)])]
1092 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1093 elem_index = elem_index % (limits.table_size + 1);
1096 })]
1097 StructTableGet { elem_index: u32 },
1098
1099 #[operands([Some(Struct(Some(type_index)))])]
1100 #[results([])]
1101 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1102 elem_index = elem_index % (limits.table_size + 1);
1105 type_index = pick_type_index(struct_type_indices, type_index)?;
1106 })]
1107 TypedStructTableSet { elem_index: u32, type_index: u32 },
1108
1109 #[operands([])]
1110 #[results([Struct(Some(type_index))])]
1111 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1112 elem_index = elem_index % (limits.table_size + 1);
1115 type_index = pick_type_index(struct_type_indices, type_index)?;
1116 })]
1117 TypedStructTableGet { elem_index: u32, type_index: u32 },
1118
1119 #[operands([None])]
1120 #[results([])]
1121 Drop,
1122
1123 #[operands([])]
1124 #[results([ExternRef])]
1125 NullExtern,
1126
1127 #[operands([])]
1128 #[results([Struct(None)])]
1129 NullStruct,
1130
1131 #[operands([])]
1132 #[results([Eq])]
1133 NullEq,
1134
1135 #[operands([Some(Eq)])]
1136 #[results([])]
1137 TakeEqCall,
1138
1139 #[operands([Some(Eq)])]
1140 #[results([])]
1141 EqLocalSet,
1142
1143 #[operands([])]
1144 #[results([Eq])]
1145 EqLocalGet,
1146
1147 #[operands([Some(Eq)])]
1148 #[results([])]
1149 EqGlobalSet,
1150
1151 #[operands([])]
1152 #[results([Eq])]
1153 EqGlobalGet,
1154
1155 #[operands([Some(Eq)])]
1156 #[results([])]
1157 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1158 elem_index = elem_index % (limits.table_size + 1);
1161 })]
1162 EqTableSet { elem_index: u32 },
1163
1164 #[operands([])]
1165 #[results([Eq])]
1166 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1167 elem_index = elem_index % (limits.table_size + 1);
1170 })]
1171 EqTableGet { elem_index: u32 },
1172
1173 #[operands([])]
1174 #[results([Struct(Some(type_index))])]
1175 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1176 type_index = pick_type_index(struct_type_indices, type_index)?;
1177 })]
1178 NullTypedStruct { type_index: u32 },
1179
1180 #[operands([Some(Struct(Some(sub_type_index)))])]
1181 #[results([Struct(Some(super_type_index))])]
1182 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1183 sub_type_index = pick_type_index(struct_type_indices, sub_type_index)?;
1184 super_type_index = pick_type_index(struct_type_indices, super_type_index)?;
1185 })]
1186 RefCastUpward { sub_type_index: u32, super_type_index: u32 },
1187
1188 #[operands([Some(Struct(Some(super_type_index)))])]
1189 #[results([Struct(Some(sub_type_index))])]
1190 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1191 sub_type_index = pick_type_index(struct_type_indices, sub_type_index)?;
1192 super_type_index = pick_type_index(struct_type_indices, super_type_index)?;
1193 })]
1194 RefCastDownward { sub_type_index: u32, super_type_index: u32 },
1195
1196 #[operands([Some(Struct(Some(type_index)))])]
1197 #[results([])]
1198 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1199 type_index = pick_type_index(struct_type_indices, type_index)?;
1200 })]
1201 StructGet { type_index: u32, field_index: u32 },
1202
1203 #[operands([Some(Struct(Some(type_index)))])]
1204 #[results([])]
1205 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1206 type_index = pick_type_index(struct_type_indices, type_index)?;
1207 })]
1208 StructGetU { type_index: u32, field_index: u32 },
1209
1210 #[operands([Some(Struct(Some(type_index)))])]
1211 #[results([])]
1212 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1213 type_index = pick_type_index(struct_type_indices, type_index)?;
1214 })]
1215 StructSet { type_index: u32, field_index: u32 },
1216
1217 #[operands([])]
1218 #[results([I31])]
1219 NullI31,
1220
1221 #[operands([])]
1222 #[results([I31])]
1223 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1224 })]
1227 RefI31 { value: u32 },
1228
1229 #[operands([Some(I31)])]
1230 #[results([])]
1231 I31LocalSet,
1232
1233 #[operands([])]
1234 #[results([I31])]
1235 I31LocalGet,
1236
1237 #[operands([Some(I31)])]
1238 #[results([])]
1239 I31GlobalSet,
1240
1241 #[operands([])]
1242 #[results([I31])]
1243 I31GlobalGet,
1244
1245 #[operands([Some(I31)])]
1246 #[results([])]
1247 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1248 elem_index = elem_index % (limits.table_size + 1);
1251 })]
1252 I31TableSet { elem_index: u32 },
1253
1254 #[operands([])]
1255 #[results([I31])]
1256 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1257 elem_index = elem_index % (limits.table_size + 1);
1260 })]
1261 I31TableGet { elem_index: u32 },
1262
1263 #[operands([Some(I31)])]
1264 #[results([])]
1265 TakeI31Call,
1266
1267 #[operands([Some(I31)])]
1268 #[results([])]
1269 I31GetS,
1270
1271 #[operands([Some(I31)])]
1272 #[results([])]
1273 I31GetU,
1274
1275 #[operands([Some(Struct(None))])]
1276 #[results([Eq])]
1277 StructRefAsEq,
1278
1279 #[operands([Some(Struct(Some(type_index)))])]
1280 #[results([Eq])]
1281 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1282 type_index = pick_type_index(struct_type_indices, type_index)?;
1283 })]
1284 TypedStructRefAsEq { type_index: u32 },
1285
1286 #[operands([Some(I31)])]
1287 #[results([Eq])]
1288 I31RefAsEq,
1289
1290 #[operands([])]
1291 #[results([Array(Some(type_index))])]
1292 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1293 type_index = pick_type_index(array_type_indices, type_index)?;
1294 })]
1295 ArrayNewDefault { type_index: u32 },
1296
1297 #[operands([])]
1298 #[results([Array(Some(type_index))])]
1299 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1300 type_index = pick_type_index(array_type_indices, type_index)?;
1301 })]
1302 ArrayNew { type_index: u32 },
1303
1304 #[operands([])]
1305 #[results([Array(Some(type_index))])]
1306 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1307 n = n % (limits.array_length + 1);
1308 type_index = pick_type_index(array_type_indices, type_index)?;
1309 })]
1310 ArrayNewFixed { type_index: u32, n: u32 },
1311
1312 #[operands([])]
1313 #[results([Array(None)])]
1314 NullArray,
1315
1316 #[operands([])]
1317 #[results([Array(Some(type_index))])]
1318 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1319 type_index = pick_type_index(array_type_indices, type_index)?;
1320 })]
1321 NullTypedArray { type_index: u32 },
1322
1323 #[operands([Some(Array(None))])]
1324 #[results([])]
1325 TakeArrayCall,
1326
1327 #[operands([Some(Array(Some(type_index)))])]
1328 #[results([])]
1329 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1330 type_index = pick_type_index(array_type_indices, type_index)?;
1331 })]
1332 TakeTypedArrayCall { type_index: u32 },
1333
1334 #[operands([Some(Array(None))])]
1335 #[results([])]
1336 ArrayLocalSet,
1337
1338 #[operands([])]
1339 #[results([Array(None)])]
1340 ArrayLocalGet,
1341
1342 #[operands([Some(Array(Some(type_index)))])]
1343 #[results([])]
1344 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1345 type_index = pick_type_index(array_type_indices, type_index)?;
1346 })]
1347 TypedArrayLocalSet { type_index: u32 },
1348
1349 #[operands([])]
1350 #[results([Array(Some(type_index))])]
1351 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1352 type_index = pick_type_index(array_type_indices, type_index)?;
1353 })]
1354 TypedArrayLocalGet { type_index: u32 },
1355
1356 #[operands([Some(Array(None))])]
1357 #[results([])]
1358 ArrayGlobalSet,
1359
1360 #[operands([])]
1361 #[results([Array(None)])]
1362 ArrayGlobalGet,
1363
1364 #[operands([Some(Array(Some(type_index)))])]
1365 #[results([])]
1366 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1367 type_index = pick_type_index(array_type_indices, type_index)?;
1368 })]
1369 TypedArrayGlobalSet { type_index: u32 },
1370
1371 #[operands([])]
1372 #[results([Array(Some(type_index))])]
1373 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1374 type_index = pick_type_index(array_type_indices, type_index)?;
1375 })]
1376 TypedArrayGlobalGet { type_index: u32 },
1377
1378 #[operands([Some(Array(None))])]
1379 #[results([])]
1380 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1381 elem_index = elem_index % (limits.table_size + 1);
1384 })]
1385 ArrayTableSet { elem_index: u32 },
1386
1387 #[operands([])]
1388 #[results([Array(None)])]
1389 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1390 elem_index = elem_index % (limits.table_size + 1);
1393 })]
1394 ArrayTableGet { elem_index: u32 },
1395
1396 #[operands([Some(Array(Some(type_index)))])]
1397 #[results([])]
1398 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1399 elem_index = elem_index % (limits.table_size + 1);
1402 type_index = pick_type_index(array_type_indices, type_index)?;
1403 })]
1404 TypedArrayTableSet { elem_index: u32, type_index: u32 },
1405
1406 #[operands([])]
1407 #[results([Array(Some(type_index))])]
1408 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1409 elem_index = elem_index % (limits.table_size + 1);
1412 type_index = pick_type_index(array_type_indices, type_index)?;
1413 })]
1414 TypedArrayTableGet { elem_index: u32, type_index: u32 },
1415
1416 #[operands([Some(Array(Some(sub_type_index)))])]
1417 #[results([Array(Some(super_type_index))])]
1418 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1419 sub_type_index = pick_type_index(array_type_indices, sub_type_index)?;
1420 super_type_index = pick_type_index(array_type_indices, super_type_index)?;
1421 })]
1422 ArrayRefCastUpward { sub_type_index: u32, super_type_index: u32 },
1423
1424 #[operands([Some(Array(Some(super_type_index)))])]
1425 #[results([Array(Some(sub_type_index))])]
1426 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1427 sub_type_index = pick_type_index(array_type_indices, sub_type_index)?;
1428 super_type_index = pick_type_index(array_type_indices, super_type_index)?;
1429 })]
1430 ArrayRefCastDownward { sub_type_index: u32, super_type_index: u32 },
1431
1432 #[operands([Some(Array(Some(type_index)))])]
1433 #[results([])]
1434 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1435 index = index % (limits.array_length + 1);
1439 type_index = pick_type_index(array_type_indices, type_index)?;
1440 })]
1441 ArrayGet { type_index: u32, index: u32 },
1442
1443 #[operands([Some(Array(Some(type_index)))])]
1444 #[results([])]
1445 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1446 index = index % (limits.array_length + 1);
1447 type_index = pick_type_index(array_type_indices, type_index)?;
1448 })]
1449 ArrayGetU { type_index: u32, index: u32 },
1450
1451 #[operands([Some(Array(Some(type_index)))])]
1452 #[results([])]
1453 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1454 index = index % (limits.array_length + 1);
1455 type_index = pick_type_index(array_type_indices, type_index)?;
1456 })]
1457 ArraySet { type_index: u32, index: u32 },
1458
1459 #[operands([Some(Array(Some(type_index)))])]
1460 #[results([])]
1461 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1462 offset = offset % (limits.array_length + 1);
1463 len = len % (limits.array_length - offset + 2);
1464 type_index = pick_type_index(array_type_indices, type_index)?;
1465 })]
1466 ArrayFill { type_index: u32, offset: u32, len: u32 },
1467
1468 #[operands([Some(Array(Some(type_index))), Some(Array(Some(type_index)))])]
1469 #[results([])]
1470 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1471 dst_offset = dst_offset % (limits.array_length + 1);
1472 src_offset = src_offset % (limits.array_length + 1);
1473 len = len % (limits.array_length - dst_offset.max(src_offset) + 2);
1474 type_index = pick_type_index(array_type_indices, type_index)?;
1475 })]
1476 ArrayCopy { type_index: u32, dst_offset: u32, src_offset: u32, len: u32 },
1477
1478 #[operands([Some(Array(None))])]
1479 #[results([])]
1480 ArrayLen,
1481
1482 #[operands([Some(Array(None))])]
1483 #[results([Eq])]
1484 ArrayRefAsEq,
1485
1486 #[operands([Some(Array(Some(type_index)))])]
1487 #[results([Eq])]
1488 #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1489 type_index = pick_type_index(array_type_indices, type_index)?;
1490 })]
1491 TypedArrayRefAsEq { type_index: u32 },
1492 }
1493 };
1494}
1495
1496macro_rules! define_gc_op_variants {
1497 (
1498 $(
1499 $( #[$attr:meta] )*
1500 $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
1501 )*
1502 ) => {
1503 #[derive(
1505 Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, mutatis::Mutate,
1506 )]
1507 #[mutatis(default_mutate = false)]
1508 #[allow(missing_docs, reason = "self-describing")]
1509 pub enum GcOp {
1510 $(
1511 $op $( { $( #[mutatis(default_mutate)] $field : $field_ty ),* } )? ,
1512 )*
1513 }
1514 };
1515}
1516for_each_gc_op!(define_gc_op_variants);
1517
1518macro_rules! define_op_names {
1519 (
1520 $(
1521 $( #[$attr:meta] )*
1522 $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
1523 )*
1524 ) => {
1525 #[cfg(test)]
1526 pub(crate) const OP_NAMES: &[&str] = &[
1527 $(stringify!($op)),*
1528 ];
1529 }
1530}
1531for_each_gc_op!(define_op_names);
1532
1533impl GcOp {
1534 #[cfg(test)]
1536 pub(crate) fn name(&self) -> &'static str {
1537 macro_rules! define_gc_op_name {
1538 (
1539 $(
1540 $( #[$attr:meta] )*
1541 $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
1542 )*
1543 ) => {
1544 match self {
1545 $(
1546 Self::$op $( { $($field: _),* } )? => stringify!($op),
1547 )*
1548 }
1549 };
1550 }
1551 for_each_gc_op!(define_gc_op_name)
1552 }
1553
1554 pub(crate) fn operand_types(&self, out: &mut Vec<Option<StackType>>) {
1556 macro_rules! define_gc_op_operand_types {
1557 (
1558 $(
1559 #[operands($operands:expr)]
1560 $( #[$attr:meta] )*
1561 $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
1562 )*
1563 ) => {{
1564 use StackType::*;
1565 match self {
1566 $(
1567 Self::$op $( { $($field),* } )? => {
1568 $(
1569 $(
1570 #[allow(unused, reason = "macro code")]
1571 let $field = *$field;
1572 )*
1573 )?
1574 let operands: [Option<StackType>; _] = $operands;
1575 out.extend(operands);
1576 }
1577 )*
1578 }
1579 }};
1580 }
1581 for_each_gc_op!(define_gc_op_operand_types)
1582 }
1583
1584 pub(crate) fn result_types(&self, out: &mut Vec<StackType>) {
1586 macro_rules! define_gc_op_result_types {
1587 (
1588 $(
1589 #[operands($operands:expr)]
1590 #[results($results:expr)]
1591 $( #[$attr:meta] )*
1592 $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
1593 )*
1594 ) => {{
1595 use StackType::*;
1596 match self {
1597 $(
1598 Self::$op $( { $($field),* } )? => {
1599 $(
1600 $(
1601 #[allow(unused, reason = "macro code")]
1602 let $field = *$field;
1603 )*
1604 )?
1605 let results: [StackType; _] = $results;
1606 out.extend(results);
1607 }
1608 )*
1609 }
1610 }};
1611 }
1612 for_each_gc_op!(define_gc_op_result_types)
1613 }
1614
1615 pub(crate) fn fixup_immediates(
1620 &self,
1621 limits: &GcOpsLimits,
1622 num_types: u32,
1623 struct_type_indices: &[u32],
1624 array_type_indices: &[u32],
1625 ) -> Option<Self> {
1626 macro_rules! define_gc_op_fixup {
1627 (
1628 $(
1629 #[operands($operands:expr)]
1630 #[results($results:expr)]
1631 $( #[fixup(|$limits:ident, $num_types:ident, $structs:ident, $arrays:ident| $fixup:expr)] )?
1632 $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
1633 )*
1634 ) => {{
1635 let _ = (limits, num_types, struct_type_indices, array_type_indices);
1636 match self {
1637 $(
1638 Self::$op $( { $($field),* } )? => {
1639 $(
1640 $(
1641 #[allow(unused_mut, unused_assignments, reason = "macro code")]
1642 let mut $field = *$field;
1643 )*
1644 #[allow(unused_variables, reason = "macro code")]
1645 let $limits = limits;
1646 #[allow(unused_variables, reason = "macro code")]
1647 let $num_types = num_types;
1648 #[allow(unused_variables, reason = "macro code")]
1649 let $structs = struct_type_indices;
1650 #[allow(unused_variables, reason = "macro code")]
1651 let $arrays = array_type_indices;
1652 $fixup;
1653 )?
1654 Some(Self::$op $( { $( $field ),* } )? )
1655 }
1656 )*
1657 }
1658 }};
1659 }
1660 for_each_gc_op!(define_gc_op_fixup)
1661 }
1662
1663 fn encode(&self, func: &mut Function, cx: EmitCtx<'_>) {
1665 let bases = cx.bases;
1666 use Dir::{Get, Set};
1667 use RefKind::{Array, Eq, Extern, I31, Struct, Typed};
1668 use Storage::{Global, Local, Table};
1669 match *self {
1670 Self::Gc => {
1672 func.instruction(&Instruction::Call(bases.funcs.gc));
1673 }
1674 Self::MakeRefs => {
1675 func.instruction(&Instruction::Call(bases.funcs.make_refs));
1676 }
1677 Self::TakeRefs => {
1678 func.instruction(&Instruction::Call(bases.funcs.take_refs));
1679 }
1680 Self::TakeStructCall => {
1681 func.instruction(&Instruction::Call(bases.funcs.take_struct));
1682 }
1683 Self::TakeEqCall => {
1684 func.instruction(&Instruction::Call(bases.funcs.take_eq));
1685 }
1686 Self::TakeArrayCall => {
1687 func.instruction(&Instruction::Call(bases.funcs.take_array));
1688 }
1689 Self::TakeTypedStructCall { type_index } | Self::TakeTypedArrayCall { type_index } => {
1690 func.instruction(&Instruction::Call(bases.funcs.typed(type_index)));
1691 }
1692 Self::TakeI31Call => encode_take_i31(func, bases),
1693
1694 Self::NullExtern => {
1696 func.instruction(&Instruction::RefNull(HeapType::EXTERN));
1697 }
1698 Self::NullStruct => {
1699 func.instruction(&Instruction::RefNull(STRUCT));
1700 }
1701 Self::NullEq => {
1702 func.instruction(&Instruction::RefNull(EQ));
1703 }
1704 Self::NullI31 => {
1705 func.instruction(&Instruction::RefNull(HeapType::I31));
1706 }
1707 Self::NullArray => {
1708 func.instruction(&Instruction::RefNull(ARRAY));
1709 }
1710 Self::NullTypedStruct { type_index } | Self::NullTypedArray { type_index } => {
1711 let ty = HeapType::Concrete(bases.wasm_type(type_index));
1712 func.instruction(&Instruction::RefNull(ty));
1713 }
1714 Self::RefI31 { value } => {
1715 func.instruction(&Instruction::I32Const(value.cast_signed()));
1716 func.instruction(&Instruction::RefI31);
1717 }
1718
1719 Self::StructNew { type_index } => encode_struct_new(func, cx, type_index, false),
1721 Self::StructNewDefault { type_index } => encode_struct_new(func, cx, type_index, true),
1722 Self::ArrayNew { type_index } => encode_array_new(func, cx, type_index),
1723 Self::ArrayNewDefault { type_index } => encode_array_new_default(func, cx, type_index),
1724 Self::ArrayNewFixed { type_index, n } => {
1725 encode_array_new_fixed(func, cx, type_index, n)
1726 }
1727
1728 Self::RefCastUpward {
1731 super_type_index, ..
1732 }
1733 | Self::ArrayRefCastUpward {
1734 super_type_index, ..
1735 } => encode_upcast(func, bases, super_type_index),
1736 Self::RefCastDownward {
1737 sub_type_index,
1738 super_type_index,
1739 }
1740 | Self::ArrayRefCastDownward {
1741 sub_type_index,
1742 super_type_index,
1743 } => encode_downcast(func, bases, sub_type_index, super_type_index),
1744 Self::StructRefAsEq
1745 | Self::TypedStructRefAsEq { .. }
1746 | Self::ArrayRefAsEq
1747 | Self::TypedArrayRefAsEq { .. }
1748 | Self::I31RefAsEq => {}
1749
1750 Self::StructGet {
1752 type_index,
1753 field_index,
1754 } => encode_struct_get(func, cx, type_index, field_index, false),
1755 Self::StructGetU {
1756 type_index,
1757 field_index,
1758 } => encode_struct_get(func, cx, type_index, field_index, true),
1759 Self::StructSet {
1760 type_index,
1761 field_index,
1762 } => encode_struct_set(func, cx, type_index, field_index),
1763 Self::ArrayGet { type_index, index } => {
1764 encode_array_get(func, cx, type_index, index, false)
1765 }
1766 Self::ArrayGetU { type_index, index } => {
1767 encode_array_get(func, cx, type_index, index, true)
1768 }
1769 Self::ArraySet { type_index, index } => encode_array_set(func, cx, type_index, index),
1770 Self::ArrayFill {
1771 type_index,
1772 offset,
1773 len,
1774 } => encode_array_fill(func, cx, type_index, offset, len),
1775 Self::ArrayCopy {
1776 type_index,
1777 dst_offset,
1778 src_offset,
1779 len,
1780 } => encode_array_copy(func, cx, type_index, dst_offset, src_offset, len),
1781 Self::ArrayLen => encode_array_len(func, bases),
1782 Self::I31GetS => encode_i31_get(func, bases, true),
1783 Self::I31GetU => encode_i31_get(func, bases, false),
1784
1785 Self::Drop => {
1786 func.instruction(&Instruction::Drop);
1787 }
1788
1789 Self::LocalGet { local_index } => {
1791 encode_root(func, bases, Extern { slot: local_index }, Local, Get)
1792 }
1793 Self::LocalSet { local_index } => {
1794 encode_root(func, bases, Extern { slot: local_index }, Local, Set)
1795 }
1796 Self::GlobalGet { global_index } => {
1797 encode_root(func, bases, Extern { slot: global_index }, Global, Get)
1798 }
1799 Self::GlobalSet { global_index } => {
1800 encode_root(func, bases, Extern { slot: global_index }, Global, Set)
1801 }
1802 Self::TableGet { elem_index } => {
1803 encode_root(func, bases, Extern { slot: 0 }, Table(elem_index), Get)
1804 }
1805 Self::TableSet { elem_index } => {
1806 encode_root(func, bases, Extern { slot: 0 }, Table(elem_index), Set)
1807 }
1808
1809 Self::StructLocalGet => encode_root(func, bases, Struct, Local, Get),
1810 Self::StructLocalSet => encode_root(func, bases, Struct, Local, Set),
1811 Self::StructGlobalGet => encode_root(func, bases, Struct, Global, Get),
1812 Self::StructGlobalSet => encode_root(func, bases, Struct, Global, Set),
1813 Self::StructTableGet { elem_index } => {
1814 encode_root(func, bases, Struct, Table(elem_index), Get)
1815 }
1816 Self::StructTableSet { elem_index } => {
1817 encode_root(func, bases, Struct, Table(elem_index), Set)
1818 }
1819
1820 Self::EqLocalGet => encode_root(func, bases, Eq, Local, Get),
1821 Self::EqLocalSet => encode_root(func, bases, Eq, Local, Set),
1822 Self::EqGlobalGet => encode_root(func, bases, Eq, Global, Get),
1823 Self::EqGlobalSet => encode_root(func, bases, Eq, Global, Set),
1824 Self::EqTableGet { elem_index } => encode_root(func, bases, Eq, Table(elem_index), Get),
1825 Self::EqTableSet { elem_index } => encode_root(func, bases, Eq, Table(elem_index), Set),
1826
1827 Self::I31LocalGet => encode_root(func, bases, I31, Local, Get),
1828 Self::I31LocalSet => encode_root(func, bases, I31, Local, Set),
1829 Self::I31GlobalGet => encode_root(func, bases, I31, Global, Get),
1830 Self::I31GlobalSet => encode_root(func, bases, I31, Global, Set),
1831 Self::I31TableGet { elem_index } => {
1832 encode_root(func, bases, I31, Table(elem_index), Get)
1833 }
1834 Self::I31TableSet { elem_index } => {
1835 encode_root(func, bases, I31, Table(elem_index), Set)
1836 }
1837
1838 Self::ArrayLocalGet => encode_root(func, bases, Array, Local, Get),
1839 Self::ArrayLocalSet => encode_root(func, bases, Array, Local, Set),
1840 Self::ArrayGlobalGet => encode_root(func, bases, Array, Global, Get),
1841 Self::ArrayGlobalSet => encode_root(func, bases, Array, Global, Set),
1842 Self::ArrayTableGet { elem_index } => {
1843 encode_root(func, bases, Array, Table(elem_index), Get)
1844 }
1845 Self::ArrayTableSet { elem_index } => {
1846 encode_root(func, bases, Array, Table(elem_index), Set)
1847 }
1848
1849 Self::TypedStructLocalGet { type_index } | Self::TypedArrayLocalGet { type_index } => {
1852 encode_root(func, bases, Typed(type_index), Local, Get)
1853 }
1854 Self::TypedStructLocalSet { type_index } | Self::TypedArrayLocalSet { type_index } => {
1855 encode_root(func, bases, Typed(type_index), Local, Set)
1856 }
1857 Self::TypedStructGlobalGet { type_index }
1858 | Self::TypedArrayGlobalGet { type_index } => {
1859 encode_root(func, bases, Typed(type_index), Global, Get)
1860 }
1861 Self::TypedStructGlobalSet { type_index }
1862 | Self::TypedArrayGlobalSet { type_index } => {
1863 encode_root(func, bases, Typed(type_index), Global, Set)
1864 }
1865 Self::TypedStructTableGet {
1866 elem_index,
1867 type_index,
1868 }
1869 | Self::TypedArrayTableGet {
1870 elem_index,
1871 type_index,
1872 } => encode_root(func, bases, Typed(type_index), Table(elem_index), Get),
1873 Self::TypedStructTableSet {
1874 elem_index,
1875 type_index,
1876 }
1877 | Self::TypedArrayTableSet {
1878 elem_index,
1879 type_index,
1880 } => encode_root(func, bases, Typed(type_index), Table(elem_index), Set),
1881 };
1882 }
1883}
1884
1885fn encode_root(
1887 func: &mut Function,
1888 bases: WasmEncodingBases,
1889 kind: RefKind,
1890 storage: Storage,
1891 dir: Dir,
1892) {
1893 let slots = bases.root_slots(kind);
1894 match (storage, dir) {
1895 (Storage::Local, Dir::Get) => {
1896 func.instruction(&Instruction::LocalGet(slots.local));
1897 }
1898 (Storage::Local, Dir::Set) => {
1899 func.instruction(&Instruction::LocalSet(slots.local));
1900 }
1901 (Storage::Global, Dir::Get) => {
1902 func.instruction(&Instruction::GlobalGet(slots.global));
1903 }
1904 (Storage::Global, Dir::Set) => {
1905 func.instruction(&Instruction::GlobalSet(slots.global));
1906 }
1907 (Storage::Table(elem), Dir::Get) => table_get(func, elem, slots.table),
1908 (Storage::Table(elem), Dir::Set) => table_set_via(func, slots.tmp, elem, slots.table),
1909 }
1910}
1911
1912fn encode_take_i31(func: &mut Function, bases: WasmEncodingBases) {
1915 let i31 = bases.locals.i31ref;
1916 func.instruction(&Instruction::LocalTee(i31));
1917 func.instruction(&Instruction::RefIsNull);
1918 func.instruction(&Instruction::If(BlockType::Empty));
1919 func.instruction(&Instruction::RefNull(HeapType::I31));
1921 func.instruction(&Instruction::I32Const(0));
1922 func.instruction(&Instruction::I32Const(0));
1923 func.instruction(&Instruction::Call(bases.funcs.take_i31));
1924 func.instruction(&Instruction::Else);
1925 func.instruction(&Instruction::LocalGet(i31));
1927 func.instruction(&Instruction::LocalGet(i31));
1928 func.instruction(&Instruction::I31GetS);
1929 func.instruction(&Instruction::LocalGet(i31));
1930 func.instruction(&Instruction::I31GetU);
1931 func.instruction(&Instruction::Call(bases.funcs.take_i31));
1932 func.instruction(&Instruction::End);
1933}
1934
1935fn encode_struct_new(func: &mut Function, cx: EmitCtx<'_>, type_index: u32, prefer_default: bool) {
1938 let fields = struct_fields(cx.types, cx.encoding_order, type_index).unwrap_or(&[]);
1939 let wasm_type = cx.bases.wasm_type(type_index);
1940 if prefer_default && fields.iter().all(|f| f.field_type.is_defaultable()) {
1941 func.instruction(&Instruction::StructNewDefault(wasm_type));
1942 } else {
1943 for field in fields {
1944 field.field_type.emit_default_const(func, cx);
1945 }
1946 func.instruction(&Instruction::StructNew(wasm_type));
1947 }
1948}
1949
1950fn encode_array_new(func: &mut Function, cx: EmitCtx<'_>, type_index: u32) {
1952 if let Some(element) = array_element(cx.types, cx.encoding_order, type_index) {
1953 element.field_type.emit_default_const(func, cx);
1954 }
1955 func.instruction(&Instruction::I32Const(cx.bases.array_length.cast_signed()));
1956 func.instruction(&Instruction::ArrayNew(cx.bases.wasm_type(type_index)));
1957}
1958
1959fn encode_array_new_default(func: &mut Function, cx: EmitCtx<'_>, type_index: u32) {
1962 let wasm_type = cx.bases.wasm_type(type_index);
1963 let len = cx.bases.array_length.cast_signed();
1964 match array_element(cx.types, cx.encoding_order, type_index) {
1965 Some(element) if !element.field_type.is_defaultable() => {
1966 element.field_type.emit_default_const(func, cx);
1967 func.instruction(&Instruction::I32Const(len));
1968 func.instruction(&Instruction::ArrayNew(wasm_type));
1969 }
1970 _ => {
1971 func.instruction(&Instruction::I32Const(len));
1972 func.instruction(&Instruction::ArrayNewDefault(wasm_type));
1973 }
1974 }
1975}
1976
1977fn encode_array_new_fixed(func: &mut Function, cx: EmitCtx<'_>, type_index: u32, n: u32) {
1979 if let Some(element) = array_element(cx.types, cx.encoding_order, type_index) {
1980 for _ in 0..n {
1981 element.field_type.emit_default_const(func, cx);
1982 }
1983 }
1984 func.instruction(&Instruction::ArrayNewFixed {
1985 array_type_index: cx.bases.wasm_type(type_index),
1986 array_size: n,
1987 });
1988}
1989
1990fn encode_upcast(func: &mut Function, bases: WasmEncodingBases, super_type_index: u32) {
1992 let heap_type = HeapType::Concrete(bases.wasm_type(super_type_index));
1993 func.instruction(&Instruction::RefCastNullable(heap_type));
1994}
1995
1996fn encode_downcast(
1998 func: &mut Function,
1999 bases: WasmEncodingBases,
2000 sub_type_index: u32,
2001 super_type_index: u32,
2002) {
2003 let sub_wasm_type = bases.wasm_type(sub_type_index);
2004 let sub_heap_type = HeapType::Concrete(sub_wasm_type);
2005 let temp_local = bases.locals.typed(super_type_index);
2006
2007 func.instruction(&Instruction::LocalTee(temp_local));
2008 func.instruction(&Instruction::RefTestNullable(sub_heap_type));
2009 func.instruction(&Instruction::If(BlockType::Result(ValType::Ref(concrete(
2010 sub_wasm_type,
2011 )))));
2012 func.instruction(&Instruction::LocalGet(temp_local));
2013 func.instruction(&Instruction::RefCastNullable(sub_heap_type));
2014 func.instruction(&Instruction::Else);
2015 func.instruction(&Instruction::RefNull(sub_heap_type));
2016 func.instruction(&Instruction::End);
2017}
2018
2019fn encode_struct_get(
2021 func: &mut Function,
2022 cx: EmitCtx<'_>,
2023 type_index: u32,
2024 field_index: u32,
2025 unsigned: bool,
2026) {
2027 let wasm_type = cx.bases.wasm_type(type_index);
2028 match struct_fields(cx.types, cx.encoding_order, type_index) {
2029 Some(fields) if !fields.is_empty() => {
2030 let typed_local = cx.bases.locals.typed(type_index);
2031 let idx = field_index % u32::try_from(fields.len()).unwrap();
2032 let field_type = fields[usize::try_from(idx).unwrap()].field_type;
2033 let get = struct_get_instruction(wasm_type, idx, field_type, unsigned);
2034 if_non_null(func, typed_local, |func| {
2035 func.instruction(&Instruction::LocalGet(typed_local));
2036 func.instruction(&get);
2037 func.instruction(&Instruction::Drop);
2039 });
2040 }
2041 _ => {
2042 func.instruction(&Instruction::Drop);
2043 }
2044 }
2045}
2046
2047fn encode_struct_set(func: &mut Function, cx: EmitCtx<'_>, type_index: u32, field_index: u32) {
2050 let wasm_type = cx.bases.wasm_type(type_index);
2051 let mutable_field = struct_fields(cx.types, cx.encoding_order, type_index)
2052 .filter(|fields| !fields.is_empty())
2053 .and_then(|fields| {
2054 let len = fields.len();
2055 let start = usize::try_from(field_index).unwrap() % len;
2056 (0..len)
2057 .map(|offset| (start + offset) % len)
2058 .find(|&i| fields[i].mutable)
2059 .map(|i| (u32::try_from(i).unwrap(), fields[i].field_type))
2060 });
2061
2062 match mutable_field {
2063 Some((idx, field_type)) => {
2064 let typed_local = cx.bases.locals.typed(type_index);
2065 if_non_null(func, typed_local, |func| {
2066 func.instruction(&Instruction::LocalGet(typed_local));
2067 field_type.emit_default_const(func, cx);
2068 func.instruction(&Instruction::StructSet {
2069 struct_type_index: wasm_type,
2070 field_index: idx,
2071 });
2072 });
2073 }
2074 None => {
2075 func.instruction(&Instruction::Drop);
2076 }
2077 }
2078}
2079
2080fn encode_array_get(
2082 func: &mut Function,
2083 cx: EmitCtx<'_>,
2084 type_index: u32,
2085 index: u32,
2086 unsigned: bool,
2087) {
2088 let wasm_type = cx.bases.wasm_type(type_index);
2089 let typed_local = cx.bases.locals.typed(type_index);
2090 match array_element(cx.types, cx.encoding_order, type_index) {
2091 Some(element) => {
2092 let get = array_get_instruction(wasm_type, element.field_type, unsigned);
2093 if_non_null(func, typed_local, |func| {
2094 func.instruction(&Instruction::LocalGet(typed_local));
2095 func.instruction(&Instruction::I32Const(index.cast_signed()));
2096 func.instruction(&get);
2097 func.instruction(&Instruction::Drop);
2099 });
2100 }
2101 None => {
2102 func.instruction(&Instruction::Drop);
2103 }
2104 }
2105}
2106
2107fn encode_array_set(func: &mut Function, cx: EmitCtx<'_>, type_index: u32, index: u32) {
2109 let wasm_type = cx.bases.wasm_type(type_index);
2110 let typed_local = cx.bases.locals.typed(type_index);
2111 match array_element(cx.types, cx.encoding_order, type_index) {
2112 Some(element) if element.mutable => {
2113 if_non_null(func, typed_local, |func| {
2114 func.instruction(&Instruction::LocalGet(typed_local));
2115 func.instruction(&Instruction::I32Const(index.cast_signed()));
2116 element.field_type.emit_default_const(func, cx);
2117 func.instruction(&Instruction::ArraySet(wasm_type));
2118 });
2119 }
2120 _ => {
2121 func.instruction(&Instruction::Drop);
2122 }
2123 }
2124}
2125
2126fn encode_array_fill(func: &mut Function, cx: EmitCtx<'_>, type_index: u32, offset: u32, len: u32) {
2128 let wasm_type = cx.bases.wasm_type(type_index);
2129 let typed_local = cx.bases.locals.typed(type_index);
2130 match array_element(cx.types, cx.encoding_order, type_index) {
2131 Some(element) if element.mutable => {
2132 if_non_null(func, typed_local, |func| {
2133 func.instruction(&Instruction::LocalGet(typed_local));
2134 func.instruction(&Instruction::I32Const(offset.cast_signed()));
2135 element.field_type.emit_default_const(func, cx);
2136 func.instruction(&Instruction::I32Const(len.cast_signed()));
2137 func.instruction(&Instruction::ArrayFill(wasm_type));
2138 });
2139 }
2140 _ => {
2141 func.instruction(&Instruction::Drop);
2142 }
2143 }
2144}
2145
2146fn encode_array_copy(
2149 func: &mut Function,
2150 cx: EmitCtx<'_>,
2151 type_index: u32,
2152 dst_offset: u32,
2153 src_offset: u32,
2154 len: u32,
2155) {
2156 let wasm_type = cx.bases.wasm_type(type_index);
2157 let dst_local = cx.bases.locals.typed(type_index);
2158 let src_local = cx.bases.locals.typed2(type_index);
2159 match array_element(cx.types, cx.encoding_order, type_index) {
2160 Some(element) if element.mutable => {
2161 func.instruction(&Instruction::LocalSet(src_local));
2162 func.instruction(&Instruction::LocalSet(dst_local));
2163 func.instruction(&Instruction::LocalGet(dst_local));
2164 func.instruction(&Instruction::RefIsNull);
2165 func.instruction(&Instruction::LocalGet(src_local));
2166 func.instruction(&Instruction::RefIsNull);
2167 func.instruction(&Instruction::I32Or);
2168 func.instruction(&Instruction::If(BlockType::Empty));
2169 func.instruction(&Instruction::Else);
2170 func.instruction(&Instruction::LocalGet(dst_local));
2171 func.instruction(&Instruction::I32Const(dst_offset.cast_signed()));
2172 func.instruction(&Instruction::LocalGet(src_local));
2173 func.instruction(&Instruction::I32Const(src_offset.cast_signed()));
2174 func.instruction(&Instruction::I32Const(len.cast_signed()));
2175 func.instruction(&Instruction::ArrayCopy {
2176 array_type_index_dst: wasm_type,
2177 array_type_index_src: wasm_type,
2178 });
2179 func.instruction(&Instruction::End);
2180 }
2181 _ => {
2182 func.instruction(&Instruction::Drop);
2183 func.instruction(&Instruction::Drop);
2184 }
2185 }
2186}
2187
2188fn encode_array_len(func: &mut Function, bases: WasmEncodingBases) {
2190 let array_local = bases.locals.arrayref;
2191 if_non_null(func, array_local, |func| {
2192 func.instruction(&Instruction::LocalGet(array_local));
2193 func.instruction(&Instruction::ArrayLen);
2194 func.instruction(&Instruction::Drop);
2195 });
2196}
2197
2198fn encode_i31_get(func: &mut Function, bases: WasmEncodingBases, signed: bool) {
2200 let i31_local = bases.locals.i31ref;
2201 let get = if signed {
2202 Instruction::I31GetS
2203 } else {
2204 Instruction::I31GetU
2205 };
2206 if_non_null(func, i31_local, |func| {
2207 func.instruction(&Instruction::LocalGet(i31_local));
2208 func.instruction(&get);
2209 func.instruction(&Instruction::Drop);
2210 });
2211}