1use alloc::{string::String, vec::Vec};
12
13use crate::binary::instr::{DecodedInstr, Instr, decode_instr_sequence_with_offsets};
14use crate::binary::module::Module;
15use crate::error::{ByteOffset, DecodeContext, DecodeErrorKind};
16use crate::types::{
17 BlockType, CodeBody, DataMode, ElemIdx, ElementInit, ElementMode, ExportDesc, FuncIdx,
18 FuncType, GlobalIdx, ImportDesc, LabelIdx, LocalDecl, LocalIdx, MemArg, MemIdx, MemType,
19 Mutability, NumType, RefType, TableIdx, TableType, TypeIdx, ValType,
20};
21use crate::validate;
22use crate::validate::error::{ValidationError, ValidationErrorKind};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27pub struct Reg(pub u32);
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct RegValue {
33 pub reg: Reg,
34 pub ty: ValType,
35}
36
37#[derive(Debug, Clone, PartialEq)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
40pub struct RegModule {
41 pub funcs: Vec<RegFunc>,
42 pub exports: Vec<RegExport>,
43 pub imported_func_count: u32,
46 pub imported_funcs: Vec<RegImport>,
49 pub imported_memories: Vec<RegMemoryImport>,
51 pub imported_globals: Vec<RegGlobalImport>,
53 pub imported_tables: Vec<RegTableImport>,
55 pub start: Option<FuncIdx>,
57 pub memories: Vec<MemType>,
59 pub globals: Vec<RegGlobal>,
61 pub tables: Vec<TableType>,
63 pub elements: Vec<RegElement>,
65 pub types: Vec<FuncType>,
68 pub data: Vec<RegDataSegment>,
70 pub imported_memory_count: u32,
72 pub imported_global_count: u32,
74 pub imported_table_count: u32,
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
80#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
81pub struct RegElement {
82 pub mode: RegElementMode,
83 pub values: Vec<RegElemValue>,
85}
86
87#[derive(Debug, Clone, PartialEq, Eq)]
89#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
90pub enum RegElementMode {
91 Active {
93 table: TableIdx,
94 offset: Vec<RegConstInstr>,
95 },
96 Passive,
98 Dropped,
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
106pub enum RegElemValue {
107 FuncRef(FuncIdx),
108 GlobalGet(GlobalIdx),
110 Null,
111}
112
113#[derive(Debug, Clone, PartialEq)]
115#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
116pub struct RegGlobal {
117 pub ty: ValType,
118 pub mutable: bool,
119 pub init: Vec<RegConstInstr>,
121}
122
123#[derive(Debug, Clone, PartialEq, Eq)]
125#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
126pub struct RegDataSegment {
127 pub mode: RegDataMode,
128 pub bytes: Vec<u8>,
129}
130
131#[derive(Debug, Clone, PartialEq, Eq)]
133#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
134pub enum RegDataMode {
135 Active {
137 memory: MemIdx,
138 offset: Vec<RegConstInstr>,
139 },
140 Passive,
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
148#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
149pub enum RegConstInstr {
150 I32Const(i32),
151 I64Const(i64),
152 F32Const(u32),
153 F64Const(u64),
154 GlobalGet(GlobalIdx),
155 RefNull,
156 RefFunc(FuncIdx),
157 I32Add,
158 I32Sub,
159 I32Mul,
160 I64Add,
161 I64Sub,
162 I64Mul,
163}
164
165#[derive(Debug, Clone, PartialEq, Eq)]
167#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
168pub struct RegImport {
169 pub module: String,
170 pub name: String,
171 pub ty: FuncType,
172}
173
174#[derive(Debug, Clone, PartialEq, Eq)]
176#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
177pub struct RegMemoryImport {
178 pub module: String,
179 pub name: String,
180 pub ty: MemType,
181}
182
183#[derive(Debug, Clone, PartialEq, Eq)]
185#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
186pub struct RegGlobalImport {
187 pub module: String,
188 pub name: String,
189 pub ty: crate::types::GlobalType,
190}
191
192#[derive(Debug, Clone, PartialEq, Eq)]
194#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
195pub struct RegTableImport {
196 pub module: String,
197 pub name: String,
198 pub ty: TableType,
199}
200
201#[derive(Debug, Clone, PartialEq, Eq)]
203#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
204pub struct RegExport {
205 pub name: String,
206 pub desc: RegExportDesc,
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
211#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
212pub enum RegExportDesc {
213 Func(FuncIdx),
214 Table(TableIdx),
215 Mem(MemIdx),
216 Global(GlobalIdx),
217}
218
219#[derive(Debug, Clone, PartialEq)]
221#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
222pub struct RegFunc {
223 pub idx: FuncIdx,
224 pub type_idx: TypeIdx,
225 pub params: Vec<ValType>,
226 pub results: Vec<ValType>,
227 pub locals: Vec<ValType>,
229 pub reg_types: Vec<ValType>,
231 pub blocks: Vec<RegBlock>,
233}
234
235#[derive(Debug, Clone, PartialEq)]
237#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
238pub struct RegBlock {
239 pub label: LabelIdx,
240 pub instrs: Vec<RegInstr>,
241 pub term: RegTerm,
242}
243
244#[derive(Debug, Clone, PartialEq)]
246#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
247pub enum RegTerm {
248 Fallthrough,
250 Br { target_block: u32, values: Vec<Reg> },
252 BrIf {
254 cond: Reg,
255 target_block: u32,
256 values: Vec<Reg>,
257 },
258 BrIfNull {
260 value: Reg,
261 target_block: u32,
262 values: Vec<Reg>,
263 },
264 BrIfNonNull {
267 value: Reg,
268 target_block: u32,
269 values: Vec<Reg>,
270 },
271 IfFork {
273 cond: Reg,
274 then_block: u32,
275 else_block: u32,
276 },
277 BrTable {
280 index: Reg,
281 targets: Vec<u32>,
282 default: u32,
283 values: Vec<Reg>,
284 },
285 Return { values: Vec<Reg> },
287 Trap,
289}
290
291impl RegBlock {
292 pub fn new(label: LabelIdx, instrs: Vec<RegInstr>, term: RegTerm) -> Self {
293 Self {
294 label,
295 instrs,
296 term,
297 }
298 }
299}
300
301#[derive(Debug, Clone, PartialEq)]
303#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
304pub struct RegInstr {
305 pub offset: ByteOffset,
306 pub op: RegOp,
307}
308
309#[derive(Debug, Clone, PartialEq)]
311#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
312pub enum RegOp {
313 LocalGet {
314 dst: Reg,
315 local: LocalIdx,
316 },
317 LocalSet {
318 local: LocalIdx,
319 value: Reg,
320 },
321 LocalTee {
322 local: LocalIdx,
323 value: Reg,
324 },
325 Drop {
326 value: Reg,
327 },
328 I32Const {
329 dst: Reg,
330 value: i32,
331 },
332 I64Const {
333 dst: Reg,
334 value: i64,
335 },
336 F32Const {
337 dst: Reg,
338 value: f32,
339 },
340 F64Const {
341 dst: Reg,
342 value: f64,
343 },
344 Unary {
345 op: UnaryOp,
346 dst: Reg,
347 value: Reg,
348 },
349 Binary {
350 op: BinaryOp,
351 dst: Reg,
352 lhs: Reg,
353 rhs: Reg,
354 },
355 Copy {
359 dst: Reg,
360 src: Reg,
361 },
362 Call {
365 func: FuncIdx,
366 args: Vec<Reg>,
367 results: Vec<Reg>,
368 },
369 Select {
371 dst: Reg,
372 v1: Reg,
373 v2: Reg,
374 cond: Reg,
375 },
376 Load {
378 op: LoadOp,
379 dst: Reg,
380 addr: Reg,
381 memarg: MemArg,
382 },
383 V128Const {
385 dst: Reg,
386 value: [u8; 16],
387 },
388 V128Splat {
390 dst: Reg,
391 shape: LaneShape,
392 src: Reg,
393 },
394 V128ExtractLane {
396 dst: Reg,
397 shape: LaneShape,
398 src: Reg,
399 lane: u8,
400 },
401 V128ReplaceLane {
403 dst: Reg,
404 shape: LaneShape,
405 vec: Reg,
406 scalar: Reg,
407 lane: u8,
408 },
409 V128Binary {
411 shape: LaneShape,
412 kind: V128BinaryKind,
413 dst: Reg,
414 lhs: Reg,
415 rhs: Reg,
416 },
417 V128Not {
419 dst: Reg,
420 src: Reg,
421 },
422 Store {
424 op: StoreOp,
425 addr: Reg,
426 value: Reg,
427 memarg: MemArg,
428 },
429 GlobalGet {
431 dst: Reg,
432 global: GlobalIdx,
433 },
434 GlobalSet {
436 global: GlobalIdx,
437 value: Reg,
438 },
439 MemorySize {
441 dst: Reg,
442 memory: MemIdx,
443 },
444 MemoryGrow {
447 dst: Reg,
448 memory: MemIdx,
449 delta: Reg,
450 },
451 MemoryInit {
453 memory: MemIdx,
454 data: crate::types::DataIdx,
455 dst: Reg,
456 src: Reg,
457 count: Reg,
458 },
459 DataDrop {
461 data: crate::types::DataIdx,
462 },
463 MemoryCopy {
465 dst_memory: MemIdx,
466 src_memory: MemIdx,
467 dst: Reg,
468 src: Reg,
469 count: Reg,
470 },
471 MemoryFill {
473 memory: MemIdx,
474 dst: Reg,
475 value: Reg,
476 count: Reg,
477 },
478 CallIndirect {
480 type_idx: TypeIdx,
481 table: TableIdx,
482 index: Reg,
483 args: Vec<Reg>,
484 results: Vec<Reg>,
485 },
486 CallRef {
488 type_idx: TypeIdx,
489 func: Reg,
490 args: Vec<Reg>,
491 results: Vec<Reg>,
492 },
493 TableGet {
495 dst: Reg,
496 table: TableIdx,
497 index: Reg,
498 },
499 TableSet {
501 table: TableIdx,
502 index: Reg,
503 value: Reg,
504 },
505 TableSize {
507 dst: Reg,
508 table: TableIdx,
509 },
510 TableGrow {
513 dst: Reg,
514 table: TableIdx,
515 value: Reg,
516 delta: Reg,
517 },
518 TableFill {
520 table: TableIdx,
521 dst: Reg,
522 value: Reg,
523 count: Reg,
524 },
525 TableCopy {
527 dst_table: TableIdx,
528 src_table: TableIdx,
529 dst: Reg,
530 src: Reg,
531 count: Reg,
532 },
533 TableInit {
535 table: TableIdx,
536 elem: ElemIdx,
537 dst: Reg,
538 src: Reg,
539 count: Reg,
540 },
541 ElemDrop {
543 elem: ElemIdx,
544 },
545 RefNull {
547 dst: Reg,
548 ref_type: RefType,
549 },
550 RefFunc {
552 dst: Reg,
553 func: FuncIdx,
554 },
555 RefIsNull {
557 dst: Reg,
558 value: Reg,
559 },
560 RefAsNonNull {
562 dst: Reg,
563 value: Reg,
564 },
565}
566
567#[derive(Debug, Clone, Copy, PartialEq, Eq)]
569#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
570pub enum LaneShape {
571 I8x16,
572 I16x8,
573 I32x4,
574 I64x2,
575 F32x4,
576 F64x2,
577}
578
579impl LaneShape {
580 pub fn lane_width(self) -> usize {
582 match self {
583 LaneShape::I8x16 => 1,
584 LaneShape::I16x8 => 2,
585 LaneShape::I32x4 | LaneShape::F32x4 => 4,
586 LaneShape::I64x2 | LaneShape::F64x2 => 8,
587 }
588 }
589
590 pub fn scalar_type(self) -> ValType {
592 match self {
593 LaneShape::I8x16 | LaneShape::I16x8 | LaneShape::I32x4 => ValType::Num(NumType::I32),
594 LaneShape::I64x2 => ValType::Num(NumType::I64),
595 LaneShape::F32x4 => ValType::Num(NumType::F32),
596 LaneShape::F64x2 => ValType::Num(NumType::F64),
597 }
598 }
599}
600
601#[derive(Debug, Clone, Copy, PartialEq, Eq)]
603#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
604pub enum V128BinaryKind {
605 Add,
606 Sub,
607 Mul,
608 Div,
609 And,
610 Or,
611 Xor,
612}
613
614#[derive(Debug, Clone, Copy, PartialEq, Eq)]
616#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
617pub enum LoadOp {
618 I32,
619 I64,
620 F32,
621 F64,
622 I32Load8S,
623 I32Load8U,
624 I32Load16S,
625 I32Load16U,
626 I64Load8S,
627 I64Load8U,
628 I64Load16S,
629 I64Load16U,
630 I64Load32S,
631 I64Load32U,
632 V128,
633}
634
635impl LoadOp {
636 pub fn byte_width(self) -> usize {
638 match self {
639 LoadOp::V128 => 16,
640 LoadOp::I32 | LoadOp::F32 | LoadOp::I64Load32S | LoadOp::I64Load32U => 4,
641 LoadOp::I64 | LoadOp::F64 => 8,
642 LoadOp::I32Load8S | LoadOp::I32Load8U | LoadOp::I64Load8S | LoadOp::I64Load8U => 1,
643 LoadOp::I32Load16S | LoadOp::I32Load16U | LoadOp::I64Load16S | LoadOp::I64Load16U => 2,
644 }
645 }
646
647 pub fn result_type(self) -> ValType {
649 match self {
650 LoadOp::V128 => ValType::Vec(crate::types::VecType::V128),
651 LoadOp::I32
652 | LoadOp::I32Load8S
653 | LoadOp::I32Load8U
654 | LoadOp::I32Load16S
655 | LoadOp::I32Load16U => ValType::Num(NumType::I32),
656 LoadOp::I64
657 | LoadOp::I64Load8S
658 | LoadOp::I64Load8U
659 | LoadOp::I64Load16S
660 | LoadOp::I64Load16U
661 | LoadOp::I64Load32S
662 | LoadOp::I64Load32U => ValType::Num(NumType::I64),
663 LoadOp::F32 => ValType::Num(NumType::F32),
664 LoadOp::F64 => ValType::Num(NumType::F64),
665 }
666 }
667
668 pub fn sign_extend(self) -> bool {
670 matches!(
671 self,
672 LoadOp::I32Load8S
673 | LoadOp::I32Load16S
674 | LoadOp::I64Load8S
675 | LoadOp::I64Load16S
676 | LoadOp::I64Load32S
677 )
678 }
679}
680
681#[derive(Debug, Clone, Copy, PartialEq, Eq)]
683#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
684pub enum StoreOp {
685 I32,
686 I64,
687 F32,
688 F64,
689 I32Store8,
690 I32Store16,
691 I64Store8,
692 I64Store16,
693 I64Store32,
694 V128,
695}
696
697impl StoreOp {
698 pub fn byte_width(self) -> usize {
700 match self {
701 StoreOp::V128 => 16,
702 StoreOp::I32 | StoreOp::F32 | StoreOp::I64Store32 => 4,
703 StoreOp::I64 | StoreOp::F64 => 8,
704 StoreOp::I32Store8 | StoreOp::I64Store8 => 1,
705 StoreOp::I32Store16 | StoreOp::I64Store16 => 2,
706 }
707 }
708
709 pub fn value_type(self) -> ValType {
711 match self {
712 StoreOp::V128 => ValType::Vec(crate::types::VecType::V128),
713 StoreOp::I32 | StoreOp::I32Store8 | StoreOp::I32Store16 => ValType::Num(NumType::I32),
714 StoreOp::I64 | StoreOp::I64Store8 | StoreOp::I64Store16 | StoreOp::I64Store32 => {
715 ValType::Num(NumType::I64)
716 }
717 StoreOp::F32 => ValType::Num(NumType::F32),
718 StoreOp::F64 => ValType::Num(NumType::F64),
719 }
720 }
721}
722
723#[derive(Debug, Clone, Copy, PartialEq, Eq)]
725#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
726pub enum UnaryOp {
727 I32Clz,
728 I32Ctz,
729 I32Popcnt,
730 I32Eqz,
731 I32WrapI64,
732 I32Extend8S,
733 I32Extend16S,
734 I32TruncF32S,
735 I32TruncF32U,
736 I32TruncF64S,
737 I32TruncF64U,
738 F32ConvertI32S,
739 F32ConvertI32U,
740 F64ConvertI32S,
741 F64ConvertI32U,
742 F32Neg,
743 F32Abs,
744 F32Sqrt,
745 F32Ceil,
746 F32Floor,
747 F32Trunc,
748 F32Nearest,
749 I64Clz,
750 I64Ctz,
751 I64Popcnt,
752 I64Eqz,
753 I64ExtendI32S,
754 I64ExtendI32U,
755 I64Extend8S,
756 I64Extend16S,
757 I64Extend32S,
758 I64TruncF32S,
759 I64TruncF32U,
760 I64TruncF64S,
761 I64TruncF64U,
762 F32ConvertI64S,
763 F32ConvertI64U,
764 F64ConvertI64S,
765 F64ConvertI64U,
766 F64Neg,
767 F64Abs,
768 F64Sqrt,
769 F64Ceil,
770 F64Floor,
771 F64Trunc,
772 F64Nearest,
773 F32DemoteF64,
774 F64PromoteF32,
775 I32ReinterpretF32,
776 F32ReinterpretI32,
777 I64ReinterpretF64,
778 F64ReinterpretI64,
779 I32TruncSatF32S,
780 I32TruncSatF32U,
781 I32TruncSatF64S,
782 I32TruncSatF64U,
783 I64TruncSatF32S,
784 I64TruncSatF32U,
785 I64TruncSatF64S,
786 I64TruncSatF64U,
787}
788
789#[derive(Debug, Clone, Copy, PartialEq, Eq)]
791#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
792pub enum BinaryOp {
793 I32Add,
794 I32Sub,
795 I32Mul,
796 I32DivS,
797 I32DivU,
798 I32RemS,
799 I32RemU,
800 I32And,
801 I32Or,
802 I32Xor,
803 I32Shl,
804 I32ShrS,
805 I32ShrU,
806 I32Rotl,
807 I32Rotr,
808 I32Eq,
809 I32Ne,
810 I32LtS,
811 I32LtU,
812 I32GtS,
813 I32GtU,
814 I32LeS,
815 I32LeU,
816 I32GeS,
817 I32GeU,
818 I64Add,
819 I64Sub,
820 I64Mul,
821 I64DivS,
822 I64DivU,
823 I64RemS,
824 I64RemU,
825 I64And,
826 I64Or,
827 I64Xor,
828 I64Shl,
829 I64ShrS,
830 I64ShrU,
831 I64Rotl,
832 I64Rotr,
833 I64Eq,
834 I64Ne,
835 I64LtS,
836 I64LtU,
837 I64GtS,
838 I64GtU,
839 I64LeS,
840 I64LeU,
841 I64GeS,
842 I64GeU,
843 F32Add,
844 F32Sub,
845 F32Mul,
846 F32Div,
847 F32Copysign,
848 F64Copysign,
849 F32Min,
850 F32Max,
851 F64Add,
852 F64Sub,
853 F64Mul,
854 F64Div,
855 F64Min,
856 F64Max,
857 F32Eq,
858 F32Ne,
859 F32Lt,
860 F32Gt,
861 F32Le,
862 F32Ge,
863 F64Eq,
864 F64Ne,
865 F64Lt,
866 F64Gt,
867 F64Le,
868 F64Ge,
869}
870
871impl UnaryOp {
872 fn name(self) -> &'static str {
873 match self {
874 UnaryOp::I32Clz => "i32.clz",
875 UnaryOp::I32Ctz => "i32.ctz",
876 UnaryOp::I32Popcnt => "i32.popcnt",
877 UnaryOp::I32Eqz => "i32.eqz",
878 UnaryOp::I32WrapI64 => "i32.wrap_i64",
879 UnaryOp::I32Extend8S => "i32.extend8_s",
880 UnaryOp::I32Extend16S => "i32.extend16_s",
881 UnaryOp::I32TruncF32S => "i32.trunc_f32_s",
882 UnaryOp::I32TruncF32U => "i32.trunc_f32_u",
883 UnaryOp::I32TruncF64S => "i32.trunc_f64_s",
884 UnaryOp::I32TruncF64U => "i32.trunc_f64_u",
885 UnaryOp::F32ConvertI32S => "f32.convert_i32_s",
886 UnaryOp::F32ConvertI32U => "f32.convert_i32_u",
887 UnaryOp::F64ConvertI32S => "f64.convert_i32_s",
888 UnaryOp::F64ConvertI32U => "f64.convert_i32_u",
889 UnaryOp::F32Neg => "f32.neg",
890 UnaryOp::F32Abs => "f32.abs",
891 UnaryOp::F32Sqrt => "f32.sqrt",
892 UnaryOp::F32Ceil => "f32.ceil",
893 UnaryOp::F32Floor => "f32.floor",
894 UnaryOp::F32Trunc => "f32.trunc",
895 UnaryOp::F32Nearest => "f32.nearest",
896 UnaryOp::I64Clz => "i64.clz",
897 UnaryOp::I64Ctz => "i64.ctz",
898 UnaryOp::I64Popcnt => "i64.popcnt",
899 UnaryOp::I64Eqz => "i64.eqz",
900 UnaryOp::I64ExtendI32S => "i64.extend_i32_s",
901 UnaryOp::I64ExtendI32U => "i64.extend_i32_u",
902 UnaryOp::I64Extend8S => "i64.extend8_s",
903 UnaryOp::I64Extend16S => "i64.extend16_s",
904 UnaryOp::I64Extend32S => "i64.extend32_s",
905 UnaryOp::I64TruncF32S => "i64.trunc_f32_s",
906 UnaryOp::I64TruncF32U => "i64.trunc_f32_u",
907 UnaryOp::I64TruncF64S => "i64.trunc_f64_s",
908 UnaryOp::I64TruncF64U => "i64.trunc_f64_u",
909 UnaryOp::F32ConvertI64S => "f32.convert_i64_s",
910 UnaryOp::F32ConvertI64U => "f32.convert_i64_u",
911 UnaryOp::F64ConvertI64S => "f64.convert_i64_s",
912 UnaryOp::F64ConvertI64U => "f64.convert_i64_u",
913 UnaryOp::F64Neg => "f64.neg",
914 UnaryOp::F64Abs => "f64.abs",
915 UnaryOp::F64Sqrt => "f64.sqrt",
916 UnaryOp::F64Ceil => "f64.ceil",
917 UnaryOp::F64Floor => "f64.floor",
918 UnaryOp::F64Trunc => "f64.trunc",
919 UnaryOp::F64Nearest => "f64.nearest",
920 UnaryOp::F32DemoteF64 => "f32.demote_f64",
921 UnaryOp::F64PromoteF32 => "f64.promote_f32",
922 UnaryOp::I32ReinterpretF32 => "i32.reinterpret_f32",
923 UnaryOp::F32ReinterpretI32 => "f32.reinterpret_i32",
924 UnaryOp::I64ReinterpretF64 => "i64.reinterpret_f64",
925 UnaryOp::F64ReinterpretI64 => "f64.reinterpret_i64",
926 UnaryOp::I32TruncSatF32S => "i32.trunc_sat_f32_s",
927 UnaryOp::I32TruncSatF32U => "i32.trunc_sat_f32_u",
928 UnaryOp::I32TruncSatF64S => "i32.trunc_sat_f64_s",
929 UnaryOp::I32TruncSatF64U => "i32.trunc_sat_f64_u",
930 UnaryOp::I64TruncSatF32S => "i64.trunc_sat_f32_s",
931 UnaryOp::I64TruncSatF32U => "i64.trunc_sat_f32_u",
932 UnaryOp::I64TruncSatF64S => "i64.trunc_sat_f64_s",
933 UnaryOp::I64TruncSatF64U => "i64.trunc_sat_f64_u",
934 }
935 }
936
937 fn input_type(self) -> ValType {
938 match self {
939 UnaryOp::I32Clz
940 | UnaryOp::I32Ctz
941 | UnaryOp::I32Popcnt
942 | UnaryOp::I32Eqz
943 | UnaryOp::I32Extend8S
944 | UnaryOp::I32Extend16S
945 | UnaryOp::I64ExtendI32S
946 | UnaryOp::I64ExtendI32U
947 | UnaryOp::F32ConvertI32S
948 | UnaryOp::F32ConvertI32U
949 | UnaryOp::F64ConvertI32S
950 | UnaryOp::F64ConvertI32U
951 | UnaryOp::F32ReinterpretI32 => ValType::Num(NumType::I32),
952 UnaryOp::I64Clz
953 | UnaryOp::I64Ctz
954 | UnaryOp::I64Popcnt
955 | UnaryOp::I64Eqz
956 | UnaryOp::I32WrapI64
957 | UnaryOp::I64Extend8S
958 | UnaryOp::I64Extend16S
959 | UnaryOp::I64Extend32S
960 | UnaryOp::F32ConvertI64S
961 | UnaryOp::F32ConvertI64U
962 | UnaryOp::F64ConvertI64S
963 | UnaryOp::F64ConvertI64U
964 | UnaryOp::F64ReinterpretI64 => ValType::Num(NumType::I64),
965 UnaryOp::F32Neg
966 | UnaryOp::F32Abs
967 | UnaryOp::F32Sqrt
968 | UnaryOp::F32Ceil
969 | UnaryOp::F32Floor
970 | UnaryOp::F32Trunc
971 | UnaryOp::F32Nearest
972 | UnaryOp::I32TruncF32S
973 | UnaryOp::I32TruncF32U
974 | UnaryOp::I64TruncF32S
975 | UnaryOp::I64TruncF32U
976 | UnaryOp::F64PromoteF32
977 | UnaryOp::I32ReinterpretF32
978 | UnaryOp::I32TruncSatF32S
979 | UnaryOp::I32TruncSatF32U
980 | UnaryOp::I64TruncSatF32S
981 | UnaryOp::I64TruncSatF32U => ValType::Num(NumType::F32),
982 UnaryOp::F64Neg
983 | UnaryOp::F64Abs
984 | UnaryOp::F64Sqrt
985 | UnaryOp::F64Ceil
986 | UnaryOp::F64Floor
987 | UnaryOp::F64Trunc
988 | UnaryOp::F64Nearest
989 | UnaryOp::I32TruncF64S
990 | UnaryOp::I32TruncF64U
991 | UnaryOp::I64TruncF64S
992 | UnaryOp::I64TruncF64U
993 | UnaryOp::F32DemoteF64
994 | UnaryOp::I64ReinterpretF64
995 | UnaryOp::I32TruncSatF64S
996 | UnaryOp::I32TruncSatF64U
997 | UnaryOp::I64TruncSatF64S
998 | UnaryOp::I64TruncSatF64U => ValType::Num(NumType::F64),
999 }
1000 }
1001
1002 fn result_type(self) -> ValType {
1003 match self {
1004 UnaryOp::I32Clz
1005 | UnaryOp::I32Ctz
1006 | UnaryOp::I32Popcnt
1007 | UnaryOp::I32Eqz
1008 | UnaryOp::I32WrapI64
1009 | UnaryOp::I32Extend8S
1010 | UnaryOp::I32Extend16S
1011 | UnaryOp::I32TruncF32S
1012 | UnaryOp::I32TruncF32U
1013 | UnaryOp::I32TruncF64S
1014 | UnaryOp::I32TruncF64U
1015 | UnaryOp::I32TruncSatF32S
1016 | UnaryOp::I32TruncSatF32U
1017 | UnaryOp::I32TruncSatF64S
1018 | UnaryOp::I32TruncSatF64U
1019 | UnaryOp::I32ReinterpretF32 => ValType::Num(NumType::I32),
1020 UnaryOp::I64Clz
1021 | UnaryOp::I64Ctz
1022 | UnaryOp::I64Popcnt
1023 | UnaryOp::I64ExtendI32S
1024 | UnaryOp::I64ExtendI32U
1025 | UnaryOp::I64Extend8S
1026 | UnaryOp::I64Extend16S
1027 | UnaryOp::I64Extend32S
1028 | UnaryOp::I64TruncF32S
1029 | UnaryOp::I64TruncF32U
1030 | UnaryOp::I64TruncF64S
1031 | UnaryOp::I64TruncF64U
1032 | UnaryOp::I64TruncSatF32S
1033 | UnaryOp::I64TruncSatF32U
1034 | UnaryOp::I64TruncSatF64S
1035 | UnaryOp::I64TruncSatF64U
1036 | UnaryOp::I64ReinterpretF64 => ValType::Num(NumType::I64),
1037 UnaryOp::F32Neg
1038 | UnaryOp::F32Abs
1039 | UnaryOp::F32Sqrt
1040 | UnaryOp::F32Ceil
1041 | UnaryOp::F32Floor
1042 | UnaryOp::F32Trunc
1043 | UnaryOp::F32Nearest
1044 | UnaryOp::F32ConvertI32S
1045 | UnaryOp::F32ConvertI32U
1046 | UnaryOp::F32ConvertI64S
1047 | UnaryOp::F32ConvertI64U
1048 | UnaryOp::F32DemoteF64
1049 | UnaryOp::F32ReinterpretI32 => ValType::Num(NumType::F32),
1050 UnaryOp::F64Neg
1051 | UnaryOp::F64Abs
1052 | UnaryOp::F64Sqrt
1053 | UnaryOp::F64Ceil
1054 | UnaryOp::F64Floor
1055 | UnaryOp::F64Trunc
1056 | UnaryOp::F64Nearest
1057 | UnaryOp::F64ConvertI32S
1058 | UnaryOp::F64ConvertI32U
1059 | UnaryOp::F64ConvertI64S
1060 | UnaryOp::F64ConvertI64U
1061 | UnaryOp::F64PromoteF32
1062 | UnaryOp::F64ReinterpretI64 => ValType::Num(NumType::F64),
1063 UnaryOp::I64Eqz => ValType::Num(NumType::I32),
1064 }
1065 }
1066}
1067
1068impl BinaryOp {
1069 fn name(self) -> &'static str {
1070 match self {
1071 BinaryOp::I32Add => "i32.add",
1072 BinaryOp::I32Sub => "i32.sub",
1073 BinaryOp::I32Mul => "i32.mul",
1074 BinaryOp::I32DivS => "i32.div_s",
1075 BinaryOp::I32DivU => "i32.div_u",
1076 BinaryOp::I32RemS => "i32.rem_s",
1077 BinaryOp::I32RemU => "i32.rem_u",
1078 BinaryOp::I32And => "i32.and",
1079 BinaryOp::I32Or => "i32.or",
1080 BinaryOp::I32Xor => "i32.xor",
1081 BinaryOp::I32Shl => "i32.shl",
1082 BinaryOp::I32ShrS => "i32.shr_s",
1083 BinaryOp::I32ShrU => "i32.shr_u",
1084 BinaryOp::I32Rotl => "i32.rotl",
1085 BinaryOp::I32Rotr => "i32.rotr",
1086 BinaryOp::I32Eq => "i32.eq",
1087 BinaryOp::I32Ne => "i32.ne",
1088 BinaryOp::I32LtS => "i32.lt_s",
1089 BinaryOp::I32LtU => "i32.lt_u",
1090 BinaryOp::I32GtS => "i32.gt_s",
1091 BinaryOp::I32GtU => "i32.gt_u",
1092 BinaryOp::I32LeS => "i32.le_s",
1093 BinaryOp::I32LeU => "i32.le_u",
1094 BinaryOp::I32GeS => "i32.ge_s",
1095 BinaryOp::I32GeU => "i32.ge_u",
1096 BinaryOp::I64Add => "i64.add",
1097 BinaryOp::I64Sub => "i64.sub",
1098 BinaryOp::I64Mul => "i64.mul",
1099 BinaryOp::I64DivS => "i64.div_s",
1100 BinaryOp::I64DivU => "i64.div_u",
1101 BinaryOp::I64RemS => "i64.rem_s",
1102 BinaryOp::I64RemU => "i64.rem_u",
1103 BinaryOp::I64And => "i64.and",
1104 BinaryOp::I64Or => "i64.or",
1105 BinaryOp::I64Xor => "i64.xor",
1106 BinaryOp::I64Shl => "i64.shl",
1107 BinaryOp::I64ShrS => "i64.shr_s",
1108 BinaryOp::I64ShrU => "i64.shr_u",
1109 BinaryOp::I64Rotl => "i64.rotl",
1110 BinaryOp::I64Rotr => "i64.rotr",
1111 BinaryOp::I64Eq => "i64.eq",
1112 BinaryOp::I64Ne => "i64.ne",
1113 BinaryOp::I64LtS => "i64.lt_s",
1114 BinaryOp::I64LtU => "i64.lt_u",
1115 BinaryOp::I64GtS => "i64.gt_s",
1116 BinaryOp::I64GtU => "i64.gt_u",
1117 BinaryOp::I64LeS => "i64.le_s",
1118 BinaryOp::I64LeU => "i64.le_u",
1119 BinaryOp::I64GeS => "i64.ge_s",
1120 BinaryOp::I64GeU => "i64.ge_u",
1121 BinaryOp::F32Add => "f32.add",
1122 BinaryOp::F32Copysign => "f32.copysign",
1123 BinaryOp::F64Copysign => "f64.copysign",
1124 BinaryOp::F32Sub => "f32.sub",
1125 BinaryOp::F32Mul => "f32.mul",
1126 BinaryOp::F32Div => "f32.div",
1127 BinaryOp::F32Min => "f32.min",
1128 BinaryOp::F32Max => "f32.max",
1129 BinaryOp::F64Add => "f64.add",
1130 BinaryOp::F64Sub => "f64.sub",
1131 BinaryOp::F64Mul => "f64.mul",
1132 BinaryOp::F64Div => "f64.div",
1133 BinaryOp::F64Min => "f64.min",
1134 BinaryOp::F64Max => "f64.max",
1135 BinaryOp::F32Eq => "f32.eq",
1136 BinaryOp::F32Ne => "f32.ne",
1137 BinaryOp::F32Lt => "f32.lt",
1138 BinaryOp::F32Gt => "f32.gt",
1139 BinaryOp::F32Le => "f32.le",
1140 BinaryOp::F32Ge => "f32.ge",
1141 BinaryOp::F64Eq => "f64.eq",
1142 BinaryOp::F64Ne => "f64.ne",
1143 BinaryOp::F64Lt => "f64.lt",
1144 BinaryOp::F64Gt => "f64.gt",
1145 BinaryOp::F64Le => "f64.le",
1146 BinaryOp::F64Ge => "f64.ge",
1147 }
1148 }
1149
1150 fn input_type(self) -> ValType {
1151 match self {
1152 BinaryOp::I32Add
1153 | BinaryOp::I32Sub
1154 | BinaryOp::I32Mul
1155 | BinaryOp::I32DivS
1156 | BinaryOp::I32DivU
1157 | BinaryOp::I32RemS
1158 | BinaryOp::I32RemU
1159 | BinaryOp::I32And
1160 | BinaryOp::I32Or
1161 | BinaryOp::I32Xor
1162 | BinaryOp::I32Shl
1163 | BinaryOp::I32ShrS
1164 | BinaryOp::I32ShrU
1165 | BinaryOp::I32Rotl
1166 | BinaryOp::I32Rotr
1167 | BinaryOp::I32Eq
1168 | BinaryOp::I32Ne
1169 | BinaryOp::I32LtS
1170 | BinaryOp::I32LtU
1171 | BinaryOp::I32GtS
1172 | BinaryOp::I32GtU
1173 | BinaryOp::I32LeS
1174 | BinaryOp::I32LeU
1175 | BinaryOp::I32GeS
1176 | BinaryOp::I32GeU => ValType::Num(NumType::I32),
1177 BinaryOp::I64Add
1178 | BinaryOp::I64Sub
1179 | BinaryOp::I64Mul
1180 | BinaryOp::I64DivS
1181 | BinaryOp::I64DivU
1182 | BinaryOp::I64RemS
1183 | BinaryOp::I64RemU
1184 | BinaryOp::I64And
1185 | BinaryOp::I64Or
1186 | BinaryOp::I64Xor
1187 | BinaryOp::I64Shl
1188 | BinaryOp::I64ShrS
1189 | BinaryOp::I64ShrU
1190 | BinaryOp::I64Rotl
1191 | BinaryOp::I64Rotr
1192 | BinaryOp::I64Eq
1193 | BinaryOp::I64Ne
1194 | BinaryOp::I64LtS
1195 | BinaryOp::I64LtU
1196 | BinaryOp::I64GtS
1197 | BinaryOp::I64GtU
1198 | BinaryOp::I64LeS
1199 | BinaryOp::I64LeU
1200 | BinaryOp::I64GeS
1201 | BinaryOp::I64GeU => ValType::Num(NumType::I64),
1202 BinaryOp::F32Add
1203 | BinaryOp::F32Sub
1204 | BinaryOp::F32Mul
1205 | BinaryOp::F32Div
1206 | BinaryOp::F32Copysign
1207 | BinaryOp::F32Min
1208 | BinaryOp::F32Max
1209 | BinaryOp::F32Eq
1210 | BinaryOp::F32Ne
1211 | BinaryOp::F32Lt
1212 | BinaryOp::F32Gt
1213 | BinaryOp::F32Le
1214 | BinaryOp::F32Ge => ValType::Num(NumType::F32),
1215 BinaryOp::F64Add
1216 | BinaryOp::F64Sub
1217 | BinaryOp::F64Mul
1218 | BinaryOp::F64Div
1219 | BinaryOp::F64Copysign
1220 | BinaryOp::F64Min
1221 | BinaryOp::F64Max
1222 | BinaryOp::F64Eq
1223 | BinaryOp::F64Ne
1224 | BinaryOp::F64Lt
1225 | BinaryOp::F64Gt
1226 | BinaryOp::F64Le
1227 | BinaryOp::F64Ge => ValType::Num(NumType::F64),
1228 }
1229 }
1230
1231 fn result_type(self) -> ValType {
1232 match self {
1233 BinaryOp::I32Add
1234 | BinaryOp::I32Sub
1235 | BinaryOp::I32Mul
1236 | BinaryOp::I32DivS
1237 | BinaryOp::I32DivU
1238 | BinaryOp::I32RemS
1239 | BinaryOp::I32RemU
1240 | BinaryOp::I32And
1241 | BinaryOp::I32Or
1242 | BinaryOp::I32Xor
1243 | BinaryOp::I32Shl
1244 | BinaryOp::I32ShrS
1245 | BinaryOp::I32ShrU
1246 | BinaryOp::I32Rotl
1247 | BinaryOp::I32Rotr => ValType::Num(NumType::I32),
1248 BinaryOp::I64Add
1249 | BinaryOp::I64Sub
1250 | BinaryOp::I64Mul
1251 | BinaryOp::I64DivS
1252 | BinaryOp::I64DivU
1253 | BinaryOp::I64RemS
1254 | BinaryOp::I64RemU
1255 | BinaryOp::I64And
1256 | BinaryOp::I64Or
1257 | BinaryOp::I64Xor
1258 | BinaryOp::I64Shl
1259 | BinaryOp::I64ShrS
1260 | BinaryOp::I64ShrU
1261 | BinaryOp::I64Rotl
1262 | BinaryOp::I64Rotr => ValType::Num(NumType::I64),
1263 BinaryOp::I32Eq
1264 | BinaryOp::I32Ne
1265 | BinaryOp::I32LtS
1266 | BinaryOp::I32LtU
1267 | BinaryOp::I32GtS
1268 | BinaryOp::I32GtU
1269 | BinaryOp::I32LeS
1270 | BinaryOp::I32LeU
1271 | BinaryOp::I32GeS
1272 | BinaryOp::I32GeU
1273 | BinaryOp::I64Eq
1274 | BinaryOp::I64Ne
1275 | BinaryOp::I64LtS
1276 | BinaryOp::I64LtU
1277 | BinaryOp::I64GtS
1278 | BinaryOp::I64GtU
1279 | BinaryOp::I64LeS
1280 | BinaryOp::I64LeU
1281 | BinaryOp::I64GeS
1282 | BinaryOp::I64GeU => ValType::Num(NumType::I32),
1283 BinaryOp::F32Add
1284 | BinaryOp::F32Sub
1285 | BinaryOp::F32Mul
1286 | BinaryOp::F32Div
1287 | BinaryOp::F32Copysign
1288 | BinaryOp::F32Min
1289 | BinaryOp::F32Max => ValType::Num(NumType::F32),
1290 BinaryOp::F64Add
1291 | BinaryOp::F64Sub
1292 | BinaryOp::F64Mul
1293 | BinaryOp::F64Div
1294 | BinaryOp::F64Copysign
1295 | BinaryOp::F64Min
1296 | BinaryOp::F64Max => ValType::Num(NumType::F64),
1297 BinaryOp::F32Eq
1298 | BinaryOp::F32Ne
1299 | BinaryOp::F32Lt
1300 | BinaryOp::F32Gt
1301 | BinaryOp::F32Le
1302 | BinaryOp::F32Ge
1303 | BinaryOp::F64Eq
1304 | BinaryOp::F64Ne
1305 | BinaryOp::F64Lt
1306 | BinaryOp::F64Gt
1307 | BinaryOp::F64Le
1308 | BinaryOp::F64Ge => ValType::Num(NumType::I32),
1309 }
1310 }
1311}
1312
1313#[derive(Debug, Clone, PartialEq, Eq)]
1315pub struct LowerError {
1316 pub offset: ByteOffset,
1317 pub function: Option<FuncIdx>,
1318 pub kind: LowerErrorKind,
1319}
1320
1321#[derive(Debug, Clone, PartialEq, Eq)]
1323pub enum LowerErrorKind {
1324 Validation(ValidationErrorKind),
1325 Decode {
1326 context: DecodeContext,
1327 kind: DecodeErrorKind,
1328 },
1329 UnsupportedInstr {
1330 op: &'static str,
1331 },
1332 StackUnderflow {
1333 op: &'static str,
1334 expected: ValType,
1335 },
1336 TypeMismatch {
1337 op: &'static str,
1338 expected: ValType,
1339 found: ValType,
1340 },
1341 InvalidLabel {
1342 label: u32,
1343 },
1344 InvalidFunction {
1345 func: u32,
1346 },
1347 InvalidGlobal {
1348 global: u32,
1349 },
1350 InvalidTable {
1351 table: u32,
1352 },
1353 InvalidType {
1354 type_idx: u32,
1355 },
1356 UnexpectedElse,
1357 MissingFunctionEnd,
1358}
1359
1360impl From<ValidationError> for LowerError {
1361 fn from(error: ValidationError) -> Self {
1362 Self {
1363 offset: error.offset,
1364 function: error.function,
1365 kind: LowerErrorKind::Validation(error.kind),
1366 }
1367 }
1368}
1369
1370impl<'a> Module<'a> {
1371 pub fn lower(&self) -> Result<RegModule, LowerError> {
1373 lower_module(self)
1374 }
1375}
1376
1377pub fn lower_module(module: &Module<'_>) -> Result<RegModule, LowerError> {
1379 validate::validate_module(module)?;
1380
1381 let imported_func_count = module.imported_function_count() as u32;
1382 let func_types = func_type_table(module);
1383 let global_types = global_type_table(module);
1384 let table_elem_types: Vec<crate::types::RefType> = module
1385 .imports()
1386 .iter()
1387 .filter_map(|import| match &import.desc {
1388 ImportDesc::Table(table) => Some(table.elem),
1389 _ => None,
1390 })
1391 .chain(module.tables().iter().map(|table| table.elem))
1392 .collect();
1393 let tables = ModuleTables {
1394 func_types: &func_types,
1395 global_types: &global_types,
1396 types: module.types(),
1397 table_elem_types: &table_elem_types,
1398 };
1399 let mut funcs = Vec::new();
1400
1401 for (defined_idx, (type_idx, code)) in module.functions().iter().zip(module.codes()).enumerate()
1402 {
1403 let func_idx = FuncIdx(imported_func_count + defined_idx as u32);
1404 let ty = &module.types()[type_idx.0 as usize];
1405 funcs.push(lower_function(func_idx, *type_idx, ty, code, &tables)?);
1406 }
1407
1408 let imported_memory_count = module
1409 .imports()
1410 .iter()
1411 .filter(|import| matches!(import.desc, ImportDesc::Mem(_)))
1412 .count() as u32;
1413 let imported_global_count = module
1414 .imports()
1415 .iter()
1416 .filter(|import| matches!(import.desc, ImportDesc::Global(_)))
1417 .count() as u32;
1418
1419 let memories = module.memories().to_vec();
1420
1421 let mut globals = Vec::with_capacity(module.globals().len());
1422 for global in module.globals() {
1423 globals.push(RegGlobal {
1424 ty: global.global_type.val_type,
1425 mutable: global.global_type.mutability == Mutability::Var,
1426 init: lower_const_expr(global.init_expr, global.init_offset)?,
1427 });
1428 }
1429
1430 let mut data = Vec::new();
1431 for segment in module.data() {
1432 let mode = match &segment.mode {
1433 DataMode::Active {
1434 memory,
1435 offset_expr,
1436 offset_offset,
1437 } => RegDataMode::Active {
1438 memory: *memory,
1439 offset: lower_const_expr(offset_expr, *offset_offset)?,
1440 },
1441 DataMode::Passive => RegDataMode::Passive,
1442 };
1443 data.push(RegDataSegment {
1444 mode,
1445 bytes: segment.init.to_vec(),
1446 });
1447 }
1448
1449 let imported_table_count = module
1450 .imports()
1451 .iter()
1452 .filter(|import| matches!(import.desc, ImportDesc::Table(_)))
1453 .count() as u32;
1454
1455 let imported_funcs = module
1456 .imports()
1457 .iter()
1458 .filter_map(|import| match import.desc {
1459 ImportDesc::Func(type_idx) => Some(RegImport {
1460 module: import.module.clone(),
1461 name: import.name.clone(),
1462 ty: module.types()[type_idx.0 as usize].clone(),
1463 }),
1464 _ => None,
1465 })
1466 .collect();
1467
1468 let imported_memories = module
1469 .imports()
1470 .iter()
1471 .filter_map(|import| match import.desc {
1472 ImportDesc::Mem(ty) => Some(RegMemoryImport {
1473 module: import.module.clone(),
1474 name: import.name.clone(),
1475 ty,
1476 }),
1477 _ => None,
1478 })
1479 .collect();
1480
1481 let imported_globals = module
1482 .imports()
1483 .iter()
1484 .filter_map(|import| match import.desc {
1485 ImportDesc::Global(ty) => Some(RegGlobalImport {
1486 module: import.module.clone(),
1487 name: import.name.clone(),
1488 ty,
1489 }),
1490 _ => None,
1491 })
1492 .collect();
1493
1494 let imported_tables = module
1495 .imports()
1496 .iter()
1497 .filter_map(|import| match &import.desc {
1498 ImportDesc::Table(ty) => Some(RegTableImport {
1499 module: import.module.clone(),
1500 name: import.name.clone(),
1501 ty: ty.clone(),
1502 }),
1503 _ => None,
1504 })
1505 .collect();
1506
1507 let start = module.start();
1508
1509 let tables = module.tables().to_vec();
1510 let types = module.types().to_vec();
1511
1512 let mut elements = Vec::with_capacity(module.elements().len());
1513 for segment in module.elements() {
1514 let mut values = Vec::new();
1515 match &segment.init {
1516 ElementInit::FuncIndices(funcs) => {
1517 values.extend(funcs.iter().map(|func| RegElemValue::FuncRef(*func)));
1518 }
1519 ElementInit::Expressions(exprs) => {
1520 for expr in exprs {
1521 values.push(lower_element_expr(expr)?);
1522 }
1523 }
1524 }
1525 let mode = match &segment.mode {
1526 ElementMode::Active {
1527 table,
1528 offset_expr,
1529 offset_offset,
1530 } => RegElementMode::Active {
1531 table: *table,
1532 offset: lower_const_expr(offset_expr, *offset_offset)?,
1533 },
1534 ElementMode::Passive => RegElementMode::Passive,
1535 ElementMode::Declarative => RegElementMode::Dropped,
1536 };
1537 elements.push(RegElement { mode, values });
1538 }
1539
1540 let exports = module
1541 .exports()
1542 .iter()
1543 .map(|export| RegExport {
1544 name: export.name.clone(),
1545 desc: match export.desc {
1546 ExportDesc::Func(idx) => RegExportDesc::Func(idx),
1547 ExportDesc::Table(idx) => RegExportDesc::Table(idx),
1548 ExportDesc::Mem(idx) => RegExportDesc::Mem(idx),
1549 ExportDesc::Global(idx) => RegExportDesc::Global(idx),
1550 },
1551 })
1552 .collect();
1553
1554 Ok(RegModule {
1555 funcs,
1556 exports,
1557 imported_func_count,
1558 imported_funcs,
1559 imported_memories,
1560 imported_globals,
1561 imported_tables,
1562 start,
1563 memories,
1564 globals,
1565 tables,
1566 elements,
1567 types,
1568 data,
1569 imported_memory_count,
1570 imported_global_count,
1571 imported_table_count,
1572 })
1573}
1574
1575fn lower_element_expr(expr: &crate::types::ElementExpr<'_>) -> Result<RegElemValue, LowerError> {
1579 let instrs =
1580 decode_instr_sequence_with_offsets(expr.expr, expr.offset).map_err(|error| LowerError {
1581 offset: error.offset,
1582 function: None,
1583 kind: LowerErrorKind::Decode {
1584 context: error.context,
1585 kind: error.kind,
1586 },
1587 })?;
1588 match instrs.as_slice() {
1589 [first, last] if matches!(last.instr, Instr::End) => match first.instr {
1590 Instr::RefFunc(func) => Ok(RegElemValue::FuncRef(func)),
1591 Instr::RefNull(_) => Ok(RegElemValue::Null),
1592 Instr::GlobalGet(global) => Ok(RegElemValue::GlobalGet(global)),
1593 ref other => Err(LowerError {
1594 offset: first.offset,
1595 function: None,
1596 kind: LowerErrorKind::UnsupportedInstr {
1597 op: instr_name(other),
1598 },
1599 }),
1600 },
1601 _ => Err(LowerError {
1602 offset: ByteOffset(expr.offset),
1603 function: None,
1604 kind: LowerErrorKind::UnsupportedInstr {
1605 op: "multi-instruction element expression",
1606 },
1607 }),
1608 }
1609}
1610
1611pub(crate) fn lower_const_expr(
1615 expr: &[u8],
1616 offset: usize,
1617) -> Result<Vec<RegConstInstr>, LowerError> {
1618 let instrs = decode_instr_sequence_with_offsets(expr, offset).map_err(|error| LowerError {
1619 offset: error.offset,
1620 function: None,
1621 kind: LowerErrorKind::Decode {
1622 context: error.context,
1623 kind: error.kind,
1624 },
1625 })?;
1626
1627 let mut lowered = Vec::with_capacity(instrs.len());
1628 for decoded in instrs {
1629 let const_instr = match decoded.instr {
1630 Instr::I32Const(value) => RegConstInstr::I32Const(value),
1631 Instr::I64Const(value) => RegConstInstr::I64Const(value),
1632 Instr::F32Const(value) => RegConstInstr::F32Const(value.to_bits()),
1633 Instr::F64Const(value) => RegConstInstr::F64Const(value.to_bits()),
1634 Instr::GlobalGet(global) => RegConstInstr::GlobalGet(global),
1635 Instr::RefNull(_) => RegConstInstr::RefNull,
1636 Instr::RefFunc(func) => RegConstInstr::RefFunc(func),
1637 Instr::I32Add => RegConstInstr::I32Add,
1638 Instr::I32Sub => RegConstInstr::I32Sub,
1639 Instr::I32Mul => RegConstInstr::I32Mul,
1640 Instr::I64Add => RegConstInstr::I64Add,
1641 Instr::I64Sub => RegConstInstr::I64Sub,
1642 Instr::I64Mul => RegConstInstr::I64Mul,
1643 Instr::End => break,
1644 ref other => {
1645 return Err(LowerError {
1646 offset: decoded.offset,
1647 function: None,
1648 kind: LowerErrorKind::UnsupportedInstr {
1649 op: instr_name(other),
1650 },
1651 });
1652 }
1653 };
1654 lowered.push(const_instr);
1655 }
1656 Ok(lowered)
1657}
1658
1659fn func_type_table<'a, 'm>(module: &'a Module<'m>) -> Vec<&'a FuncType> {
1662 let mut table = Vec::new();
1663 for import in module.imports() {
1664 if let ImportDesc::Func(type_idx) = import.desc {
1665 table.push(&module.types()[type_idx.0 as usize]);
1666 }
1667 }
1668 for type_idx in module.functions() {
1669 table.push(&module.types()[type_idx.0 as usize]);
1670 }
1671 table
1672}
1673
1674fn global_type_table(module: &Module<'_>) -> Vec<ValType> {
1678 module
1679 .imports()
1680 .iter()
1681 .filter_map(|import| match import.desc {
1682 ImportDesc::Global(global) => Some(global.val_type),
1683 _ => None,
1684 })
1685 .chain(
1686 module
1687 .globals()
1688 .iter()
1689 .map(|global| global.global_type.val_type),
1690 )
1691 .collect()
1692}
1693
1694fn lower_function(
1695 func_idx: FuncIdx,
1696 type_idx: TypeIdx,
1697 ty: &FuncType,
1698 code: &CodeBody<'_>,
1699 tables: &ModuleTables<'_>,
1700) -> Result<RegFunc, LowerError> {
1701 let instrs = code
1702 .instructions_with_offsets()
1703 .map_err(|error| LowerError {
1704 offset: error.offset,
1705 function: Some(func_idx),
1706 kind: LowerErrorKind::Decode {
1707 context: error.context,
1708 kind: error.kind,
1709 },
1710 })?;
1711
1712 let locals = local_types(ty, code.locals.as_slice());
1713 let mut builder = FuncBuilder::new(func_idx, type_idx, ty, locals, tables);
1714
1715 for decoded in instrs {
1716 if builder.lower_instr(decoded)? {
1717 return Ok(builder.finish());
1718 }
1719 }
1720
1721 Err(LowerError {
1722 offset: ByteOffset(code.body_offset + code.body.len()),
1723 function: Some(func_idx),
1724 kind: LowerErrorKind::MissingFunctionEnd,
1725 })
1726}
1727
1728fn local_types(ty: &FuncType, locals: &[LocalDecl]) -> Vec<ValType> {
1729 let local_count = locals
1730 .iter()
1731 .map(|local| local.count as usize)
1732 .sum::<usize>();
1733 let mut types = Vec::with_capacity(ty.params.len() + local_count);
1734 types.extend_from_slice(ty.params.as_slice());
1735 for local in locals {
1736 for _ in 0..local.count {
1737 types.push(local.val_type);
1738 }
1739 }
1740 types
1741}
1742
1743struct FuncBuilder<'b> {
1744 func_idx: FuncIdx,
1745 type_idx: TypeIdx,
1746 params: Vec<ValType>,
1747 results: Vec<ValType>,
1748 locals: Vec<ValType>,
1749 stack: Vec<RegValue>,
1750 reg_types: Vec<ValType>,
1751 blocks: Vec<RegBlock>,
1752 current_instrs: Vec<RegInstr>,
1753 tables: &'b ModuleTables<'b>,
1755 label_stack: Vec<LabelFrame>,
1758 pending_branches: Vec<PendingBranch>,
1760 pending_trampolines: Vec<TrampolineReq>,
1763}
1764
1765struct PendingBranch {
1768 block: usize,
1770 frame_pos: usize,
1772 values: Vec<Reg>,
1774 slot: BranchSlot,
1776}
1777
1778struct LoopTarget {
1781 header: u32,
1782 param_regs: Vec<Reg>,
1783}
1784
1785enum RefBranchKind {
1787 OnNull,
1789 OnNonNull,
1792}
1793
1794struct TrampolineReq {
1800 block: usize,
1802 slot: BranchSlot,
1804 param_regs: Vec<Reg>,
1806 values: Vec<Reg>,
1808 header: u32,
1810 offset: ByteOffset,
1812}
1813
1814#[derive(Debug, Clone, Copy)]
1816enum BranchSlot {
1817 Single,
1819 Table(usize),
1822}
1823
1824struct LabelFrame {
1825 #[allow(dead_code)]
1827 label: LabelIdx,
1828 kind: FrameKind,
1830 result_types: Vec<ValType>,
1832 param_types: Vec<ValType>,
1836 param_regs: Vec<Reg>,
1840 height: usize,
1842 unreachable: bool,
1845}
1846
1847fn ref_compatible(found: ValType, expected: ValType) -> bool {
1854 fn kind(ty: &crate::types::RefType) -> u8 {
1855 match ty {
1856 crate::types::RefType::FuncRef => 0,
1857 crate::types::RefType::ExternRef => 1,
1858 crate::types::RefType::Typed { heap, .. } => match heap {
1859 crate::types::HeapType::Func | crate::types::HeapType::Type(_) => 0,
1860 crate::types::HeapType::Extern => 1,
1861 },
1862 }
1863 }
1864 match (found, expected) {
1865 (ValType::Ref(found), ValType::Ref(expected)) => kind(&found) == kind(&expected),
1866 _ => false,
1867 }
1868}
1869
1870struct ModuleTables<'a> {
1876 func_types: &'a [&'a FuncType],
1877 global_types: &'a [ValType],
1878 types: &'a [FuncType],
1879 table_elem_types: &'a [crate::types::RefType],
1880}
1881
1882enum FrameKind {
1884 Block,
1886 Loop { header_block: u32 },
1888 If { cond_block: usize, else_seen: bool },
1891}
1892
1893impl<'b> FuncBuilder<'b> {
1894 fn new(
1895 func_idx: FuncIdx,
1896 type_idx: TypeIdx,
1897 ty: &FuncType,
1898 locals: Vec<ValType>,
1899 tables: &'b ModuleTables<'b>,
1900 ) -> Self {
1901 Self {
1904 func_idx,
1905 type_idx,
1906 params: ty.params.clone(),
1907 results: ty.results.clone(),
1908 locals,
1909 stack: Vec::new(),
1910 reg_types: Vec::new(),
1911 blocks: Vec::new(),
1912 current_instrs: Vec::new(),
1913 label_stack: alloc::vec![LabelFrame {
1914 label: LabelIdx(0),
1915 kind: FrameKind::Block,
1916 result_types: ty.results.clone(),
1917 param_types: Vec::new(),
1918 param_regs: Vec::new(),
1919 height: 0,
1920 unreachable: false,
1921 }],
1922 pending_branches: Vec::new(),
1923 pending_trampolines: Vec::new(),
1924 tables,
1925 }
1926 }
1927
1928 fn finish_block_with_label(&mut self, label: LabelIdx, term: RegTerm) {
1929 let instrs = core::mem::take(&mut self.current_instrs);
1930 self.blocks.push(RegBlock::new(label, instrs, term));
1931 }
1932
1933 fn finish_block(&mut self, term: RegTerm) {
1934 let label = LabelIdx(self.blocks.len() as u32);
1935 self.finish_block_with_label(label, term);
1936 }
1937
1938 fn finish_cond_ref_branch(
1942 &mut self,
1943 offset: ByteOffset,
1944 frame_pos: usize,
1945 loop_header: Option<LoopTarget>,
1946 values: Vec<Reg>,
1947 value_reg: Reg,
1948 kind: RefBranchKind,
1949 ) {
1950 let make_term = |target_block: u32, values: Vec<Reg>| match kind {
1951 RefBranchKind::OnNull => RegTerm::BrIfNull {
1952 value: value_reg,
1953 target_block,
1954 values,
1955 },
1956 RefBranchKind::OnNonNull => RegTerm::BrIfNonNull {
1957 value: value_reg,
1958 target_block,
1959 values,
1960 },
1961 };
1962 let br_block_idx = self.blocks.len();
1963 if let Some(loop_target) = loop_header {
1964 self.pending_trampolines.push(TrampolineReq {
1965 block: br_block_idx,
1966 slot: BranchSlot::Single,
1967 param_regs: loop_target.param_regs,
1968 values: values.clone(),
1969 header: loop_target.header,
1970 offset,
1971 });
1972 self.finish_block(make_term(0, values));
1973 } else {
1974 self.pending_branches.push(PendingBranch {
1975 block: br_block_idx,
1976 frame_pos,
1977 values: values.clone(),
1978 slot: BranchSlot::Single,
1979 });
1980 self.finish_block(make_term(0, values));
1981 }
1982 }
1983
1984 fn finish(mut self) -> RegFunc {
1985 if !self.current_instrs.is_empty() || self.blocks.is_empty() {
1987 self.finish_block(RegTerm::Fallthrough);
1988 }
1989 for req in core::mem::take(&mut self.pending_trampolines) {
1994 let trampoline_idx = self.blocks.len() as u32;
1995 let instrs = req
1996 .param_regs
1997 .iter()
1998 .zip(req.values.iter())
1999 .filter(|(dst, src)| dst != src)
2000 .map(|(dst, src)| RegInstr {
2001 offset: req.offset,
2002 op: RegOp::Copy {
2003 dst: *dst,
2004 src: *src,
2005 },
2006 })
2007 .collect();
2008 self.blocks.push(RegBlock::new(
2009 LabelIdx(trampoline_idx),
2010 instrs,
2011 RegTerm::Br {
2012 target_block: req.header,
2013 values: Vec::new(),
2014 },
2015 ));
2016 match (req.slot, &mut self.blocks[req.block].term) {
2017 (
2018 BranchSlot::Single,
2019 RegTerm::BrIf { target_block, .. }
2020 | RegTerm::BrIfNull { target_block, .. }
2021 | RegTerm::BrIfNonNull { target_block, .. },
2022 ) => {
2023 *target_block = trampoline_idx;
2024 }
2025 (
2026 BranchSlot::Table(slot),
2027 RegTerm::BrTable {
2028 targets, default, ..
2029 },
2030 ) => {
2031 if slot < targets.len() {
2032 targets[slot] = trampoline_idx;
2033 } else {
2034 *default = trampoline_idx;
2035 }
2036 }
2037 (slot, term) => {
2038 unreachable!("trampoline slot {slot:?} on non-branch terminator {term:?}")
2039 }
2040 }
2041 }
2042 RegFunc {
2043 idx: self.func_idx,
2044 type_idx: self.type_idx,
2045 params: self.params,
2046 results: self.results,
2047 locals: self.locals,
2048 reg_types: self.reg_types,
2049 blocks: self.blocks,
2050 }
2051 }
2052 fn lower_instr(&mut self, decoded: DecodedInstr) -> Result<bool, LowerError> {
2054 let offset = decoded.offset;
2055 match decoded.instr {
2056 Instr::LocalGet(local) => {
2057 let ty = self.locals[local.0 as usize];
2058 let dst = self.alloc_reg(ty);
2059 self.stack.push(RegValue { reg: dst, ty });
2060 self.emit(offset, RegOp::LocalGet { dst, local });
2061 }
2062 Instr::LocalSet(local) => {
2063 let expected = self.locals[local.0 as usize];
2064 let value = self.pop_expect(offset, "local.set", expected)?;
2065 self.emit(
2066 offset,
2067 RegOp::LocalSet {
2068 local,
2069 value: value.reg,
2070 },
2071 );
2072 }
2073 Instr::LocalTee(local) => {
2074 let expected = self.locals[local.0 as usize];
2075 let value = self.pop_expect(offset, "local.tee", expected)?;
2076 self.stack.push(value);
2077 self.emit(
2078 offset,
2079 RegOp::LocalTee {
2080 local,
2081 value: value.reg,
2082 },
2083 );
2084 }
2085 Instr::Drop => {
2086 let value = self.pop_any(offset, "drop")?;
2087 self.emit(offset, RegOp::Drop { value: value.reg });
2088 }
2089 Instr::Select => {
2090 let cond = self.pop_expect(offset, "select", ValType::Num(NumType::I32))?;
2091 let v2 = self.pop_any(offset, "select")?;
2095 let v1 = self.pop_expect(offset, "select", v2.ty)?;
2096 let dst = self.alloc_reg(v1.ty);
2097 self.stack.push(RegValue {
2098 reg: dst,
2099 ty: v1.ty,
2100 });
2101 self.emit(
2102 offset,
2103 RegOp::Select {
2104 dst,
2105 v1: v1.reg,
2106 v2: v2.reg,
2107 cond: cond.reg,
2108 },
2109 );
2110 }
2111 Instr::SelectTyped(types) => {
2112 let cond = self.pop_expect(offset, "select", ValType::Num(NumType::I32))?;
2113 let Some(&ty) = types.first() else {
2115 return Err(LowerError {
2116 offset,
2117 function: Some(self.func_idx),
2118 kind: LowerErrorKind::UnsupportedInstr {
2119 op: "select with empty type annotation",
2120 },
2121 });
2122 };
2123 let v2 = self.pop_expect(offset, "select", ty)?;
2124 let v1 = self.pop_expect(offset, "select", ty)?;
2125 let dst = self.alloc_reg(ty);
2126 self.stack.push(RegValue { reg: dst, ty });
2127 self.emit(
2128 offset,
2129 RegOp::Select {
2130 dst,
2131 v1: v1.reg,
2132 v2: v2.reg,
2133 cond: cond.reg,
2134 },
2135 );
2136 }
2137 Instr::I32Const(value) => {
2138 let dst = self.alloc_reg(ValType::Num(NumType::I32));
2139 self.stack.push(RegValue {
2140 reg: dst,
2141 ty: ValType::Num(NumType::I32),
2142 });
2143 self.emit(offset, RegOp::I32Const { dst, value });
2144 }
2145 Instr::I64Const(value) => {
2146 let dst = self.alloc_reg(ValType::Num(NumType::I64));
2147 self.stack.push(RegValue {
2148 reg: dst,
2149 ty: ValType::Num(NumType::I64),
2150 });
2151 self.emit(offset, RegOp::I64Const { dst, value });
2152 }
2153 Instr::F32Const(value) => {
2154 let dst = self.alloc_reg(ValType::Num(NumType::F32));
2155 self.stack.push(RegValue {
2156 reg: dst,
2157 ty: ValType::Num(NumType::F32),
2158 });
2159 self.emit(offset, RegOp::F32Const { dst, value });
2160 }
2161 Instr::F64Const(value) => {
2162 let dst = self.alloc_reg(ValType::Num(NumType::F64));
2163 self.stack.push(RegValue {
2164 reg: dst,
2165 ty: ValType::Num(NumType::F64),
2166 });
2167 self.emit(offset, RegOp::F64Const { dst, value });
2168 }
2169 Instr::Unreachable => {
2170 self.finish_block(RegTerm::Trap);
2171 self.set_unreachable();
2172 }
2173 Instr::Nop => {}
2174 Instr::Block(block_type) => {
2175 self.finish_block(RegTerm::Fallthrough);
2176 self.enter_frame(offset, "block", FrameKind::Block, block_type)?;
2177 }
2178 Instr::Loop(block_type) => {
2179 self.finish_block(RegTerm::Fallthrough);
2180 let header_block = self.blocks.len() as u32;
2184 self.enter_frame(offset, "loop", FrameKind::Loop { header_block }, block_type)?;
2185 }
2186 Instr::If(block_type) => {
2187 let cond = self.pop_expect(offset, "if", ValType::Num(NumType::I32))?;
2188 let cond_block = self.blocks.len();
2189 self.finish_block(RegTerm::IfFork {
2190 cond: cond.reg,
2191 then_block: (cond_block + 1) as u32,
2192 else_block: 0,
2195 });
2196 self.enter_frame(
2197 offset,
2198 "if",
2199 FrameKind::If {
2200 cond_block,
2201 else_seen: false,
2202 },
2203 block_type,
2204 )?;
2205 }
2206 Instr::Else => {
2207 let frame_pos = self.label_stack.len() - 1;
2208 let (cond_block, result_types, frame_height) = match self.label_stack.last() {
2209 Some(LabelFrame {
2210 kind:
2211 FrameKind::If {
2212 cond_block,
2213 else_seen: false,
2214 },
2215 result_types,
2216 height,
2217 ..
2218 }) => (*cond_block, result_types.clone(), *height),
2219 _ => {
2220 return Err(LowerError {
2221 offset,
2222 function: Some(self.func_idx),
2223 kind: LowerErrorKind::UnexpectedElse,
2224 });
2225 }
2226 };
2227 let mut values = Vec::with_capacity(result_types.len());
2231 for &expected in result_types.iter().rev() {
2232 let found = self.pop_expect(offset, "else", expected)?;
2233 values.push(found.reg);
2234 }
2235 values.reverse();
2236 let br_block_idx = self.blocks.len();
2237 self.pending_branches.push(PendingBranch {
2238 block: br_block_idx,
2239 frame_pos,
2240 values: values.clone(),
2241 slot: BranchSlot::Single,
2242 });
2243 self.finish_block(RegTerm::Br {
2244 target_block: 0,
2245 values,
2246 });
2247 let else_start = self.blocks.len() as u32;
2249 if let RegTerm::IfFork { else_block, .. } = &mut self.blocks[cond_block].term {
2250 *else_block = else_start;
2251 }
2252 self.stack.truncate(frame_height);
2255 let (param_regs, param_types) = {
2256 let frame = self.label_stack.last().expect("if frame checked above");
2257 (frame.param_regs.clone(), frame.param_types.clone())
2258 };
2259 for (®, &ty) in param_regs.iter().zip(param_types.iter()) {
2260 self.stack.push(RegValue { reg, ty });
2261 }
2262 let frame = self.label_stack.last_mut().expect("if frame checked above");
2263 frame.unreachable = false;
2264 if let FrameKind::If { else_seen, .. } = &mut frame.kind {
2265 *else_seen = true;
2266 }
2267 }
2268 Instr::End => {
2269 if self.label_stack.len() == 1 {
2273 let values = self.pop_results(offset)?;
2274 self.label_stack.pop();
2275 self.finish_block(RegTerm::Fallthrough);
2280 let continuation_idx = self.blocks.len() as u32;
2281 for pending in core::mem::take(&mut self.pending_branches) {
2282 match &mut self.blocks[pending.block].term {
2283 RegTerm::Br { target_block, .. }
2284 | RegTerm::BrIf { target_block, .. }
2285 | RegTerm::BrIfNull { target_block, .. }
2286 | RegTerm::BrIfNonNull { target_block, .. } => {
2287 *target_block = continuation_idx;
2288 }
2289 RegTerm::BrTable {
2290 targets, default, ..
2291 } => {
2292 if let BranchSlot::Table(slot) = pending.slot {
2293 if slot < targets.len() {
2294 targets[slot] = continuation_idx;
2295 } else {
2296 *default = continuation_idx;
2297 }
2298 }
2299 }
2300 other => unreachable!(
2301 "pending branch block has non-branch terminator: {other:?}"
2302 ),
2303 }
2304 for (dst, src) in values.iter().zip(pending.values.iter()) {
2305 if dst != src {
2306 self.blocks[pending.block].instrs.push(RegInstr {
2307 offset,
2308 op: RegOp::Copy {
2309 dst: *dst,
2310 src: *src,
2311 },
2312 });
2313 }
2314 }
2315 }
2316 self.finish_block(RegTerm::Return { values });
2317 return Ok(true);
2318 }
2319 let result_types = self
2324 .label_stack
2325 .last()
2326 .ok_or(LowerError {
2327 offset,
2328 function: Some(self.func_idx),
2329 kind: LowerErrorKind::MissingFunctionEnd,
2330 })?
2331 .result_types
2332 .clone();
2333 let mut values = Vec::with_capacity(result_types.len());
2334 for &expected in result_types.iter().rev() {
2335 let found = self.pop_expect(offset, "end", expected)?;
2336 values.push(found.reg);
2337 }
2338 values.reverse();
2339 let frame = self
2340 .label_stack
2341 .pop()
2342 .expect("frame presence checked above");
2343 self.stack.truncate(frame.height);
2346 for (®, &ty) in values.iter().zip(frame.result_types.iter()) {
2347 self.stack.push(RegValue { reg, ty });
2348 }
2349 self.finish_block(RegTerm::Fallthrough);
2351 let frame_pos = self.label_stack.len(); let mut else_trampoline: Option<(usize, u32, usize)> = None;
2360 if let FrameKind::If {
2361 cond_block,
2362 else_seen: false,
2363 } = frame.kind
2364 && !frame.param_regs.is_empty()
2365 {
2366 let body_end_idx = self.blocks.len() - 1;
2369 let trampoline_idx = self.blocks.len() as u32;
2370 let instrs = values
2371 .iter()
2372 .zip(frame.param_regs.iter())
2373 .filter(|(dst, src)| dst != src)
2374 .map(|(dst, src)| RegInstr {
2375 offset,
2376 op: RegOp::Copy {
2377 dst: *dst,
2378 src: *src,
2379 },
2380 })
2381 .collect();
2382 self.blocks.push(RegBlock::new(
2383 LabelIdx(trampoline_idx),
2384 instrs,
2385 RegTerm::Fallthrough,
2386 ));
2387 else_trampoline = Some((cond_block, trampoline_idx, body_end_idx));
2388 }
2389 let continuation_idx = self.blocks.len() as u32;
2394 let mut remaining = Vec::with_capacity(self.pending_branches.len());
2395 for pending in core::mem::take(&mut self.pending_branches) {
2396 if pending.frame_pos != frame_pos {
2397 remaining.push(pending);
2398 continue;
2399 }
2400 let term = &mut self.blocks[pending.block].term;
2401 match pending.slot {
2402 BranchSlot::Single => match term {
2403 RegTerm::Br { target_block, .. }
2404 | RegTerm::BrIf { target_block, .. }
2405 | RegTerm::BrIfNull { target_block, .. }
2406 | RegTerm::BrIfNonNull { target_block, .. } => {
2407 *target_block = continuation_idx;
2408 }
2409 other => unreachable!(
2410 "pending branch block has non-branch terminator: {other:?}"
2411 ),
2412 },
2413 BranchSlot::Table(slot) => match term {
2414 RegTerm::BrTable {
2415 targets, default, ..
2416 } => {
2417 if slot < targets.len() {
2418 targets[slot] = continuation_idx;
2419 } else {
2420 *default = continuation_idx;
2421 }
2422 }
2423 other => unreachable!(
2424 "pending branch block has non-branch terminator: {other:?}"
2425 ),
2426 },
2427 }
2428 for (dst, src) in values.iter().zip(pending.values.iter()) {
2432 if dst != src {
2433 self.blocks[pending.block].instrs.push(RegInstr {
2434 offset,
2435 op: RegOp::Copy {
2436 dst: *dst,
2437 src: *src,
2438 },
2439 });
2440 }
2441 }
2442 }
2443 self.pending_branches = remaining;
2444 if let FrameKind::If {
2448 cond_block,
2449 else_seen: false,
2450 } = frame.kind
2451 {
2452 if let RegTerm::IfFork { else_block, .. } = &mut self.blocks[cond_block].term {
2453 *else_block = match else_trampoline {
2454 Some((_, trampoline_idx, _)) => trampoline_idx,
2455 None => continuation_idx,
2456 };
2457 }
2458 if let Some((_, _, body_end_idx)) = else_trampoline {
2459 self.blocks[body_end_idx].term = RegTerm::Br {
2460 target_block: continuation_idx,
2461 values: Vec::new(),
2462 };
2463 }
2464 }
2465 self.finish_block(RegTerm::Fallthrough);
2468 }
2469 Instr::Br(label) => {
2470 let frame_pos = self.label_position(offset, label)?;
2471 let (branch_types, loop_header) = self.branch_types_at(frame_pos);
2475 let mut values = Vec::with_capacity(branch_types.len());
2476 for &expected in branch_types.iter().rev() {
2477 let found = self.pop_expect(offset, "br", expected)?;
2478 values.push(found.reg);
2479 }
2480 values.reverse();
2481 if let Some(loop_target) = loop_header {
2482 self.emit_copies(offset, &loop_target.param_regs, &values);
2485 self.finish_block(RegTerm::Br {
2486 target_block: loop_target.header,
2487 values,
2488 });
2489 } else {
2490 let br_block_idx = self.blocks.len();
2491 self.pending_branches.push(PendingBranch {
2492 block: br_block_idx,
2493 frame_pos,
2494 values: values.clone(),
2495 slot: BranchSlot::Single,
2496 });
2497 self.finish_block(RegTerm::Br {
2498 target_block: 0,
2499 values,
2500 });
2501 }
2502 self.set_unreachable();
2503 }
2504 Instr::BrIf(label) => {
2505 let cond = self.pop_expect(offset, "br_if", ValType::Num(NumType::I32))?;
2506 let frame_pos = self.label_position(offset, label)?;
2507 let (branch_types, loop_header) = self.branch_types_at(frame_pos);
2508 let mut values = Vec::with_capacity(branch_types.len());
2509 for &expected in branch_types.iter().rev() {
2510 let found = self.pop_expect(offset, "br_if", expected)?;
2511 values.push(found.reg);
2512 }
2513 values.reverse();
2514 for (®, &ty) in values.iter().zip(branch_types.iter()) {
2516 self.stack.push(RegValue { reg, ty });
2517 }
2518 if let Some(loop_target) = loop_header {
2519 let br_block_idx = self.blocks.len();
2523 self.pending_trampolines.push(TrampolineReq {
2524 block: br_block_idx,
2525 slot: BranchSlot::Single,
2526 param_regs: loop_target.param_regs,
2527 values: values.clone(),
2528 header: loop_target.header,
2529 offset,
2530 });
2531 self.finish_block(RegTerm::BrIf {
2532 cond: cond.reg,
2533 target_block: 0,
2534 values,
2535 });
2536 } else {
2537 let br_block_idx = self.blocks.len();
2538 self.pending_branches.push(PendingBranch {
2539 block: br_block_idx,
2540 frame_pos,
2541 values: values.clone(),
2542 slot: BranchSlot::Single,
2543 });
2544 self.finish_block(RegTerm::BrIf {
2545 cond: cond.reg,
2546 target_block: 0,
2547 values,
2548 });
2549 }
2550 }
2551 Instr::BrTable { targets, default } => {
2552 let index = self.pop_expect(offset, "br_table", ValType::Num(NumType::I32))?;
2553 let default_pos = self.label_position(offset, default)?;
2556 let (branch_types, _) = self.branch_types_at(default_pos);
2557 let mut values = Vec::with_capacity(branch_types.len());
2558 for &expected in branch_types.iter().rev() {
2559 let found = self.pop_expect(offset, "br_table", expected)?;
2560 values.push(found.reg);
2561 }
2562 values.reverse();
2563 let br_block_idx = self.blocks.len();
2567 let mut target_blocks = Vec::with_capacity(targets.len() + 1);
2568 for (slot, target) in targets.iter().chain(core::iter::once(&default)).enumerate() {
2569 let frame_pos = self.label_position(offset, *target)?;
2570 let (_, loop_header) = self.branch_types_at(frame_pos);
2571 match loop_header {
2572 Some(loop_target) => {
2573 target_blocks.push(0);
2574 self.pending_trampolines.push(TrampolineReq {
2575 block: br_block_idx,
2576 slot: BranchSlot::Table(slot),
2577 param_regs: loop_target.param_regs,
2578 values: values.clone(),
2579 header: loop_target.header,
2580 offset,
2581 });
2582 }
2583 None => {
2584 target_blocks.push(0);
2585 self.pending_branches.push(PendingBranch {
2586 block: br_block_idx,
2587 frame_pos,
2588 values: values.clone(),
2589 slot: BranchSlot::Table(slot),
2590 });
2591 }
2592 }
2593 }
2594 let default_block = target_blocks.pop().expect("default included above");
2595 self.finish_block(RegTerm::BrTable {
2596 index: index.reg,
2597 targets: target_blocks,
2598 default: default_block,
2599 values,
2600 });
2601 self.set_unreachable();
2602 }
2603 Instr::BrOnNull(label) => {
2604 let value = self.pop_any(offset, "br_on_null")?;
2605 let frame_pos = self.label_position(offset, label)?;
2606 let (branch_types, loop_header) = self.branch_types_at(frame_pos);
2607 let mut values = Vec::with_capacity(branch_types.len());
2608 for &expected in branch_types.iter().rev() {
2609 let found = self.pop_expect(offset, "br_on_null", expected)?;
2610 values.push(found.reg);
2611 }
2612 values.reverse();
2613 for (®, &ty) in values.iter().zip(branch_types.iter()) {
2616 self.stack.push(RegValue { reg, ty });
2617 }
2618 self.stack.push(value);
2619 self.finish_cond_ref_branch(
2620 offset,
2621 frame_pos,
2622 loop_header,
2623 values,
2624 value.reg,
2625 RefBranchKind::OnNull,
2626 );
2627 }
2628 Instr::BrOnNonNull(label) => {
2629 let value = self.pop_any(offset, "br_on_non_null")?;
2630 let frame_pos = self.label_position(offset, label)?;
2631 let (branch_types, loop_header) = self.branch_types_at(frame_pos);
2632 let leading_types = match branch_types.split_last() {
2635 Some((_, leading)) => leading,
2636 None => &[][..],
2637 };
2638 let mut values = Vec::with_capacity(branch_types.len());
2639 for &expected in leading_types.iter().rev() {
2640 let found = self.pop_expect(offset, "br_on_non_null", expected)?;
2641 values.push(found.reg);
2642 }
2643 values.reverse();
2644 for (®, &ty) in values.iter().zip(leading_types.iter()) {
2646 self.stack.push(RegValue { reg, ty });
2647 }
2648 values.push(value.reg);
2650 self.finish_cond_ref_branch(
2651 offset,
2652 frame_pos,
2653 loop_header,
2654 values,
2655 value.reg,
2656 RefBranchKind::OnNonNull,
2657 );
2658 }
2659 Instr::Return => {
2660 let values = self.pop_results(offset)?;
2661 self.finish_block(RegTerm::Return { values });
2662 self.set_unreachable();
2666 }
2667 Instr::CallRef(type_idx) => {
2668 let Some(ty) = self.tables.types.get(type_idx.0 as usize) else {
2669 return Err(LowerError {
2670 offset,
2671 function: Some(self.func_idx),
2672 kind: LowerErrorKind::InvalidType {
2673 type_idx: type_idx.0,
2674 },
2675 });
2676 };
2677 let func = self.pop_any(offset, "call_ref")?;
2679 let mut args = Vec::with_capacity(ty.params.len());
2680 for &expected in ty.params.iter().rev() {
2681 let found = self.pop_expect(offset, "call_ref", expected)?;
2682 args.push(found.reg);
2683 }
2684 args.reverse();
2685 let mut results = Vec::with_capacity(ty.results.len());
2686 for &ty in ty.results.iter() {
2687 let dst = self.alloc_reg(ty);
2688 self.stack.push(RegValue { reg: dst, ty });
2689 results.push(dst);
2690 }
2691 self.emit(
2692 offset,
2693 RegOp::CallRef {
2694 type_idx,
2695 func: func.reg,
2696 args,
2697 results,
2698 },
2699 );
2700 }
2701 Instr::Call(func) => {
2702 let Some(callee_ty) = self.tables.func_types.get(func.0 as usize) else {
2703 return Err(LowerError {
2704 offset,
2705 function: Some(self.func_idx),
2706 kind: LowerErrorKind::InvalidFunction { func: func.0 },
2707 });
2708 };
2709 let mut args = Vec::with_capacity(callee_ty.params.len());
2710 for &expected in callee_ty.params.iter().rev() {
2711 let found = self.pop_expect(offset, "call", expected)?;
2712 args.push(found.reg);
2713 }
2714 args.reverse();
2715 let mut results = Vec::with_capacity(callee_ty.results.len());
2716 for &ty in callee_ty.results.iter() {
2717 let dst = self.alloc_reg(ty);
2718 self.stack.push(RegValue { reg: dst, ty });
2719 results.push(dst);
2720 }
2721 self.emit(
2722 offset,
2723 RegOp::Call {
2724 func,
2725 args,
2726 results,
2727 },
2728 );
2729 }
2730 Instr::GlobalGet(global) => {
2731 let ty = self.global_type(offset, global)?;
2732 let dst = self.alloc_reg(ty);
2733 self.stack.push(RegValue { reg: dst, ty });
2734 self.emit(offset, RegOp::GlobalGet { dst, global });
2735 }
2736 Instr::GlobalSet(global) => {
2737 let ty = self.global_type(offset, global)?;
2738 let value = self.pop_expect(offset, "global.set", ty)?;
2739 self.emit(
2740 offset,
2741 RegOp::GlobalSet {
2742 global,
2743 value: value.reg,
2744 },
2745 );
2746 }
2747 Instr::MemorySize(memory) => {
2748 let ty = ValType::Num(NumType::I32);
2749 let dst = self.alloc_reg(ty);
2750 self.stack.push(RegValue { reg: dst, ty });
2751 self.emit(offset, RegOp::MemorySize { dst, memory });
2752 }
2753 Instr::MemoryGrow(memory) => {
2754 let delta = self.pop_expect(offset, "memory.grow", ValType::Num(NumType::I32))?;
2755 let ty = ValType::Num(NumType::I32);
2756 let dst = self.alloc_reg(ty);
2757 self.stack.push(RegValue { reg: dst, ty });
2758 self.emit(
2759 offset,
2760 RegOp::MemoryGrow {
2761 dst,
2762 memory,
2763 delta: delta.reg,
2764 },
2765 );
2766 }
2767 Instr::MemoryInit(data_idx, mem_idx) => {
2768 let count = self.pop_expect(offset, "memory.init", ValType::Num(NumType::I32))?;
2769 let src = self.pop_expect(offset, "memory.init", ValType::Num(NumType::I32))?;
2770 let dst = self.pop_expect(offset, "memory.init", ValType::Num(NumType::I32))?;
2771 self.emit(
2772 offset,
2773 RegOp::MemoryInit {
2774 memory: mem_idx,
2775 data: data_idx,
2776 dst: dst.reg,
2777 src: src.reg,
2778 count: count.reg,
2779 },
2780 );
2781 }
2782 Instr::DataDrop(data_idx) => {
2783 self.emit(offset, RegOp::DataDrop { data: data_idx });
2784 }
2785 Instr::MemoryCopy { dst, src } => {
2786 let count = self.pop_expect(offset, "memory.copy", ValType::Num(NumType::I32))?;
2787 let src_idx = self.pop_expect(offset, "memory.copy", ValType::Num(NumType::I32))?;
2788 let dst_idx = self.pop_expect(offset, "memory.copy", ValType::Num(NumType::I32))?;
2789 self.emit(
2790 offset,
2791 RegOp::MemoryCopy {
2792 dst_memory: dst,
2793 src_memory: src,
2794 dst: dst_idx.reg,
2795 src: src_idx.reg,
2796 count: count.reg,
2797 },
2798 );
2799 }
2800 Instr::MemoryFill(memory) => {
2801 let count = self.pop_expect(offset, "memory.fill", ValType::Num(NumType::I32))?;
2802 let value = self.pop_expect(offset, "memory.fill", ValType::Num(NumType::I32))?;
2803 let dst = self.pop_expect(offset, "memory.fill", ValType::Num(NumType::I32))?;
2804 self.emit(
2805 offset,
2806 RegOp::MemoryFill {
2807 memory,
2808 dst: dst.reg,
2809 value: value.reg,
2810 count: count.reg,
2811 },
2812 );
2813 }
2814 Instr::CallIndirect {
2815 type_idx,
2816 table_idx,
2817 } => {
2818 let Some(ty) = self.tables.types.get(type_idx.0 as usize) else {
2819 return Err(LowerError {
2820 offset,
2821 function: Some(self.func_idx),
2822 kind: LowerErrorKind::InvalidType {
2823 type_idx: type_idx.0,
2824 },
2825 });
2826 };
2827 let index = self.pop_expect(offset, "call_indirect", ValType::Num(NumType::I32))?;
2828 let mut args = Vec::with_capacity(ty.params.len());
2829 for &expected in ty.params.iter().rev() {
2830 let found = self.pop_expect(offset, "call_indirect", expected)?;
2831 args.push(found.reg);
2832 }
2833 args.reverse();
2834 let mut results = Vec::with_capacity(ty.results.len());
2835 for &result_ty in ty.results.iter() {
2836 let dst = self.alloc_reg(result_ty);
2837 self.stack.push(RegValue {
2838 reg: dst,
2839 ty: result_ty,
2840 });
2841 results.push(dst);
2842 }
2843 self.emit(
2844 offset,
2845 RegOp::CallIndirect {
2846 type_idx,
2847 table: table_idx,
2848 index: index.reg,
2849 args,
2850 results,
2851 },
2852 );
2853 }
2854 Instr::TableGet(table) => {
2855 let index = self.pop_expect(offset, "table.get", ValType::Num(NumType::I32))?;
2856 let ty = ValType::Ref(self.table_elem_type(offset, table)?);
2857 let dst = self.alloc_reg(ty);
2858 self.stack.push(RegValue { reg: dst, ty });
2859 self.emit(
2860 offset,
2861 RegOp::TableGet {
2862 dst,
2863 table,
2864 index: index.reg,
2865 },
2866 );
2867 }
2868 Instr::TableSet(table) => {
2869 let ty = ValType::Ref(self.table_elem_type(offset, table)?);
2870 let value = self.pop_expect(offset, "table.set", ty)?;
2871 let index = self.pop_expect(offset, "table.set", ValType::Num(NumType::I32))?;
2872 self.emit(
2873 offset,
2874 RegOp::TableSet {
2875 table,
2876 index: index.reg,
2877 value: value.reg,
2878 },
2879 );
2880 }
2881 Instr::TableSize(table) => {
2882 let ty = ValType::Num(NumType::I32);
2883 let dst = self.alloc_reg(ty);
2884 self.stack.push(RegValue { reg: dst, ty });
2885 self.emit(offset, RegOp::TableSize { dst, table });
2886 }
2887 Instr::TableGrow(table) => {
2888 let delta = self.pop_expect(offset, "table.grow", ValType::Num(NumType::I32))?;
2889 let elem_ty = ValType::Ref(self.table_elem_type(offset, table)?);
2890 let value = self.pop_expect(offset, "table.grow", elem_ty)?;
2891 let ty = ValType::Num(NumType::I32);
2892 let dst = self.alloc_reg(ty);
2893 self.stack.push(RegValue { reg: dst, ty });
2894 self.emit(
2895 offset,
2896 RegOp::TableGrow {
2897 dst,
2898 table,
2899 value: value.reg,
2900 delta: delta.reg,
2901 },
2902 );
2903 }
2904 Instr::TableFill(table) => {
2905 let count = self.pop_expect(offset, "table.fill", ValType::Num(NumType::I32))?;
2906 let elem_ty = ValType::Ref(self.table_elem_type(offset, table)?);
2907 let value = self.pop_expect(offset, "table.fill", elem_ty)?;
2908 let dst_idx = self.pop_expect(offset, "table.fill", ValType::Num(NumType::I32))?;
2909 self.emit(
2910 offset,
2911 RegOp::TableFill {
2912 table,
2913 dst: dst_idx.reg,
2914 value: value.reg,
2915 count: count.reg,
2916 },
2917 );
2918 }
2919 Instr::TableCopy { dst, src } => {
2920 let count = self.pop_expect(offset, "table.copy", ValType::Num(NumType::I32))?;
2921 let src_idx = self.pop_expect(offset, "table.copy", ValType::Num(NumType::I32))?;
2922 let dst_idx = self.pop_expect(offset, "table.copy", ValType::Num(NumType::I32))?;
2923 self.emit(
2924 offset,
2925 RegOp::TableCopy {
2926 dst_table: dst,
2927 src_table: src,
2928 dst: dst_idx.reg,
2929 src: src_idx.reg,
2930 count: count.reg,
2931 },
2932 );
2933 }
2934 Instr::TableInit {
2935 elem_idx,
2936 table_idx,
2937 } => {
2938 let count = self.pop_expect(offset, "table.init", ValType::Num(NumType::I32))?;
2939 let src = self.pop_expect(offset, "table.init", ValType::Num(NumType::I32))?;
2940 let dst = self.pop_expect(offset, "table.init", ValType::Num(NumType::I32))?;
2941 self.emit(
2942 offset,
2943 RegOp::TableInit {
2944 table: table_idx,
2945 elem: elem_idx,
2946 dst: dst.reg,
2947 src: src.reg,
2948 count: count.reg,
2949 },
2950 );
2951 }
2952 Instr::ElemDrop(elem) => {
2953 self.emit(offset, RegOp::ElemDrop { elem });
2954 }
2955 Instr::RefNull(ref_type) => {
2956 let ty = ValType::Ref(ref_type);
2957 let dst = self.alloc_reg(ty);
2958 self.stack.push(RegValue { reg: dst, ty });
2959 self.emit(offset, RegOp::RefNull { dst, ref_type });
2960 }
2961 Instr::RefFunc(func) => {
2962 let ty = ValType::Ref(crate::types::RefType::FuncRef);
2965 let dst = self.alloc_reg(ty);
2966 self.stack.push(RegValue { reg: dst, ty });
2967 self.emit(offset, RegOp::RefFunc { dst, func });
2968 }
2969 Instr::RefIsNull => {
2970 let value = self.pop_any(offset, "ref.is_null")?;
2971 let ty = ValType::Num(NumType::I32);
2972 let dst = self.alloc_reg(ty);
2973 self.stack.push(RegValue { reg: dst, ty });
2974 self.emit(
2975 offset,
2976 RegOp::RefIsNull {
2977 dst,
2978 value: value.reg,
2979 },
2980 );
2981 }
2982 Instr::RefAsNonNull => {
2983 let value = self.pop_any(offset, "ref.as_non_null")?;
2984 let ty = match value.ty {
2988 ValType::Ref(_) => value.ty,
2989 _ => ValType::Ref(crate::types::RefType::FuncRef),
2990 };
2991 let dst = self.alloc_reg(ty);
2992 self.stack.push(RegValue { reg: dst, ty });
2993 self.emit(
2994 offset,
2995 RegOp::RefAsNonNull {
2996 dst,
2997 value: value.reg,
2998 },
2999 );
3000 }
3001 Instr::V128Const(value) => {
3002 let ty = ValType::Vec(crate::types::VecType::V128);
3003 let dst = self.alloc_reg(ty);
3004 self.stack.push(RegValue { reg: dst, ty });
3005 self.emit(offset, RegOp::V128Const { dst, value });
3006 }
3007 Instr::I8x16Splat => self.lower_splat(offset, LaneShape::I8x16)?,
3008 Instr::I16x8Splat => self.lower_splat(offset, LaneShape::I16x8)?,
3009 Instr::I32x4Splat => self.lower_splat(offset, LaneShape::I32x4)?,
3010 Instr::I64x2Splat => self.lower_splat(offset, LaneShape::I64x2)?,
3011 Instr::F32x4Splat => self.lower_splat(offset, LaneShape::F32x4)?,
3012 Instr::F64x2Splat => self.lower_splat(offset, LaneShape::F64x2)?,
3013 Instr::I32x4ExtractLane(lane) => {
3014 self.lower_extract_lane(offset, LaneShape::I32x4, lane)?
3015 }
3016 Instr::F32x4ExtractLane(lane) => {
3017 self.lower_extract_lane(offset, LaneShape::F32x4, lane)?
3018 }
3019 Instr::I32x4ReplaceLane(lane) => {
3020 self.lower_replace_lane(offset, LaneShape::I32x4, lane)?
3021 }
3022 Instr::F32x4ReplaceLane(lane) => {
3023 self.lower_replace_lane(offset, LaneShape::F32x4, lane)?
3024 }
3025 Instr::V128Not => {
3026 let src = self.pop_expect(
3027 offset,
3028 "v128.not",
3029 ValType::Vec(crate::types::VecType::V128),
3030 )?;
3031 let ty = ValType::Vec(crate::types::VecType::V128);
3032 let dst = self.alloc_reg(ty);
3033 self.stack.push(RegValue { reg: dst, ty });
3034 self.emit(offset, RegOp::V128Not { dst, src: src.reg });
3035 }
3036 Instr::V128And => {
3037 self.lower_v128_binary(offset, LaneShape::I8x16, V128BinaryKind::And)?
3038 }
3039 Instr::V128Or => {
3040 self.lower_v128_binary(offset, LaneShape::I8x16, V128BinaryKind::Or)?
3041 }
3042 Instr::V128Xor => {
3043 self.lower_v128_binary(offset, LaneShape::I8x16, V128BinaryKind::Xor)?
3044 }
3045 Instr::I8x16Add => {
3046 self.lower_v128_binary(offset, LaneShape::I8x16, V128BinaryKind::Add)?
3047 }
3048 Instr::I8x16Sub => {
3049 self.lower_v128_binary(offset, LaneShape::I8x16, V128BinaryKind::Sub)?
3050 }
3051 Instr::I16x8Add => {
3052 self.lower_v128_binary(offset, LaneShape::I16x8, V128BinaryKind::Add)?
3053 }
3054 Instr::I16x8Sub => {
3055 self.lower_v128_binary(offset, LaneShape::I16x8, V128BinaryKind::Sub)?
3056 }
3057 Instr::I32x4Add => {
3058 self.lower_v128_binary(offset, LaneShape::I32x4, V128BinaryKind::Add)?
3059 }
3060 Instr::I32x4Sub => {
3061 self.lower_v128_binary(offset, LaneShape::I32x4, V128BinaryKind::Sub)?
3062 }
3063 Instr::I32x4Mul => {
3064 self.lower_v128_binary(offset, LaneShape::I32x4, V128BinaryKind::Mul)?
3065 }
3066 Instr::I64x2Add => {
3067 self.lower_v128_binary(offset, LaneShape::I64x2, V128BinaryKind::Add)?
3068 }
3069 Instr::I64x2Sub => {
3070 self.lower_v128_binary(offset, LaneShape::I64x2, V128BinaryKind::Sub)?
3071 }
3072 Instr::F32x4Add => {
3073 self.lower_v128_binary(offset, LaneShape::F32x4, V128BinaryKind::Add)?
3074 }
3075 Instr::F32x4Sub => {
3076 self.lower_v128_binary(offset, LaneShape::F32x4, V128BinaryKind::Sub)?
3077 }
3078 Instr::F32x4Mul => {
3079 self.lower_v128_binary(offset, LaneShape::F32x4, V128BinaryKind::Mul)?
3080 }
3081 Instr::F32x4Div => {
3082 self.lower_v128_binary(offset, LaneShape::F32x4, V128BinaryKind::Div)?
3083 }
3084 Instr::F64x2Add => {
3085 self.lower_v128_binary(offset, LaneShape::F64x2, V128BinaryKind::Add)?
3086 }
3087 Instr::F64x2Sub => {
3088 self.lower_v128_binary(offset, LaneShape::F64x2, V128BinaryKind::Sub)?
3089 }
3090 Instr::F64x2Mul => {
3091 self.lower_v128_binary(offset, LaneShape::F64x2, V128BinaryKind::Mul)?
3092 }
3093 Instr::F64x2Div => {
3094 self.lower_v128_binary(offset, LaneShape::F64x2, V128BinaryKind::Div)?
3095 }
3096 instr => {
3097 if let Some(op) = unary_op(&instr) {
3098 self.lower_unary_op(offset, op)?;
3099 } else if let Some(op) = binary_op(&instr) {
3100 self.lower_binary_op(offset, op)?;
3101 } else if let Some((op, memarg)) = load_op(&instr) {
3102 self.lower_load(offset, op, memarg)?;
3103 } else if let Some((op, memarg)) = store_op(&instr) {
3104 self.lower_store(offset, op, memarg)?;
3105 } else {
3106 return Err(LowerError {
3107 offset,
3108 function: Some(self.func_idx),
3109 kind: LowerErrorKind::UnsupportedInstr {
3110 op: instr_name(&instr),
3111 },
3112 });
3113 }
3114 }
3115 }
3116
3117 Ok(false)
3118 }
3119
3120 fn alloc_reg(&mut self, ty: ValType) -> Reg {
3121 let reg = Reg(self.reg_types.len() as u32);
3122 self.reg_types.push(ty);
3123 reg
3124 }
3125
3126 fn emit(&mut self, offset: ByteOffset, op: RegOp) {
3127 self.current_instrs.push(RegInstr { offset, op });
3128 }
3129
3130 fn pop_any(&mut self, offset: ByteOffset, op: &'static str) -> Result<RegValue, LowerError> {
3131 if self.at_frame_boundary() {
3132 if self.current_frame_unreachable() {
3133 let ty = ValType::Num(NumType::I32);
3137 let reg = self.alloc_reg(ty);
3138 return Ok(RegValue { reg, ty });
3139 }
3140 return Err(LowerError {
3141 offset,
3142 function: Some(self.func_idx),
3143 kind: LowerErrorKind::StackUnderflow {
3144 op,
3145 expected: ValType::Num(NumType::I32),
3146 },
3147 });
3148 }
3149 Ok(self.stack.pop().expect("stack height checked"))
3150 }
3151
3152 fn at_frame_boundary(&self) -> bool {
3155 let frame = self
3156 .label_stack
3157 .last()
3158 .expect("function frame is always present");
3159 self.stack.len() == frame.height
3160 }
3161
3162 fn current_frame_unreachable(&self) -> bool {
3163 self.label_stack
3164 .last()
3165 .expect("function frame is always present")
3166 .unreachable
3167 }
3168
3169 fn label_position(&self, offset: ByteOffset, label: LabelIdx) -> Result<usize, LowerError> {
3172 let label_idx = label.0 as usize;
3173 if label_idx >= self.label_stack.len() {
3174 return Err(LowerError {
3175 offset,
3176 function: Some(self.func_idx),
3177 kind: LowerErrorKind::InvalidLabel { label: label.0 },
3178 });
3179 }
3180 Ok(self.label_stack.len() - 1 - label_idx)
3181 }
3182
3183 fn enter_frame(
3188 &mut self,
3189 offset: ByteOffset,
3190 op: &'static str,
3191 kind: FrameKind,
3192 block_type: BlockType,
3193 ) -> Result<(), LowerError> {
3194 let (param_types, result_types) = self.block_type_sig(offset, block_type)?;
3195 let mut param_regs = Vec::with_capacity(param_types.len());
3196 for &expected in param_types.iter().rev() {
3197 let found = self.pop_expect(offset, op, expected)?;
3198 param_regs.push(found.reg);
3199 }
3200 param_regs.reverse();
3201 let height = self.stack.len();
3202 for (®, &ty) in param_regs.iter().zip(param_types.iter()) {
3203 self.stack.push(RegValue { reg, ty });
3204 }
3205 self.label_stack.push(LabelFrame {
3206 label: LabelIdx(self.label_stack.len() as u32),
3207 kind,
3208 result_types,
3209 param_types,
3210 param_regs,
3211 height,
3212 unreachable: false,
3213 });
3214 Ok(())
3215 }
3216
3217 fn block_type_sig(
3219 &self,
3220 offset: ByteOffset,
3221 block_type: BlockType,
3222 ) -> Result<(Vec<ValType>, Vec<ValType>), LowerError> {
3223 match block_type {
3224 BlockType::Empty => Ok((Vec::new(), Vec::new())),
3225 BlockType::Val(ty) => Ok((Vec::new(), alloc::vec![ty])),
3226 BlockType::TypeIdx(idx) => {
3227 let ty = self.tables.types.get(idx as usize).ok_or(LowerError {
3228 offset,
3229 function: Some(self.func_idx),
3230 kind: LowerErrorKind::InvalidType { type_idx: idx },
3231 })?;
3232 Ok((ty.params.clone(), ty.results.clone()))
3233 }
3234 }
3235 }
3236
3237 fn emit_copies(&mut self, offset: ByteOffset, dsts: &[Reg], srcs: &[Reg]) {
3240 for (&dst, &src) in dsts.iter().zip(srcs.iter()) {
3241 if dst != src {
3242 self.emit(offset, RegOp::Copy { dst, src });
3243 }
3244 }
3245 }
3246
3247 fn branch_types_at(&self, frame_pos: usize) -> (Vec<ValType>, Option<LoopTarget>) {
3250 let frame = &self.label_stack[frame_pos];
3251 match frame.kind {
3252 FrameKind::Loop { header_block } => (
3253 frame.param_types.clone(),
3254 Some(LoopTarget {
3255 header: header_block,
3256 param_regs: frame.param_regs.clone(),
3257 }),
3258 ),
3259 _ => (frame.result_types.clone(), None),
3260 }
3261 }
3262
3263 fn set_unreachable(&mut self) {
3266 let frame = self
3267 .label_stack
3268 .last_mut()
3269 .expect("function frame is always present");
3270 self.stack.truncate(frame.height);
3271 frame.unreachable = true;
3272 }
3273
3274 fn lower_binary_op(&mut self, offset: ByteOffset, op: BinaryOp) -> Result<(), LowerError> {
3275 let input = op.input_type();
3276 let output = op.result_type();
3277 let rhs = self.pop_expect(offset, op.name(), input)?;
3278 let lhs = self.pop_expect(offset, op.name(), input)?;
3279 let dst = self.alloc_reg(output);
3280 self.stack.push(RegValue {
3281 reg: dst,
3282 ty: output,
3283 });
3284 self.emit(
3285 offset,
3286 RegOp::Binary {
3287 op,
3288 dst,
3289 lhs: lhs.reg,
3290 rhs: rhs.reg,
3291 },
3292 );
3293 Ok(())
3294 }
3295
3296 fn lower_splat(&mut self, offset: ByteOffset, shape: LaneShape) -> Result<(), LowerError> {
3297 let src = self.pop_expect(offset, "splat", shape.scalar_type())?;
3298 let ty = ValType::Vec(crate::types::VecType::V128);
3299 let dst = self.alloc_reg(ty);
3300 self.stack.push(RegValue { reg: dst, ty });
3301 self.emit(
3302 offset,
3303 RegOp::V128Splat {
3304 dst,
3305 shape,
3306 src: src.reg,
3307 },
3308 );
3309 Ok(())
3310 }
3311
3312 fn lower_extract_lane(
3313 &mut self,
3314 offset: ByteOffset,
3315 shape: LaneShape,
3316 lane: u8,
3317 ) -> Result<(), LowerError> {
3318 let src = self.pop_expect(
3319 offset,
3320 "extract_lane",
3321 ValType::Vec(crate::types::VecType::V128),
3322 )?;
3323 let ty = shape.scalar_type();
3324 let dst = self.alloc_reg(ty);
3325 self.stack.push(RegValue { reg: dst, ty });
3326 self.emit(
3327 offset,
3328 RegOp::V128ExtractLane {
3329 dst,
3330 shape,
3331 src: src.reg,
3332 lane,
3333 },
3334 );
3335 Ok(())
3336 }
3337
3338 fn lower_replace_lane(
3339 &mut self,
3340 offset: ByteOffset,
3341 shape: LaneShape,
3342 lane: u8,
3343 ) -> Result<(), LowerError> {
3344 let scalar = self.pop_expect(offset, "replace_lane", shape.scalar_type())?;
3345 let vec = self.pop_expect(
3346 offset,
3347 "replace_lane",
3348 ValType::Vec(crate::types::VecType::V128),
3349 )?;
3350 let ty = ValType::Vec(crate::types::VecType::V128);
3351 let dst = self.alloc_reg(ty);
3352 self.stack.push(RegValue { reg: dst, ty });
3353 self.emit(
3354 offset,
3355 RegOp::V128ReplaceLane {
3356 dst,
3357 shape,
3358 vec: vec.reg,
3359 scalar: scalar.reg,
3360 lane,
3361 },
3362 );
3363 Ok(())
3364 }
3365
3366 fn lower_v128_binary(
3367 &mut self,
3368 offset: ByteOffset,
3369 shape: LaneShape,
3370 kind: V128BinaryKind,
3371 ) -> Result<(), LowerError> {
3372 let v128 = ValType::Vec(crate::types::VecType::V128);
3373 let rhs = self.pop_expect(offset, "simd.binary", v128)?;
3374 let lhs = self.pop_expect(offset, "simd.binary", v128)?;
3375 let dst = self.alloc_reg(v128);
3376 self.stack.push(RegValue { reg: dst, ty: v128 });
3377 self.emit(
3378 offset,
3379 RegOp::V128Binary {
3380 shape,
3381 kind,
3382 dst,
3383 lhs: lhs.reg,
3384 rhs: rhs.reg,
3385 },
3386 );
3387 Ok(())
3388 }
3389
3390 fn global_type(&self, offset: ByteOffset, global: GlobalIdx) -> Result<ValType, LowerError> {
3391 self.tables
3392 .global_types
3393 .get(global.0 as usize)
3394 .copied()
3395 .ok_or(LowerError {
3396 offset,
3397 function: Some(self.func_idx),
3398 kind: LowerErrorKind::InvalidGlobal { global: global.0 },
3399 })
3400 }
3401
3402 fn table_elem_type(
3403 &self,
3404 offset: ByteOffset,
3405 table: TableIdx,
3406 ) -> Result<crate::types::RefType, LowerError> {
3407 self.tables
3408 .table_elem_types
3409 .get(table.0 as usize)
3410 .copied()
3411 .ok_or(LowerError {
3412 offset,
3413 function: Some(self.func_idx),
3414 kind: LowerErrorKind::InvalidTable { table: table.0 },
3415 })
3416 }
3417
3418 fn lower_load(
3419 &mut self,
3420 offset: ByteOffset,
3421 op: LoadOp,
3422 memarg: MemArg,
3423 ) -> Result<(), LowerError> {
3424 let addr = self.pop_expect(offset, "load", ValType::Num(NumType::I32))?;
3425 let ty = op.result_type();
3426 let dst = self.alloc_reg(ty);
3427 self.stack.push(RegValue { reg: dst, ty });
3428 self.emit(
3429 offset,
3430 RegOp::Load {
3431 op,
3432 dst,
3433 addr: addr.reg,
3434 memarg,
3435 },
3436 );
3437 Ok(())
3438 }
3439
3440 fn lower_store(
3441 &mut self,
3442 offset: ByteOffset,
3443 op: StoreOp,
3444 memarg: MemArg,
3445 ) -> Result<(), LowerError> {
3446 let value = self.pop_expect(offset, "store", op.value_type())?;
3447 let addr = self.pop_expect(offset, "store", ValType::Num(NumType::I32))?;
3448 self.emit(
3449 offset,
3450 RegOp::Store {
3451 op,
3452 addr: addr.reg,
3453 value: value.reg,
3454 memarg,
3455 },
3456 );
3457 Ok(())
3458 }
3459
3460 fn lower_unary_op(&mut self, offset: ByteOffset, op: UnaryOp) -> Result<(), LowerError> {
3461 let input = op.input_type();
3462 let output = op.result_type();
3463 let value = self.pop_expect(offset, op.name(), input)?;
3464 let dst = self.alloc_reg(output);
3465 self.stack.push(RegValue {
3466 reg: dst,
3467 ty: output,
3468 });
3469 self.emit(
3470 offset,
3471 RegOp::Unary {
3472 op,
3473 dst,
3474 value: value.reg,
3475 },
3476 );
3477 Ok(())
3478 }
3479
3480 fn pop_expect(
3481 &mut self,
3482 offset: ByteOffset,
3483 op: &'static str,
3484 expected: ValType,
3485 ) -> Result<RegValue, LowerError> {
3486 if self.at_frame_boundary() {
3487 if self.current_frame_unreachable() {
3488 let reg = self.alloc_reg(expected);
3491 return Ok(RegValue { reg, ty: expected });
3492 }
3493 return Err(LowerError {
3494 offset,
3495 function: Some(self.func_idx),
3496 kind: LowerErrorKind::StackUnderflow { op, expected },
3497 });
3498 }
3499
3500 let found = self.stack.pop().expect("stack height checked");
3501
3502 if found.ty != expected
3506 && !ref_compatible(found.ty, expected)
3507 && !self.current_frame_unreachable()
3508 {
3509 return Err(LowerError {
3510 offset,
3511 function: Some(self.func_idx),
3512 kind: LowerErrorKind::TypeMismatch {
3513 op,
3514 expected,
3515 found: found.ty,
3516 },
3517 });
3518 }
3519
3520 Ok(found)
3521 }
3522
3523 fn pop_results(&mut self, offset: ByteOffset) -> Result<Vec<Reg>, LowerError> {
3524 let results = self.results.clone();
3525 let mut values = Vec::with_capacity(results.len());
3526 for &expected in results.iter().rev() {
3527 let found = self.pop_expect(offset, "function end", expected)?;
3528 values.push(found.reg);
3529 }
3530 values.reverse();
3531 Ok(values)
3532 }
3533}
3534
3535fn load_op(instr: &Instr) -> Option<(LoadOp, MemArg)> {
3536 let (op, memarg) = match *instr {
3537 Instr::I32Load(memarg) => (LoadOp::I32, memarg),
3538 Instr::I64Load(memarg) => (LoadOp::I64, memarg),
3539 Instr::F32Load(memarg) => (LoadOp::F32, memarg),
3540 Instr::F64Load(memarg) => (LoadOp::F64, memarg),
3541 Instr::I32Load8S(memarg) => (LoadOp::I32Load8S, memarg),
3542 Instr::I32Load8U(memarg) => (LoadOp::I32Load8U, memarg),
3543 Instr::I32Load16S(memarg) => (LoadOp::I32Load16S, memarg),
3544 Instr::I32Load16U(memarg) => (LoadOp::I32Load16U, memarg),
3545 Instr::I64Load8S(memarg) => (LoadOp::I64Load8S, memarg),
3546 Instr::I64Load8U(memarg) => (LoadOp::I64Load8U, memarg),
3547 Instr::I64Load16S(memarg) => (LoadOp::I64Load16S, memarg),
3548 Instr::I64Load16U(memarg) => (LoadOp::I64Load16U, memarg),
3549 Instr::I64Load32S(memarg) => (LoadOp::I64Load32S, memarg),
3550 Instr::I64Load32U(memarg) => (LoadOp::I64Load32U, memarg),
3551 Instr::V128Load(memarg) => (LoadOp::V128, memarg),
3552 _ => return None,
3553 };
3554 Some((op, memarg))
3555}
3556
3557fn store_op(instr: &Instr) -> Option<(StoreOp, MemArg)> {
3558 let (op, memarg) = match *instr {
3559 Instr::I32Store(memarg) => (StoreOp::I32, memarg),
3560 Instr::I64Store(memarg) => (StoreOp::I64, memarg),
3561 Instr::F32Store(memarg) => (StoreOp::F32, memarg),
3562 Instr::F64Store(memarg) => (StoreOp::F64, memarg),
3563 Instr::I32Store8(memarg) => (StoreOp::I32Store8, memarg),
3564 Instr::I32Store16(memarg) => (StoreOp::I32Store16, memarg),
3565 Instr::I64Store8(memarg) => (StoreOp::I64Store8, memarg),
3566 Instr::I64Store16(memarg) => (StoreOp::I64Store16, memarg),
3567 Instr::I64Store32(memarg) => (StoreOp::I64Store32, memarg),
3568 Instr::V128Store(memarg) => (StoreOp::V128, memarg),
3569 _ => return None,
3570 };
3571 Some((op, memarg))
3572}
3573
3574fn unary_op(instr: &Instr) -> Option<UnaryOp> {
3575 match instr {
3576 Instr::I32Clz => Some(UnaryOp::I32Clz),
3577 Instr::I32Ctz => Some(UnaryOp::I32Ctz),
3578 Instr::I32Popcnt => Some(UnaryOp::I32Popcnt),
3579 Instr::I32Eqz => Some(UnaryOp::I32Eqz),
3580 Instr::I32WrapI64 => Some(UnaryOp::I32WrapI64),
3581 Instr::I32Extend8S => Some(UnaryOp::I32Extend8S),
3582 Instr::I32Extend16S => Some(UnaryOp::I32Extend16S),
3583 Instr::I32TruncF32S => Some(UnaryOp::I32TruncF32S),
3584 Instr::I32TruncF32U => Some(UnaryOp::I32TruncF32U),
3585 Instr::I32TruncF64S => Some(UnaryOp::I32TruncF64S),
3586 Instr::I32TruncF64U => Some(UnaryOp::I32TruncF64U),
3587 Instr::F32ConvertI32S => Some(UnaryOp::F32ConvertI32S),
3588 Instr::F32ConvertI32U => Some(UnaryOp::F32ConvertI32U),
3589 Instr::F64ConvertI32S => Some(UnaryOp::F64ConvertI32S),
3590 Instr::F64ConvertI32U => Some(UnaryOp::F64ConvertI32U),
3591 Instr::F32Neg => Some(UnaryOp::F32Neg),
3592 Instr::F32Abs => Some(UnaryOp::F32Abs),
3593 Instr::F32Sqrt => Some(UnaryOp::F32Sqrt),
3594 Instr::F32Ceil => Some(UnaryOp::F32Ceil),
3595 Instr::F32Floor => Some(UnaryOp::F32Floor),
3596 Instr::F32Trunc => Some(UnaryOp::F32Trunc),
3597 Instr::F32Nearest => Some(UnaryOp::F32Nearest),
3598 Instr::I64Clz => Some(UnaryOp::I64Clz),
3599 Instr::I64Ctz => Some(UnaryOp::I64Ctz),
3600 Instr::I64Popcnt => Some(UnaryOp::I64Popcnt),
3601 Instr::I64Eqz => Some(UnaryOp::I64Eqz),
3602 Instr::I64ExtendI32S => Some(UnaryOp::I64ExtendI32S),
3603 Instr::I64ExtendI32U => Some(UnaryOp::I64ExtendI32U),
3604 Instr::I64Extend8S => Some(UnaryOp::I64Extend8S),
3605 Instr::I64Extend16S => Some(UnaryOp::I64Extend16S),
3606 Instr::I64Extend32S => Some(UnaryOp::I64Extend32S),
3607 Instr::I64TruncF32S => Some(UnaryOp::I64TruncF32S),
3608 Instr::I64TruncF32U => Some(UnaryOp::I64TruncF32U),
3609 Instr::I64TruncF64S => Some(UnaryOp::I64TruncF64S),
3610 Instr::I64TruncF64U => Some(UnaryOp::I64TruncF64U),
3611 Instr::F32ConvertI64S => Some(UnaryOp::F32ConvertI64S),
3612 Instr::F32ConvertI64U => Some(UnaryOp::F32ConvertI64U),
3613 Instr::F64ConvertI64S => Some(UnaryOp::F64ConvertI64S),
3614 Instr::F64ConvertI64U => Some(UnaryOp::F64ConvertI64U),
3615 Instr::F64Neg => Some(UnaryOp::F64Neg),
3616 Instr::F64Abs => Some(UnaryOp::F64Abs),
3617 Instr::F64Sqrt => Some(UnaryOp::F64Sqrt),
3618 Instr::F64Ceil => Some(UnaryOp::F64Ceil),
3619 Instr::F64Floor => Some(UnaryOp::F64Floor),
3620 Instr::F64Trunc => Some(UnaryOp::F64Trunc),
3621 Instr::F64Nearest => Some(UnaryOp::F64Nearest),
3622 Instr::F32DemoteF64 => Some(UnaryOp::F32DemoteF64),
3623 Instr::F64PromoteF32 => Some(UnaryOp::F64PromoteF32),
3624 Instr::I32ReinterpretF32 => Some(UnaryOp::I32ReinterpretF32),
3625 Instr::F32ReinterpretI32 => Some(UnaryOp::F32ReinterpretI32),
3626 Instr::I64ReinterpretF64 => Some(UnaryOp::I64ReinterpretF64),
3627 Instr::F64ReinterpretI64 => Some(UnaryOp::F64ReinterpretI64),
3628 Instr::I32TruncSatF32S => Some(UnaryOp::I32TruncSatF32S),
3629 Instr::I32TruncSatF32U => Some(UnaryOp::I32TruncSatF32U),
3630 Instr::I32TruncSatF64S => Some(UnaryOp::I32TruncSatF64S),
3631 Instr::I32TruncSatF64U => Some(UnaryOp::I32TruncSatF64U),
3632 Instr::I64TruncSatF32S => Some(UnaryOp::I64TruncSatF32S),
3633 Instr::I64TruncSatF32U => Some(UnaryOp::I64TruncSatF32U),
3634 Instr::I64TruncSatF64S => Some(UnaryOp::I64TruncSatF64S),
3635 Instr::I64TruncSatF64U => Some(UnaryOp::I64TruncSatF64U),
3636 _ => None,
3637 }
3638}
3639
3640fn binary_op(instr: &Instr) -> Option<BinaryOp> {
3641 match instr {
3642 Instr::I32Add => Some(BinaryOp::I32Add),
3643 Instr::I32Sub => Some(BinaryOp::I32Sub),
3644 Instr::I32Mul => Some(BinaryOp::I32Mul),
3645 Instr::I32DivS => Some(BinaryOp::I32DivS),
3646 Instr::I32DivU => Some(BinaryOp::I32DivU),
3647 Instr::I32RemS => Some(BinaryOp::I32RemS),
3648 Instr::I32RemU => Some(BinaryOp::I32RemU),
3649 Instr::I32And => Some(BinaryOp::I32And),
3650 Instr::I32Or => Some(BinaryOp::I32Or),
3651 Instr::I32Xor => Some(BinaryOp::I32Xor),
3652 Instr::I32Shl => Some(BinaryOp::I32Shl),
3653 Instr::I32ShrS => Some(BinaryOp::I32ShrS),
3654 Instr::I32ShrU => Some(BinaryOp::I32ShrU),
3655 Instr::I32Rotl => Some(BinaryOp::I32Rotl),
3656 Instr::I32Rotr => Some(BinaryOp::I32Rotr),
3657 Instr::I32Eq => Some(BinaryOp::I32Eq),
3658 Instr::I32Ne => Some(BinaryOp::I32Ne),
3659 Instr::I32LtS => Some(BinaryOp::I32LtS),
3660 Instr::I32LtU => Some(BinaryOp::I32LtU),
3661 Instr::I32GtS => Some(BinaryOp::I32GtS),
3662 Instr::I32GtU => Some(BinaryOp::I32GtU),
3663 Instr::I32LeS => Some(BinaryOp::I32LeS),
3664 Instr::I32LeU => Some(BinaryOp::I32LeU),
3665 Instr::I32GeS => Some(BinaryOp::I32GeS),
3666 Instr::I32GeU => Some(BinaryOp::I32GeU),
3667 Instr::I64Add => Some(BinaryOp::I64Add),
3668 Instr::I64Sub => Some(BinaryOp::I64Sub),
3669 Instr::I64Mul => Some(BinaryOp::I64Mul),
3670 Instr::I64DivS => Some(BinaryOp::I64DivS),
3671 Instr::I64DivU => Some(BinaryOp::I64DivU),
3672 Instr::I64RemS => Some(BinaryOp::I64RemS),
3673 Instr::I64RemU => Some(BinaryOp::I64RemU),
3674 Instr::I64And => Some(BinaryOp::I64And),
3675 Instr::I64Or => Some(BinaryOp::I64Or),
3676 Instr::I64Xor => Some(BinaryOp::I64Xor),
3677 Instr::I64Shl => Some(BinaryOp::I64Shl),
3678 Instr::I64ShrS => Some(BinaryOp::I64ShrS),
3679 Instr::I64ShrU => Some(BinaryOp::I64ShrU),
3680 Instr::I64Rotl => Some(BinaryOp::I64Rotl),
3681 Instr::I64Rotr => Some(BinaryOp::I64Rotr),
3682 Instr::I64Eq => Some(BinaryOp::I64Eq),
3683 Instr::I64Ne => Some(BinaryOp::I64Ne),
3684 Instr::I64LtS => Some(BinaryOp::I64LtS),
3685 Instr::I64LtU => Some(BinaryOp::I64LtU),
3686 Instr::I64GtS => Some(BinaryOp::I64GtS),
3687 Instr::I64GtU => Some(BinaryOp::I64GtU),
3688 Instr::I64LeS => Some(BinaryOp::I64LeS),
3689 Instr::I64LeU => Some(BinaryOp::I64LeU),
3690 Instr::I64GeS => Some(BinaryOp::I64GeS),
3691 Instr::I64GeU => Some(BinaryOp::I64GeU),
3692 Instr::F32Add => Some(BinaryOp::F32Add),
3693 Instr::F32Copysign => Some(BinaryOp::F32Copysign),
3694 Instr::F64Copysign => Some(BinaryOp::F64Copysign),
3695 Instr::F32Sub => Some(BinaryOp::F32Sub),
3696 Instr::F32Mul => Some(BinaryOp::F32Mul),
3697 Instr::F32Div => Some(BinaryOp::F32Div),
3698 Instr::F32Min => Some(BinaryOp::F32Min),
3699 Instr::F32Max => Some(BinaryOp::F32Max),
3700 Instr::F64Add => Some(BinaryOp::F64Add),
3701 Instr::F64Sub => Some(BinaryOp::F64Sub),
3702 Instr::F64Mul => Some(BinaryOp::F64Mul),
3703 Instr::F64Div => Some(BinaryOp::F64Div),
3704 Instr::F64Min => Some(BinaryOp::F64Min),
3705 Instr::F64Max => Some(BinaryOp::F64Max),
3706 Instr::F32Eq => Some(BinaryOp::F32Eq),
3707 Instr::F32Ne => Some(BinaryOp::F32Ne),
3708 Instr::F32Lt => Some(BinaryOp::F32Lt),
3709 Instr::F32Gt => Some(BinaryOp::F32Gt),
3710 Instr::F32Le => Some(BinaryOp::F32Le),
3711 Instr::F32Ge => Some(BinaryOp::F32Ge),
3712 Instr::F64Eq => Some(BinaryOp::F64Eq),
3713 Instr::F64Ne => Some(BinaryOp::F64Ne),
3714 Instr::F64Lt => Some(BinaryOp::F64Lt),
3715 Instr::F64Gt => Some(BinaryOp::F64Gt),
3716 Instr::F64Le => Some(BinaryOp::F64Le),
3717 Instr::F64Ge => Some(BinaryOp::F64Ge),
3718 _ => None,
3719 }
3720}
3721
3722fn instr_name(instr: &Instr) -> &'static str {
3723 match instr {
3724 Instr::Unreachable => "unreachable",
3725 Instr::Nop => "nop",
3726 Instr::Block(_) => "block",
3727 Instr::Loop(_) => "loop",
3728 Instr::If(_) => "if",
3729 Instr::Else => "else",
3730 Instr::Br(_) => "br",
3731 Instr::BrIf(_) => "br_if",
3732 Instr::BrTable { .. } => "br_table",
3733 Instr::Return => "return",
3734 Instr::Call(_) => "call",
3735 Instr::CallIndirect { .. } => "call_indirect",
3736 Instr::LocalSet(_) => "local.set",
3737 Instr::LocalTee(_) => "local.tee",
3738 Instr::GlobalGet(_) => "global.get",
3739 Instr::GlobalSet(_) => "global.set",
3740 Instr::TableGet(_) => "table.get",
3741 Instr::TableSet(_) => "table.set",
3742 Instr::I64Add => "i64.add",
3743 _ => "instruction",
3744 }
3745}
3746
3747#[cfg(test)]
3748mod tests {
3749 use alloc::vec;
3750
3751 use super::*;
3752 use crate::binary::module::Module;
3753
3754 #[test]
3755 fn lower_simple_add_fixture_to_register_ir() {
3756 let bytes = baedeker_testdata::fixture_bytes("add");
3757 let module = Module::decode(&bytes).unwrap();
3758 let reg_module = module.lower().unwrap();
3759
3760 assert_eq!(reg_module.funcs.len(), 1);
3761 let func = ®_module.funcs[0];
3762 assert_eq!(
3763 func.params,
3764 vec![ValType::Num(NumType::I32), ValType::Num(NumType::I32)]
3765 );
3766 assert_eq!(func.results, vec![ValType::Num(NumType::I32)]);
3767 assert_eq!(func.reg_types, vec![ValType::Num(NumType::I32); 3]);
3768 assert_eq!(
3769 func.blocks[0]
3770 .instrs
3771 .iter()
3772 .map(|instr| &instr.op)
3773 .collect::<Vec<_>>(),
3774 vec![
3775 &RegOp::LocalGet {
3776 dst: Reg(0),
3777 local: LocalIdx(1),
3778 },
3779 &RegOp::LocalGet {
3780 dst: Reg(1),
3781 local: LocalIdx(0),
3782 },
3783 &RegOp::Binary {
3784 op: BinaryOp::I32Add,
3785 dst: Reg(2),
3786 lhs: Reg(0),
3787 rhs: Reg(1),
3788 },
3789 ]
3791 );
3792 }
3793
3794 #[test]
3795 fn lower_block_and_return() {
3796 let bytes = [
3797 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x0a, 0x07, 0x01, 0x05, 0x00, 0x02, 0x40, 0x0b, 0x0b, ];
3804 let module = Module::decode(&bytes).unwrap();
3805 let reg_module = module.lower().unwrap();
3806 let func = ®_module.funcs[0];
3807 assert_eq!(func.blocks.len(), 5);
3809 assert!(matches!(func.blocks[0].term, RegTerm::Fallthrough));
3810 assert!(matches!(
3811 func.blocks[func.blocks.len() - 2].term,
3812 RegTerm::Fallthrough
3813 ));
3814 assert!(matches!(
3815 func.blocks.last().unwrap().term,
3816 RegTerm::Return { .. }
3817 ));
3818 }
3819
3820 #[test]
3821 fn execute_lowered_add_fixture() {
3822 let bytes = baedeker_testdata::fixture_bytes("add");
3823 let module = Module::decode(&bytes).unwrap();
3824 let reg_module = module.lower().unwrap();
3825
3826 let result = crate::runtime::execute_func(
3827 ®_module.funcs[0],
3828 &[
3829 crate::runtime::Value::I32(20),
3830 crate::runtime::Value::I32(22),
3831 ],
3832 )
3833 .unwrap();
3834
3835 assert_eq!(result, vec![crate::runtime::Value::I32(42)]);
3836 }
3837
3838 #[test]
3839 fn lower_local_set_temp_storage() {
3840 let module = Module::decode(local_set_temp_module()).unwrap();
3841 let reg_module = module.lower().unwrap();
3842 let func = ®_module.funcs[0];
3843
3844 assert_eq!(func.reg_types, vec![ValType::Num(NumType::I32); 4]);
3845 assert_eq!(
3846 func.blocks[0]
3847 .instrs
3848 .iter()
3849 .map(|instr| &instr.op)
3850 .collect::<Vec<_>>(),
3851 vec![
3852 &RegOp::I32Const {
3853 dst: Reg(0),
3854 value: 40,
3855 },
3856 &RegOp::LocalSet {
3857 local: LocalIdx(0),
3858 value: Reg(0),
3859 },
3860 &RegOp::LocalGet {
3861 dst: Reg(1),
3862 local: LocalIdx(0),
3863 },
3864 &RegOp::I32Const {
3865 dst: Reg(2),
3866 value: 2,
3867 },
3868 &RegOp::Binary {
3869 op: BinaryOp::I32Add,
3870 dst: Reg(3),
3871 lhs: Reg(1),
3872 rhs: Reg(2),
3873 },
3874 ]
3876 );
3877 }
3878
3879 #[test]
3880 fn execute_local_set_temp_storage() {
3881 let module = Module::decode(local_set_temp_module()).unwrap();
3882 let reg_module = module.lower().unwrap();
3883
3884 let result = crate::runtime::execute_func(®_module.funcs[0], &[]).unwrap();
3885
3886 assert_eq!(result, vec![crate::runtime::Value::I32(42)]);
3887 }
3888
3889 #[test]
3890 fn lower_local_tee_keeps_value_on_stack() {
3891 let module = Module::decode(local_tee_stack_module()).unwrap();
3892 let reg_module = module.lower().unwrap();
3893 let func = ®_module.funcs[0];
3894
3895 assert_eq!(func.reg_types, vec![ValType::Num(NumType::I32); 3]);
3896 assert_eq!(
3897 func.blocks[0]
3898 .instrs
3899 .iter()
3900 .map(|instr| &instr.op)
3901 .collect::<Vec<_>>(),
3902 vec![
3903 &RegOp::I32Const {
3904 dst: Reg(0),
3905 value: 40,
3906 },
3907 &RegOp::LocalTee {
3908 local: LocalIdx(0),
3909 value: Reg(0),
3910 },
3911 &RegOp::I32Const {
3912 dst: Reg(1),
3913 value: 2,
3914 },
3915 &RegOp::Binary {
3916 op: BinaryOp::I32Add,
3917 dst: Reg(2),
3918 lhs: Reg(0),
3919 rhs: Reg(1),
3920 },
3921 ]
3923 );
3924 }
3925
3926 #[test]
3927 fn execute_local_tee_stack_value() {
3928 let module = Module::decode(local_tee_stack_module()).unwrap();
3929 let reg_module = module.lower().unwrap();
3930
3931 let result = crate::runtime::execute_func(®_module.funcs[0], &[]).unwrap();
3932
3933 assert_eq!(result, vec![crate::runtime::Value::I32(42)]);
3934 }
3935
3936 fn local_set_temp_module() -> &'static [u8] {
3937 &[
3938 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x0a, 0x0f, 0x01, 0x0d, 0x01, 0x01, 0x7f, 0x41, 0x28, 0x21, 0x00, 0x20, 0x00, 0x41, 0x02, 0x6a, 0x0b, ]
3950 }
3951
3952 fn local_tee_stack_module() -> &'static [u8] {
3953 &[
3954 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x0a, 0x0d, 0x01, 0x0b, 0x01, 0x01, 0x7f, 0x41, 0x28, 0x22, 0x00, 0x41, 0x02, 0x6a, 0x0b, ]
3965 }
3966
3967 #[test]
3968 fn lower_i32_sub_and_mul_cohort() {
3969 let module = Module::decode(i32_sub_mul_module()).unwrap();
3970 let reg_module = module.lower().unwrap();
3971 let func = ®_module.funcs[0];
3972
3973 assert_eq!(func.reg_types, vec![ValType::Num(NumType::I32); 5]);
3974 assert_eq!(
3975 func.blocks[0]
3976 .instrs
3977 .iter()
3978 .map(|instr| &instr.op)
3979 .collect::<Vec<_>>(),
3980 vec![
3981 &RegOp::I32Const {
3982 dst: Reg(0),
3983 value: 50,
3984 },
3985 &RegOp::I32Const {
3986 dst: Reg(1),
3987 value: 8,
3988 },
3989 &RegOp::Binary {
3990 op: BinaryOp::I32Sub,
3991 dst: Reg(2),
3992 lhs: Reg(0),
3993 rhs: Reg(1),
3994 },
3995 &RegOp::I32Const {
3996 dst: Reg(3),
3997 value: 3,
3998 },
3999 &RegOp::Binary {
4000 op: BinaryOp::I32Mul,
4001 dst: Reg(4),
4002 lhs: Reg(2),
4003 rhs: Reg(3),
4004 },
4005 ]
4007 );
4008 }
4009
4010 #[test]
4011 fn execute_i32_sub_and_mul_cohort() {
4012 let module = Module::decode(i32_sub_mul_module()).unwrap();
4013 let reg_module = module.lower().unwrap();
4014
4015 let result = crate::runtime::execute_func(®_module.funcs[0], &[]).unwrap();
4016
4017 assert_eq!(result, vec![crate::runtime::Value::I32(126)]);
4018 }
4019
4020 #[test]
4021 fn lower_i64_add_cohort() {
4022 let module = Module::decode(i64_add_module()).unwrap();
4023 let reg_module = module.lower().unwrap();
4024 let func = ®_module.funcs[0];
4025
4026 assert_eq!(func.reg_types, vec![ValType::Num(NumType::I64); 3]);
4027 assert_eq!(
4028 func.blocks[0]
4029 .instrs
4030 .iter()
4031 .map(|instr| &instr.op)
4032 .collect::<Vec<_>>(),
4033 vec![
4034 &RegOp::I64Const {
4035 dst: Reg(0),
4036 value: 20,
4037 },
4038 &RegOp::I64Const {
4039 dst: Reg(1),
4040 value: 22,
4041 },
4042 &RegOp::Binary {
4043 op: BinaryOp::I64Add,
4044 dst: Reg(2),
4045 lhs: Reg(0),
4046 rhs: Reg(1),
4047 },
4048 ]
4050 );
4051 }
4052
4053 #[test]
4054 fn execute_i64_add_cohort() {
4055 let module = Module::decode(i64_add_module()).unwrap();
4056 let reg_module = module.lower().unwrap();
4057
4058 let result = crate::runtime::execute_func(®_module.funcs[0], &[]).unwrap();
4059
4060 assert_eq!(result, vec![crate::runtime::Value::I64(42)]);
4061 }
4062
4063 fn i32_sub_mul_module() -> &'static [u8] {
4064 &[
4065 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x0a, 0x0c, 0x01, 0x0a, 0x00, 0x41, 0x32, 0x41, 0x08, 0x6b, 0x41, 0x03, 0x6c, 0x0b, ]
4077 }
4078
4079 fn i64_add_module() -> &'static [u8] {
4080 &[
4081 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7e, 0x03, 0x02, 0x01, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x42, 0x14, 0x42, 0x16, 0x7c, 0x0b, ]
4091 }
4092
4093 #[test]
4094 fn lower_i32_eqz_cohort() {
4095 let module = Module::decode(i32_eqz_module()).unwrap();
4096 let reg_module = module.lower().unwrap();
4097 let func = ®_module.funcs[0];
4098
4099 assert_eq!(func.reg_types, vec![ValType::Num(NumType::I32); 2]);
4100 assert_eq!(
4101 func.blocks[0]
4102 .instrs
4103 .iter()
4104 .map(|instr| &instr.op)
4105 .collect::<Vec<_>>(),
4106 vec![
4107 &RegOp::LocalGet {
4108 dst: Reg(0),
4109 local: LocalIdx(0),
4110 },
4111 &RegOp::Unary {
4112 op: UnaryOp::I32Eqz,
4113 dst: Reg(1),
4114 value: Reg(0),
4115 },
4116 ]
4118 );
4119 }
4120
4121 #[test]
4122 fn execute_i32_eqz_cohort() {
4123 let module = Module::decode(i32_eqz_module()).unwrap();
4124 let reg_module = module.lower().unwrap();
4125
4126 let zero =
4127 crate::runtime::execute_func(®_module.funcs[0], &[crate::runtime::Value::I32(0)])
4128 .unwrap();
4129 let nonzero =
4130 crate::runtime::execute_func(®_module.funcs[0], &[crate::runtime::Value::I32(7)])
4131 .unwrap();
4132
4133 assert_eq!(zero, vec![crate::runtime::Value::I32(1)]);
4134 assert_eq!(nonzero, vec![crate::runtime::Value::I32(0)]);
4135 }
4136
4137 fn i32_eqz_module() -> &'static [u8] {
4138 &[
4139 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x0a, 0x07, 0x01, 0x05, 0x00, 0x20, 0x00, 0x45, 0x0b, ]
4148 }
4149
4150 #[test]
4151 fn lower_explicit_return_instruction() {
4152 let module = Module::decode(explicit_return_module()).unwrap();
4153 let reg_module = module.lower().unwrap();
4154 let func = ®_module.funcs[0];
4155
4156 assert_eq!(
4157 func.blocks[0]
4158 .instrs
4159 .iter()
4160 .map(|instr| &instr.op)
4161 .collect::<Vec<_>>(),
4162 vec![
4163 &RegOp::I32Const {
4164 dst: Reg(0),
4165 value: 42,
4166 },
4167 ]
4169 );
4170 }
4171
4172 #[test]
4173 fn execute_explicit_return_instruction() {
4174 let module = Module::decode(explicit_return_module()).unwrap();
4175 let reg_module = module.lower().unwrap();
4176
4177 let result = crate::runtime::execute_func(®_module.funcs[0], &[]).unwrap();
4178
4179 assert_eq!(result, vec![crate::runtime::Value::I32(42)]);
4180 }
4181
4182 fn explicit_return_module() -> &'static [u8] {
4183 &[
4184 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x0a, 0x07, 0x01, 0x05, 0x00, 0x41, 0x2a, 0x0f, 0x0b, ]
4193 }
4194
4195 fn lower_wat(source: &str) -> RegModule {
4197 let buf = wast::parser::ParseBuffer::new(source).unwrap();
4198 let mut wat = wast::parser::parse::<wast::Wat<'_>>(&buf).unwrap();
4199 let bytes = wat.encode().unwrap();
4200 let module = Module::decode(&bytes).unwrap();
4201 module.lower().unwrap()
4202 }
4203
4204 fn run_wat(source: &str, args: &[crate::runtime::Value]) -> Vec<crate::runtime::Value> {
4205 let reg_module = lower_wat(source);
4206 crate::runtime::execute_func(®_module.funcs[0], args).unwrap()
4207 }
4208
4209 #[test]
4210 fn lower_if_else_shape() {
4211 let reg_module = lower_wat(
4212 "(module (func (param i32) (result i32)
4213 local.get 0
4214 if (result i32)
4215 i32.const 1
4216 else
4217 i32.const 2
4218 end))",
4219 );
4220 let func = ®_module.funcs[0];
4221
4222 let RegTerm::IfFork {
4225 then_block,
4226 else_block,
4227 ..
4228 } = func.blocks[0].term
4229 else {
4230 panic!("expected IfFork, got {:?}", func.blocks[0].term);
4231 };
4232 assert_eq!(then_block, 1);
4233 assert_ne!(else_block, 0);
4234 assert!(matches!(
4236 func.blocks[then_block as usize].term,
4237 RegTerm::Br { .. }
4238 ));
4239 }
4240
4241 #[test]
4242 fn execute_if_else_paths() {
4243 let source = "(module (func (param i32) (result i32)
4244 local.get 0
4245 if (result i32)
4246 i32.const 1
4247 else
4248 i32.const 2
4249 end))";
4250 assert_eq!(
4251 run_wat(source, &[crate::runtime::Value::I32(1)]),
4252 vec![crate::runtime::Value::I32(1)]
4253 );
4254 assert_eq!(
4255 run_wat(source, &[crate::runtime::Value::I32(0)]),
4256 vec![crate::runtime::Value::I32(2)]
4257 );
4258 }
4259
4260 #[test]
4261 fn lower_if_without_else_patches_else_to_continuation() {
4262 let reg_module = lower_wat(
4263 "(module (func (param i32) (result i32)
4264 local.get 0
4265 if
4266 i32.const 42
4267 drop
4268 end
4269 i32.const 7))",
4270 );
4271 let func = ®_module.funcs[0];
4272 let RegTerm::IfFork { else_block, .. } = func.blocks[0].term else {
4273 panic!("expected IfFork, got {:?}", func.blocks[0].term);
4274 };
4275 let mut idx = else_block;
4278 loop {
4279 match func.blocks[idx as usize].term {
4280 RegTerm::Fallthrough => idx += 1,
4281 RegTerm::Return { .. } => break,
4282 ref other => panic!("unexpected terminator on else path: {other:?}"),
4283 }
4284 }
4285 }
4286
4287 #[test]
4288 fn lower_loop_back_edge_targets_header() {
4289 let reg_module = lower_wat(
4290 "(module (func (param i32) (result i32)
4291 (local i32)
4292 block
4293 loop
4294 local.get 0
4295 i32.eqz
4296 br_if 1
4297 local.get 0
4298 i32.const 1
4299 i32.sub
4300 local.set 0
4301 br 0
4302 end
4303 end
4304 local.get 1))",
4305 );
4306 let func = ®_module.funcs[0];
4307 let back_edge = func
4310 .blocks
4311 .iter()
4312 .enumerate()
4313 .find_map(|(idx, block)| match block.term {
4314 RegTerm::Br { target_block, .. } if (target_block as usize) < idx => {
4315 Some(target_block)
4316 }
4317 _ => None,
4318 })
4319 .expect("expected a loop back-edge Br");
4320 assert_eq!(back_edge, 2);
4323 }
4324
4325 #[test]
4326 fn execute_loop_sum() {
4327 let source = "(module (func (param i32) (result i32)
4328 (local i32)
4329 block
4330 loop
4331 local.get 0
4332 i32.eqz
4333 br_if 1
4334 local.get 1
4335 local.get 0
4336 i32.add
4337 local.set 1
4338 local.get 0
4339 i32.const 1
4340 i32.sub
4341 local.set 0
4342 br 0
4343 end
4344 end
4345 local.get 1))";
4346 assert_eq!(
4347 run_wat(source, &[crate::runtime::Value::I32(5)]),
4348 vec![crate::runtime::Value::I32(15)]
4349 );
4350 assert_eq!(
4351 run_wat(source, &[crate::runtime::Value::I32(0)]),
4352 vec![crate::runtime::Value::I32(0)]
4353 );
4354 }
4355
4356 #[test]
4357 fn lower_br_value_appends_copy_to_branch_block() {
4358 let reg_module = lower_wat(
4359 "(module (func (result i32)
4360 block (result i32)
4361 i32.const 1
4362 br 0
4363 i32.const 2
4364 end))",
4365 );
4366 let func = ®_module.funcs[0];
4367 let br_block = func
4370 .blocks
4371 .iter()
4372 .find(|block| matches!(block.term, RegTerm::Br { .. }))
4373 .expect("expected a Br block");
4374 assert!(
4375 br_block
4376 .instrs
4377 .iter()
4378 .any(|instr| matches!(instr.op, RegOp::Copy { .. })),
4379 "expected a Copy instruction in the branch block: {br_block:?}"
4380 );
4381 }
4382
4383 #[test]
4384 fn execute_br_value_delivers_branch_site_value() {
4385 let source = "(module (func (result i32)
4386 block (result i32)
4387 i32.const 1
4388 br 0
4389 i32.const 2
4390 end))";
4391 assert_eq!(run_wat(source, &[]), vec![crate::runtime::Value::I32(1)]);
4392 }
4393
4394 #[test]
4395 fn execute_return_does_not_stop_lowering() {
4396 let source = "(module (func (param i32) (result i32)
4399 block
4400 local.get 0
4401 br_if 0
4402 i32.const 10
4403 return
4404 end
4405 i32.const 20))";
4406 assert_eq!(
4407 run_wat(source, &[crate::runtime::Value::I32(0)]),
4408 vec![crate::runtime::Value::I32(10)]
4409 );
4410 assert_eq!(
4411 run_wat(source, &[crate::runtime::Value::I32(1)]),
4412 vec![crate::runtime::Value::I32(20)]
4413 );
4414 }
4415
4416 #[test]
4417 fn execute_unreachable_traps() {
4418 let reg_module = lower_wat("(module (func (result i32) unreachable))");
4419 let error = crate::runtime::execute_func(®_module.funcs[0], &[]).unwrap_err();
4420 assert_eq!(
4421 error.kind,
4422 crate::runtime::RuntimeErrorKind::Trap(crate::runtime::RuntimeTrap::Unreachable)
4423 );
4424 }
4425
4426 #[test]
4427 fn fuel_exhaustion_stops_runaway_and_bounded_finishes() {
4428 let reg_module = lower_wat("(module (func (export \"spin\") (loop br 0)))");
4430 let store = crate::runtime::Store::instantiate(®_module).unwrap();
4431 store.set_fuel(Some(1_000));
4432 let error = crate::runtime::execute_export(®_module, &store, "spin", &[]).unwrap_err();
4433 assert_eq!(error.kind, crate::runtime::RuntimeErrorKind::FuelExhausted);
4434 assert_eq!(store.fuel(), Some(0));
4435
4436 let reg_module = lower_wat(
4438 "(module (func (export \"sum\") (result i32) (local i32 i32) \
4439 (block (loop \
4440 local.get 0 i32.const 10 i32.ge_s br_if 1 \
4441 local.get 1 local.get 0 i32.add local.set 1 \
4442 local.get 0 i32.const 1 i32.add local.set 0 \
4443 br 0)) \
4444 local.get 1))",
4445 );
4446 let store = crate::runtime::Store::instantiate(®_module).unwrap();
4447 store.set_fuel(Some(10_000));
4448 let result = crate::runtime::execute_export(®_module, &store, "sum", &[]).unwrap();
4449 assert_eq!(result, vec![crate::runtime::Value::I32(45)]);
4450 assert!(store.fuel().unwrap() < 10_000);
4451
4452 let reg_module = lower_wat("(module (func (export \"one\") (result i32) i32.const 1))");
4454 let store = crate::runtime::Store::instantiate(®_module).unwrap();
4455 assert_eq!(store.fuel(), None);
4456 let result = crate::runtime::execute_export(®_module, &store, "one", &[]).unwrap();
4457 assert_eq!(result, vec![crate::runtime::Value::I32(1)]);
4458 }
4459
4460 fn run_wat_export(
4463 source: &str,
4464 name: &str,
4465 args: &[crate::runtime::Value],
4466 ) -> Result<Vec<crate::runtime::Value>, crate::runtime::RuntimeError> {
4467 let reg_module = lower_wat(source);
4468 let store = crate::runtime::Store::instantiate(®_module).unwrap();
4469 crate::runtime::execute_export(®_module, &store, name, args)
4470 }
4471
4472 #[test]
4473 fn lower_call_shape() {
4474 let reg_module = lower_wat(
4475 "(module
4476 (func $add (param i32 i32) (result i32)
4477 local.get 0 local.get 1 i32.add)
4478 (func (export \"main\") (param i32 i32) (result i32)
4479 local.get 0 local.get 1 call $add))",
4480 );
4481 let func = ®_module.funcs[1];
4482 let call = func.blocks[0]
4483 .instrs
4484 .iter()
4485 .find_map(|instr| match &instr.op {
4486 RegOp::Call {
4487 func,
4488 args,
4489 results,
4490 } => Some((func, args, results)),
4491 _ => None,
4492 })
4493 .expect("expected a Call op");
4494 assert_eq!(*call.0, FuncIdx(0));
4495 assert_eq!(call.1.as_slice(), &[Reg(0), Reg(1)]);
4496 assert_eq!(call.2.as_slice(), &[Reg(2)]);
4497 }
4498
4499 #[test]
4500 fn execute_call_recursion_and_multi_result() {
4501 let fac = run_wat_export(
4502 "(module
4503 (func $fac (param i32) (result i32)
4504 local.get 0
4505 i32.const 2
4506 i32.lt_s
4507 if (result i32)
4508 i32.const 1
4509 else
4510 local.get 0
4511 local.get 0
4512 i32.const 1
4513 i32.sub
4514 call $fac
4515 i32.mul
4516 end)
4517 (func (export \"fac\") (param i32) (result i32)
4518 local.get 0
4519 call $fac))",
4520 "fac",
4521 &[crate::runtime::Value::I32(5)],
4522 );
4523 assert_eq!(fac, Ok(vec![crate::runtime::Value::I32(120)]));
4524
4525 let rem = run_wat_export(
4526 "(module
4527 (func $divmod (param i32 i32) (result i32 i32)
4528 local.get 0 local.get 1 i32.div_u
4529 local.get 0 local.get 1 i32.rem_u)
4530 (func (export \"rem\") (param i32 i32) (result i32)
4531 (local i32)
4532 local.get 0 local.get 1 call $divmod
4533 local.set 2
4534 drop
4535 local.get 2))",
4536 "rem",
4537 &[
4538 crate::runtime::Value::I32(17),
4539 crate::runtime::Value::I32(5),
4540 ],
4541 );
4542 assert_eq!(rem, Ok(vec![crate::runtime::Value::I32(2)]));
4543 }
4544
4545 #[test]
4546 fn execute_call_ref_and_null_trap() {
4547 let result = run_wat_export(
4549 "(module (type $ii (func (param i32) (result i32)))\
4550 (elem declare func $dbl)\
4551 (func $dbl (type $ii) local.get 0 i32.const 2 i32.mul)\
4552 (func (export \"go\") (param i32) (result i32)\
4553 local.get 0 ref.func $dbl call_ref $ii))",
4554 "go",
4555 &[crate::runtime::Value::I32(21)],
4556 )
4557 .unwrap();
4558 assert_eq!(result, vec![crate::runtime::Value::I32(42)]);
4559
4560 let error = run_wat_export(
4562 "(module (type $ii (func (param i32) (result i32)))\
4563 (func (export \"go\") (param i32) (result i32)\
4564 local.get 0 ref.null $ii call_ref $ii))",
4565 "go",
4566 &[crate::runtime::Value::I32(1)],
4567 )
4568 .unwrap_err();
4569 assert_eq!(
4570 error.kind,
4571 crate::runtime::RuntimeErrorKind::Trap(
4572 crate::runtime::RuntimeTrap::NullFunctionReference
4573 )
4574 );
4575 }
4576
4577 #[test]
4578 fn execute_br_on_null_paths() {
4579 let source = "(module (type $ii (func (result i32)))\
4581 (elem declare func $f)\
4582 (func $f (type $ii) i32.const 7)\
4583 (func (export \"null\") (result i32)\
4584 (block ref.null $ii br_on_null 0 call_ref $ii return)\
4585 i32.const 99)\
4586 (func (export \"nonnull\") (result i32)\
4587 (block ref.func $f br_on_null 0 call_ref $ii return)\
4588 i32.const 99))";
4589 let result = run_wat_export(source, "null", &[]).unwrap();
4590 assert_eq!(result, vec![crate::runtime::Value::I32(99)]);
4591 let result = run_wat_export(source, "nonnull", &[]).unwrap();
4592 assert_eq!(result, vec![crate::runtime::Value::I32(7)]);
4593 }
4594
4595 #[test]
4596 fn execute_br_on_non_null_forwards_ref() {
4597 let result = run_wat_export(
4600 "(module (type $ii (func (result i32)))\
4601 (elem declare func $f)\
4602 (func $f (type $ii) i32.const 7)\
4603 (func (export \"nonnull\") (result i32)\
4604 (block (result (ref $ii)) ref.func $f br_on_non_null 0 unreachable)\
4605 call_ref $ii)\
4606 (func (export \"null\") (result i32)\
4607 (block (result (ref $ii)) ref.null $ii br_on_non_null 0 unreachable)\
4608 call_ref $ii))",
4609 "nonnull",
4610 &[],
4611 )
4612 .unwrap();
4613 assert_eq!(result, vec![crate::runtime::Value::I32(7)]);
4614 }
4615
4616 #[test]
4617 fn execute_ref_as_non_null_trap_and_passthrough() {
4618 let error = run_wat_export(
4619 "(module (type $ii (func))\
4620 (func (export \"go\") (result i32)\
4621 ref.null $ii ref.as_non_null drop i32.const 0))",
4622 "go",
4623 &[],
4624 )
4625 .unwrap_err();
4626 assert_eq!(
4627 error.kind,
4628 crate::runtime::RuntimeErrorKind::Trap(crate::runtime::RuntimeTrap::NullReference)
4629 );
4630
4631 let result = run_wat_export(
4632 "(module (type $ii (func (result i32)))\
4633 (elem declare func $f)\
4634 (func $f (type $ii) i32.const 7)\
4635 (func (export \"go\") (result i32)\
4636 ref.func $f ref.as_non_null call_ref $ii))",
4637 "go",
4638 &[],
4639 )
4640 .unwrap();
4641 assert_eq!(result, vec![crate::runtime::Value::I32(7)]);
4642 }
4643
4644 #[test]
4645 fn execute_call_exhaustion_traps() {
4646 let error = std::thread::Builder::new()
4649 .stack_size(32 * 1024 * 1024)
4650 .spawn(|| {
4651 run_wat_export(
4652 "(module (func $boom (export \"boom\") call $boom))",
4653 "boom",
4654 &[],
4655 )
4656 })
4657 .expect("spawn exhaustion test thread")
4658 .join()
4659 .expect("exhaustion test thread panicked")
4660 .unwrap_err();
4661 assert_eq!(
4662 error.kind,
4663 crate::runtime::RuntimeErrorKind::Trap(crate::runtime::RuntimeTrap::CallStackExhausted)
4664 );
4665 }
4666
4667 #[test]
4668 fn execute_select_variants() {
4669 let source = "(module (func (param i32 i32 i32) (result i32)
4670 local.get 0
4671 local.get 1
4672 local.get 2
4673 select))";
4674 assert_eq!(
4675 run_wat(
4676 source,
4677 &[
4678 crate::runtime::Value::I32(10),
4679 crate::runtime::Value::I32(20),
4680 crate::runtime::Value::I32(1),
4681 ],
4682 ),
4683 vec![crate::runtime::Value::I32(10)]
4684 );
4685 assert_eq!(
4686 run_wat(
4687 source,
4688 &[
4689 crate::runtime::Value::I32(10),
4690 crate::runtime::Value::I32(20),
4691 crate::runtime::Value::I32(0),
4692 ],
4693 ),
4694 vec![crate::runtime::Value::I32(20)]
4695 );
4696 }
4697
4698 #[test]
4699 fn lower_select_shape() {
4700 let reg_module = lower_wat(
4701 "(module (func (param i32 i32 i32) (result i32)
4702 local.get 0
4703 local.get 1
4704 local.get 2
4705 select))",
4706 );
4707 let func = ®_module.funcs[0];
4708 assert!(func.blocks[0].instrs.iter().any(|instr| matches!(
4709 instr.op,
4710 RegOp::Select {
4711 dst: Reg(3),
4712 v1: Reg(0),
4713 v2: Reg(1),
4714 cond: Reg(2),
4715 }
4716 )));
4717 }
4718
4719 #[test]
4720 fn lower_br_table_shape_and_patching() {
4721 let reg_module = lower_wat(
4722 "(module (func (param i32) (result i32)
4723 block (result i32)
4724 block (result i32)
4725 i32.const 0
4726 local.get 0
4727 br_table 0 1
4728 end
4729 i32.const 10
4730 i32.add
4731 br 0
4732 end))",
4733 );
4734 let func = ®_module.funcs[0];
4735 let br_table_block = func
4736 .blocks
4737 .iter()
4738 .find(|block| matches!(block.term, RegTerm::BrTable { .. }))
4739 .expect("expected a BrTable block");
4740 let RegTerm::BrTable {
4741 targets, default, ..
4742 } = &br_table_block.term
4743 else {
4744 unreachable!()
4745 };
4746 assert_eq!(targets.len(), 1);
4748 assert_ne!(targets[0], 0);
4749 assert_ne!(*default, 0);
4750 assert_ne!(targets[0], *default);
4751 assert!(
4753 br_table_block
4754 .instrs
4755 .iter()
4756 .any(|instr| matches!(instr.op, RegOp::Copy { .. }))
4757 );
4758 }
4759
4760 #[test]
4761 fn execute_br_table_dispatch() {
4762 let source = "(module (func (param i32) (result i32)
4763 block (result i32)
4764 block (result i32)
4765 i32.const 0
4766 local.get 0
4767 br_table 0 1
4768 end
4769 i32.const 10
4770 i32.add
4771 br 0
4772 end))";
4773 assert_eq!(
4774 run_wat(source, &[crate::runtime::Value::I32(0)]),
4775 vec![crate::runtime::Value::I32(10)]
4776 );
4777 assert_eq!(
4778 run_wat(source, &[crate::runtime::Value::I32(1)]),
4779 vec![crate::runtime::Value::I32(0)]
4780 );
4781 assert_eq!(
4783 run_wat(source, &[crate::runtime::Value::I32(9)]),
4784 vec![crate::runtime::Value::I32(0)]
4785 );
4786 assert_eq!(
4787 run_wat(source, &[crate::runtime::Value::I32(-1)]),
4788 vec![crate::runtime::Value::I32(0)]
4789 );
4790 }
4791
4792 #[test]
4793 fn lower_load_store_shape() {
4794 let reg_module = lower_wat(
4795 "(module
4796 (memory 1)
4797 (func (export \"f\") (param i32) (result i32)
4798 local.get 0
4799 local.get 0
4800 i32.load offset=4
4801 i32.store
4802 i32.const 0))",
4803 );
4804 let func = ®_module.funcs[0];
4805 let ops: Vec<&RegOp> = func.blocks[0]
4806 .instrs
4807 .iter()
4808 .map(|instr| &instr.op)
4809 .collect();
4810 assert!(matches!(
4811 ops[2],
4812 RegOp::Load {
4813 op: LoadOp::I32,
4814 memarg: MemArg { offset: 4, .. },
4815 ..
4816 }
4817 ));
4818 assert!(matches!(
4819 ops[3],
4820 RegOp::Store {
4821 op: StoreOp::I32,
4822 ..
4823 }
4824 ));
4825 }
4826
4827 #[test]
4828 fn execute_memory_roundtrip_and_oob_trap() {
4829 let source = "(module
4830 (memory 1)
4831 (func (export \"roundtrip\") (param i32 i32) (result i32)
4832 local.get 0
4833 local.get 1
4834 i32.store
4835 local.get 0
4836 i32.load)
4837 (func (export \"load\") (param i32) (result i32)
4838 local.get 0
4839 i32.load))";
4840 assert_eq!(
4841 run_wat_export(
4842 source,
4843 "roundtrip",
4844 &[
4845 crate::runtime::Value::I32(8),
4846 crate::runtime::Value::I32(-3)
4847 ],
4848 ),
4849 Ok(vec![crate::runtime::Value::I32(-3)])
4850 );
4851 let error = run_wat_export(source, "load", &[crate::runtime::Value::I32(65533)])
4853 .expect_err("expected OOB trap");
4854 assert_eq!(
4855 error.kind,
4856 crate::runtime::RuntimeErrorKind::Trap(
4857 crate::runtime::RuntimeTrap::OutOfBoundsMemoryAccess
4858 )
4859 );
4860 }
4861
4862 #[test]
4863 fn execute_globals_and_data_segments() {
4864 let source = "(module
4865 (memory 1)
4866 (global $g (mut i32) (i32.const 10))
4867 (data (i32.const 4) \"\\2a\\00\\00\\00\")
4868 (func (export \"bump\") (result i32)
4869 global.get $g
4870 i32.const 1
4871 i32.add
4872 global.set $g
4873 global.get $g)
4874 (func (export \"load4\") (result i32)
4875 i32.const 4
4876 i32.load))";
4877 assert_eq!(
4879 run_wat_export(source, "load4", &[]),
4880 Ok(vec![crate::runtime::Value::I32(42)])
4881 );
4882 assert_eq!(
4883 run_wat_export(source, "bump", &[]),
4884 Ok(vec![crate::runtime::Value::I32(11)])
4885 );
4886 }
4887
4888 #[test]
4889 fn lower_call_indirect_shape() {
4890 let reg_module = lower_wat(
4891 "(module
4892 (type $t (func (param i32) (result i32)))
4893 (table 1 funcref)
4894 (func (export \"apply\") (param i32 i32) (result i32)
4895 local.get 1
4896 local.get 0
4897 call_indirect (type $t)))",
4898 );
4899 let func = ®_module.funcs[0];
4900 let call = func.blocks[0]
4901 .instrs
4902 .iter()
4903 .find_map(|instr| match &instr.op {
4904 RegOp::CallIndirect {
4905 type_idx,
4906 table,
4907 args,
4908 results,
4909 ..
4910 } => Some((type_idx, table, args, results)),
4911 _ => None,
4912 })
4913 .expect("expected a CallIndirect op");
4914 assert_eq!(*call.0, TypeIdx(0));
4915 assert_eq!(*call.1, TableIdx(0));
4916 assert_eq!(call.2.as_slice(), &[Reg(0)]);
4917 assert_eq!(call.3.as_slice(), &[Reg(2)]);
4918 }
4919
4920 #[test]
4921 fn execute_call_indirect_and_traps() {
4922 let source = "(module
4923 (type $t (func (result i32)))
4924 (table 2 funcref)
4925 (func $f (type $t) i32.const 42)
4926 (elem (i32.const 0) $f)
4927 (func (export \"go\") (param i32) (result i32)
4928 local.get 0
4929 call_indirect (type $t)))";
4930 assert_eq!(
4931 run_wat_export(source, "go", &[crate::runtime::Value::I32(0)]),
4932 Ok(vec![crate::runtime::Value::I32(42)])
4933 );
4934 let error = run_wat_export(source, "go", &[crate::runtime::Value::I32(1)])
4936 .expect_err("expected uninitialized element trap");
4937 assert_eq!(
4938 error.kind,
4939 crate::runtime::RuntimeErrorKind::Trap(
4940 crate::runtime::RuntimeTrap::UninitializedElement
4941 )
4942 );
4943 let error = run_wat_export(source, "go", &[crate::runtime::Value::I32(7)])
4945 .expect_err("expected undefined element trap");
4946 assert_eq!(
4947 error.kind,
4948 crate::runtime::RuntimeErrorKind::Trap(crate::runtime::RuntimeTrap::UndefinedElement)
4949 );
4950 }
4951
4952 #[test]
4953 fn execute_table_grow_fill_copy() {
4954 let source = "(module
4955 (type $t (func (result i32)))
4956 (table 1 funcref)
4957 (func $f (type $t) i32.const 9)
4958 (elem declare func $f)
4959 (func (export \"go\") (param i32) (result i32)
4960 local.get 0
4961 call_indirect (type $t))
4962 (func (export \"setup\") (result i32)
4963 ref.func $f
4964 i32.const 2
4965 table.grow
4966 drop
4967 i32.const 1
4968 ref.func $f
4969 i32.const 2
4970 table.fill
4971 i32.const 1
4972 i32.const 2
4973 i32.const 1
4974 table.copy
4975 table.size))";
4976 let reg_module = lower_wat(source);
4978 let store = crate::runtime::Store::instantiate(®_module).unwrap();
4979 assert_eq!(
4980 crate::runtime::execute_export(®_module, &store, "setup", &[]),
4981 Ok(vec![crate::runtime::Value::I32(3)])
4982 );
4983 assert_eq!(
4984 crate::runtime::execute_export(
4985 ®_module,
4986 &store,
4987 "go",
4988 &[crate::runtime::Value::I32(2)],
4989 ),
4990 Ok(vec![crate::runtime::Value::I32(9)])
4991 );
4992 }
4993
4994 #[test]
4995 fn lower_loop_with_params_consumes_them_into_frame() {
4996 let reg_module = lower_wat(
4997 "(module (func (param i32 i32) (result i32)
4998 local.get 0
4999 local.get 1
5000 block (param i32 i32) (result i32)
5001 i32.add
5002 end))",
5003 );
5004 let func = ®_module.funcs[0];
5005 assert_eq!(
5009 crate::runtime::execute_func(
5010 &func.clone(),
5011 &[
5012 crate::runtime::Value::I32(30),
5013 crate::runtime::Value::I32(12)
5014 ],
5015 ),
5016 Ok(vec![crate::runtime::Value::I32(42)])
5017 );
5018 }
5019
5020 #[test]
5021 fn lower_br_if_to_loop_with_params_uses_trampoline() {
5022 let reg_module = lower_wat(
5023 "(module (func (param i32) (result i32)
5024 (local $n i32)
5025 i32.const 0
5026 local.get 0
5027 loop (param i32 i32) (result i32)
5028 local.set $n
5029 local.get $n
5030 i32.add
5031 local.get $n
5032 i32.const 1
5033 i32.sub
5034 local.tee $n
5035 local.get $n
5036 i32.const 1
5037 i32.ge_s
5038 br_if 0
5039 drop
5040 end))",
5041 );
5042 let func = ®_module.funcs[0];
5043 let trampoline = func.blocks.iter().find(|block| {
5046 block
5047 .instrs
5048 .iter()
5049 .any(|instr| matches!(instr.op, RegOp::Copy { .. }))
5050 && matches!(block.term, RegTerm::Br { .. })
5051 });
5052 assert!(
5053 trampoline.is_some(),
5054 "expected a trampoline block with param copies"
5055 );
5056 let br_if = func
5058 .blocks
5059 .iter()
5060 .find_map(|block| match &block.term {
5061 RegTerm::BrIf { target_block, .. } => Some(*target_block),
5062 _ => None,
5063 })
5064 .expect("expected a BrIf terminator");
5065 let trampoline_idx = func
5066 .blocks
5067 .iter()
5068 .position(|block| core::ptr::eq(block, trampoline.unwrap()))
5069 .unwrap() as u32;
5070 assert_eq!(br_if, trampoline_idx);
5071 }
5072
5073 #[test]
5074 fn lower_v128_binary_shape() {
5075 let reg_module = lower_wat(
5076 "(module (func (result v128)
5077 v128.const i32x4 1 2 3 4
5078 v128.const i32x4 5 6 7 8
5079 i32x4.add))",
5080 );
5081 let func = ®_module.funcs[0];
5082 let op = func.blocks[0]
5083 .instrs
5084 .iter()
5085 .find_map(|instr| match &instr.op {
5086 RegOp::V128Binary {
5087 shape, kind, dst, ..
5088 } => Some((shape, kind, dst)),
5089 _ => None,
5090 })
5091 .expect("expected a V128Binary op");
5092 assert_eq!(*op.0, LaneShape::I32x4);
5093 assert_eq!(*op.1, V128BinaryKind::Add);
5094 assert_eq!(*op.2, Reg(2));
5095 }
5096
5097 #[test]
5098 fn execute_v128_arithmetic_and_memory() {
5099 let source = "(module
5100 (memory 1)
5101 (func (export \"add_store\") (param i32) (result i32)
5102 local.get 0
5103 v128.const i32x4 1 2 3 4
5104 v128.const i32x4 10 20 30 40
5105 i32x4.add
5106 v128.store
5107 local.get 0
5108 i32.load))";
5109 assert_eq!(
5111 run_wat_export(source, "add_store", &[crate::runtime::Value::I32(16)]),
5112 Ok(vec![crate::runtime::Value::I32(11)])
5113 );
5114 }
5115
5116 #[test]
5117 fn execute_saturating_truncation_edges() {
5118 let source = "(module (func (export \"sat\") (param f32) (result i32)
5119 local.get 0
5120 i32.trunc_sat_f32_s))";
5121 assert_eq!(
5123 run_wat_export(source, "sat", &[crate::runtime::Value::F32(f32::NAN)]),
5124 Ok(vec![crate::runtime::Value::I32(0)])
5125 );
5126 assert_eq!(
5128 run_wat_export(source, "sat", &[crate::runtime::Value::F32(3e9)]),
5129 Ok(vec![crate::runtime::Value::I32(i32::MAX)])
5130 );
5131 assert_eq!(
5133 run_wat_export(source, "sat", &[crate::runtime::Value::F32(-2.9)]),
5134 Ok(vec![crate::runtime::Value::I32(-2)])
5135 );
5136 }
5137
5138 #[test]
5139 fn lower_bulk_memory_ops_shape() {
5140 let reg_module = lower_wat(
5141 "(module
5142 (memory 1)
5143 (data $d \"ab\")
5144 (func (export \"f\") (param i32)
5145 local.get 0
5146 i32.const 0
5147 i32.const 2
5148 memory.init $d
5149 local.get 0
5150 i32.const 1
5151 i32.const 8
5152 memory.fill
5153 data.drop $d))",
5154 );
5155 let func = ®_module.funcs[0];
5156 let ops: Vec<&RegOp> = func.blocks[0]
5157 .instrs
5158 .iter()
5159 .map(|instr| &instr.op)
5160 .collect();
5161 assert!(matches!(ops[3], RegOp::MemoryInit { .. }));
5162 assert!(matches!(ops[7], RegOp::MemoryFill { .. }));
5163 assert!(matches!(ops[8], RegOp::DataDrop { .. }));
5164 }
5165
5166 #[test]
5167 fn execute_host_function_milestone() {
5168 let source = "(module
5171 (import \"env\" \"print_i32\" (func $print (param i32)))
5172 (func (export \"main\")
5173 i32.const 42
5174 call $print))";
5175 let reg_module = lower_wat(source);
5176 let mut store = crate::runtime::Store::instantiate(®_module).unwrap();
5177
5178 let recorded = alloc::rc::Rc::new(core::cell::RefCell::new(Vec::new()));
5179 let sink = recorded.clone();
5180 store
5181 .register_host_func(
5182 "env",
5183 "print_i32",
5184 crate::runtime::HostFunction::new(
5185 crate::types::FuncType {
5186 params: vec![ValType::Num(NumType::I32)],
5187 results: vec![],
5188 },
5189 move |args| {
5190 sink.borrow_mut().push(args[0]);
5191 Ok(vec![])
5192 },
5193 ),
5194 )
5195 .unwrap();
5196
5197 let result = crate::runtime::execute_export(®_module, &store, "main", &[]);
5198 assert_eq!(result, Ok(vec![]));
5199 assert_eq!(
5200 recorded.borrow().as_slice(),
5201 &[crate::runtime::Value::I32(42)]
5202 );
5203 }
5204
5205 #[test]
5206 fn execute_host_function_with_results() {
5207 let source = "(module
5208 (import \"env\" \"double\" (func $double (param i32) (result i32)))
5209 (func (export \"go\") (param i32) (result i32)
5210 local.get 0
5211 call $double
5212 i32.const 2
5213 i32.add))";
5214 let reg_module = lower_wat(source);
5215 let mut store = crate::runtime::Store::instantiate(®_module).unwrap();
5216 store
5217 .register_host_func(
5218 "env",
5219 "double",
5220 crate::runtime::HostFunction::new(
5221 crate::types::FuncType {
5222 params: vec![ValType::Num(NumType::I32)],
5223 results: vec![ValType::Num(NumType::I32)],
5224 },
5225 |args| {
5226 let crate::runtime::Value::I32(v) = args[0] else {
5227 unreachable!()
5228 };
5229 Ok(vec![crate::runtime::Value::I32(v * 2)])
5230 },
5231 ),
5232 )
5233 .unwrap();
5234
5235 assert_eq!(
5236 crate::runtime::execute_export(
5237 ®_module,
5238 &store,
5239 "go",
5240 &[crate::runtime::Value::I32(20)],
5241 ),
5242 Ok(vec![crate::runtime::Value::I32(42)])
5243 );
5244 }
5245
5246 #[test]
5247 fn unregistered_import_fails_on_call_not_instantiation() {
5248 let source = "(module
5249 (import \"env\" \"missing\" (func $missing))
5250 (func (export \"go\")
5251 call $missing))";
5252 let reg_module = lower_wat(source);
5253 let store = crate::runtime::Store::instantiate(®_module).unwrap();
5255 let error = crate::runtime::execute_export(®_module, &store, "go", &[])
5256 .expect_err("expected UnknownImport on call");
5257 assert_eq!(
5258 error.kind,
5259 crate::runtime::RuntimeErrorKind::UnknownImport {
5260 module: "env".into(),
5261 name: "missing".into(),
5262 }
5263 );
5264 }
5265
5266 #[test]
5267 fn host_registration_rejects_mismatch_and_unknown() {
5268 let source = "(module
5269 (import \"env\" \"f\" (func $f (param i32))))";
5270 let reg_module = lower_wat(source);
5271 let mut store = crate::runtime::Store::instantiate(®_module).unwrap();
5272
5273 let error = store
5275 .register_host_func(
5276 "env",
5277 "f",
5278 crate::runtime::HostFunction::new(
5279 crate::types::FuncType {
5280 params: vec![],
5281 results: vec![],
5282 },
5283 |_| Ok(vec![]),
5284 ),
5285 )
5286 .expect_err("expected ImportTypeMismatch");
5287 assert!(matches!(
5288 error.kind,
5289 crate::runtime::RuntimeErrorKind::ImportTypeMismatch { .. }
5290 ));
5291
5292 let error = store
5294 .register_host_func(
5295 "env",
5296 "nope",
5297 crate::runtime::HostFunction::new(
5298 crate::types::FuncType {
5299 params: vec![],
5300 results: vec![],
5301 },
5302 |_| Ok(vec![]),
5303 ),
5304 )
5305 .expect_err("expected UnknownImport");
5306 assert_eq!(
5307 error.kind,
5308 crate::runtime::RuntimeErrorKind::UnknownImport {
5309 module: "env".into(),
5310 name: "nope".into(),
5311 }
5312 );
5313 }
5314
5315 #[test]
5316 fn execute_imported_memory() {
5317 let source = "(module
5318 (import \"env\" \"mem\" (memory 1 2))
5319 (func (export \"roundtrip\") (param i32 i32) (result i32)
5320 local.get 0
5321 local.get 1
5322 i32.store
5323 local.get 0
5324 i32.load)
5325 (func (export \"grow\") (param i32) (result i32)
5326 local.get 0
5327 memory.grow))";
5328 let reg_module = lower_wat(source);
5329 let imports = crate::runtime::Imports::new().memory(
5330 "env",
5331 "mem",
5332 crate::types::MemType {
5333 limits: crate::types::Limits {
5334 min: 1,
5335 max: Some(2),
5336 },
5337 },
5338 );
5339 let store = crate::runtime::Store::instantiate_with_imports(®_module, &imports).unwrap();
5340
5341 assert_eq!(
5342 crate::runtime::execute_export(
5343 ®_module,
5344 &store,
5345 "roundtrip",
5346 &[
5347 crate::runtime::Value::I32(8),
5348 crate::runtime::Value::I32(42)
5349 ],
5350 ),
5351 Ok(vec![crate::runtime::Value::I32(42)])
5352 );
5353 assert_eq!(
5355 crate::runtime::execute_export(
5356 ®_module,
5357 &store,
5358 "grow",
5359 &[crate::runtime::Value::I32(1)],
5360 ),
5361 Ok(vec![crate::runtime::Value::I32(1)])
5362 );
5363 assert_eq!(
5364 crate::runtime::execute_export(
5365 ®_module,
5366 &store,
5367 "grow",
5368 &[crate::runtime::Value::I32(1)],
5369 ),
5370 Ok(vec![crate::runtime::Value::I32(-1)])
5371 );
5372 }
5373
5374 #[test]
5375 fn execute_imported_global_and_chained_init() {
5376 let source = "(module
5377 (import \"env\" \"base\" (global $base i32))
5378 (global $derived i32 (i32.add (global.get $base) (i32.const 8)))
5379 (func (export \"get_derived\") (result i32)
5380 global.get $derived))";
5381 let reg_module = lower_wat(source);
5382 let imports = crate::runtime::Imports::new().global(
5383 "env",
5384 "base",
5385 crate::types::GlobalType {
5386 val_type: ValType::Num(NumType::I32),
5387 mutability: crate::types::Mutability::Const,
5388 },
5389 crate::runtime::Value::I32(42),
5390 );
5391 let store = crate::runtime::Store::instantiate_with_imports(®_module, &imports).unwrap();
5392
5393 assert_eq!(
5395 crate::runtime::execute_export(®_module, &store, "get_derived", &[]),
5396 Ok(vec![crate::runtime::Value::I32(50)])
5397 );
5398 }
5399
5400 #[test]
5401 fn execute_imported_mutable_global() {
5402 let source = "(module
5403 (import \"env\" \"counter\" (global $counter (mut i32)))
5404 (func (export \"bump\") (result i32)
5405 global.get $counter
5406 i32.const 1
5407 i32.add
5408 global.set $counter
5409 global.get $counter))";
5410 let reg_module = lower_wat(source);
5411 let imports = crate::runtime::Imports::new().global(
5412 "env",
5413 "counter",
5414 crate::types::GlobalType {
5415 val_type: ValType::Num(NumType::I32),
5416 mutability: crate::types::Mutability::Var,
5417 },
5418 crate::runtime::Value::I32(41),
5419 );
5420 let store = crate::runtime::Store::instantiate_with_imports(®_module, &imports).unwrap();
5421 assert_eq!(
5422 crate::runtime::execute_export(®_module, &store, "bump", &[]),
5423 Ok(vec![crate::runtime::Value::I32(42)])
5424 );
5425 }
5426
5427 #[test]
5428 fn execute_imported_table() {
5429 let source = "(module
5430 (import \"env\" \"tbl\" (table 2 funcref))
5431 (type $t (func (result i32)))
5432 (func $f (type $t) i32.const 42)
5433 (elem declare func $f)
5434 (func (export \"go\") (param i32) (result i32)
5435 local.get 0
5436 call_indirect (type $t))
5437 (func (export \"seed\")
5438 i32.const 1
5439 ref.func $f
5440 table.set))";
5441 let reg_module = lower_wat(source);
5442 let imports = crate::runtime::Imports::new().table(
5443 "env",
5444 "tbl",
5445 crate::types::TableType {
5446 elem: crate::types::RefType::FuncRef,
5447 limits: crate::types::Limits { min: 2, max: None },
5448 init: None,
5449 },
5450 );
5451 let store = crate::runtime::Store::instantiate_with_imports(®_module, &imports).unwrap();
5452
5453 crate::runtime::execute_export(®_module, &store, "seed", &[]).unwrap();
5454 assert_eq!(
5455 crate::runtime::execute_export(
5456 ®_module,
5457 &store,
5458 "go",
5459 &[crate::runtime::Value::I32(1)],
5460 ),
5461 Ok(vec![crate::runtime::Value::I32(42)])
5462 );
5463 }
5464
5465 #[test]
5466 fn start_function_runs_at_instantiation() {
5467 let source = "(module
5468 (global $g (mut i32) (i32.const 0))
5469 (func $init
5470 i32.const 42
5471 global.set $g)
5472 (func (export \"get\") (result i32)
5473 global.get $g)
5474 (start $init))";
5475 let reg_module = lower_wat(source);
5476 let mut store = crate::runtime::Store::instantiate(®_module).unwrap();
5477 store.run_start(®_module).unwrap();
5478 assert_eq!(
5479 crate::runtime::execute_export(®_module, &store, "get", &[]),
5480 Ok(vec![crate::runtime::Value::I32(42)])
5481 );
5482 }
5483
5484 #[test]
5485 fn instantiate_missing_and_mismatched_state_imports() {
5486 let source = "(module
5487 (import \"env\" \"mem\" (memory 2))
5488 (import \"env\" \"g\" (global i32)))";
5489 let reg_module = lower_wat(source);
5490
5491 let error =
5493 crate::runtime::Store::instantiate(®_module).expect_err("expected UnknownImport");
5494 assert_eq!(
5495 error.kind,
5496 crate::runtime::RuntimeErrorKind::UnknownImport {
5497 module: "env".into(),
5498 name: "mem".into(),
5499 }
5500 );
5501
5502 let imports = crate::runtime::Imports::new()
5504 .memory(
5505 "env",
5506 "mem",
5507 crate::types::MemType {
5508 limits: crate::types::Limits { min: 1, max: None },
5509 },
5510 )
5511 .global(
5512 "env",
5513 "g",
5514 crate::types::GlobalType {
5515 val_type: ValType::Num(NumType::I32),
5516 mutability: crate::types::Mutability::Const,
5517 },
5518 crate::runtime::Value::I32(0),
5519 );
5520 let error = crate::runtime::Store::instantiate_with_imports(®_module, &imports)
5521 .expect_err("expected ImportTypeMismatch");
5522 assert!(matches!(
5523 error.kind,
5524 crate::runtime::RuntimeErrorKind::ImportTypeMismatch { .. }
5525 ));
5526 }
5527
5528 #[test]
5529 fn linked_function_call_across_modules() {
5530 let module_a = alloc::rc::Rc::new(lower_wat(
5533 "(module
5534 (func (export \"add\") (param i32 i32) (result i32)
5535 local.get 0 local.get 1 i32.add))",
5536 ));
5537 let store_a = alloc::rc::Rc::new(core::cell::RefCell::new(
5538 crate::runtime::Store::instantiate(&module_a).unwrap(),
5539 ));
5540 let func_idx = store_a.borrow().export_func("add").unwrap();
5541
5542 let module_b = lower_wat(
5543 "(module
5544 (import \"a\" \"add\" (func $add (param i32 i32) (result i32)))
5545 (func (export \"go\") (param i32 i32) (result i32)
5546 local.get 0 local.get 1 call $add))",
5547 );
5548 let mut store_b = crate::runtime::Store::instantiate(&module_b).unwrap();
5549 let ty = module_b.imported_funcs[0].ty.clone();
5550 store_b
5551 .register_host_func(
5552 "a",
5553 "add",
5554 crate::runtime::link_func(module_a.clone(), store_a.clone(), func_idx, ty),
5555 )
5556 .unwrap();
5557
5558 assert_eq!(
5559 crate::runtime::execute_export(
5560 &module_b,
5561 &store_b,
5562 "go",
5563 &[
5564 crate::runtime::Value::I32(20),
5565 crate::runtime::Value::I32(22)
5566 ],
5567 ),
5568 Ok(vec![crate::runtime::Value::I32(42)])
5569 );
5570 }
5571
5572 #[test]
5573 fn shared_memory_across_modules() {
5574 let module_a = alloc::rc::Rc::new(lower_wat(
5577 "(module
5578 (memory (export \"mem\") 1)
5579 (func (export \"load\") (param i32) (result i32)
5580 local.get 0 i32.load))",
5581 ));
5582 let store_a = alloc::rc::Rc::new(core::cell::RefCell::new(
5583 crate::runtime::Store::instantiate(&module_a).unwrap(),
5584 ));
5585 let (mem_ty, shared_mem) = store_a.borrow().export_memory("mem").unwrap();
5586
5587 let module_b = lower_wat(
5588 "(module
5589 (import \"a\" \"mem\" (memory 1))
5590 (func (export \"store\") (param i32 i32)
5591 local.get 0 local.get 1 i32.store))",
5592 );
5593 let imports = crate::runtime::Imports::new().shared_memory("a", "mem", mem_ty, shared_mem);
5594 let store_b = crate::runtime::Store::instantiate_with_imports(&module_b, &imports).unwrap();
5595
5596 crate::runtime::execute_export(
5597 &module_b,
5598 &store_b,
5599 "store",
5600 &[
5601 crate::runtime::Value::I32(16),
5602 crate::runtime::Value::I32(42),
5603 ],
5604 )
5605 .unwrap();
5606
5607 let store_a_mut = store_a.borrow_mut();
5609 assert_eq!(
5610 crate::runtime::execute_export(
5611 &module_a,
5612 &store_a_mut,
5613 "load",
5614 &[crate::runtime::Value::I32(16)],
5615 ),
5616 Ok(vec![crate::runtime::Value::I32(42)])
5617 );
5618 }
5619
5620 #[test]
5621 fn shared_global_across_modules() {
5622 let module_a = alloc::rc::Rc::new(lower_wat(
5623 "(module
5624 (global (export \"counter\") (mut i32) (i32.const 41))
5625 (func (export \"get\") (result i32) global.get 0))",
5626 ));
5627 let store_a = alloc::rc::Rc::new(core::cell::RefCell::new(
5628 crate::runtime::Store::instantiate(&module_a).unwrap(),
5629 ));
5630 let (global_ty, shared_global) = store_a.borrow().export_global("counter").unwrap();
5631
5632 let module_b = lower_wat(
5633 "(module
5634 (import \"a\" \"counter\" (global (mut i32)))
5635 (func (export \"bump\") (result i32)
5636 global.get 0
5637 i32.const 1
5638 i32.add
5639 global.set 0
5640 global.get 0))",
5641 );
5642 let imports =
5643 crate::runtime::Imports::new().shared_global("a", "counter", global_ty, shared_global);
5644 let store_b = crate::runtime::Store::instantiate_with_imports(&module_b, &imports).unwrap();
5645
5646 crate::runtime::execute_export(&module_b, &store_b, "bump", &[]).unwrap();
5647
5648 let store_a_mut = store_a.borrow_mut();
5649 assert_eq!(
5650 crate::runtime::execute_export(&module_a, &store_a_mut, "get", &[]),
5651 Ok(vec![crate::runtime::Value::I32(42)])
5652 );
5653 }
5654
5655 #[test]
5656 fn reentrant_store_call_fails_gracefully() {
5657 let module_a = alloc::rc::Rc::new(lower_wat("(module (func (export \"noop\")))"));
5660 let store_a = alloc::rc::Rc::new(core::cell::RefCell::new(
5661 crate::runtime::Store::instantiate(&module_a).unwrap(),
5662 ));
5663 let func_idx = store_a.borrow().export_func("noop").unwrap();
5664 let mut guard = crate::runtime::link_func(
5665 module_a,
5666 store_a.clone(),
5667 func_idx,
5668 crate::types::FuncType {
5669 params: vec![],
5670 results: vec![],
5671 },
5672 );
5673
5674 let _hold = store_a.borrow_mut(); let error = guard.call(&[]).expect_err("expected ReentrantStore");
5676 assert_eq!(error.kind, crate::runtime::RuntimeErrorKind::ReentrantStore);
5677 }
5678
5679 #[test]
5680 fn lower_else_without_if_is_an_error() {
5681 let bytes = [
5683 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x0a, 0x05, 0x01, 0x03, 0x00, 0x05, 0x0b, ];
5691 let module = Module::decode(&bytes).unwrap();
5692 let error = module.lower().unwrap_err();
5695 assert!(matches!(
5696 error.kind,
5697 LowerErrorKind::Validation(_) | LowerErrorKind::UnexpectedElse
5698 ));
5699 }
5700}