1use alloc::{string::String, vec::Vec};
11
12mod store;
13mod table;
14
15pub mod gpu;
16pub mod host;
17pub mod verify;
18
19pub use table::Table;
20
21pub use host::{HostFunction, link_func};
22pub use store::{Imports, LinkGroup, PAGE_SIZE, Store};
23
24use crate::lower::{
25 BinaryOp, LaneShape, Reg, RegFunc, RegInstr, RegModule, RegOp, RegTerm, UnaryOp, V128BinaryKind,
26};
27use crate::types::{FuncIdx, MemArg, NumType, RefType, TableIdx, ValType};
28
29#[derive(Debug, Clone, Copy, PartialEq)]
31pub enum Value {
32 I32(i32),
33 I64(i64),
34 F32(f32),
35 F64(f64),
36 FuncRef(Option<(u32, u32)>),
41 ExternRef(Option<u32>),
44 V128([u8; 16]),
47}
48
49impl Value {
50 fn val_type(self) -> ValType {
51 match self {
52 Value::I32(_) => ValType::Num(NumType::I32),
53 Value::I64(_) => ValType::Num(NumType::I64),
54 Value::F32(_) => ValType::Num(NumType::F32),
55 Value::F64(_) => ValType::Num(NumType::F64),
56 Value::FuncRef(_) => ValType::Ref(RefType::FuncRef),
57 Value::ExternRef(_) => ValType::Ref(RefType::ExternRef),
58 Value::V128(_) => ValType::Vec(crate::types::VecType::V128),
59 }
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
65pub struct RuntimeError {
66 pub kind: RuntimeErrorKind,
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
71pub enum RuntimeErrorKind {
72 ArityMismatch {
73 expected: usize,
74 found: usize,
75 },
76 TypeMismatch {
77 expected: ValType,
78 found: ValType,
79 },
80 Trap(RuntimeTrap),
81 UninitializedLocal {
82 local: u32,
83 },
84 UninitializedRegister {
85 reg: Reg,
86 },
87 UnknownRegister {
88 reg: Reg,
89 },
90 UnknownExport {
91 name: String,
92 },
93 ExportedFunctionNotLowered {
94 func: u32,
95 },
96 UnknownFunction {
97 func: u32,
98 },
99 UnknownImport {
100 module: String,
101 name: String,
102 },
103 ImportTypeMismatch {
104 module: String,
105 name: String,
106 },
107 ReentrantStore,
108 UnknownInstance {
109 instance: u32,
110 },
111 ResourceLimitExceeded {
112 what: &'static str,
113 },
114 FuelExhausted,
116 HostError {
118 message: String,
119 },
120 ImportedFunctionCallUnsupported {
121 func: u32,
122 },
123 UnknownMemory {
124 memory: u32,
125 },
126 UnknownDataSegment {
127 data: u32,
128 },
129 UnknownGlobal {
130 global: u32,
131 },
132 UnknownTable {
133 table: u32,
134 },
135 UnknownElem {
136 elem: u32,
137 },
138 UnknownType {
139 type_idx: u32,
140 },
141 ImportedMemoryAccessUnsupported {
142 memory: u32,
143 },
144 ImportedGlobalAccessUnsupported {
145 global: u32,
146 },
147 ImportedTableAccessUnsupported {
148 table: u32,
149 },
150 InvalidConstExpr,
151 InvalidLaneIndex {
152 lane: u8,
153 },
154 Gpu(crate::runtime::gpu::GpuError),
155 MissingStore,
156 MissingReturn,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
161pub enum RuntimeTrap {
162 Unreachable,
163 CallStackExhausted,
164 OutOfBoundsMemoryAccess,
165 OutOfBoundsTableAccess,
166 UndefinedElement,
167 UninitializedElement,
168 IndirectCallTypeMismatch,
169 IntegerDivideByZero,
170 IntegerOverflow,
171 InvalidConversionToInteger,
172 NullFunctionReference,
173 NullReference,
174}
175
176impl RuntimeTrap {
177 pub fn wast_message(self) -> &'static str {
179 match self {
180 RuntimeTrap::Unreachable => "unreachable",
181 RuntimeTrap::CallStackExhausted => "call stack exhausted",
182 RuntimeTrap::OutOfBoundsMemoryAccess => "out of bounds memory access",
183 RuntimeTrap::OutOfBoundsTableAccess => "out of bounds table access",
184 RuntimeTrap::UndefinedElement => "undefined element",
185 RuntimeTrap::UninitializedElement => "uninitialized element",
186 RuntimeTrap::IndirectCallTypeMismatch => "indirect call type mismatch",
187 RuntimeTrap::IntegerDivideByZero => "integer divide by zero",
188 RuntimeTrap::IntegerOverflow => "integer overflow",
189 RuntimeTrap::InvalidConversionToInteger => "invalid conversion to integer",
190 RuntimeTrap::NullFunctionReference => "null function reference",
191 RuntimeTrap::NullReference => "null reference",
192 }
193 }
194}
195
196pub fn execute_export(
198 module: &RegModule,
199 store: &Store,
200 name: &str,
201 args: &[Value],
202) -> Result<Vec<Value>, RuntimeError> {
203 let export = module
204 .exports
205 .iter()
206 .find(|export| export.name == name)
207 .ok_or_else(|| RuntimeError {
208 kind: RuntimeErrorKind::UnknownExport { name: name.into() },
209 })?;
210
211 let func_idx = match export.desc {
212 crate::lower::RegExportDesc::Func(idx) => idx,
213 _ => {
214 return Err(RuntimeError {
215 kind: RuntimeErrorKind::UnknownExport { name: name.into() },
216 });
217 }
218 };
219 if func_idx.0 < module.imported_func_count {
222 return store.call_host(func_idx.0, args);
223 }
224 let func = module
225 .funcs
226 .iter()
227 .find(|func| func.idx == func_idx)
228 .ok_or(RuntimeError {
229 kind: RuntimeErrorKind::ExportedFunctionNotLowered { func: func_idx.0 },
230 })?;
231
232 execute_func_in(Some(module), Some(store), func, args, 0)
233}
234
235fn execute_call(
237 module: Option<&RegModule>,
238 store: Option<&Store>,
239 callee_idx: &FuncIdx,
240 call_args: &[Value],
241 depth: usize,
242) -> Result<Vec<Value>, RuntimeError> {
243 let module = module.ok_or(RuntimeError {
244 kind: RuntimeErrorKind::UnknownFunction { func: callee_idx.0 },
245 })?;
246 if callee_idx.0 < module.imported_func_count {
247 let store = store.ok_or(RuntimeError {
250 kind: RuntimeErrorKind::MissingStore,
251 })?;
252 return store.call_host(callee_idx.0, call_args);
253 }
254 let callee = module
255 .funcs
256 .iter()
257 .find(|func| func.idx == *callee_idx)
258 .ok_or(RuntimeError {
259 kind: RuntimeErrorKind::UnknownFunction { func: callee_idx.0 },
260 })?;
261 execute_func_in(Some(module), store, callee, call_args, depth + 1)
262}
263
264#[allow(clippy::too_many_arguments)]
266fn execute_call_indirect(
267 module: Option<&RegModule>,
268 store: Option<&Store>,
269 type_idx: &crate::types::TypeIdx,
270 table: &TableIdx,
271 idx: u32,
272 call_args: &[Value],
273 depth: usize,
274) -> Result<Vec<Value>, RuntimeError> {
275 let module = module.ok_or(RuntimeError {
276 kind: RuntimeErrorKind::UnknownFunction { func: 0 },
277 })?;
278 let store = store.ok_or(RuntimeError {
279 kind: RuntimeErrorKind::MissingStore,
280 })?;
281 let target = store
282 .with_table(table.0, |table| table.get(idx))
283 .ok_or(RuntimeError {
284 kind: RuntimeErrorKind::UnknownTable { table: table.0 },
285 })?
286 .ok_or(trap(RuntimeTrap::UndefinedElement))?;
287 let Value::FuncRef(func_idx) = target else {
288 return Err(RuntimeError {
289 kind: RuntimeErrorKind::TypeMismatch {
290 expected: ValType::Ref(RefType::FuncRef),
291 found: target.val_type(),
292 },
293 });
294 };
295 let Some((instance_id, func_idx)) = func_idx else {
296 return Err(trap(RuntimeTrap::UninitializedElement));
297 };
298 execute_funcref(
299 module,
300 store,
301 type_idx,
302 instance_id,
303 func_idx,
304 call_args,
305 depth,
306 )
307}
308
309fn execute_call_ref(
311 module: Option<&RegModule>,
312 store: Option<&Store>,
313 type_idx: &crate::types::TypeIdx,
314 target: Value,
315 call_args: &[Value],
316 depth: usize,
317) -> Result<Vec<Value>, RuntimeError> {
318 let module = module.ok_or(RuntimeError {
319 kind: RuntimeErrorKind::UnknownFunction { func: 0 },
320 })?;
321 let store = store.ok_or(RuntimeError {
322 kind: RuntimeErrorKind::MissingStore,
323 })?;
324 let Value::FuncRef(func_idx) = target else {
325 return Err(RuntimeError {
326 kind: RuntimeErrorKind::TypeMismatch {
327 expected: ValType::Ref(RefType::FuncRef),
328 found: target.val_type(),
329 },
330 });
331 };
332 let Some((instance_id, func_idx)) = func_idx else {
333 return Err(trap(RuntimeTrap::NullFunctionReference));
334 };
335 execute_funcref(
336 module,
337 store,
338 type_idx,
339 instance_id,
340 func_idx,
341 call_args,
342 depth,
343 )
344}
345
346#[allow(clippy::too_many_arguments)]
350fn execute_funcref(
351 module: &RegModule,
352 store: &Store,
353 type_idx: &crate::types::TypeIdx,
354 instance_id: u32,
355 func_idx: u32,
356 call_args: &[Value],
357 depth: usize,
358) -> Result<Vec<Value>, RuntimeError> {
359 if instance_id != store.instance_id() {
360 let (module, target_store) = {
364 let group = store.link_group().ok_or(RuntimeError {
365 kind: RuntimeErrorKind::UnknownInstance {
366 instance: instance_id,
367 },
368 })?;
369 let group = group.borrow();
370 let Some((module, store)) = group.get(&instance_id) else {
371 return Err(RuntimeError {
372 kind: RuntimeErrorKind::UnknownInstance {
373 instance: instance_id,
374 },
375 });
376 };
377 (module.clone(), store.clone())
378 };
379 let expected = module.types.get(type_idx.0 as usize).ok_or(RuntimeError {
380 kind: RuntimeErrorKind::UnknownType {
381 type_idx: type_idx.0,
382 },
383 })?;
384 let actual = if func_idx < module.imported_func_count {
385 &module
386 .imported_funcs
387 .get(func_idx as usize)
388 .ok_or(RuntimeError {
389 kind: RuntimeErrorKind::UnknownFunction { func: func_idx },
390 })?
391 .ty
392 } else {
393 let callee = module
394 .funcs
395 .iter()
396 .find(|func| func.idx.0 == func_idx)
397 .ok_or(RuntimeError {
398 kind: RuntimeErrorKind::UnknownFunction { func: func_idx },
399 })?;
400 module
401 .types
402 .get(callee.type_idx.0 as usize)
403 .ok_or(RuntimeError {
404 kind: RuntimeErrorKind::UnknownType {
405 type_idx: callee.type_idx.0,
406 },
407 })?
408 };
409 if expected != actual {
410 return Err(trap(RuntimeTrap::IndirectCallTypeMismatch));
411 }
412 let target_store = target_store.borrow();
413 if func_idx < module.imported_func_count {
414 return target_store.call_host(func_idx, call_args);
415 }
416 let callee = module
417 .funcs
418 .iter()
419 .find(|func| func.idx.0 == func_idx)
420 .ok_or(RuntimeError {
421 kind: RuntimeErrorKind::UnknownFunction { func: func_idx },
422 })?;
423 return execute_func_in(
424 Some(&module),
425 Some(&*target_store),
426 callee,
427 call_args,
428 depth + 1,
429 );
430 }
431 let expected = module.types.get(type_idx.0 as usize).ok_or(RuntimeError {
436 kind: RuntimeErrorKind::UnknownType {
437 type_idx: type_idx.0,
438 },
439 })?;
440 if func_idx < module.imported_func_count {
441 let import_ty = &module
442 .imported_funcs
443 .get(func_idx as usize)
444 .ok_or(RuntimeError {
445 kind: RuntimeErrorKind::UnknownFunction { func: func_idx },
446 })?
447 .ty;
448 if expected != import_ty {
449 return Err(trap(RuntimeTrap::IndirectCallTypeMismatch));
450 }
451 return store.call_host(func_idx, call_args);
453 }
454 let callee = module
455 .funcs
456 .iter()
457 .find(|func| func.idx.0 == func_idx)
458 .ok_or(RuntimeError {
459 kind: RuntimeErrorKind::UnknownFunction { func: func_idx },
460 })?;
461 let actual = module
462 .types
463 .get(callee.type_idx.0 as usize)
464 .ok_or(RuntimeError {
465 kind: RuntimeErrorKind::UnknownType {
466 type_idx: callee.type_idx.0,
467 },
468 })?;
469 if expected != actual {
470 return Err(trap(RuntimeTrap::IndirectCallTypeMismatch));
471 }
472 execute_func_in(Some(module), Some(store), callee, call_args, depth + 1)
473}
474
475const MAX_CALL_DEPTH: usize = 512;
479
480pub fn execute_func(func: &RegFunc, args: &[Value]) -> Result<Vec<Value>, RuntimeError> {
486 execute_func_in(None, None, func, args, 0)
487}
488
489pub(crate) fn execute_func_in(
493 module: Option<&RegModule>,
494 store: Option<&Store>,
495 func: &RegFunc,
496 args: &[Value],
497 depth: usize,
498) -> Result<Vec<Value>, RuntimeError> {
499 if depth >= MAX_CALL_DEPTH {
500 return Err(trap(RuntimeTrap::CallStackExhausted));
501 }
502 if args.len() != func.params.len() {
503 return Err(RuntimeError {
504 kind: RuntimeErrorKind::ArityMismatch {
505 expected: func.params.len(),
506 found: args.len(),
507 },
508 });
509 }
510
511 for (&arg, &expected) in args.iter().zip(func.params.iter()) {
512 if !value_satisfies(expected, arg) {
513 return Err(RuntimeError {
514 kind: RuntimeErrorKind::TypeMismatch {
515 expected,
516 found: arg.val_type(),
517 },
518 });
519 }
520 }
521
522 let mut locals = alloc::vec![None; func.locals.len()];
523 for (idx, &arg) in args.iter().enumerate() {
524 locals[idx] = Some(arg);
525 }
526 for (slot, &ty) in locals.iter_mut().zip(func.locals.iter()).skip(args.len()) {
529 if slot.is_none() {
530 *slot = match ty {
531 ValType::Num(NumType::I32) => Some(Value::I32(0)),
532 ValType::Num(NumType::I64) => Some(Value::I64(0)),
533 ValType::Num(NumType::F32) => Some(Value::F32(0.0)),
534 ValType::Num(NumType::F64) => Some(Value::F64(0.0)),
535 ValType::Ref(ref_type) if ref_nullable(&ref_type) => {
536 Some(ref_null_value(&ref_type))
537 }
538 ValType::Vec(_) => Some(Value::V128([0; 16])),
539 _ => None,
540 };
541 }
542 }
543
544 let mut registers = alloc::vec![None; func.reg_types.len()];
545
546 if func.blocks.is_empty() {
547 return Err(RuntimeError {
548 kind: RuntimeErrorKind::MissingReturn,
549 });
550 }
551
552 let mut block_idx: u32 = 0;
553 let max_iterations = 10_000_000;
557 let mut iteration: usize = 0;
558 loop {
559 iteration += 1;
560 if iteration > max_iterations {
561 return Err(RuntimeError {
562 kind: RuntimeErrorKind::MissingReturn,
563 });
564 }
565 if let Some(store) = store
566 && !store.charge_fuel()
567 {
568 return Err(RuntimeError {
569 kind: RuntimeErrorKind::FuelExhausted,
570 });
571 }
572 let block = func.blocks.get(block_idx as usize).ok_or(RuntimeError {
573 kind: RuntimeErrorKind::MissingReturn,
574 })?;
575
576 for instr in &block.instrs {
578 if let Some(store) = store
579 && !store.charge_fuel()
580 {
581 return Err(RuntimeError {
582 kind: RuntimeErrorKind::FuelExhausted,
583 });
584 }
585 if let RegOp::Call {
586 func: callee_idx,
587 args: arg_regs,
588 results,
589 } = &instr.op
590 {
591 let call_args = arg_regs
592 .iter()
593 .map(|®| get_reg(®isters, reg))
594 .collect::<Result<Vec<_>, _>>()?;
595 let returned = execute_call(module, store, callee_idx, &call_args, depth)?;
596 for (&dst, value) in results.iter().zip(returned) {
597 set_reg(&mut registers, dst, value)?;
598 }
599 } else if let RegOp::CallIndirect {
600 type_idx,
601 table,
602 index,
603 args: arg_regs,
604 results,
605 } = &instr.op
606 {
607 let idx = expect_addr(get_reg(®isters, *index)?)?;
608 let call_args = arg_regs
609 .iter()
610 .map(|®| get_reg(®isters, reg))
611 .collect::<Result<Vec<_>, _>>()?;
612 let returned =
613 execute_call_indirect(module, store, type_idx, table, idx, &call_args, depth)?;
614 for (&dst, value) in results.iter().zip(returned) {
615 set_reg(&mut registers, dst, value)?;
616 }
617 } else if let RegOp::CallRef {
618 type_idx,
619 func,
620 args: arg_regs,
621 results,
622 } = &instr.op
623 {
624 let target = get_reg(®isters, *func)?;
625 let call_args = arg_regs
626 .iter()
627 .map(|®| get_reg(®isters, reg))
628 .collect::<Result<Vec<_>, _>>()?;
629 let returned =
630 execute_call_ref(module, store, type_idx, target, &call_args, depth)?;
631 for (&dst, value) in results.iter().zip(returned) {
632 set_reg(&mut registers, dst, value)?;
633 }
634 } else {
635 execute_reg_op(store, &mut registers, &mut locals, instr)?;
636 }
637 }
638
639 match &block.term {
641 RegTerm::Return { values } => {
642 let mut results = Vec::with_capacity(values.len());
643 for ® in values {
644 results.push(get_reg(®isters, reg)?);
645 }
646 return Ok(results);
647 }
648 RegTerm::IfFork {
649 cond,
650 then_block,
651 else_block,
652 } => {
653 let val = get_reg(®isters, *cond)?;
654 if let Value::I32(v) = val {
655 if v != 0 {
656 block_idx = *then_block;
657 } else {
658 block_idx = *else_block;
659 }
660 } else {
661 block_idx = *else_block;
662 }
663 }
664 RegTerm::Br { target_block, .. } => {
665 block_idx = *target_block;
666 }
667 RegTerm::BrIf {
668 cond, target_block, ..
669 } => {
670 let val = get_reg(®isters, *cond)?;
671 if let Value::I32(v) = val {
672 if v != 0 {
673 block_idx = *target_block;
674 } else {
675 block_idx += 1;
676 }
677 } else {
678 block_idx += 1;
679 }
680 }
681 RegTerm::BrIfNull {
682 value,
683 target_block,
684 ..
685 } => {
686 let val = get_reg(®isters, *value)?;
687 if is_null_ref(&val) {
688 block_idx = *target_block;
689 } else {
690 block_idx += 1;
691 }
692 }
693 RegTerm::BrIfNonNull {
694 value,
695 target_block,
696 ..
697 } => {
698 let val = get_reg(®isters, *value)?;
699 if is_null_ref(&val) {
700 block_idx += 1;
701 } else {
702 block_idx = *target_block;
703 }
704 }
705 RegTerm::BrTable {
706 index,
707 targets,
708 default,
709 ..
710 } => {
711 let val = get_reg(®isters, *index)?;
712 let idx = match val {
715 Value::I32(v) => v as u32 as usize,
716 _ => targets.len(),
717 };
718 block_idx = if idx < targets.len() {
719 targets[idx]
720 } else {
721 *default
722 };
723 }
724 RegTerm::Fallthrough => {
725 block_idx += 1;
726 if block_idx as usize >= func.blocks.len() {
727 return Err(RuntimeError {
728 kind: RuntimeErrorKind::MissingReturn,
729 });
730 }
731 }
732 RegTerm::Trap => {
733 return Err(trap(RuntimeTrap::Unreachable));
734 }
735 }
736 }
737}
738
739fn execute_reg_op(
740 store: Option<&Store>,
741 registers: &mut [Option<Value>],
742 locals: &mut [Option<Value>],
743 instr: &RegInstr,
744) -> Result<(), RuntimeError> {
745 match &instr.op {
746 RegOp::LocalGet { dst, local } => {
747 let value = locals
748 .get(local.0 as usize)
749 .and_then(|value| *value)
750 .ok_or(RuntimeError {
751 kind: RuntimeErrorKind::UninitializedLocal { local: local.0 },
752 })?;
753 set_reg(registers, *dst, value)?;
754 }
755 RegOp::LocalSet { local, value } | RegOp::LocalTee { local, value } => {
756 let value = get_reg(registers, *value)?;
757 let slot = locals.get_mut(local.0 as usize).ok_or(RuntimeError {
758 kind: RuntimeErrorKind::UninitializedLocal { local: local.0 },
759 })?;
760 *slot = Some(value);
761 }
762 RegOp::Drop { value } => {
763 get_reg(registers, *value)?;
764 }
765 RegOp::I32Const { dst, value } => {
766 set_reg(registers, *dst, Value::I32(*value))?;
767 }
768 RegOp::I64Const { dst, value } => {
769 set_reg(registers, *dst, Value::I64(*value))?;
770 }
771 RegOp::F32Const { dst, value } => {
772 set_reg(registers, *dst, Value::F32(*value))?;
773 }
774 RegOp::F64Const { dst, value } => {
775 set_reg(registers, *dst, Value::F64(*value))?;
776 }
777 RegOp::Unary { op, dst, value } => execute_unary_op(registers, *op, *dst, *value)?,
778 RegOp::Binary { op, dst, lhs, rhs } => execute_binary_op(registers, *op, *dst, *lhs, *rhs)?,
779 RegOp::Copy { dst, src } => {
780 let value = get_reg(registers, *src)?;
781 set_reg(registers, *dst, value)?;
782 }
783 RegOp::Select { dst, v1, v2, cond } => {
784 let cond_value = get_reg(registers, *cond)?;
785 let taken = matches!(cond_value, Value::I32(v) if v != 0);
786 let value = get_reg(registers, if taken { *v1 } else { *v2 })?;
787 set_reg(registers, *dst, value)?;
788 }
789 RegOp::Call { func, .. } => {
790 return Err(RuntimeError {
793 kind: RuntimeErrorKind::UnknownFunction { func: func.0 },
794 });
795 }
796 RegOp::CallIndirect { .. } => {
797 return Err(RuntimeError {
799 kind: RuntimeErrorKind::UnknownFunction { func: 0 },
800 });
801 }
802 RegOp::CallRef { .. } => {
803 return Err(RuntimeError {
805 kind: RuntimeErrorKind::UnknownFunction { func: 0 },
806 });
807 }
808 RegOp::RefAsNonNull { dst, value } => {
809 let val = get_reg(registers, *value)?;
810 if is_null_ref(&val) {
811 return Err(trap(RuntimeTrap::NullReference));
812 }
813 set_reg(registers, *dst, val)?;
814 }
815 RegOp::Load {
816 op,
817 dst,
818 addr,
819 memarg,
820 } => {
821 let addr = expect_addr(get_reg(registers, *addr)?)?;
822 let store = require_store(store)?;
823 let mem = store.shared_memory(memarg.memory.0).ok_or(RuntimeError {
824 kind: RuntimeErrorKind::UnknownMemory {
825 memory: memarg.memory.0,
826 },
827 })?;
828 let mem = mem.borrow();
829 let range = memory_bounds(&mem, memarg, addr, op.byte_width())?;
830 let bytes = &mem[range];
831 let value = match op {
832 crate::lower::LoadOp::I32 => {
833 Value::I32(i32::from_le_bytes(bytes.try_into().expect("width checked")))
834 }
835 crate::lower::LoadOp::I64 => {
836 Value::I64(i64::from_le_bytes(bytes.try_into().expect("width checked")))
837 }
838 crate::lower::LoadOp::F32 => Value::F32(f32::from_bits(u32::from_le_bytes(
839 bytes.try_into().expect("width checked"),
840 ))),
841 crate::lower::LoadOp::F64 => Value::F64(f64::from_bits(u64::from_le_bytes(
842 bytes.try_into().expect("width checked"),
843 ))),
844 crate::lower::LoadOp::I32Load8S => Value::I32(bytes[0] as i8 as i32),
845 crate::lower::LoadOp::I32Load8U => Value::I32(bytes[0] as i32),
846 crate::lower::LoadOp::I32Load16S => {
847 Value::I32(i16::from_le_bytes(bytes.try_into().expect("width checked")) as i32)
848 }
849 crate::lower::LoadOp::I32Load16U => {
850 Value::I32(u16::from_le_bytes(bytes.try_into().expect("width checked")) as i32)
851 }
852 crate::lower::LoadOp::I64Load8S => Value::I64(bytes[0] as i8 as i64),
853 crate::lower::LoadOp::I64Load8U => Value::I64(bytes[0] as i64),
854 crate::lower::LoadOp::I64Load16S => {
855 Value::I64(i16::from_le_bytes(bytes.try_into().expect("width checked")) as i64)
856 }
857 crate::lower::LoadOp::I64Load16U => {
858 Value::I64(u16::from_le_bytes(bytes.try_into().expect("width checked")) as i64)
859 }
860 crate::lower::LoadOp::I64Load32S => {
861 Value::I64(i32::from_le_bytes(bytes.try_into().expect("width checked")) as i64)
862 }
863 crate::lower::LoadOp::I64Load32U => {
864 Value::I64(u32::from_le_bytes(bytes.try_into().expect("width checked")) as i64)
865 }
866 crate::lower::LoadOp::V128 => Value::V128(bytes.try_into().expect("width checked")),
867 };
868 set_reg(registers, *dst, value)?;
869 }
870 RegOp::Store {
871 op,
872 addr,
873 value,
874 memarg,
875 } => {
876 let addr = expect_addr(get_reg(registers, *addr)?)?;
877 let value = get_reg(registers, *value)?;
878 let store = require_store(store)?;
879 let mem = store.shared_memory(memarg.memory.0).ok_or(RuntimeError {
880 kind: RuntimeErrorKind::UnknownMemory {
881 memory: memarg.memory.0,
882 },
883 })?;
884 let mut mem = mem.borrow_mut();
885 let range = memory_bounds(&mem, memarg, addr, op.byte_width())?;
886 let bytes = &mut mem[range];
887 match (op, value) {
888 (crate::lower::StoreOp::I32, Value::I32(v)) => {
889 bytes.copy_from_slice(&v.to_le_bytes());
890 }
891 (crate::lower::StoreOp::I64, Value::I64(v)) => {
892 bytes.copy_from_slice(&v.to_le_bytes());
893 }
894 (crate::lower::StoreOp::F32, Value::F32(v)) => {
895 bytes.copy_from_slice(&v.to_bits().to_le_bytes());
896 }
897 (crate::lower::StoreOp::F64, Value::F64(v)) => {
898 bytes.copy_from_slice(&v.to_bits().to_le_bytes());
899 }
900 (crate::lower::StoreOp::I32Store8, Value::I32(v)) => {
901 bytes[0] = v as u8;
902 }
903 (crate::lower::StoreOp::I64Store8, Value::I64(v)) => {
904 bytes[0] = v as u8;
905 }
906 (crate::lower::StoreOp::I32Store16, Value::I32(v)) => {
907 bytes.copy_from_slice(&(v as u16).to_le_bytes());
908 }
909 (crate::lower::StoreOp::I64Store16, Value::I64(v)) => {
910 bytes.copy_from_slice(&(v as u16).to_le_bytes());
911 }
912 (crate::lower::StoreOp::I64Store32, Value::I64(v)) => {
913 bytes.copy_from_slice(&(v as u32).to_le_bytes());
914 }
915 (crate::lower::StoreOp::V128, Value::V128(v)) => {
916 bytes.copy_from_slice(&v);
917 }
918 (op, value) => {
919 return Err(RuntimeError {
920 kind: RuntimeErrorKind::TypeMismatch {
921 expected: op.value_type(),
922 found: value.val_type(),
923 },
924 });
925 }
926 }
927 }
928 RegOp::GlobalGet { dst, global } => {
929 let store = require_store(store)?;
930 let value = store.global(global.0).ok_or(RuntimeError {
931 kind: RuntimeErrorKind::UnknownGlobal { global: global.0 },
932 })?;
933 set_reg(registers, *dst, value)?;
934 }
935 RegOp::GlobalSet { global, value } => {
936 let store = require_store(store)?;
937 let value = get_reg(registers, *value)?;
938 store.set_global(global.0, value).ok_or(RuntimeError {
939 kind: RuntimeErrorKind::UnknownGlobal { global: global.0 },
940 })?;
941 }
942 RegOp::MemorySize { dst, memory } => {
943 let store = require_store(store)?;
944 let pages = store
945 .with_memory(memory.0, |mem| mem.len())
946 .ok_or(RuntimeError {
947 kind: RuntimeErrorKind::UnknownMemory { memory: memory.0 },
948 })?
949 / PAGE_SIZE;
950 set_reg(registers, *dst, Value::I32(pages as i32))?;
951 }
952 RegOp::MemoryGrow { dst, memory, delta } => {
953 let store = require_store(store)?;
954 let delta = expect_addr(get_reg(registers, *delta)?)?;
955 let max_pages = store
956 .memory_type(memory.0)
957 .and_then(|ty| ty.limits.max)
958 .unwrap_or(65536) as usize;
959 let mem = store.shared_memory(memory.0).ok_or(RuntimeError {
960 kind: RuntimeErrorKind::UnknownMemory { memory: memory.0 },
961 })?;
962 let result = {
963 let mut mem = mem.borrow_mut();
964 let old_pages = mem.len() / PAGE_SIZE;
965 match old_pages.checked_add(delta as usize) {
966 Some(new_pages)
967 if new_pages <= max_pages && grow_memory_fallible(&mut mem, new_pages) =>
968 {
969 old_pages as i32
970 }
971 _ => -1,
972 }
973 };
974 set_reg(registers, *dst, Value::I32(result))?;
975 }
976 RegOp::MemoryInit {
977 memory,
978 data,
979 dst,
980 src,
981 count,
982 } => {
983 let store = require_store(store)?;
984 let dst = expect_addr(get_reg(registers, *dst)?)? as usize;
985 let src = expect_addr(get_reg(registers, *src)?)? as usize;
986 let count = expect_addr(get_reg(registers, *count)?)? as usize;
987 let (bytes, dst_end) = {
989 let segment = store
990 .with_data(data.0, |segment| segment.cloned())
991 .ok_or(RuntimeError {
992 kind: RuntimeErrorKind::UnknownDataSegment { data: data.0 },
993 })?
994 .ok_or(trap(RuntimeTrap::OutOfBoundsMemoryAccess))?;
995 let segment = &segment;
996 let (Some(src_end), Some(dst_end)) =
997 (src.checked_add(count), dst.checked_add(count))
998 else {
999 return Err(trap(RuntimeTrap::OutOfBoundsMemoryAccess));
1000 };
1001 if src_end > segment.len() {
1002 return Err(trap(RuntimeTrap::OutOfBoundsMemoryAccess));
1003 }
1004 (segment[src..src_end].to_vec(), dst_end)
1005 };
1006 store
1007 .with_memory_mut(memory.0, |mem| {
1008 if dst_end > mem.len() {
1009 return Err(trap(RuntimeTrap::OutOfBoundsMemoryAccess));
1010 }
1011 mem[dst..dst_end].copy_from_slice(&bytes);
1012 Ok(())
1013 })
1014 .ok_or(RuntimeError {
1015 kind: RuntimeErrorKind::UnknownMemory { memory: memory.0 },
1016 })??;
1017 }
1018 RegOp::DataDrop { data } => {
1019 let store = require_store(store)?;
1020 store.drop_data(data.0).ok_or(RuntimeError {
1021 kind: RuntimeErrorKind::UnknownDataSegment { data: data.0 },
1022 })?;
1023 }
1024 RegOp::MemoryCopy {
1025 dst_memory,
1026 src_memory,
1027 dst,
1028 src,
1029 count,
1030 } => {
1031 let store = require_store(store)?;
1032 let dst = expect_addr(get_reg(registers, *dst)?)? as usize;
1033 let src = expect_addr(get_reg(registers, *src)?)? as usize;
1034 let count = expect_addr(get_reg(registers, *count)?)? as usize;
1035 let src_len = store
1038 .with_memory(src_memory.0, |mem| mem.len())
1039 .ok_or(RuntimeError {
1040 kind: RuntimeErrorKind::UnknownMemory {
1041 memory: src_memory.0,
1042 },
1043 })?;
1044 let dst_len = store
1045 .with_memory(dst_memory.0, |mem| mem.len())
1046 .ok_or(RuntimeError {
1047 kind: RuntimeErrorKind::UnknownMemory {
1048 memory: dst_memory.0,
1049 },
1050 })?;
1051 let (Some(src_end), Some(dst_end)) = (src.checked_add(count), dst.checked_add(count))
1052 else {
1053 return Err(trap(RuntimeTrap::OutOfBoundsMemoryAccess));
1054 };
1055 if src_end > src_len || dst_end > dst_len {
1056 return Err(trap(RuntimeTrap::OutOfBoundsMemoryAccess));
1057 }
1058 let temp: Vec<u8> = store
1059 .with_memory(src_memory.0, |mem| mem[src..src_end].to_vec())
1060 .ok_or(RuntimeError {
1061 kind: RuntimeErrorKind::UnknownMemory {
1062 memory: src_memory.0,
1063 },
1064 })?;
1065 store
1066 .with_memory_mut(dst_memory.0, |mem| {
1067 mem[dst..dst_end].copy_from_slice(&temp);
1068 })
1069 .ok_or(RuntimeError {
1070 kind: RuntimeErrorKind::UnknownMemory {
1071 memory: dst_memory.0,
1072 },
1073 })?;
1074 }
1075 RegOp::MemoryFill {
1076 memory,
1077 dst,
1078 value,
1079 count,
1080 } => {
1081 let store = require_store(store)?;
1082 let dst = expect_addr(get_reg(registers, *dst)?)? as usize;
1083 let count = expect_addr(get_reg(registers, *count)?)? as usize;
1084 let value = expect_addr(get_reg(registers, *value)?)? as u8;
1085 store
1086 .with_memory_mut(memory.0, |mem| {
1087 let Some(end) = dst.checked_add(count) else {
1088 return Err(trap(RuntimeTrap::OutOfBoundsMemoryAccess));
1089 };
1090 if end > mem.len() {
1091 return Err(trap(RuntimeTrap::OutOfBoundsMemoryAccess));
1092 }
1093 mem[dst..end].fill(value);
1094 Ok(())
1095 })
1096 .ok_or(RuntimeError {
1097 kind: RuntimeErrorKind::UnknownMemory { memory: memory.0 },
1098 })??;
1099 }
1100 RegOp::TableGet { dst, table, index } => {
1101 let store = require_store(store)?;
1102 let idx = expect_addr(get_reg(registers, *index)?)?;
1103 let value = store
1104 .with_table(table.0, |table| table.get(idx))
1105 .ok_or(RuntimeError {
1106 kind: RuntimeErrorKind::UnknownTable { table: table.0 },
1107 })?
1108 .ok_or(trap(RuntimeTrap::OutOfBoundsTableAccess))?;
1109 set_reg(registers, *dst, value)?;
1110 }
1111 RegOp::TableSet {
1112 table,
1113 index,
1114 value,
1115 } => {
1116 let store = require_store(store)?;
1117 let idx = expect_addr(get_reg(registers, *index)?)?;
1118 let value = get_reg(registers, *value)?;
1119 store
1120 .with_table_mut(table.0, |table| {
1121 if table.set(idx, value) {
1122 Ok(())
1123 } else {
1124 Err(trap(RuntimeTrap::OutOfBoundsTableAccess))
1125 }
1126 })
1127 .ok_or(RuntimeError {
1128 kind: RuntimeErrorKind::UnknownTable { table: table.0 },
1129 })??;
1130 }
1131 RegOp::TableSize { dst, table } => {
1132 let store = require_store(store)?;
1133 let len = store
1134 .with_table(table.0, |table| table.len())
1135 .ok_or(RuntimeError {
1136 kind: RuntimeErrorKind::UnknownTable { table: table.0 },
1137 })?;
1138 set_reg(registers, *dst, Value::I32(len as i32))?;
1139 }
1140 RegOp::TableGrow {
1141 dst,
1142 table,
1143 value,
1144 delta,
1145 } => {
1146 let store = require_store(store)?;
1147 let delta = expect_addr(get_reg(registers, *delta)?)?;
1148 let value = get_reg(registers, *value)?;
1149 let tbl = store.shared_table(table.0).ok_or(RuntimeError {
1150 kind: RuntimeErrorKind::UnknownTable { table: table.0 },
1151 })?;
1152 let result = {
1153 let mut tbl = tbl.borrow_mut();
1154 match tbl.grow(delta, value) {
1155 Some(old) => old as i32,
1156 None => -1,
1157 }
1158 };
1159 set_reg(registers, *dst, Value::I32(result))?;
1160 }
1161 RegOp::TableFill {
1162 table,
1163 dst,
1164 value,
1165 count,
1166 } => {
1167 let store = require_store(store)?;
1168 let dst = expect_addr(get_reg(registers, *dst)?)?;
1169 let count = expect_addr(get_reg(registers, *count)?)?;
1170 let value = get_reg(registers, *value)?;
1171 store
1172 .with_table_mut(table.0, |tbl| {
1173 if tbl.fill(dst, value, count) {
1174 Ok(())
1175 } else {
1176 Err(trap(RuntimeTrap::OutOfBoundsTableAccess))
1177 }
1178 })
1179 .ok_or(RuntimeError {
1180 kind: RuntimeErrorKind::UnknownTable { table: table.0 },
1181 })??;
1182 }
1183 RegOp::TableCopy {
1184 dst_table,
1185 src_table,
1186 dst,
1187 src,
1188 count,
1189 } => {
1190 let store = require_store(store)?;
1191 let dst = expect_addr(get_reg(registers, *dst)?)?;
1192 let src = expect_addr(get_reg(registers, *src)?)?;
1193 let count = expect_addr(get_reg(registers, *count)?)?;
1194 let temp: Vec<Value> = store
1197 .with_table(src_table.0, |table| table.read_slice(src, count))
1198 .ok_or(RuntimeError {
1199 kind: RuntimeErrorKind::UnknownTable { table: src_table.0 },
1200 })?
1201 .ok_or(trap(RuntimeTrap::OutOfBoundsTableAccess))?;
1202 store
1203 .with_table_mut(dst_table.0, |table| table.write_slice(dst, &temp))
1204 .ok_or(RuntimeError {
1205 kind: RuntimeErrorKind::UnknownTable { table: dst_table.0 },
1206 })?
1207 .then_some(())
1208 .ok_or(trap(RuntimeTrap::OutOfBoundsTableAccess))?;
1209 }
1210 RegOp::TableInit {
1211 table,
1212 elem,
1213 dst,
1214 src,
1215 count,
1216 } => {
1217 let store = require_store(store)?;
1218 let dst = expect_addr(get_reg(registers, *dst)?)? as usize;
1219 let src = expect_addr(get_reg(registers, *src)?)? as usize;
1220 let count = expect_addr(get_reg(registers, *count)?)? as usize;
1221 let segment = store
1222 .with_elem(elem.0, |segment| segment.cloned())
1223 .ok_or(RuntimeError {
1224 kind: RuntimeErrorKind::UnknownElem { elem: elem.0 },
1225 })?
1226 .ok_or(trap(RuntimeTrap::OutOfBoundsTableAccess))?;
1227 let segment = &segment;
1228 let table_len = store
1229 .with_table(table.0, |table| table.len())
1230 .ok_or(RuntimeError {
1231 kind: RuntimeErrorKind::UnknownTable { table: table.0 },
1232 })?;
1233 let (Some(src_end), Some(dst_end)) = (src.checked_add(count), dst.checked_add(count))
1234 else {
1235 return Err(trap(RuntimeTrap::OutOfBoundsTableAccess));
1236 };
1237 if src_end > segment.len() || dst_end as u32 > table_len {
1238 return Err(trap(RuntimeTrap::OutOfBoundsTableAccess));
1239 }
1240 let temp: Vec<Value> = segment[src..src_end].to_vec();
1241 store
1242 .with_table_mut(table.0, |table| {
1243 if table.write_slice(dst as u32, &temp) {
1244 Ok(())
1245 } else {
1246 Err(trap(RuntimeTrap::OutOfBoundsTableAccess))
1247 }
1248 })
1249 .ok_or(RuntimeError {
1250 kind: RuntimeErrorKind::UnknownTable { table: table.0 },
1251 })??;
1252 }
1253 RegOp::ElemDrop { elem } => {
1254 let store = require_store(store)?;
1255 store.drop_elem(elem.0).ok_or(RuntimeError {
1256 kind: RuntimeErrorKind::UnknownElem { elem: elem.0 },
1257 })?;
1258 }
1259 RegOp::RefNull { dst, ref_type } => {
1260 set_reg(registers, *dst, ref_null_value(ref_type))?;
1261 }
1262 RegOp::RefFunc { dst, func } => {
1263 let store = require_store(store)?;
1264 set_reg(
1265 registers,
1266 *dst,
1267 Value::FuncRef(Some((store.instance_id(), func.0))),
1268 )?;
1269 }
1270 RegOp::RefIsNull { dst, value } => {
1271 let value = get_reg(registers, *value)?;
1272 set_reg(registers, *dst, Value::I32(is_null_ref(&value) as i32))?;
1273 }
1274 RegOp::V128Const { dst, value } => {
1275 set_reg(registers, *dst, Value::V128(*value))?;
1276 }
1277 RegOp::V128Splat { dst, shape, src } => {
1278 let scalar = get_reg(registers, *src)?;
1279 let bytes = splat_bytes(*shape, scalar)?;
1280 set_reg(registers, *dst, Value::V128(bytes))?;
1281 }
1282 RegOp::V128ExtractLane {
1283 dst,
1284 shape,
1285 src,
1286 lane,
1287 } => {
1288 let Value::V128(bytes) = get_reg(registers, *src)? else {
1289 return Err(RuntimeError {
1290 kind: RuntimeErrorKind::TypeMismatch {
1291 expected: ValType::Vec(crate::types::VecType::V128),
1292 found: get_reg(registers, *src)?.val_type(),
1293 },
1294 });
1295 };
1296 let value = extract_lane(*shape, &bytes, *lane)?;
1297 set_reg(registers, *dst, value)?;
1298 }
1299 RegOp::V128ReplaceLane {
1300 dst,
1301 shape,
1302 vec,
1303 scalar,
1304 lane,
1305 } => {
1306 let Value::V128(mut bytes) = get_reg(registers, *vec)? else {
1307 return Err(RuntimeError {
1308 kind: RuntimeErrorKind::TypeMismatch {
1309 expected: ValType::Vec(crate::types::VecType::V128),
1310 found: get_reg(registers, *vec)?.val_type(),
1311 },
1312 });
1313 };
1314 let scalar = get_reg(registers, *scalar)?;
1315 replace_lane(*shape, &mut bytes, *lane, scalar)?;
1316 set_reg(registers, *dst, Value::V128(bytes))?;
1317 }
1318 RegOp::V128Binary {
1319 shape,
1320 kind,
1321 dst,
1322 lhs,
1323 rhs,
1324 } => {
1325 let Value::V128(lhs_bytes) = get_reg(registers, *lhs)? else {
1326 return Err(RuntimeError {
1327 kind: RuntimeErrorKind::TypeMismatch {
1328 expected: ValType::Vec(crate::types::VecType::V128),
1329 found: get_reg(registers, *lhs)?.val_type(),
1330 },
1331 });
1332 };
1333 let Value::V128(rhs_bytes) = get_reg(registers, *rhs)? else {
1334 return Err(RuntimeError {
1335 kind: RuntimeErrorKind::TypeMismatch {
1336 expected: ValType::Vec(crate::types::VecType::V128),
1337 found: get_reg(registers, *rhs)?.val_type(),
1338 },
1339 });
1340 };
1341 let bytes = v128_binary(*shape, *kind, &lhs_bytes, &rhs_bytes);
1342 set_reg(registers, *dst, Value::V128(bytes))?;
1343 }
1344 RegOp::V128Not { dst, src } => {
1345 let Value::V128(bytes) = get_reg(registers, *src)? else {
1346 return Err(RuntimeError {
1347 kind: RuntimeErrorKind::TypeMismatch {
1348 expected: ValType::Vec(crate::types::VecType::V128),
1349 found: get_reg(registers, *src)?.val_type(),
1350 },
1351 });
1352 };
1353 let mut out = [0u8; 16];
1354 for (dst_byte, src_byte) in out.iter_mut().zip(bytes.iter()) {
1355 *dst_byte = !src_byte;
1356 }
1357 set_reg(registers, *dst, Value::V128(out))?;
1358 }
1359 }
1360 Ok(())
1361}
1362
1363fn require_store(store: Option<&Store>) -> Result<&Store, RuntimeError> {
1364 store.ok_or(RuntimeError {
1365 kind: RuntimeErrorKind::MissingStore,
1366 })
1367}
1368
1369fn value_satisfies(expected: ValType, value: Value) -> bool {
1374 use crate::types::HeapType;
1375 match (expected, value) {
1376 (ValType::Ref(expected_ref), value) => match (expected_ref, value) {
1377 (RefType::FuncRef, Value::FuncRef(_)) => true,
1378 (RefType::ExternRef, Value::ExternRef(_)) => true,
1379 (RefType::Typed { nullable, heap }, value) => {
1380 let non_null = match value {
1381 Value::FuncRef(inner) => inner.is_some(),
1382 Value::ExternRef(inner) => inner.is_some(),
1383 _ => return false,
1384 };
1385 if !nullable && !non_null {
1386 return false;
1387 }
1388 matches!(
1389 (heap, value),
1390 (HeapType::Func | HeapType::Type(_), Value::FuncRef(_))
1391 | (HeapType::Extern, Value::ExternRef(_))
1392 )
1393 }
1394 _ => false,
1395 },
1396 (expected, value) => expected == value.val_type(),
1397 }
1398}
1399
1400fn wasm_f32_min(a: f32, b: f32) -> f32 {
1402 if a.is_nan() || b.is_nan() {
1403 return f32::from_bits(0x7fc0_0000);
1404 }
1405 if a == b {
1406 if a == 0.0 && (a.is_sign_negative() || b.is_sign_negative()) {
1407 return -0.0;
1408 }
1409 return a;
1410 }
1411 if a < b { a } else { b }
1412}
1413
1414fn wasm_f32_max(a: f32, b: f32) -> f32 {
1416 if a.is_nan() || b.is_nan() {
1417 return f32::from_bits(0x7fc0_0000);
1418 }
1419 if a == b {
1420 if a == 0.0 && (a.is_sign_positive() || b.is_sign_positive()) {
1421 return 0.0;
1422 }
1423 return a;
1424 }
1425 if a > b { a } else { b }
1426}
1427
1428fn wasm_f64_min(a: f64, b: f64) -> f64 {
1429 if a.is_nan() || b.is_nan() {
1430 return f64::from_bits(0x7ff8_0000_0000_0000);
1431 }
1432 if a == b {
1433 if a == 0.0 && (a.is_sign_negative() || b.is_sign_negative()) {
1434 return -0.0;
1435 }
1436 return a;
1437 }
1438 if a < b { a } else { b }
1439}
1440
1441fn wasm_f64_max(a: f64, b: f64) -> f64 {
1442 if a.is_nan() || b.is_nan() {
1443 return f64::from_bits(0x7ff8_0000_0000_0000);
1444 }
1445 if a == b {
1446 if a == 0.0 && (a.is_sign_positive() || b.is_sign_positive()) {
1447 return 0.0;
1448 }
1449 return a;
1450 }
1451 if a > b { a } else { b }
1452}
1453
1454fn grow_memory_fallible(mem: &mut Vec<u8>, new_pages: usize) -> bool {
1457 let additional = new_pages
1458 .saturating_mul(PAGE_SIZE)
1459 .saturating_sub(mem.len());
1460 if mem.try_reserve(additional).is_err() {
1461 return false;
1462 }
1463 mem.resize(new_pages * PAGE_SIZE, 0);
1464 true
1465}
1466
1467fn memory_bounds(
1472 mem: &[u8],
1473 memarg: &MemArg,
1474 addr: u32,
1475 width: usize,
1476) -> Result<core::ops::Range<usize>, RuntimeError> {
1477 let ea = addr as u64 + memarg.offset as u64;
1478 let end = ea
1479 .checked_add(width as u64)
1480 .ok_or(trap(RuntimeTrap::OutOfBoundsMemoryAccess))?;
1481 if end > mem.len() as u64 {
1482 return Err(trap(RuntimeTrap::OutOfBoundsMemoryAccess));
1483 }
1484 Ok(ea as usize..end as usize)
1485}
1486
1487fn is_null_ref(value: &Value) -> bool {
1489 matches!(value, Value::FuncRef(None) | Value::ExternRef(None))
1490}
1491
1492fn expect_addr(value: Value) -> Result<u32, RuntimeError> {
1494 match value {
1495 Value::I32(value) => Ok(value as u32),
1496 other => Err(RuntimeError {
1497 kind: RuntimeErrorKind::TypeMismatch {
1498 expected: ValType::Num(NumType::I32),
1499 found: other.val_type(),
1500 },
1501 }),
1502 }
1503}
1504
1505fn ref_nullable(ref_type: &RefType) -> bool {
1507 match ref_type {
1508 RefType::FuncRef | RefType::ExternRef => true,
1509 RefType::Typed { nullable, .. } => *nullable,
1510 }
1511}
1512
1513fn ref_null_value(ref_type: &RefType) -> Value {
1515 match ref_type {
1516 RefType::ExternRef
1517 | RefType::Typed {
1518 heap: crate::types::HeapType::Extern,
1519 ..
1520 } => Value::ExternRef(None),
1521 _ => Value::FuncRef(None),
1522 }
1523}
1524
1525macro_rules! lane_int_op {
1527 ($kind:expr, $a:expr, $b:expr, $ty:ty) => {{
1528 let (a, b) = ($a as $ty, $b as $ty);
1529 match $kind {
1530 V128BinaryKind::Add => a.wrapping_add(b),
1531 V128BinaryKind::Sub => a.wrapping_sub(b),
1532 V128BinaryKind::Mul => a.wrapping_mul(b),
1533 V128BinaryKind::Div => a.wrapping_div(b),
1534 _ => unreachable!("bitwise kinds handled separately"),
1535 }
1536 }};
1537}
1538
1539macro_rules! lane_float_op {
1541 ($kind:expr, $a:expr, $b:expr, $ty:ty) => {{
1542 let (a, b) = ($a as $ty, $b as $ty);
1543 match $kind {
1544 V128BinaryKind::Add => a + b,
1545 V128BinaryKind::Sub => a - b,
1546 V128BinaryKind::Mul => a * b,
1547 V128BinaryKind::Div => a / b,
1548 _ => unreachable!("bitwise kinds handled separately"),
1549 }
1550 }};
1551}
1552
1553fn scalar_lane_bytes(shape: LaneShape, scalar: Value) -> Result<[u8; 8], RuntimeError> {
1556 let mut buf = [0u8; 8];
1557 match (shape, scalar) {
1558 (LaneShape::I8x16, Value::I32(v)) => buf[0] = v as u8,
1559 (LaneShape::I16x8, Value::I32(v)) => buf[..2].copy_from_slice(&(v as u16).to_le_bytes()),
1560 (LaneShape::I32x4, Value::I32(v)) => buf[..4].copy_from_slice(&v.to_le_bytes()),
1561 (LaneShape::I64x2, Value::I64(v)) => buf.copy_from_slice(&v.to_le_bytes()),
1562 (LaneShape::F32x4, Value::F32(v)) => {
1563 buf[..4].copy_from_slice(&v.to_bits().to_le_bytes());
1564 }
1565 (LaneShape::F64x2, Value::F64(v)) => buf.copy_from_slice(&v.to_bits().to_le_bytes()),
1566 (shape, value) => {
1567 return Err(RuntimeError {
1568 kind: RuntimeErrorKind::TypeMismatch {
1569 expected: shape.scalar_type(),
1570 found: value.val_type(),
1571 },
1572 });
1573 }
1574 }
1575 Ok(buf)
1576}
1577
1578fn splat_bytes(shape: LaneShape, scalar: Value) -> Result<[u8; 16], RuntimeError> {
1580 let lane = scalar_lane_bytes(shape, scalar)?;
1581 let width = shape.lane_width();
1582 let mut out = [0u8; 16];
1583 for chunk in out.chunks_exact_mut(width) {
1584 chunk.copy_from_slice(&lane[..width]);
1585 }
1586 Ok(out)
1587}
1588
1589fn extract_lane(shape: LaneShape, bytes: &[u8; 16], lane: u8) -> Result<Value, RuntimeError> {
1591 let width = shape.lane_width();
1592 let start = lane as usize * width;
1593 if start + width > 16 {
1594 return Err(RuntimeError {
1595 kind: RuntimeErrorKind::InvalidLaneIndex { lane },
1596 });
1597 }
1598 let lane_bytes = &bytes[start..start + width];
1599 Ok(match shape {
1600 LaneShape::I8x16 => Value::I32(lane_bytes[0] as i8 as i32),
1601 LaneShape::I16x8 => {
1602 Value::I32(i16::from_le_bytes(lane_bytes.try_into().expect("width")) as i32)
1603 }
1604 LaneShape::I32x4 => Value::I32(i32::from_le_bytes(lane_bytes.try_into().expect("width"))),
1605 LaneShape::I64x2 => Value::I64(i64::from_le_bytes(lane_bytes.try_into().expect("width"))),
1606 LaneShape::F32x4 => Value::F32(f32::from_bits(u32::from_le_bytes(
1607 lane_bytes.try_into().expect("width"),
1608 ))),
1609 LaneShape::F64x2 => Value::F64(f64::from_bits(u64::from_le_bytes(
1610 lane_bytes.try_into().expect("width"),
1611 ))),
1612 })
1613}
1614
1615fn replace_lane(
1617 shape: LaneShape,
1618 bytes: &mut [u8; 16],
1619 lane: u8,
1620 scalar: Value,
1621) -> Result<(), RuntimeError> {
1622 let width = shape.lane_width();
1623 let start = lane as usize * width;
1624 if start + width > 16 {
1625 return Err(RuntimeError {
1626 kind: RuntimeErrorKind::InvalidLaneIndex { lane },
1627 });
1628 }
1629 let lane_bytes = scalar_lane_bytes(shape, scalar)?;
1630 bytes[start..start + width].copy_from_slice(&lane_bytes[..width]);
1631 Ok(())
1632}
1633
1634fn v128_binary(shape: LaneShape, kind: V128BinaryKind, lhs: &[u8; 16], rhs: &[u8; 16]) -> [u8; 16] {
1636 let mut out = [0u8; 16];
1637 match kind {
1638 V128BinaryKind::And => {
1639 for i in 0..16 {
1640 out[i] = lhs[i] & rhs[i];
1641 }
1642 }
1643 V128BinaryKind::Or => {
1644 for i in 0..16 {
1645 out[i] = lhs[i] | rhs[i];
1646 }
1647 }
1648 V128BinaryKind::Xor => {
1649 for i in 0..16 {
1650 out[i] = lhs[i] ^ rhs[i];
1651 }
1652 }
1653 _ => {
1654 let width = shape.lane_width();
1655 for ((dst_lane, lhs_lane), rhs_lane) in out
1656 .chunks_exact_mut(width)
1657 .zip(lhs.chunks_exact(width))
1658 .zip(rhs.chunks_exact(width))
1659 {
1660 apply_lane_binary(shape, kind, dst_lane, lhs_lane, rhs_lane);
1661 }
1662 }
1663 }
1664 out
1665}
1666
1667fn apply_lane_binary(
1668 shape: LaneShape,
1669 kind: V128BinaryKind,
1670 dst: &mut [u8],
1671 lhs: &[u8],
1672 rhs: &[u8],
1673) {
1674 match shape {
1675 LaneShape::I8x16 => {
1676 dst[0] = lane_int_op!(kind, lhs[0], rhs[0], i8) as u8;
1677 }
1678 LaneShape::I16x8 => {
1679 let a = i16::from_le_bytes(lhs.try_into().expect("width"));
1680 let b = i16::from_le_bytes(rhs.try_into().expect("width"));
1681 let result = lane_int_op!(kind, a, b, i16);
1682 dst.copy_from_slice(&result.to_le_bytes());
1683 }
1684 LaneShape::I32x4 => {
1685 let a = i32::from_le_bytes(lhs.try_into().expect("width"));
1686 let b = i32::from_le_bytes(rhs.try_into().expect("width"));
1687 let result = lane_int_op!(kind, a, b, i32);
1688 dst.copy_from_slice(&result.to_le_bytes());
1689 }
1690 LaneShape::I64x2 => {
1691 let a = i64::from_le_bytes(lhs.try_into().expect("width"));
1692 let b = i64::from_le_bytes(rhs.try_into().expect("width"));
1693 let result = lane_int_op!(kind, a, b, i64);
1694 dst.copy_from_slice(&result.to_le_bytes());
1695 }
1696 LaneShape::F32x4 => {
1697 let a = f32::from_bits(u32::from_le_bytes(lhs.try_into().expect("width")));
1698 let b = f32::from_bits(u32::from_le_bytes(rhs.try_into().expect("width")));
1699 let result = lane_float_op!(kind, a, b, f32);
1700 dst.copy_from_slice(&result.to_bits().to_le_bytes());
1701 }
1702 LaneShape::F64x2 => {
1703 let a = f64::from_bits(u64::from_le_bytes(lhs.try_into().expect("width")));
1704 let b = f64::from_bits(u64::from_le_bytes(rhs.try_into().expect("width")));
1705 let result = lane_float_op!(kind, a, b, f64);
1706 dst.copy_from_slice(&result.to_bits().to_le_bytes());
1707 }
1708 }
1709}
1710
1711fn set_reg(registers: &mut [Option<Value>], reg: Reg, value: Value) -> Result<(), RuntimeError> {
1712 let slot = registers.get_mut(reg.0 as usize).ok_or(RuntimeError {
1713 kind: RuntimeErrorKind::UnknownRegister { reg },
1714 })?;
1715 *slot = Some(value);
1716 Ok(())
1717}
1718
1719fn get_reg(registers: &[Option<Value>], reg: Reg) -> Result<Value, RuntimeError> {
1720 registers
1721 .get(reg.0 as usize)
1722 .copied()
1723 .ok_or(RuntimeError {
1724 kind: RuntimeErrorKind::UnknownRegister { reg },
1725 })?
1726 .ok_or(RuntimeError {
1727 kind: RuntimeErrorKind::UninitializedRegister { reg },
1728 })
1729}
1730
1731fn execute_unary_op(
1732 registers: &mut [Option<Value>],
1733 op: UnaryOp,
1734 dst: Reg,
1735 value: Reg,
1736) -> Result<(), RuntimeError> {
1737 match op {
1738 UnaryOp::I32Clz => {
1739 execute_i32_unary(registers, dst, value, |value| value.leading_zeros() as i32)
1740 }
1741 UnaryOp::I32Ctz => {
1742 execute_i32_unary(registers, dst, value, |value| value.trailing_zeros() as i32)
1743 }
1744 UnaryOp::I32Popcnt => {
1745 execute_i32_unary(registers, dst, value, |value| value.count_ones() as i32)
1746 }
1747 UnaryOp::I32Eqz => execute_i32_unary(registers, dst, value, |value| i32::from(value == 0)),
1748 UnaryOp::I32WrapI64 => execute_i64_to_i32(registers, dst, value, |value| value as i32),
1749 UnaryOp::I32Extend8S => {
1750 execute_i32_unary(registers, dst, value, |value| i32::from(value as i8))
1751 }
1752 UnaryOp::I32Extend16S => {
1753 execute_i32_unary(registers, dst, value, |value| i32::from(value as i16))
1754 }
1755 UnaryOp::I64Clz => {
1756 execute_i64_unary(registers, dst, value, |value| value.leading_zeros() as i64)
1757 }
1758 UnaryOp::I64Ctz => {
1759 execute_i64_unary(registers, dst, value, |value| value.trailing_zeros() as i64)
1760 }
1761 UnaryOp::I64Popcnt => {
1762 execute_i64_unary(registers, dst, value, |value| value.count_ones() as i64)
1763 }
1764 UnaryOp::I64Eqz => execute_i64_test(registers, dst, value, |value| i32::from(value == 0)),
1765 UnaryOp::I64ExtendI32S => execute_i32_to_i64(registers, dst, value, i64::from),
1766 UnaryOp::I64ExtendI32U => {
1767 execute_i32_to_i64(registers, dst, value, |value| i64::from(value as u32))
1768 }
1769 UnaryOp::I64Extend8S => {
1770 execute_i64_unary(registers, dst, value, |value| i64::from(value as i8))
1771 }
1772 UnaryOp::I64Extend16S => {
1773 execute_i64_unary(registers, dst, value, |value| i64::from(value as i16))
1774 }
1775 UnaryOp::I64Extend32S => {
1776 execute_i64_unary(registers, dst, value, |value| i64::from(value as i32))
1777 }
1778 UnaryOp::F32Neg => execute_f32_unary(registers, dst, value, |value| -value),
1779 UnaryOp::F32Abs => execute_f32_unary(registers, dst, value, libm::fabsf),
1780 UnaryOp::F32Sqrt => execute_f32_unary(registers, dst, value, libm::sqrtf),
1781 UnaryOp::F32Ceil => execute_f32_unary(registers, dst, value, libm::ceilf),
1782 UnaryOp::F32Floor => execute_f32_unary(registers, dst, value, libm::floorf),
1783 UnaryOp::F32Trunc => execute_f32_unary(registers, dst, value, libm::truncf),
1784 UnaryOp::F32Nearest => execute_f32_unary(registers, dst, value, libm::rintf),
1785 UnaryOp::F64Neg => execute_f64_unary(registers, dst, value, |value| -value),
1786 UnaryOp::F64Abs => execute_f64_unary(registers, dst, value, libm::fabs),
1787 UnaryOp::F64Sqrt => execute_f64_unary(registers, dst, value, libm::sqrt),
1788 UnaryOp::F64Ceil => execute_f64_unary(registers, dst, value, libm::ceil),
1789 UnaryOp::F64Floor => execute_f64_unary(registers, dst, value, libm::floor),
1790 UnaryOp::F64Trunc => execute_f64_unary(registers, dst, value, libm::trunc),
1791 UnaryOp::F64Nearest => execute_f64_unary(registers, dst, value, libm::rint),
1792 UnaryOp::I32TruncF32S => {
1793 execute_f32_to_i32_checked(registers, dst, value, |v| v as i32, false)
1794 }
1795 UnaryOp::I32TruncF32U => {
1796 execute_f32_to_i32_checked(registers, dst, value, |v| v as u32 as i32, true)
1797 }
1798 UnaryOp::I32TruncF64S => {
1799 execute_f64_to_i32_checked(registers, dst, value, |v| v as i32, false)
1800 }
1801 UnaryOp::I32TruncF64U => {
1802 execute_f64_to_i32_checked(registers, dst, value, |v| v as u32 as i32, true)
1803 }
1804 UnaryOp::I64TruncF32S => {
1805 execute_f32_to_i64_checked(registers, dst, value, |v| v as i64, false)
1806 }
1807 UnaryOp::I64TruncF32U => {
1808 execute_f32_to_i64_checked(registers, dst, value, |v| v as u64 as i64, true)
1809 }
1810 UnaryOp::I64TruncF64S => {
1811 execute_f64_to_i64_checked(registers, dst, value, |v| v as i64, false)
1812 }
1813 UnaryOp::I64TruncF64U => {
1814 execute_f64_to_i64_checked(registers, dst, value, |v| v as u64 as i64, true)
1815 }
1816 UnaryOp::F32ConvertI32S => execute_i32_to_f32(registers, dst, value, |v| v as f32),
1817 UnaryOp::F32ConvertI32U => execute_i32_to_f32(registers, dst, value, |v| (v as u32) as f32),
1818 UnaryOp::F32ConvertI64S => execute_i64_to_f32(registers, dst, value, |v| v as f32),
1819 UnaryOp::F32ConvertI64U => execute_i64_to_f32(registers, dst, value, |v| (v as u64) as f32),
1820 UnaryOp::F64ConvertI32S => execute_i32_to_f64(registers, dst, value, |v| v as f64),
1821 UnaryOp::F64ConvertI32U => execute_i32_to_f64(registers, dst, value, |v| (v as u32) as f64),
1822 UnaryOp::F64ConvertI64S => execute_i64_to_f64(registers, dst, value, |v| v as f64),
1823 UnaryOp::F64ConvertI64U => execute_i64_to_f64(registers, dst, value, |v| (v as u64) as f64),
1824 UnaryOp::F32DemoteF64 => execute_f64_to_f32(registers, dst, value, |v| v as f32),
1825 UnaryOp::F64PromoteF32 => execute_f32_to_f64(registers, dst, value, |v| v as f64),
1826 UnaryOp::I32ReinterpretF32 => {
1827 execute_f32_to_i32(registers, dst, value, |v| v.to_bits() as i32)
1828 }
1829 UnaryOp::F32ReinterpretI32 => {
1830 execute_i32_to_f32(registers, dst, value, |v| f32::from_bits(v as u32))
1831 }
1832 UnaryOp::I64ReinterpretF64 => execute_f64_to_i64(registers, dst, value, |v| {
1833 i64::from_ne_bytes(v.to_ne_bytes())
1834 }),
1835 UnaryOp::F64ReinterpretI64 => execute_i64_to_f64(registers, dst, value, |v| {
1836 f64::from_ne_bytes(v.to_ne_bytes())
1837 }),
1838 UnaryOp::I32TruncSatF32S => {
1839 execute_f32_to_i32_saturating(registers, dst, value, |v: f32| -> i32 {
1840 if v.is_nan() {
1841 0
1842 } else if v >= (i32::MAX as f32) {
1843 i32::MAX
1844 } else if v <= (i32::MIN as f32) {
1845 i32::MIN
1846 } else {
1847 v as i32
1848 }
1849 })
1850 }
1851 UnaryOp::I32TruncSatF32U => {
1852 execute_f32_to_i32_saturating(registers, dst, value, |v: f32| -> i32 {
1853 if v.is_nan() || v <= -1.0 {
1854 0
1855 } else if v >= (u32::MAX as f32) {
1856 u32::MAX as i32
1857 } else {
1858 v as u32 as i32
1859 }
1860 })
1861 }
1862 UnaryOp::I32TruncSatF64S => {
1863 execute_f64_to_i32_saturating(registers, dst, value, |v: f64| -> i32 {
1864 if v.is_nan() {
1865 0
1866 } else if v >= (i32::MAX as f64) {
1867 i32::MAX
1868 } else if v <= (i32::MIN as f64) {
1869 i32::MIN
1870 } else {
1871 v as i32
1872 }
1873 })
1874 }
1875 UnaryOp::I32TruncSatF64U => {
1876 execute_f64_to_i32_saturating(registers, dst, value, |v: f64| -> i32 {
1877 if v.is_nan() || v <= -1.0 {
1878 0
1879 } else if v >= (u32::MAX as f64) {
1880 u32::MAX as i32
1881 } else {
1882 v as u32 as i32
1883 }
1884 })
1885 }
1886 UnaryOp::I64TruncSatF32S => {
1887 execute_f32_to_i64_saturating(registers, dst, value, |v: f32| -> i64 {
1888 if v.is_nan() {
1889 0
1890 } else if v >= (i64::MAX as f32) {
1891 i64::MAX
1892 } else if v <= (i64::MIN as f32) {
1893 i64::MIN
1894 } else {
1895 v as i64
1896 }
1897 })
1898 }
1899 UnaryOp::I64TruncSatF32U => {
1900 execute_f32_to_i64_saturating(registers, dst, value, |v: f32| -> i64 {
1901 if v.is_nan() || v <= -1.0 {
1902 0
1903 } else if v >= (u64::MAX as f32) {
1904 u64::MAX as i64
1905 } else {
1906 v as u64 as i64
1907 }
1908 })
1909 }
1910 UnaryOp::I64TruncSatF64S => {
1911 execute_f64_to_i64_saturating(registers, dst, value, |v: f64| -> i64 {
1912 if v.is_nan() {
1913 0
1914 } else if v >= (i64::MAX as f64) {
1915 i64::MAX
1916 } else if v <= (i64::MIN as f64) {
1917 i64::MIN
1918 } else {
1919 v as i64
1920 }
1921 })
1922 }
1923 UnaryOp::I64TruncSatF64U => {
1924 execute_f64_to_i64_saturating(registers, dst, value, |v: f64| -> i64 {
1925 if v.is_nan() || v <= -1.0 {
1926 0
1927 } else if v >= (u64::MAX as f64) {
1928 u64::MAX as i64
1929 } else {
1930 v as u64 as i64
1931 }
1932 })
1933 }
1934 }
1935}
1936
1937fn execute_binary_op(
1938 registers: &mut [Option<Value>],
1939 op: BinaryOp,
1940 dst: Reg,
1941 lhs: Reg,
1942 rhs: Reg,
1943) -> Result<(), RuntimeError> {
1944 match op {
1945 BinaryOp::I32Add => {
1946 execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs.wrapping_add(rhs))
1947 }
1948 BinaryOp::I32Sub => {
1949 execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs.wrapping_sub(rhs))
1950 }
1951 BinaryOp::I32Mul => {
1952 execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs.wrapping_mul(rhs))
1953 }
1954 BinaryOp::I32DivS => execute_i32_binary_checked(registers, dst, lhs, rhs, i32_div_s),
1955 BinaryOp::I32DivU => execute_i32_binary_checked(registers, dst, lhs, rhs, i32_div_u),
1956 BinaryOp::I32RemS => execute_i32_binary_checked(registers, dst, lhs, rhs, i32_rem_s),
1957 BinaryOp::I32RemU => execute_i32_binary_checked(registers, dst, lhs, rhs, i32_rem_u),
1958 BinaryOp::I32And => execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs & rhs),
1959 BinaryOp::I32Or => execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs | rhs),
1960 BinaryOp::I32Xor => execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs ^ rhs),
1961 BinaryOp::I32Shl => execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| {
1962 lhs.wrapping_shl(rhs as u32)
1963 }),
1964 BinaryOp::I32ShrS => execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| {
1965 lhs >> ((rhs as u32) & 31)
1966 }),
1967 BinaryOp::I32ShrU => execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| {
1968 ((lhs as u32) >> ((rhs as u32) & 31)) as i32
1969 }),
1970 BinaryOp::I32Rotl => execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| {
1971 lhs.rotate_left(rhs as u32)
1972 }),
1973 BinaryOp::I32Rotr => execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| {
1974 lhs.rotate_right(rhs as u32)
1975 }),
1976 BinaryOp::I32Eq => {
1977 execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| i32::from(lhs == rhs))
1978 }
1979 BinaryOp::I32Ne => {
1980 execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| i32::from(lhs != rhs))
1981 }
1982 BinaryOp::I32LtS => {
1983 execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| i32::from(lhs < rhs))
1984 }
1985 BinaryOp::I32LtU => execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| {
1986 i32::from((lhs as u32) < (rhs as u32))
1987 }),
1988 BinaryOp::I32GtS => {
1989 execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| i32::from(lhs > rhs))
1990 }
1991 BinaryOp::I32GtU => execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| {
1992 i32::from((lhs as u32) > (rhs as u32))
1993 }),
1994 BinaryOp::I32LeS => {
1995 execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| i32::from(lhs <= rhs))
1996 }
1997 BinaryOp::I32LeU => execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| {
1998 i32::from((lhs as u32) <= (rhs as u32))
1999 }),
2000 BinaryOp::I32GeS => {
2001 execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| i32::from(lhs >= rhs))
2002 }
2003 BinaryOp::I32GeU => execute_i32_binary(registers, dst, lhs, rhs, |lhs, rhs| {
2004 i32::from((lhs as u32) >= (rhs as u32))
2005 }),
2006 BinaryOp::I64Add => {
2007 execute_i64_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs.wrapping_add(rhs))
2008 }
2009 BinaryOp::I64Sub => {
2010 execute_i64_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs.wrapping_sub(rhs))
2011 }
2012 BinaryOp::I64Mul => {
2013 execute_i64_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs.wrapping_mul(rhs))
2014 }
2015 BinaryOp::I64DivS => execute_i64_binary_checked(registers, dst, lhs, rhs, i64_div_s),
2016 BinaryOp::I64DivU => execute_i64_binary_checked(registers, dst, lhs, rhs, i64_div_u),
2017 BinaryOp::I64RemS => execute_i64_binary_checked(registers, dst, lhs, rhs, i64_rem_s),
2018 BinaryOp::I64RemU => execute_i64_binary_checked(registers, dst, lhs, rhs, i64_rem_u),
2019 BinaryOp::I64And => execute_i64_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs & rhs),
2020 BinaryOp::I64Or => execute_i64_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs | rhs),
2021 BinaryOp::I64Xor => execute_i64_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs ^ rhs),
2022 BinaryOp::I64Shl => execute_i64_binary(registers, dst, lhs, rhs, |lhs, rhs| {
2023 lhs.wrapping_shl(rhs as u32)
2024 }),
2025 BinaryOp::I64ShrS => execute_i64_binary(registers, dst, lhs, rhs, |lhs, rhs| {
2026 lhs >> ((rhs as u32) & 63)
2027 }),
2028 BinaryOp::I64ShrU => execute_i64_binary(registers, dst, lhs, rhs, |lhs, rhs| {
2029 ((lhs as u64) >> ((rhs as u32) & 63)) as i64
2030 }),
2031 BinaryOp::I64Rotl => execute_i64_binary(registers, dst, lhs, rhs, |lhs, rhs| {
2032 lhs.rotate_left(rhs as u32)
2033 }),
2034 BinaryOp::I64Rotr => execute_i64_binary(registers, dst, lhs, rhs, |lhs, rhs| {
2035 lhs.rotate_right(rhs as u32)
2036 }),
2037 BinaryOp::I64Eq => execute_i64_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs == rhs),
2038 BinaryOp::I64Ne => execute_i64_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs != rhs),
2039 BinaryOp::I64LtS => execute_i64_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs < rhs),
2040 BinaryOp::I64LtU => execute_i64_compare(registers, dst, lhs, rhs, |lhs, rhs| {
2041 (lhs as u64) < (rhs as u64)
2042 }),
2043 BinaryOp::I64GtS => execute_i64_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs > rhs),
2044 BinaryOp::I64GtU => execute_i64_compare(registers, dst, lhs, rhs, |lhs, rhs| {
2045 (lhs as u64) > (rhs as u64)
2046 }),
2047 BinaryOp::I64LeS => execute_i64_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs <= rhs),
2048 BinaryOp::I64LeU => execute_i64_compare(registers, dst, lhs, rhs, |lhs, rhs| {
2049 (lhs as u64) <= (rhs as u64)
2050 }),
2051 BinaryOp::I64GeS => execute_i64_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs >= rhs),
2052 BinaryOp::I64GeU => execute_i64_compare(registers, dst, lhs, rhs, |lhs, rhs| {
2053 (lhs as u64) >= (rhs as u64)
2054 }),
2055 BinaryOp::F32Add => execute_f32_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs + rhs),
2056 BinaryOp::F32Copysign => execute_f32_binary(registers, dst, lhs, rhs, libm::copysignf),
2057 BinaryOp::F32Sub => execute_f32_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs - rhs),
2058 BinaryOp::F32Mul => execute_f32_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs * rhs),
2059 BinaryOp::F32Div => execute_f32_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs / rhs),
2060 BinaryOp::F32Min => execute_f32_binary(registers, dst, lhs, rhs, wasm_f32_min),
2061 BinaryOp::F32Max => execute_f32_binary(registers, dst, lhs, rhs, wasm_f32_max),
2062 BinaryOp::F64Add => execute_f64_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs + rhs),
2063 BinaryOp::F64Copysign => execute_f64_binary(registers, dst, lhs, rhs, libm::copysign),
2064 BinaryOp::F64Sub => execute_f64_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs - rhs),
2065 BinaryOp::F64Mul => execute_f64_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs * rhs),
2066 BinaryOp::F64Div => execute_f64_binary(registers, dst, lhs, rhs, |lhs, rhs| lhs / rhs),
2067 BinaryOp::F64Min => execute_f64_binary(registers, dst, lhs, rhs, wasm_f64_min),
2068 BinaryOp::F64Max => execute_f64_binary(registers, dst, lhs, rhs, wasm_f64_max),
2069 BinaryOp::F32Eq => execute_f32_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs == rhs),
2070 BinaryOp::F32Ne => execute_f32_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs != rhs),
2071 BinaryOp::F32Lt => execute_f32_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs < rhs),
2072 BinaryOp::F32Gt => execute_f32_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs > rhs),
2073 BinaryOp::F32Le => execute_f32_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs <= rhs),
2074 BinaryOp::F32Ge => execute_f32_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs >= rhs),
2075 BinaryOp::F64Eq => execute_f64_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs == rhs),
2076 BinaryOp::F64Ne => execute_f64_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs != rhs),
2077 BinaryOp::F64Lt => execute_f64_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs < rhs),
2078 BinaryOp::F64Gt => execute_f64_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs > rhs),
2079 BinaryOp::F64Le => execute_f64_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs <= rhs),
2080 BinaryOp::F64Ge => execute_f64_compare(registers, dst, lhs, rhs, |lhs, rhs| lhs >= rhs),
2081 }
2082}
2083
2084fn execute_i32_binary(
2085 registers: &mut [Option<Value>],
2086 dst: Reg,
2087 lhs: Reg,
2088 rhs: Reg,
2089 op: impl FnOnce(i32, i32) -> i32,
2090) -> Result<(), RuntimeError> {
2091 let lhs = expect_i32(get_reg(registers, lhs)?)?;
2092 let rhs = expect_i32(get_reg(registers, rhs)?)?;
2093 set_reg(registers, dst, Value::I32(op(lhs, rhs)))
2094}
2095
2096fn execute_i32_binary_checked(
2097 registers: &mut [Option<Value>],
2098 dst: Reg,
2099 lhs: Reg,
2100 rhs: Reg,
2101 op: impl FnOnce(i32, i32) -> Result<i32, RuntimeError>,
2102) -> Result<(), RuntimeError> {
2103 let lhs = expect_i32(get_reg(registers, lhs)?)?;
2104 let rhs = expect_i32(get_reg(registers, rhs)?)?;
2105 set_reg(registers, dst, Value::I32(op(lhs, rhs)?))
2106}
2107
2108fn execute_i32_unary(
2109 registers: &mut [Option<Value>],
2110 dst: Reg,
2111 value: Reg,
2112 op: impl FnOnce(i32) -> i32,
2113) -> Result<(), RuntimeError> {
2114 let value = expect_i32(get_reg(registers, value)?)?;
2115 set_reg(registers, dst, Value::I32(op(value)))
2116}
2117
2118fn execute_i64_to_i32(
2119 registers: &mut [Option<Value>],
2120 dst: Reg,
2121 value: Reg,
2122 op: impl FnOnce(i64) -> i32,
2123) -> Result<(), RuntimeError> {
2124 let value = expect_i64(get_reg(registers, value)?)?;
2125 set_reg(registers, dst, Value::I32(op(value)))
2126}
2127
2128fn execute_i32_to_i64(
2129 registers: &mut [Option<Value>],
2130 dst: Reg,
2131 value: Reg,
2132 op: impl FnOnce(i32) -> i64,
2133) -> Result<(), RuntimeError> {
2134 let value = expect_i32(get_reg(registers, value)?)?;
2135 set_reg(registers, dst, Value::I64(op(value)))
2136}
2137
2138fn execute_i64_unary(
2139 registers: &mut [Option<Value>],
2140 dst: Reg,
2141 value: Reg,
2142 op: impl FnOnce(i64) -> i64,
2143) -> Result<(), RuntimeError> {
2144 let value = expect_i64(get_reg(registers, value)?)?;
2145 set_reg(registers, dst, Value::I64(op(value)))
2146}
2147
2148fn execute_i64_binary(
2149 registers: &mut [Option<Value>],
2150 dst: Reg,
2151 lhs: Reg,
2152 rhs: Reg,
2153 op: impl FnOnce(i64, i64) -> i64,
2154) -> Result<(), RuntimeError> {
2155 let lhs = expect_i64(get_reg(registers, lhs)?)?;
2156 let rhs = expect_i64(get_reg(registers, rhs)?)?;
2157 set_reg(registers, dst, Value::I64(op(lhs, rhs)))
2158}
2159
2160fn execute_i64_binary_checked(
2161 registers: &mut [Option<Value>],
2162 dst: Reg,
2163 lhs: Reg,
2164 rhs: Reg,
2165 op: impl FnOnce(i64, i64) -> Result<i64, RuntimeError>,
2166) -> Result<(), RuntimeError> {
2167 let lhs = expect_i64(get_reg(registers, lhs)?)?;
2168 let rhs = expect_i64(get_reg(registers, rhs)?)?;
2169 set_reg(registers, dst, Value::I64(op(lhs, rhs)?))
2170}
2171
2172fn execute_i64_test(
2173 registers: &mut [Option<Value>],
2174 dst: Reg,
2175 value: Reg,
2176 op: impl FnOnce(i64) -> i32,
2177) -> Result<(), RuntimeError> {
2178 let value = expect_i64(get_reg(registers, value)?)?;
2179 set_reg(registers, dst, Value::I32(op(value)))
2180}
2181
2182fn execute_i64_compare(
2183 registers: &mut [Option<Value>],
2184 dst: Reg,
2185 lhs: Reg,
2186 rhs: Reg,
2187 op: impl FnOnce(i64, i64) -> bool,
2188) -> Result<(), RuntimeError> {
2189 let lhs = expect_i64(get_reg(registers, lhs)?)?;
2190 let rhs = expect_i64(get_reg(registers, rhs)?)?;
2191 set_reg(registers, dst, Value::I32(i32::from(op(lhs, rhs))))
2192}
2193
2194fn i32_div_s(lhs: i32, rhs: i32) -> Result<i32, RuntimeError> {
2195 if rhs == 0 {
2196 return Err(trap(RuntimeTrap::IntegerDivideByZero));
2197 }
2198 if lhs == i32::MIN && rhs == -1 {
2199 return Err(trap(RuntimeTrap::IntegerOverflow));
2200 }
2201 Ok(lhs / rhs)
2202}
2203
2204fn i32_div_u(lhs: i32, rhs: i32) -> Result<i32, RuntimeError> {
2205 if rhs == 0 {
2206 return Err(trap(RuntimeTrap::IntegerDivideByZero));
2207 }
2208 Ok(((lhs as u32) / (rhs as u32)) as i32)
2209}
2210
2211fn i32_rem_s(lhs: i32, rhs: i32) -> Result<i32, RuntimeError> {
2212 if rhs == 0 {
2213 return Err(trap(RuntimeTrap::IntegerDivideByZero));
2214 }
2215 if lhs == i32::MIN && rhs == -1 {
2216 return Ok(0);
2217 }
2218 Ok(lhs % rhs)
2219}
2220
2221fn i32_rem_u(lhs: i32, rhs: i32) -> Result<i32, RuntimeError> {
2222 if rhs == 0 {
2223 return Err(trap(RuntimeTrap::IntegerDivideByZero));
2224 }
2225 Ok(((lhs as u32) % (rhs as u32)) as i32)
2226}
2227
2228fn i64_div_s(lhs: i64, rhs: i64) -> Result<i64, RuntimeError> {
2229 if rhs == 0 {
2230 return Err(trap(RuntimeTrap::IntegerDivideByZero));
2231 }
2232 if lhs == i64::MIN && rhs == -1 {
2233 return Err(trap(RuntimeTrap::IntegerOverflow));
2234 }
2235 Ok(lhs / rhs)
2236}
2237
2238fn i64_div_u(lhs: i64, rhs: i64) -> Result<i64, RuntimeError> {
2239 if rhs == 0 {
2240 return Err(trap(RuntimeTrap::IntegerDivideByZero));
2241 }
2242 Ok(((lhs as u64) / (rhs as u64)) as i64)
2243}
2244
2245fn i64_rem_s(lhs: i64, rhs: i64) -> Result<i64, RuntimeError> {
2246 if rhs == 0 {
2247 return Err(trap(RuntimeTrap::IntegerDivideByZero));
2248 }
2249 if lhs == i64::MIN && rhs == -1 {
2250 return Ok(0);
2251 }
2252 Ok(lhs % rhs)
2253}
2254
2255fn i64_rem_u(lhs: i64, rhs: i64) -> Result<i64, RuntimeError> {
2256 if rhs == 0 {
2257 return Err(trap(RuntimeTrap::IntegerDivideByZero));
2258 }
2259 Ok(((lhs as u64) % (rhs as u64)) as i64)
2260}
2261
2262fn trap(trap: RuntimeTrap) -> RuntimeError {
2263 RuntimeError {
2264 kind: RuntimeErrorKind::Trap(trap),
2265 }
2266}
2267
2268fn execute_f32_unary(
2269 registers: &mut [Option<Value>],
2270 dst: Reg,
2271 value: Reg,
2272 op: impl FnOnce(f32) -> f32,
2273) -> Result<(), RuntimeError> {
2274 let value = expect_f32(get_reg(registers, value)?)?;
2275 set_reg(registers, dst, Value::F32(op(value)))
2276}
2277
2278fn execute_f32_binary(
2279 registers: &mut [Option<Value>],
2280 dst: Reg,
2281 lhs: Reg,
2282 rhs: Reg,
2283 op: impl FnOnce(f32, f32) -> f32,
2284) -> Result<(), RuntimeError> {
2285 let lhs = expect_f32(get_reg(registers, lhs)?)?;
2286 let rhs = expect_f32(get_reg(registers, rhs)?)?;
2287 set_reg(registers, dst, Value::F32(op(lhs, rhs)))
2288}
2289
2290fn execute_f32_compare(
2291 registers: &mut [Option<Value>],
2292 dst: Reg,
2293 lhs: Reg,
2294 rhs: Reg,
2295 op: impl FnOnce(f32, f32) -> bool,
2296) -> Result<(), RuntimeError> {
2297 let lhs = expect_f32(get_reg(registers, lhs)?)?;
2298 let rhs = expect_f32(get_reg(registers, rhs)?)?;
2299 set_reg(registers, dst, Value::I32(i32::from(op(lhs, rhs))))
2300}
2301
2302fn execute_f64_unary(
2303 registers: &mut [Option<Value>],
2304 dst: Reg,
2305 value: Reg,
2306 op: impl FnOnce(f64) -> f64,
2307) -> Result<(), RuntimeError> {
2308 let value = expect_f64(get_reg(registers, value)?)?;
2309 set_reg(registers, dst, Value::F64(op(value)))
2310}
2311
2312fn execute_f64_binary(
2313 registers: &mut [Option<Value>],
2314 dst: Reg,
2315 lhs: Reg,
2316 rhs: Reg,
2317 op: impl FnOnce(f64, f64) -> f64,
2318) -> Result<(), RuntimeError> {
2319 let lhs = expect_f64(get_reg(registers, lhs)?)?;
2320 let rhs = expect_f64(get_reg(registers, rhs)?)?;
2321 set_reg(registers, dst, Value::F64(op(lhs, rhs)))
2322}
2323
2324fn execute_f64_compare(
2325 registers: &mut [Option<Value>],
2326 dst: Reg,
2327 lhs: Reg,
2328 rhs: Reg,
2329 op: impl FnOnce(f64, f64) -> bool,
2330) -> Result<(), RuntimeError> {
2331 let lhs = expect_f64(get_reg(registers, lhs)?)?;
2332 let rhs = expect_f64(get_reg(registers, rhs)?)?;
2333 set_reg(registers, dst, Value::I32(i32::from(op(lhs, rhs))))
2334}
2335
2336fn execute_i32_to_f32(
2337 registers: &mut [Option<Value>],
2338 dst: Reg,
2339 value: Reg,
2340 op: impl FnOnce(i32) -> f32,
2341) -> Result<(), RuntimeError> {
2342 let value = expect_i32(get_reg(registers, value)?)?;
2343 set_reg(registers, dst, Value::F32(op(value)))
2344}
2345
2346fn execute_i64_to_f32(
2347 registers: &mut [Option<Value>],
2348 dst: Reg,
2349 value: Reg,
2350 op: impl FnOnce(i64) -> f32,
2351) -> Result<(), RuntimeError> {
2352 let value = expect_i64(get_reg(registers, value)?)?;
2353 set_reg(registers, dst, Value::F32(op(value)))
2354}
2355
2356fn execute_i32_to_f64(
2357 registers: &mut [Option<Value>],
2358 dst: Reg,
2359 value: Reg,
2360 op: impl FnOnce(i32) -> f64,
2361) -> Result<(), RuntimeError> {
2362 let value = expect_i32(get_reg(registers, value)?)?;
2363 set_reg(registers, dst, Value::F64(op(value)))
2364}
2365
2366fn execute_i64_to_f64(
2367 registers: &mut [Option<Value>],
2368 dst: Reg,
2369 value: Reg,
2370 op: impl FnOnce(i64) -> f64,
2371) -> Result<(), RuntimeError> {
2372 let value = expect_i64(get_reg(registers, value)?)?;
2373 set_reg(registers, dst, Value::F64(op(value)))
2374}
2375
2376fn execute_f32_to_i32(
2377 registers: &mut [Option<Value>],
2378 dst: Reg,
2379 value: Reg,
2380 op: impl FnOnce(f32) -> i32,
2381) -> Result<(), RuntimeError> {
2382 let value = expect_f32(get_reg(registers, value)?)?;
2383 set_reg(registers, dst, Value::I32(op(value)))
2384}
2385
2386fn execute_f64_to_i64(
2387 registers: &mut [Option<Value>],
2388 dst: Reg,
2389 value: Reg,
2390 op: impl FnOnce(f64) -> i64,
2391) -> Result<(), RuntimeError> {
2392 let value = expect_f64(get_reg(registers, value)?)?;
2393 set_reg(registers, dst, Value::I64(op(value)))
2394}
2395
2396fn execute_f32_to_f64(
2397 registers: &mut [Option<Value>],
2398 dst: Reg,
2399 value: Reg,
2400 op: impl FnOnce(f32) -> f64,
2401) -> Result<(), RuntimeError> {
2402 let value = expect_f32(get_reg(registers, value)?)?;
2403 set_reg(registers, dst, Value::F64(op(value)))
2404}
2405
2406fn execute_f64_to_f32(
2407 registers: &mut [Option<Value>],
2408 dst: Reg,
2409 value: Reg,
2410 op: impl FnOnce(f64) -> f32,
2411) -> Result<(), RuntimeError> {
2412 let value = expect_f64(get_reg(registers, value)?)?;
2413 set_reg(registers, dst, Value::F32(op(value)))
2414}
2415
2416fn execute_f32_to_i32_checked(
2417 registers: &mut [Option<Value>],
2418 dst: Reg,
2419 value: Reg,
2420 op: impl FnOnce(f32) -> i32,
2421 unsigned: bool,
2422) -> Result<(), RuntimeError> {
2423 let value = expect_f32(get_reg(registers, value)?)?;
2424 if value.is_nan() {
2425 return Err(trap(RuntimeTrap::InvalidConversionToInteger));
2426 }
2427 if unsigned {
2428 if value <= -1.0 || value >= (u32::MAX as f32) {
2429 return Err(trap(RuntimeTrap::IntegerOverflow));
2430 }
2431 } else if value >= (i32::MAX as f32) || value < (i32::MIN as f32) {
2432 return Err(trap(RuntimeTrap::IntegerOverflow));
2433 }
2434 set_reg(registers, dst, Value::I32(op(value)))
2435}
2436
2437fn execute_f64_to_i32_checked(
2438 registers: &mut [Option<Value>],
2439 dst: Reg,
2440 value: Reg,
2441 op: impl FnOnce(f64) -> i32,
2442 unsigned: bool,
2443) -> Result<(), RuntimeError> {
2444 let value = expect_f64(get_reg(registers, value)?)?;
2445 if value.is_nan() {
2446 return Err(trap(RuntimeTrap::InvalidConversionToInteger));
2447 }
2448 if unsigned {
2449 if value <= -1.0 || value >= 4294967296.0 {
2450 return Err(trap(RuntimeTrap::IntegerOverflow));
2451 }
2452 } else if value >= 2147483648.0 || value <= -2147483649.0 {
2453 return Err(trap(RuntimeTrap::IntegerOverflow));
2454 }
2455 set_reg(registers, dst, Value::I32(op(value)))
2456}
2457
2458fn execute_f32_to_i64_checked(
2459 registers: &mut [Option<Value>],
2460 dst: Reg,
2461 value: Reg,
2462 op: impl FnOnce(f32) -> i64,
2463 unsigned: bool,
2464) -> Result<(), RuntimeError> {
2465 let value = expect_f32(get_reg(registers, value)?)?;
2466 if value.is_nan() {
2467 return Err(trap(RuntimeTrap::InvalidConversionToInteger));
2468 }
2469 if unsigned {
2470 if value <= -1.0 || value >= (u64::MAX as f32) {
2471 return Err(trap(RuntimeTrap::IntegerOverflow));
2472 }
2473 } else if value >= (i64::MAX as f32) || value < (i64::MIN as f32) {
2474 return Err(trap(RuntimeTrap::IntegerOverflow));
2475 }
2476 set_reg(registers, dst, Value::I64(op(value)))
2477}
2478
2479fn execute_f64_to_i64_checked(
2480 registers: &mut [Option<Value>],
2481 dst: Reg,
2482 value: Reg,
2483 op: impl FnOnce(f64) -> i64,
2484 unsigned: bool,
2485) -> Result<(), RuntimeError> {
2486 let value = expect_f64(get_reg(registers, value)?)?;
2487 if value.is_nan() {
2488 return Err(trap(RuntimeTrap::InvalidConversionToInteger));
2489 }
2490 if unsigned {
2491 if value <= -1.0 || value >= (u64::MAX as f64) {
2492 return Err(trap(RuntimeTrap::IntegerOverflow));
2493 }
2494 } else if value >= (i64::MAX as f64) || value < (i64::MIN as f64) {
2495 return Err(trap(RuntimeTrap::IntegerOverflow));
2496 }
2497 set_reg(registers, dst, Value::I64(op(value)))
2498}
2499
2500fn execute_f32_to_i32_saturating(
2501 registers: &mut [Option<Value>],
2502 dst: Reg,
2503 value: Reg,
2504 op: impl FnOnce(f32) -> i32,
2505) -> Result<(), RuntimeError> {
2506 let value = expect_f32(get_reg(registers, value)?)?;
2507 set_reg(registers, dst, Value::I32(op(value)))
2508}
2509
2510fn execute_f64_to_i32_saturating(
2511 registers: &mut [Option<Value>],
2512 dst: Reg,
2513 value: Reg,
2514 op: impl FnOnce(f64) -> i32,
2515) -> Result<(), RuntimeError> {
2516 let value = expect_f64(get_reg(registers, value)?)?;
2517 set_reg(registers, dst, Value::I32(op(value)))
2518}
2519
2520fn execute_f32_to_i64_saturating(
2521 registers: &mut [Option<Value>],
2522 dst: Reg,
2523 value: Reg,
2524 op: impl FnOnce(f32) -> i64,
2525) -> Result<(), RuntimeError> {
2526 let value = expect_f32(get_reg(registers, value)?)?;
2527 set_reg(registers, dst, Value::I64(op(value)))
2528}
2529
2530fn execute_f64_to_i64_saturating(
2531 registers: &mut [Option<Value>],
2532 dst: Reg,
2533 value: Reg,
2534 op: impl FnOnce(f64) -> i64,
2535) -> Result<(), RuntimeError> {
2536 let value = expect_f64(get_reg(registers, value)?)?;
2537 set_reg(registers, dst, Value::I64(op(value)))
2538}
2539
2540fn expect_i32(value: Value) -> Result<i32, RuntimeError> {
2541 match value {
2542 Value::I32(value) => Ok(value),
2543 value => Err(RuntimeError {
2544 kind: RuntimeErrorKind::TypeMismatch {
2545 expected: ValType::Num(NumType::I32),
2546 found: value.val_type(),
2547 },
2548 }),
2549 }
2550}
2551
2552fn expect_i64(value: Value) -> Result<i64, RuntimeError> {
2553 match value {
2554 Value::I64(value) => Ok(value),
2555 value => Err(RuntimeError {
2556 kind: RuntimeErrorKind::TypeMismatch {
2557 expected: ValType::Num(NumType::I64),
2558 found: value.val_type(),
2559 },
2560 }),
2561 }
2562}
2563
2564fn expect_f32(value: Value) -> Result<f32, RuntimeError> {
2565 match value {
2566 Value::F32(value) => Ok(value),
2567 value => Err(RuntimeError {
2568 kind: RuntimeErrorKind::TypeMismatch {
2569 expected: ValType::Num(NumType::F32),
2570 found: value.val_type(),
2571 },
2572 }),
2573 }
2574}
2575
2576fn expect_f64(value: Value) -> Result<f64, RuntimeError> {
2577 match value {
2578 Value::F64(value) => Ok(value),
2579 value => Err(RuntimeError {
2580 kind: RuntimeErrorKind::TypeMismatch {
2581 expected: ValType::Num(NumType::F64),
2582 found: value.val_type(),
2583 },
2584 }),
2585 }
2586}
2587
2588#[cfg(test)]
2589mod tests {
2590 use super::*;
2591 use crate::binary::module::Module;
2592
2593 #[test]
2594 fn execute_exported_add_by_name() {
2595 let reg_module = lowered_add_module();
2596
2597 let store = Store::instantiate(®_module).unwrap();
2598 let result = execute_export(
2599 ®_module,
2600 &store,
2601 "add",
2602 &[Value::I32(20), Value::I32(22)],
2603 )
2604 .unwrap();
2605
2606 assert_eq!(result, alloc::vec![Value::I32(42)]);
2607 }
2608
2609 #[test]
2610 fn reject_unknown_export_name() {
2611 let reg_module = lowered_add_module();
2612
2613 let store = Store::instantiate(®_module).unwrap();
2614 let err = execute_export(®_module, &store, "missing", &[]).unwrap_err();
2615
2616 assert_eq!(
2617 err.kind,
2618 RuntimeErrorKind::UnknownExport {
2619 name: "missing".into(),
2620 }
2621 );
2622 }
2623
2624 #[test]
2625 fn reject_export_call_with_missing_arg() {
2626 let reg_module = lowered_add_module();
2627
2628 let store = Store::instantiate(®_module).unwrap();
2629 let err = execute_export(®_module, &store, "add", &[Value::I32(20)]).unwrap_err();
2630
2631 assert_eq!(
2632 err.kind,
2633 RuntimeErrorKind::ArityMismatch {
2634 expected: 2,
2635 found: 1,
2636 }
2637 );
2638 }
2639
2640 #[test]
2641 fn reject_export_call_with_extra_arg() {
2642 let reg_module = lowered_add_module();
2643
2644 let store = Store::instantiate(®_module).unwrap();
2645 let err = execute_export(
2646 ®_module,
2647 &store,
2648 "add",
2649 &[Value::I32(20), Value::I32(22), Value::I32(1)],
2650 )
2651 .unwrap_err();
2652
2653 assert_eq!(
2654 err.kind,
2655 RuntimeErrorKind::ArityMismatch {
2656 expected: 2,
2657 found: 3,
2658 }
2659 );
2660 }
2661
2662 #[test]
2663 fn reject_export_call_with_wrong_arg_type() {
2664 let reg_module = lowered_add_module();
2665
2666 let store = Store::instantiate(®_module).unwrap();
2667 let err = execute_export(
2668 ®_module,
2669 &store,
2670 "add",
2671 &[Value::I64(20), Value::I32(22)],
2672 )
2673 .unwrap_err();
2674
2675 assert_eq!(
2676 err.kind,
2677 RuntimeErrorKind::TypeMismatch {
2678 expected: ValType::Num(NumType::I32),
2679 found: ValType::Num(NumType::I64),
2680 }
2681 );
2682 }
2683
2684 fn lowered_add_module() -> RegModule {
2685 let bytes = baedeker_testdata::fixture_bytes("add");
2686 let module = Module::decode(&bytes).unwrap();
2687 module.lower().unwrap()
2688 }
2689}