wasm/execution/interpreter_loop/
numeric.rs

1use core::ops::ControlFlow;
2
3use crate::{
4    assert_validated::UnwrapValidatedExt,
5    core::reader::types::opcode,
6    execution::interpreter_loop::{define_instruction_fn, Args},
7    value::{self, F32, F64},
8    TrapError,
9};
10
11// t.const
12define_instruction_fn! {
13    i32_const,
14    fuel_check = flat(opcode::I32_CONST),
15    |Args {
16         resumable, wasm, ..
17     }| {
18        let constant = wasm.read_var_i32().unwrap_validated();
19        trace!("Instruction: i32.const [] -> [{constant}]");
20        resumable.stack.push_value(constant.into())?;
21        Ok(ControlFlow::Continue(()))
22    }
23}
24
25define_instruction_fn! {
26    i64_const,
27    fuel_check = flat(opcode::I64_CONST),
28    |Args {
29         wasm, resumable, ..
30     }| {
31        let constant = wasm.read_var_i64().unwrap_validated();
32        trace!("Instruction: i64.const [] -> [{constant}]");
33        resumable.stack.push_value(constant.into())?;
34        Ok(ControlFlow::Continue(()))
35    }
36}
37
38define_instruction_fn! {
39    f32_const,
40    fuel_check = flat(opcode::F32_CONST),
41    |Args {
42         resumable, wasm, ..
43     }| {
44        let constant = F32::from_bits(wasm.read_f32().unwrap_validated());
45        trace!("Instruction: f32.const [] -> [{constant:.7}]");
46        resumable.stack.push_value(constant.into())?;
47        Ok(ControlFlow::Continue(()))
48    }
49}
50
51define_instruction_fn! {
52    f64_const,
53    fuel_check = flat(opcode::F64_CONST),
54    |Args {
55         wasm, resumable, ..
56     }| {
57        let constant = F64::from_bits(wasm.read_f64().unwrap_validated());
58        trace!("Instruction: f64.const [] -> [{constant}]");
59        resumable.stack.push_value(constant.into())?;
60        Ok(ControlFlow::Continue(()))
61    }
62}
63
64// i32.unop
65define_instruction_fn! {
66    i32_clz,
67    fuel_check = flat(opcode::I32_CLZ),
68    |Args { resumable, .. }| {
69        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
70        let res = v1.leading_zeros() as i32;
71
72        trace!("Instruction: i32.clz [{v1}] -> [{res}]");
73        resumable.stack.push_value(res.into())?;
74        Ok(ControlFlow::Continue(()))
75    }
76}
77
78define_instruction_fn! {
79    i32_ctz,
80    fuel_check = flat(opcode::I32_CTZ),
81    |Args { resumable, .. }| {
82        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
83        let res = v1.trailing_zeros() as i32;
84
85        trace!("Instruction: i32.ctz [{v1}] -> [{res}]");
86        resumable.stack.push_value(res.into())?;
87        Ok(ControlFlow::Continue(()))
88    }
89}
90
91define_instruction_fn! {
92    i32_popcnt,
93    fuel_check = flat(opcode::I32_POPCNT),
94    |Args { resumable, .. }| {
95        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
96        let res = v1.count_ones() as i32;
97
98        trace!("Instruction: i32.popcnt [{v1}] -> [{res}]");
99        resumable.stack.push_value(res.into())?;
100        Ok(ControlFlow::Continue(()))
101    }
102}
103
104// i64.unop
105define_instruction_fn! {
106    i64_clz,
107    fuel_check = flat(opcode::I64_CLZ),
108    |Args { resumable, .. }| {
109        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
110        let res = v1.leading_zeros() as i64;
111
112        trace!("Instruction: i64.clz [{v1}] -> [{res}]");
113        resumable.stack.push_value(res.into())?;
114        Ok(ControlFlow::Continue(()))
115    }
116}
117
118define_instruction_fn! {
119    i64_ctz,
120    fuel_check = flat(opcode::I64_CTZ),
121    |Args { resumable, .. }| {
122        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
123        let res = v1.trailing_zeros() as i64;
124
125        trace!("Instruction: i64.ctz [{v1}] -> [{res}]");
126        resumable.stack.push_value(res.into())?;
127        Ok(ControlFlow::Continue(()))
128    }
129}
130
131define_instruction_fn! {
132    i64_popcnt,
133    fuel_check = flat(opcode::I64_POPCNT),
134    |Args { resumable, .. }| {
135        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
136        let res = v1.count_ones() as i64;
137
138        trace!("Instruction: i64.popcnt [{v1}] -> [{res}]");
139        resumable.stack.push_value(res.into())?;
140        Ok(ControlFlow::Continue(()))
141    }
142}
143
144// f32.unop
145define_instruction_fn! {
146    f32_abs,
147    fuel_check = flat(opcode::F32_ABS),
148    |Args { resumable, .. }| {
149        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
150        let res: value::F32 = v1.abs();
151
152        trace!("Instruction: f32.abs [{v1}] -> [{res}]");
153        resumable.stack.push_value(res.into())?;
154        Ok(ControlFlow::Continue(()))
155    }
156}
157
158define_instruction_fn! {
159    f32_neg,
160    fuel_check = flat(opcode::F32_NEG),
161    |Args { resumable, .. }| {
162        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
163        let res: value::F32 = v1.neg();
164
165        trace!("Instruction: f32.neg [{v1}] -> [{res}]");
166        resumable.stack.push_value(res.into())?;
167        Ok(ControlFlow::Continue(()))
168    }
169}
170
171define_instruction_fn! {
172    f32_ceil,
173    fuel_check = flat(opcode::F32_CEIL),
174    |Args { resumable, .. }| {
175        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
176        let res: value::F32 = v1.ceil();
177
178        trace!("Instruction: f32.ceil [{v1}] -> [{res}]");
179        resumable.stack.push_value(res.into())?;
180        Ok(ControlFlow::Continue(()))
181    }
182}
183
184define_instruction_fn! {
185    f32_floor,
186    fuel_check = flat(opcode::F32_FLOOR),
187    |Args { resumable, .. }| {
188        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
189        let res: value::F32 = v1.floor();
190
191        trace!("Instruction: f32.floor [{v1}] -> [{res}]");
192        resumable.stack.push_value(res.into())?;
193        Ok(ControlFlow::Continue(()))
194    }
195}
196
197define_instruction_fn! {
198    f32_trunc,
199    fuel_check = flat(opcode::F32_TRUNC),
200    |Args { resumable, .. }| {
201        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
202        let res: value::F32 = v1.trunc();
203
204        trace!("Instruction: f32.trunc [{v1}] -> [{res}]");
205        resumable.stack.push_value(res.into())?;
206        Ok(ControlFlow::Continue(()))
207    }
208}
209
210define_instruction_fn! {
211    f32_nearest,
212    fuel_check = flat(opcode::F32_NEAREST),
213    |Args { resumable, .. }| {
214        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
215        let res: value::F32 = v1.nearest();
216
217        trace!("Instruction: f32.nearest [{v1}] -> [{res}]");
218        resumable.stack.push_value(res.into())?;
219        Ok(ControlFlow::Continue(()))
220    }
221}
222
223define_instruction_fn! {
224    f32_sqrt,
225    fuel_check = flat(opcode::F32_SQRT),
226    |Args { resumable, .. }| {
227        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
228        let res: value::F32 = v1.sqrt();
229
230        trace!("Instruction: f32.sqrt [{v1}] -> [{res}]");
231        resumable.stack.push_value(res.into())?;
232        Ok(ControlFlow::Continue(()))
233    }
234}
235
236// f64.unop
237define_instruction_fn! {
238    f64_abs,
239    fuel_check = flat(opcode::F64_ABS),
240    |Args { resumable, .. }| {
241        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
242        let res: value::F64 = v1.abs();
243
244        trace!("Instruction: f64.abs [{v1}] -> [{res}]");
245        resumable.stack.push_value(res.into())?;
246        Ok(ControlFlow::Continue(()))
247    }
248}
249
250define_instruction_fn! {
251    f64_neg,
252    fuel_check = flat(opcode::F64_NEG),
253    |Args { resumable, .. }| {
254        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
255        let res: value::F64 = v1.neg();
256
257        trace!("Instruction: f64.neg [{v1}] -> [{res}]");
258        resumable.stack.push_value(res.into())?;
259        Ok(ControlFlow::Continue(()))
260    }
261}
262
263define_instruction_fn! {
264    f64_ceil,
265    fuel_check = flat(opcode::F64_CEIL),
266    |Args { resumable, .. }| {
267        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
268        let res: value::F64 = v1.ceil();
269
270        trace!("Instruction: f64.ceil [{v1}] -> [{res}]");
271        resumable.stack.push_value(res.into())?;
272        Ok(ControlFlow::Continue(()))
273    }
274}
275
276define_instruction_fn! {
277    f64_floor,
278    fuel_check = flat(opcode::F64_FLOOR),
279    |Args { resumable, .. }| {
280        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
281        let res: value::F64 = v1.floor();
282
283        trace!("Instruction: f64.floor [{v1}] -> [{res}]");
284        resumable.stack.push_value(res.into())?;
285        Ok(ControlFlow::Continue(()))
286    }
287}
288
289define_instruction_fn! {
290    f64_trunc,
291    fuel_check = flat(opcode::F64_TRUNC),
292    |Args { resumable, .. }| {
293        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
294        let res: value::F64 = v1.trunc();
295
296        trace!("Instruction: f64.trunc [{v1}] -> [{res}]");
297        resumable.stack.push_value(res.into())?;
298        Ok(ControlFlow::Continue(()))
299    }
300}
301
302define_instruction_fn! {
303    f64_nearest,
304    fuel_check = flat(opcode::F64_NEAREST),
305    |Args { resumable, .. }| {
306        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
307        let res: value::F64 = v1.nearest();
308
309        trace!("Instruction: f64.nearest [{v1}] -> [{res}]");
310        resumable.stack.push_value(res.into())?;
311        Ok(ControlFlow::Continue(()))
312    }
313}
314
315define_instruction_fn! {
316    f64_sqrt,
317    fuel_check = flat(opcode::F64_SQRT),
318    |Args { resumable, .. }| {
319        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
320        let res: value::F64 = v1.sqrt();
321
322        trace!("Instruction: f64.sqrt [{v1}] -> [{res}]");
323        resumable.stack.push_value(res.into())?;
324        Ok(ControlFlow::Continue(()))
325    }
326}
327
328// i32.binop
329define_instruction_fn! {
330    i32_add,
331    fuel_check = flat(opcode::I32_ADD),
332    |Args { resumable, .. }| {
333        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
334        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
335        let res = v1.wrapping_add(v2);
336
337        trace!("Instruction: i32.add [{v1} {v2}] -> [{res}]");
338        resumable.stack.push_value(res.into())?;
339        Ok(ControlFlow::Continue(()))
340    }
341}
342
343define_instruction_fn! {
344    i32_sub,
345    fuel_check = flat(opcode::I32_SUB),
346    |Args { resumable, .. }| {
347        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
348        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
349        let res = v1.wrapping_sub(v2);
350
351        trace!("Instruction: i32.sub [{v1} {v2}] -> [{res}]");
352        resumable.stack.push_value(res.into())?;
353        Ok(ControlFlow::Continue(()))
354    }
355}
356
357define_instruction_fn! {
358    i32_mul,
359    fuel_check = flat(opcode::I32_MUL),
360    |Args { resumable, .. }| {
361        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
362        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
363        let res = v1.wrapping_mul(v2);
364
365        trace!("Instruction: i32.mul [{v1} {v2}] -> [{res}]");
366        resumable.stack.push_value(res.into())?;
367        Ok(ControlFlow::Continue(()))
368    }
369}
370
371define_instruction_fn! {
372    i32_div_s,
373    fuel_check = flat(opcode::I32_DIV_S),
374    |Args { resumable, .. }| {
375        let dividend: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
376        let divisor: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
377
378        if dividend == 0 {
379            return Err(TrapError::DivideBy0.into());
380        }
381        if divisor == i32::MIN && dividend == -1 {
382            return Err(TrapError::UnrepresentableResult.into());
383        }
384
385        let res = divisor / dividend;
386
387        trace!("Instruction: i32.div_s [{divisor} {dividend}] -> [{res}]");
388        resumable.stack.push_value(res.into())?;
389        Ok(ControlFlow::Continue(()))
390    }
391}
392
393define_instruction_fn! {
394    i32_div_u,
395    fuel_check = flat(opcode::I32_DIV_U),
396    |Args { resumable, .. }| {
397        let dividend: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
398        let divisor: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
399
400        let dividend = dividend as u32;
401        let divisor = divisor as u32;
402
403        if dividend == 0 {
404            return Err(TrapError::DivideBy0.into());
405        }
406
407        let res = (divisor / dividend) as i32;
408
409        trace!("Instruction: i32.div_u [{divisor} {dividend}] -> [{res}]");
410        resumable.stack.push_value(res.into())?;
411        Ok(ControlFlow::Continue(()))
412    }
413}
414
415define_instruction_fn! {
416    i32_rem_s,
417    fuel_check = flat(opcode::I32_REM_S),
418    |Args { resumable, .. }| {
419        let dividend: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
420        let divisor: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
421
422        if dividend == 0 {
423            return Err(TrapError::DivideBy0.into());
424        }
425
426        let res = divisor.checked_rem(dividend);
427        let res = res.unwrap_or_default();
428
429        trace!("Instruction: i32.rem_s [{divisor} {dividend}] -> [{res}]");
430        resumable.stack.push_value(res.into())?;
431        Ok(ControlFlow::Continue(()))
432    }
433}
434
435define_instruction_fn! {
436    i32_rem_u,
437    fuel_check = flat(opcode::I32_REM_U),
438    |Args { resumable, .. }| {
439        let dividend: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
440        let divisor: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
441
442        let dividend = dividend as u32;
443        let divisor = divisor as u32;
444
445        if dividend == 0 {
446            return Err(TrapError::DivideBy0.into());
447        }
448
449        let res = divisor.checked_rem(dividend);
450        let res = res.unwrap_or_default() as i32;
451
452        trace!("Instruction: i32.rem_u [{divisor} {dividend}] -> [{res}]");
453        resumable.stack.push_value(res.into())?;
454        Ok(ControlFlow::Continue(()))
455    }
456}
457
458define_instruction_fn! {
459    i32_and,
460    fuel_check = flat(opcode::I32_AND),
461    |Args { resumable, .. }| {
462        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
463        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
464        let res = v1 & v2;
465
466        trace!("Instruction: i32.and [{v1} {v2}] -> [{res}]");
467        resumable.stack.push_value(res.into())?;
468        Ok(ControlFlow::Continue(()))
469    }
470}
471
472define_instruction_fn! {
473    i32_or,
474    fuel_check = flat(opcode::I32_OR),
475    |Args { resumable, .. }| {
476        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
477        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
478        let res = v1 | v2;
479
480        trace!("Instruction: i32.or [{v1} {v2}] -> [{res}]");
481        resumable.stack.push_value(res.into())?;
482        Ok(ControlFlow::Continue(()))
483    }
484}
485
486define_instruction_fn! {
487    i32_xor,
488    fuel_check = flat(opcode::I32_XOR),
489    |Args { resumable, .. }| {
490        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
491        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
492        let res = v1 ^ v2;
493
494        trace!("Instruction: i32.xor [{v1} {v2}] -> [{res}]");
495        resumable.stack.push_value(res.into())?;
496        Ok(ControlFlow::Continue(()))
497    }
498}
499
500define_instruction_fn! {
501    i32_shl,
502    fuel_check = flat(opcode::I32_SHL),
503    |Args { resumable, .. }| {
504        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
505        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
506        let res = v2.wrapping_shl(v1 as u32);
507
508        trace!("Instruction: i32.shl [{v2} {v1}] -> [{res}]");
509        resumable.stack.push_value(res.into())?;
510        Ok(ControlFlow::Continue(()))
511    }
512}
513
514define_instruction_fn! {
515    i32_shr_s,
516    fuel_check = flat(opcode::I32_SHR_S),
517    |Args { resumable, .. }| {
518        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
519        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
520
521        let res = v2.wrapping_shr(v1 as u32);
522
523        trace!("Instruction: i32.shr_s [{v2} {v1}] -> [{res}]");
524        resumable.stack.push_value(res.into())?;
525        Ok(ControlFlow::Continue(()))
526    }
527}
528
529define_instruction_fn! {
530    i32_shr_u,
531    fuel_check = flat(opcode::I32_SHR_U),
532    |Args { resumable, .. }| {
533        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
534        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
535
536        let res = (v2 as u32).wrapping_shr(v1 as u32) as i32;
537
538        trace!("Instruction: i32.shr_u [{v2} {v1}] -> [{res}]");
539        resumable.stack.push_value(res.into())?;
540        Ok(ControlFlow::Continue(()))
541    }
542}
543
544define_instruction_fn! {
545    i32_rotl,
546    fuel_check = flat(opcode::I32_ROTL),
547    |Args { resumable, .. }| {
548        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
549        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
550
551        let res = v2.rotate_left(v1 as u32);
552
553        trace!("Instruction: i32.rotl [{v2} {v1}] -> [{res}]");
554        resumable.stack.push_value(res.into())?;
555        Ok(ControlFlow::Continue(()))
556    }
557}
558
559define_instruction_fn! {
560    i32_rotr,
561    fuel_check = flat(opcode::I32_ROTR),
562    |Args { resumable, .. }| {
563        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
564        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
565
566        let res = v2.rotate_right(v1 as u32);
567
568        trace!("Instruction: i32.rotr [{v2} {v1}] -> [{res}]");
569        resumable.stack.push_value(res.into())?;
570        Ok(ControlFlow::Continue(()))
571    }
572}
573
574// i64.binop
575define_instruction_fn! {
576    i64_add,
577    fuel_check = flat(opcode::I64_ADD),
578    |Args { resumable, .. }| {
579        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
580        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
581        let res = v1.wrapping_add(v2);
582
583        trace!("Instruction: i64.add [{v1} {v2}] -> [{res}]");
584        resumable.stack.push_value(res.into())?;
585        Ok(ControlFlow::Continue(()))
586    }
587}
588
589define_instruction_fn! {
590    i64_sub,
591    fuel_check = flat(opcode::I64_SUB),
592    |Args { resumable, .. }| {
593        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
594        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
595        let res = v1.wrapping_sub(v2);
596
597        trace!("Instruction: i64.sub [{v1} {v2}] -> [{res}]");
598        resumable.stack.push_value(res.into())?;
599        Ok(ControlFlow::Continue(()))
600    }
601}
602
603define_instruction_fn! {
604    i64_mul,
605    fuel_check = flat(opcode::I64_MUL),
606    |Args { resumable, .. }| {
607        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
608        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
609        let res = v1.wrapping_mul(v2);
610
611        trace!("Instruction: i64.mul [{v1} {v2}] -> [{res}]");
612        resumable.stack.push_value(res.into())?;
613        Ok(ControlFlow::Continue(()))
614    }
615}
616
617define_instruction_fn! {
618    i64_div_s,
619    fuel_check = flat(opcode::I64_DIV_S),
620    |Args { resumable, .. }| {
621        let dividend: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
622        let divisor: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
623
624        if dividend == 0 {
625            return Err(TrapError::DivideBy0.into());
626        }
627        if divisor == i64::MIN && dividend == -1 {
628            return Err(TrapError::UnrepresentableResult.into());
629        }
630
631        let res = divisor / dividend;
632
633        trace!("Instruction: i64.div_s [{divisor} {dividend}] -> [{res}]");
634        resumable.stack.push_value(res.into())?;
635        Ok(ControlFlow::Continue(()))
636    }
637}
638
639define_instruction_fn! {
640    i64_div_u,
641    fuel_check = flat(opcode::I64_DIV_U),
642    |Args { resumable, .. }| {
643        let dividend: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
644        let divisor: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
645
646        let dividend = dividend as u64;
647        let divisor = divisor as u64;
648
649        if dividend == 0 {
650            return Err(TrapError::DivideBy0.into());
651        }
652
653        let res = (divisor / dividend) as i64;
654
655        trace!("Instruction: i64.div_u [{divisor} {dividend}] -> [{res}]");
656        resumable.stack.push_value(res.into())?;
657        Ok(ControlFlow::Continue(()))
658    }
659}
660
661define_instruction_fn! {
662    i64_rem_s,
663    fuel_check = flat(opcode::I64_REM_S),
664    |Args { resumable, .. }| {
665        let dividend: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
666        let divisor: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
667
668        if dividend == 0 {
669            return Err(TrapError::DivideBy0.into());
670        }
671
672        let res = divisor.checked_rem(dividend);
673        let res = res.unwrap_or_default();
674
675        trace!("Instruction: i64.rem_s [{divisor} {dividend}] -> [{res}]");
676        resumable.stack.push_value(res.into())?;
677        Ok(ControlFlow::Continue(()))
678    }
679}
680
681define_instruction_fn! {
682    i64_rem_u,
683    fuel_check = flat(opcode::I64_REM_U),
684    |Args { resumable, .. }| {
685        let dividend: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
686        let divisor: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
687
688        let dividend = dividend as u64;
689        let divisor = divisor as u64;
690
691        if dividend == 0 {
692            return Err(TrapError::DivideBy0.into());
693        }
694
695        let res = (divisor % dividend) as i64;
696
697        trace!("Instruction: i64.rem_u [{divisor} {dividend}] -> [{res}]");
698        resumable.stack.push_value(res.into())?;
699        Ok(ControlFlow::Continue(()))
700    }
701}
702
703define_instruction_fn! {
704    i64_and,
705    fuel_check = flat(opcode::I64_AND),
706    |Args { resumable, .. }| {
707        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
708        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
709
710        let res = v1 & v2;
711
712        trace!("Instruction: i64.and [{v1} {v2}] -> [{res}]");
713        resumable.stack.push_value(res.into())?;
714        Ok(ControlFlow::Continue(()))
715    }
716}
717
718define_instruction_fn! {
719    i64_or,
720    fuel_check = flat(opcode::I64_OR),
721    |Args { resumable, .. }| {
722        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
723        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
724
725        let res = v1 | v2;
726
727        trace!("Instruction: i64.or [{v1} {v2}] -> [{res}]");
728        resumable.stack.push_value(res.into())?;
729        Ok(ControlFlow::Continue(()))
730    }
731}
732
733define_instruction_fn! {
734    i64_xor,
735    fuel_check = flat(opcode::I64_XOR),
736    |Args { resumable, .. }| {
737        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
738        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
739
740        let res = v1 ^ v2;
741
742        trace!("Instruction: i64.xor [{v1} {v2}] -> [{res}]");
743        resumable.stack.push_value(res.into())?;
744        Ok(ControlFlow::Continue(()))
745    }
746}
747
748define_instruction_fn! {
749    i64_shl,
750    fuel_check = flat(opcode::I64_SHL),
751    |Args { resumable, .. }| {
752        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
753        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
754
755        let res = v1.wrapping_shl((v2 & 63) as u32);
756
757        trace!("Instruction: i64.shl [{v1} {v2}] -> [{res}]");
758        resumable.stack.push_value(res.into())?;
759        Ok(ControlFlow::Continue(()))
760    }
761}
762
763define_instruction_fn! {
764    i64_shr_s,
765    fuel_check = flat(opcode::I64_SHR_S),
766    |Args { resumable, .. }| {
767        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
768        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
769
770        let res = v1.wrapping_shr((v2 & 63) as u32);
771
772        trace!("Instruction: i64.shr_s [{v1} {v2}] -> [{res}]");
773        resumable.stack.push_value(res.into())?;
774        Ok(ControlFlow::Continue(()))
775    }
776}
777
778define_instruction_fn! {
779    i64_shr_u,
780    fuel_check = flat(opcode::I64_SHR_U),
781    |Args { resumable, .. }| {
782        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
783        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
784
785        let res = (v1 as u64).wrapping_shr((v2 & 63) as u32);
786
787        trace!("Instruction: i64.shr_u [{v1} {v2}] -> [{res}]");
788        resumable.stack.push_value(res.into())?;
789        Ok(ControlFlow::Continue(()))
790    }
791}
792
793define_instruction_fn! {
794    i64_rotl,
795    fuel_check = flat(opcode::I64_ROTL),
796    |Args { resumable, .. }| {
797        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
798        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
799
800        let res = v1.rotate_left((v2 & 63) as u32);
801
802        trace!("Instruction: i64.rotl [{v1} {v2}] -> [{res}]");
803        resumable.stack.push_value(res.into())?;
804        Ok(ControlFlow::Continue(()))
805    }
806}
807
808define_instruction_fn! {
809    i64_rotr,
810    fuel_check = flat(opcode::I64_ROTR),
811    |Args { resumable, .. }| {
812        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
813        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
814
815        let res = v1.rotate_right((v2 & 63) as u32);
816
817        trace!("Instruction: i64.rotr [{v1} {v2}] -> [{res}]");
818        resumable.stack.push_value(res.into())?;
819        Ok(ControlFlow::Continue(()))
820    }
821}
822
823// f32.binop
824define_instruction_fn! {
825    f32_add,
826    fuel_check = flat(opcode::F32_ADD),
827    |Args { resumable, .. }| {
828        let v2: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
829        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
830        let res: value::F32 = v1 + v2;
831
832        trace!("Instruction: f32.add [{v1} {v2}] -> [{res}]");
833        resumable.stack.push_value(res.into())?;
834        Ok(ControlFlow::Continue(()))
835    }
836}
837
838define_instruction_fn! {
839    f32_sub,
840    fuel_check = flat(opcode::F32_SUB),
841    |Args { resumable, .. }| {
842        let v2: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
843        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
844        let res: value::F32 = v1 - v2;
845
846        trace!("Instruction: f32.sub [{v1} {v2}] -> [{res}]");
847        resumable.stack.push_value(res.into())?;
848        Ok(ControlFlow::Continue(()))
849    }
850}
851
852define_instruction_fn! {
853    f32_mul,
854    fuel_check = flat(opcode::F32_MUL),
855    |Args { resumable, .. }| {
856        let v2: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
857        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
858        let res: value::F32 = v1 * v2;
859
860        trace!("Instruction: f32.mul [{v1} {v2}] -> [{res}]");
861        resumable.stack.push_value(res.into())?;
862        Ok(ControlFlow::Continue(()))
863    }
864}
865
866define_instruction_fn! {
867    f32_div,
868    fuel_check = flat(opcode::F32_DIV),
869    |Args { resumable, .. }| {
870        let v2: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
871        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
872        let res: value::F32 = v1 / v2;
873
874        trace!("Instruction: f32.div [{v1} {v2}] -> [{res}]");
875        resumable.stack.push_value(res.into())?;
876        Ok(ControlFlow::Continue(()))
877    }
878}
879
880define_instruction_fn! {
881    f32_min,
882    fuel_check = flat(opcode::F32_MIN),
883    |Args { resumable, .. }| {
884        let v2: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
885        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
886        let res: value::F32 = v1.min(v2);
887
888        trace!("Instruction: f32.min [{v1} {v2}] -> [{res}]");
889        resumable.stack.push_value(res.into())?;
890        Ok(ControlFlow::Continue(()))
891    }
892}
893
894define_instruction_fn! {
895    f32_max,
896    fuel_check = flat(opcode::F32_MAX),
897    |Args { resumable, .. }| {
898        let v2: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
899        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
900        let res: value::F32 = v1.max(v2);
901
902        trace!("Instruction: f32.max [{v1} {v2}] -> [{res}]");
903        resumable.stack.push_value(res.into())?;
904        Ok(ControlFlow::Continue(()))
905    }
906}
907
908define_instruction_fn! {
909    f32_copysign,
910    fuel_check = flat(opcode::F32_COPYSIGN),
911    |Args { resumable, .. }| {
912        let v2: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
913        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
914        let res: value::F32 = v1.copysign(v2);
915
916        trace!("Instruction: f32.copysign [{v1} {v2}] -> [{res}]");
917        resumable.stack.push_value(res.into())?;
918        Ok(ControlFlow::Continue(()))
919    }
920}
921
922// f64.binop
923define_instruction_fn! {
924    f64_add,
925    fuel_check = flat(opcode::F64_ADD),
926    |Args { resumable, .. }| {
927        let v2: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
928        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
929        let res: value::F64 = v1 + v2;
930
931        trace!("Instruction: f64.add [{v1} {v2}] -> [{res}]");
932        resumable.stack.push_value(res.into())?;
933        Ok(ControlFlow::Continue(()))
934    }
935}
936
937define_instruction_fn! {
938    f64_sub,
939    fuel_check = flat(opcode::F64_SUB),
940    |Args { resumable, .. }| {
941        let v2: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
942        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
943        let res: value::F64 = v1 - v2;
944
945        trace!("Instruction: f64.sub [{v1} {v2}] -> [{res}]");
946        resumable.stack.push_value(res.into())?;
947        Ok(ControlFlow::Continue(()))
948    }
949}
950
951define_instruction_fn! {
952    f64_mul,
953    fuel_check = flat(opcode::F64_MUL),
954    |Args { resumable, .. }| {
955        let v2: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
956        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
957        let res: value::F64 = v1 * v2;
958
959        trace!("Instruction: f64.mul [{v1} {v2}] -> [{res}]");
960        resumable.stack.push_value(res.into())?;
961        Ok(ControlFlow::Continue(()))
962    }
963}
964
965define_instruction_fn! {
966    f64_div,
967    fuel_check = flat(opcode::F64_DIV),
968    |Args { resumable, .. }| {
969        let v2: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
970        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
971        let res: value::F64 = v1 / v2;
972
973        trace!("Instruction: f64.div [{v1} {v2}] -> [{res}]");
974        resumable.stack.push_value(res.into())?;
975        Ok(ControlFlow::Continue(()))
976    }
977}
978
979define_instruction_fn! {
980    f64_min,
981    fuel_check = flat(opcode::F64_MIN),
982    |Args { resumable, .. }| {
983        let v2: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
984        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
985        let res: value::F64 = v1.min(v2);
986
987        trace!("Instruction: f64.min [{v1} {v2}] -> [{res}]");
988        resumable.stack.push_value(res.into())?;
989        Ok(ControlFlow::Continue(()))
990    }
991}
992
993define_instruction_fn! {
994    f64_max,
995    fuel_check = flat(opcode::F64_MAX),
996    |Args { resumable, .. }| {
997        let v2: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
998        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
999        let res: value::F64 = v1.max(v2);
1000
1001        trace!("Instruction: f64.max [{v1} {v2}] -> [{res}]");
1002        resumable.stack.push_value(res.into())?;
1003        Ok(ControlFlow::Continue(()))
1004    }
1005}
1006
1007define_instruction_fn! {
1008    f64_copysign,
1009    fuel_check = flat(opcode::F64_COPYSIGN),
1010    |Args { resumable, .. }| {
1011        let v2: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1012        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1013        let res: value::F64 = v1.copysign(v2);
1014
1015        trace!("Instruction: f64.copysign [{v1} {v2}] -> [{res}]");
1016        resumable.stack.push_value(res.into())?;
1017        Ok(ControlFlow::Continue(()))
1018    }
1019}
1020
1021// i32.testop
1022define_instruction_fn! {
1023    i32_eqz,
1024    fuel_check = flat(opcode::I32_EQZ),
1025    |Args { resumable, .. }| {
1026        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1027
1028        let res = if v1 == 0 { 1 } else { 0 };
1029
1030        trace!("Instruction: i32.eqz [{v1}] -> [{res}]");
1031        resumable.stack.push_value(res.into())?;
1032        Ok(ControlFlow::Continue(()))
1033    }
1034}
1035
1036// i64.testop
1037define_instruction_fn! {
1038    i64_eqz,
1039    fuel_check = flat(opcode::I64_EQZ),
1040    |Args { resumable, .. }| {
1041        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1042
1043        let res = if v1 == 0 { 1 } else { 0 };
1044
1045        trace!("Instruction: i64.eqz [{v1}] -> [{res}]");
1046        resumable.stack.push_value(res.into())?;
1047        Ok(ControlFlow::Continue(()))
1048    }
1049}
1050
1051// i32.relop
1052define_instruction_fn! {
1053    i32_eq,
1054    fuel_check = flat(opcode::I32_EQ),
1055    |Args { resumable, .. }| {
1056        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1057        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1058
1059        let res = if v1 == v2 { 1 } else { 0 };
1060
1061        trace!("Instruction: i32.eq [{v1} {v2}] -> [{res}]");
1062        resumable.stack.push_value(res.into())?;
1063        Ok(ControlFlow::Continue(()))
1064    }
1065}
1066
1067define_instruction_fn! {
1068    i32_ne,
1069    fuel_check = flat(opcode::I32_NE),
1070    |Args { resumable, .. }| {
1071        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1072        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1073
1074        let res = if v1 != v2 { 1 } else { 0 };
1075
1076        trace!("Instruction: i32.ne [{v1} {v2}] -> [{res}]");
1077        resumable.stack.push_value(res.into())?;
1078        Ok(ControlFlow::Continue(()))
1079    }
1080}
1081
1082define_instruction_fn! {
1083    i32_lt_s,
1084    fuel_check = flat(opcode::I32_LT_S),
1085    |Args { resumable, .. }| {
1086        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1087        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1088
1089        let res = if v1 < v2 { 1 } else { 0 };
1090
1091        trace!("Instruction: i32.lt_s [{v1} {v2}] -> [{res}]");
1092        resumable.stack.push_value(res.into())?;
1093        Ok(ControlFlow::Continue(()))
1094    }
1095}
1096
1097define_instruction_fn! {
1098    i32_lt_u,
1099    fuel_check = flat(opcode::I32_LT_U),
1100    |Args { resumable, .. }| {
1101        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1102        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1103
1104        let res = if (v1 as u32) < (v2 as u32) { 1 } else { 0 };
1105
1106        trace!("Instruction: i32.lt_u [{v1} {v2}] -> [{res}]");
1107        resumable.stack.push_value(res.into())?;
1108        Ok(ControlFlow::Continue(()))
1109    }
1110}
1111
1112define_instruction_fn! {
1113    i32_gt_s,
1114    fuel_check = flat(opcode::I32_GT_S),
1115    |Args { resumable, .. }| {
1116        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1117        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1118
1119        let res = if v1 > v2 { 1 } else { 0 };
1120
1121        trace!("Instruction: i32.gt_s [{v1} {v2}] -> [{res}]");
1122        resumable.stack.push_value(res.into())?;
1123        Ok(ControlFlow::Continue(()))
1124    }
1125}
1126
1127define_instruction_fn! {
1128    i32_gt_u,
1129    fuel_check = flat(opcode::I32_GT_U),
1130    |Args { resumable, .. }| {
1131        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1132        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1133
1134        let res = if (v1 as u32) > (v2 as u32) { 1 } else { 0 };
1135
1136        trace!("Instruction: i32.gt_u [{v1} {v2}] -> [{res}]");
1137        resumable.stack.push_value(res.into())?;
1138        Ok(ControlFlow::Continue(()))
1139    }
1140}
1141
1142define_instruction_fn! {
1143    i32_le_s,
1144    fuel_check = flat(opcode::I32_LE_S),
1145    |Args { resumable, .. }| {
1146        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1147        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1148
1149        let res = if v1 <= v2 { 1 } else { 0 };
1150
1151        trace!("Instruction: i32.le_s [{v1} {v2}] -> [{res}]");
1152        resumable.stack.push_value(res.into())?;
1153        Ok(ControlFlow::Continue(()))
1154    }
1155}
1156
1157define_instruction_fn! {
1158    i32_le_u,
1159    fuel_check = flat(opcode::I32_LE_U),
1160    |Args { resumable, .. }| {
1161        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1162        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1163
1164        let res = if (v1 as u32) <= (v2 as u32) { 1 } else { 0 };
1165
1166        trace!("Instruction: i32.le_u [{v1} {v2}] -> [{res}]");
1167        resumable.stack.push_value(res.into())?;
1168        Ok(ControlFlow::Continue(()))
1169    }
1170}
1171
1172define_instruction_fn! {
1173    i32_ge_s,
1174    fuel_check = flat(opcode::I32_GE_S),
1175    |Args { resumable, .. }| {
1176        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1177        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1178
1179        let res = if v1 >= v2 { 1 } else { 0 };
1180
1181        trace!("Instruction: i32.ge_s [{v1} {v2}] -> [{res}]");
1182        resumable.stack.push_value(res.into())?;
1183        Ok(ControlFlow::Continue(()))
1184    }
1185}
1186
1187define_instruction_fn! {
1188    i32_ge_u,
1189    fuel_check = flat(opcode::I32_GE_U),
1190    |Args { resumable, .. }| {
1191        let v2: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1192        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1193
1194        let res = if (v1 as u32) >= (v2 as u32) { 1 } else { 0 };
1195
1196        trace!("Instruction: i32.ge_u [{v1} {v2}] -> [{res}]");
1197        resumable.stack.push_value(res.into())?;
1198        Ok(ControlFlow::Continue(()))
1199    }
1200}
1201
1202// i64.relop
1203define_instruction_fn! {
1204    i64_eq,
1205    fuel_check = flat(opcode::I64_EQ),
1206    |Args { resumable, .. }| {
1207        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1208        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1209
1210        let res = if v1 == v2 { 1 } else { 0 };
1211
1212        trace!("Instruction: i64.eq [{v1} {v2}] -> [{res}]");
1213        resumable.stack.push_value(res.into())?;
1214        Ok(ControlFlow::Continue(()))
1215    }
1216}
1217
1218define_instruction_fn! {
1219    i64_ne,
1220    fuel_check = flat(opcode::I64_NE),
1221    |Args { resumable, .. }| {
1222        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1223        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1224
1225        let res = if v1 != v2 { 1 } else { 0 };
1226
1227        trace!("Instruction: i64.ne [{v1} {v2}] -> [{res}]");
1228        resumable.stack.push_value(res.into())?;
1229        Ok(ControlFlow::Continue(()))
1230    }
1231}
1232
1233define_instruction_fn! {
1234    i64_lt_s,
1235    fuel_check = flat(opcode::I64_LT_S),
1236    |Args { resumable, .. }| {
1237        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1238        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1239
1240        let res = if v1 < v2 { 1 } else { 0 };
1241
1242        trace!("Instruction: i64.lt_s [{v1} {v2}] -> [{res}]");
1243        resumable.stack.push_value(res.into())?;
1244        Ok(ControlFlow::Continue(()))
1245    }
1246}
1247
1248define_instruction_fn! {
1249    i64_lt_u,
1250    fuel_check = flat(opcode::I64_LT_U),
1251    |Args { resumable, .. }| {
1252        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1253        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1254
1255        let res = if (v1 as u64) < (v2 as u64) { 1 } else { 0 };
1256
1257        trace!("Instruction: i64.lt_u [{v1} {v2}] -> [{res}]");
1258        resumable.stack.push_value(res.into())?;
1259        Ok(ControlFlow::Continue(()))
1260    }
1261}
1262
1263define_instruction_fn! {
1264    i64_gt_s,
1265    fuel_check = flat(opcode::I64_GT_S),
1266    |Args { resumable, .. }| {
1267        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1268        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1269
1270        let res = if v1 > v2 { 1 } else { 0 };
1271
1272        trace!("Instruction: i64.gt_s [{v1} {v2}] -> [{res}]");
1273        resumable.stack.push_value(res.into())?;
1274        Ok(ControlFlow::Continue(()))
1275    }
1276}
1277
1278define_instruction_fn! {
1279    i64_gt_u,
1280    fuel_check = flat(opcode::I64_GT_U),
1281    |Args { resumable, .. }| {
1282        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1283        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1284
1285        let res = if (v1 as u64) > (v2 as u64) { 1 } else { 0 };
1286
1287        trace!("Instruction: i64.gt_u [{v1} {v2}] -> [{res}]");
1288        resumable.stack.push_value(res.into())?;
1289        Ok(ControlFlow::Continue(()))
1290    }
1291}
1292
1293define_instruction_fn! {
1294    i64_le_s,
1295    fuel_check = flat(opcode::I64_LE_S),
1296    |Args { resumable, .. }| {
1297        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1298        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1299
1300        let res = if v1 <= v2 { 1 } else { 0 };
1301
1302        trace!("Instruction: i64.le_s [{v1} {v2}] -> [{res}]");
1303        resumable.stack.push_value(res.into())?;
1304        Ok(ControlFlow::Continue(()))
1305    }
1306}
1307
1308define_instruction_fn! {
1309    i64_le_u,
1310    fuel_check = flat(opcode::I64_LE_U),
1311    |Args { resumable, .. }| {
1312        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1313        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1314
1315        let res = if (v1 as u64) <= (v2 as u64) { 1 } else { 0 };
1316
1317        trace!("Instruction: i64.le_u [{v1} {v2}] -> [{res}]");
1318        resumable.stack.push_value(res.into())?;
1319        Ok(ControlFlow::Continue(()))
1320    }
1321}
1322
1323define_instruction_fn! {
1324    i64_ge_s,
1325    fuel_check = flat(opcode::I64_GE_S),
1326    |Args { resumable, .. }| {
1327        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1328        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1329
1330        let res = if v1 >= v2 { 1 } else { 0 };
1331
1332        trace!("Instruction: i64.ge_s [{v1} {v2}] -> [{res}]");
1333        resumable.stack.push_value(res.into())?;
1334        Ok(ControlFlow::Continue(()))
1335    }
1336}
1337
1338define_instruction_fn! {
1339    i64_ge_u,
1340    fuel_check = flat(opcode::I64_GE_U),
1341    |Args { resumable, .. }| {
1342        let v2: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1343        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1344
1345        let res = if (v1 as u64) >= (v2 as u64) { 1 } else { 0 };
1346
1347        trace!("Instruction: i64.ge_u [{v1} {v2}] -> [{res}]");
1348        resumable.stack.push_value(res.into())?;
1349        Ok(ControlFlow::Continue(()))
1350    }
1351}
1352
1353// f32.relop
1354define_instruction_fn! {
1355    f32_eq,
1356    fuel_check = flat(opcode::F32_EQ),
1357    |Args { resumable, .. }| {
1358        let v2: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1359        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1360
1361        let res = if v1 == v2 { 1 } else { 0 };
1362
1363        trace!("Instruction: f32.eq [{v1} {v2}] -> [{res}]");
1364        resumable.stack.push_value(res.into())?;
1365        Ok(ControlFlow::Continue(()))
1366    }
1367}
1368
1369define_instruction_fn! {
1370    f32_ne,
1371    fuel_check = flat(opcode::F32_NE),
1372    |Args { resumable, .. }| {
1373        let v2: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1374        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1375
1376        let res = if v1 != v2 { 1 } else { 0 };
1377
1378        trace!("Instruction: f32.ne [{v1} {v2}] -> [{res}]");
1379        resumable.stack.push_value(res.into())?;
1380        Ok(ControlFlow::Continue(()))
1381    }
1382}
1383
1384define_instruction_fn! {
1385    f32_lt,
1386    fuel_check = flat(opcode::F32_LT),
1387    |Args { resumable, .. }| {
1388        let v2: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1389        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1390
1391        let res = if v1 < v2 { 1 } else { 0 };
1392
1393        trace!("Instruction: f32.lt [{v1} {v2}] -> [{res}]");
1394        resumable.stack.push_value(res.into())?;
1395        Ok(ControlFlow::Continue(()))
1396    }
1397}
1398
1399define_instruction_fn! {
1400    f32_gt,
1401    fuel_check = flat(opcode::F32_GT),
1402    |Args { resumable, .. }| {
1403        let v2: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1404        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1405
1406        let res = if v1 > v2 { 1 } else { 0 };
1407
1408        trace!("Instruction: f32.gt [{v1} {v2}] -> [{res}]");
1409        resumable.stack.push_value(res.into())?;
1410        Ok(ControlFlow::Continue(()))
1411    }
1412}
1413
1414define_instruction_fn! {
1415    f32_le,
1416    fuel_check = flat(opcode::F32_LE),
1417    |Args { resumable, .. }| {
1418        let v2: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1419        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1420
1421        let res = if v1 <= v2 { 1 } else { 0 };
1422
1423        trace!("Instruction: f32.le [{v1} {v2}] -> [{res}]");
1424        resumable.stack.push_value(res.into())?;
1425        Ok(ControlFlow::Continue(()))
1426    }
1427}
1428
1429define_instruction_fn! {
1430    f32_ge,
1431    fuel_check = flat(opcode::F32_GE),
1432    |Args { resumable, .. }| {
1433        let v2: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1434        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1435
1436        let res = if v1 >= v2 { 1 } else { 0 };
1437
1438        trace!("Instruction: f32.ge [{v1} {v2}] -> [{res}]");
1439        resumable.stack.push_value(res.into())?;
1440        Ok(ControlFlow::Continue(()))
1441    }
1442}
1443
1444// f64.relop
1445define_instruction_fn! {
1446    f64_eq,
1447    fuel_check = flat(opcode::F64_EQ),
1448    |Args { resumable, .. }| {
1449        let v2: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1450        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1451
1452        let res = if v1 == v2 { 1 } else { 0 };
1453
1454        trace!("Instruction: f64.eq [{v1} {v2}] -> [{res}]");
1455        resumable.stack.push_value(res.into())?;
1456        Ok(ControlFlow::Continue(()))
1457    }
1458}
1459
1460define_instruction_fn! {
1461    f64_ne,
1462    fuel_check = flat(opcode::F64_NE),
1463    |Args { resumable, .. }| {
1464        let v2: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1465        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1466
1467        let res = if v1 != v2 { 1 } else { 0 };
1468
1469        trace!("Instruction: f64.ne [{v1} {v2}] -> [{res}]");
1470        resumable.stack.push_value(res.into())?;
1471        Ok(ControlFlow::Continue(()))
1472    }
1473}
1474
1475define_instruction_fn! {
1476    f64_lt,
1477    fuel_check = flat(opcode::F64_LT),
1478    |Args { resumable, .. }| {
1479        let v2: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1480        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1481
1482        let res = if v1 < v2 { 1 } else { 0 };
1483
1484        trace!("Instruction: f64.lt [{v1} {v2}] -> [{res}]");
1485        resumable.stack.push_value(res.into())?;
1486        Ok(ControlFlow::Continue(()))
1487    }
1488}
1489
1490define_instruction_fn! {
1491    f64_gt,
1492    fuel_check = flat(opcode::F64_GT),
1493    |Args { resumable, .. }| {
1494        let v2: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1495        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1496
1497        let res = if v1 > v2 { 1 } else { 0 };
1498
1499        trace!("Instruction: f64.gt [{v1} {v2}] -> [{res}]");
1500        resumable.stack.push_value(res.into())?;
1501        Ok(ControlFlow::Continue(()))
1502    }
1503}
1504
1505define_instruction_fn! {
1506    f64_le,
1507    fuel_check = flat(opcode::F64_LE),
1508    |Args { resumable, .. }| {
1509        let v2: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1510        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1511
1512        let res = if v1 <= v2 { 1 } else { 0 };
1513
1514        trace!("Instruction: f64.le [{v1} {v2}] -> [{res}]");
1515        resumable.stack.push_value(res.into())?;
1516        Ok(ControlFlow::Continue(()))
1517    }
1518}
1519
1520define_instruction_fn! {
1521    f64_ge,
1522    fuel_check = flat(opcode::F64_GE),
1523    |Args { resumable, .. }| {
1524        let v2: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1525        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1526
1527        let res = if v1 >= v2 { 1 } else { 0 };
1528
1529        trace!("Instruction: f64.ge [{v1} {v2}] -> [{res}]");
1530        resumable.stack.push_value(res.into())?;
1531        Ok(ControlFlow::Continue(()))
1532    }
1533}
1534
1535// i32.cvtop
1536define_instruction_fn! {
1537    i32_wrap_i64,
1538    fuel_check = flat(opcode::I32_WRAP_I64),
1539    |Args { resumable, .. }| {
1540        let v: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
1541        let res: i32 = v as i32;
1542
1543        trace!("Instruction: i32.wrap_i64 [{v}] -> [{res}]");
1544        resumable.stack.push_value(res.into())?;
1545        Ok(ControlFlow::Continue(()))
1546    }
1547}
1548
1549define_instruction_fn! {
1550    i32_trunc_f32_s,
1551    fuel_check = flat(opcode::I32_TRUNC_F32_S),
1552    |Args { resumable, .. }| {
1553        let v: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1554        if v.is_infinity() {
1555            return Err(TrapError::UnrepresentableResult.into());
1556        }
1557        if v.is_nan() {
1558            return Err(TrapError::BadConversionToInteger.into());
1559        }
1560        if v >= value::F32(2147483648.0) || v <= value::F32(-2147483904.0) {
1561            return Err(TrapError::UnrepresentableResult.into());
1562        }
1563
1564        let res: i32 = v.as_i32();
1565
1566        trace!("Instruction: i32.trunc_f32_s [{v:.7}] -> [{res}]");
1567        resumable.stack.push_value(res.into())?;
1568        Ok(ControlFlow::Continue(()))
1569    }
1570}
1571
1572define_instruction_fn! {
1573    i32_trunc_f32_u,
1574    fuel_check = flat(opcode::I32_TRUNC_F32_U),
1575    |Args { resumable, .. }| {
1576        let v: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1577        if v.is_infinity() {
1578            return Err(TrapError::UnrepresentableResult.into());
1579        }
1580        if v.is_nan() {
1581            return Err(TrapError::BadConversionToInteger.into());
1582        }
1583        if v >= value::F32(4294967296.0) || v <= value::F32(-1.0) {
1584            return Err(TrapError::UnrepresentableResult.into());
1585        }
1586
1587        let res: i32 = v.as_u32() as i32;
1588
1589        trace!("Instruction: i32.trunc_f32_u [{v:.7}] -> [{res}]");
1590        resumable.stack.push_value(res.into())?;
1591        Ok(ControlFlow::Continue(()))
1592    }
1593}
1594
1595define_instruction_fn! {
1596    i32_trunc_f64_s,
1597    fuel_check = flat(opcode::I32_TRUNC_F64_S),
1598    |Args { resumable, .. }| {
1599        let v: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1600        if v.is_infinity() {
1601            return Err(TrapError::UnrepresentableResult.into());
1602        }
1603        if v.is_nan() {
1604            return Err(TrapError::BadConversionToInteger.into());
1605        }
1606        if v >= value::F64(2147483648.0) || v <= value::F64(-2147483649.0) {
1607            return Err(TrapError::UnrepresentableResult.into());
1608        }
1609
1610        let res: i32 = v.as_i32();
1611
1612        trace!("Instruction: i32.trunc_f64_s [{v:.7}] -> [{res}]");
1613        resumable.stack.push_value(res.into())?;
1614        Ok(ControlFlow::Continue(()))
1615    }
1616}
1617
1618define_instruction_fn! {
1619    i32_trunc_f64_u,
1620    fuel_check = flat(opcode::I32_TRUNC_F64_U),
1621    |Args { resumable, .. }| {
1622        let v: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1623        if v.is_infinity() {
1624            return Err(TrapError::UnrepresentableResult.into());
1625        }
1626        if v.is_nan() {
1627            return Err(TrapError::BadConversionToInteger.into());
1628        }
1629        if v >= value::F64(4294967296.0) || v <= value::F64(-1.0) {
1630            return Err(TrapError::UnrepresentableResult.into());
1631        }
1632
1633        let res: i32 = v.as_u32() as i32;
1634
1635        trace!("Instruction: i32.trunc_f32_u [{v:.7}] -> [{res}]");
1636        resumable.stack.push_value(res.into())?;
1637        Ok(ControlFlow::Continue(()))
1638    }
1639}
1640
1641define_instruction_fn! {
1642    i32_reinterpret_f32,
1643    fuel_check = flat(opcode::I32_REINTERPRET_F32),
1644    |Args { resumable, .. }| {
1645        let v: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1646        let res: i32 = v.reinterpret_as_i32();
1647
1648        trace!("Instruction: i32.reinterpret_f32 [{v:.7}] -> [{res}]");
1649        resumable.stack.push_value(res.into())?;
1650        Ok(ControlFlow::Continue(()))
1651    }
1652}
1653
1654define_instruction_fn! {
1655    i32_extend8_s,
1656    fuel_check = flat(opcode::I32_EXTEND8_S),
1657    |Args { resumable, .. }| {
1658        let mut v: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1659
1660        if v | 0xFF != 0xFF {
1661            trace!("Number v ({}) not contained in 8 bits, truncating", v);
1662            v &= 0xFF;
1663        }
1664
1665        let res = if v | 0x7F != 0x7F { v | 0xFFFFFF00 } else { v };
1666
1667        resumable.stack.push_value(res.into())?;
1668
1669        trace!("Instruction i32.extend8_s [{}] -> [{}]", v, res);
1670        Ok(ControlFlow::Continue(()))
1671    }
1672}
1673
1674define_instruction_fn! {
1675    i32_extend16_s,
1676    fuel_check = flat(opcode::I32_EXTEND16_S),
1677    |Args { resumable, .. }| {
1678        let mut v: u32 = resumable.stack.pop_value().try_into().unwrap_validated();
1679
1680        if v | 0xFFFF != 0xFFFF {
1681            trace!("Number v ({}) not contained in 16 bits, truncating", v);
1682            v &= 0xFFFF;
1683        }
1684
1685        let res = if v | 0x7FFF != 0x7FFF {
1686            v | 0xFFFF0000
1687        } else {
1688            v
1689        };
1690
1691        resumable.stack.push_value(res.into())?;
1692
1693        trace!("Instruction i32.extend16_s [{}] -> [{}]", v, res);
1694        Ok(ControlFlow::Continue(()))
1695    }
1696}
1697
1698define_instruction_fn! {
1699    i32_trunc_sat_f32_s,
1700    fuel_check = flat_fc(opcode::fc_extensions::I32_TRUNC_SAT_F32_S),
1701    |Args { resumable, .. }| {
1702        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1703        let res = {
1704            if v1.is_nan() {
1705                0
1706            } else if v1.is_negative_infinity() {
1707                i32::MIN
1708            } else if v1.is_infinity() {
1709                i32::MAX
1710            } else {
1711                v1.as_i32()
1712            }
1713        };
1714
1715        trace!("Instruction: i32.trunc_sat_f32_s [{v1}] -> [{res}]");
1716        resumable.stack.push_value(res.into())?;
1717        Ok(ControlFlow::Continue(()))
1718    }
1719}
1720
1721define_instruction_fn! {
1722    i32_trunc_sat_f32_u,
1723    fuel_check = flat_fc(opcode::fc_extensions::I32_TRUNC_SAT_F32_U),
1724    |Args { resumable, .. }| {
1725        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1726        let res = {
1727            if v1.is_nan() || v1.is_negative_infinity() {
1728                0
1729            } else if v1.is_infinity() {
1730                u32::MAX as i32
1731            } else {
1732                v1.as_u32() as i32
1733            }
1734        };
1735
1736        trace!("Instruction: i32.trunc_sat_f32_u [{v1}] -> [{res}]");
1737        resumable.stack.push_value(res.into())?;
1738        Ok(ControlFlow::Continue(()))
1739    }
1740}
1741
1742define_instruction_fn! {
1743    i32_trunc_sat_f64_s,
1744    fuel_check = flat_fc(opcode::fc_extensions::I32_TRUNC_SAT_F64_S),
1745    |Args { resumable, .. }| {
1746        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1747        let res = {
1748            if v1.is_nan() {
1749                0
1750            } else if v1.is_negative_infinity() {
1751                i32::MIN
1752            } else if v1.is_infinity() {
1753                i32::MAX
1754            } else {
1755                v1.as_i32()
1756            }
1757        };
1758
1759        trace!("Instruction: i32.trunc_sat_f64_s [{v1}] -> [{res}]");
1760        resumable.stack.push_value(res.into())?;
1761        Ok(ControlFlow::Continue(()))
1762    }
1763}
1764
1765define_instruction_fn! {
1766    i32_trunc_sat_f64_u,
1767    fuel_check = flat_fc(opcode::fc_extensions::I32_TRUNC_SAT_F64_U),
1768    |Args { resumable, .. }| {
1769        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1770        let res = {
1771            if v1.is_nan() || v1.is_negative_infinity() {
1772                0
1773            } else if v1.is_infinity() {
1774                u32::MAX as i32
1775            } else {
1776                v1.as_u32() as i32
1777            }
1778        };
1779
1780        trace!("Instruction: i32.trunc_sat_f64_u [{v1}] -> [{res}]");
1781        resumable.stack.push_value(res.into())?;
1782        Ok(ControlFlow::Continue(()))
1783    }
1784}
1785
1786// i64.cvtop
1787define_instruction_fn! {
1788    i64_extend_i32_s,
1789    fuel_check = flat(opcode::I64_EXTEND_I32_S),
1790    |Args { resumable, .. }| {
1791        let v: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1792
1793        let res: i64 = v as i64;
1794
1795        trace!("Instruction: i64.extend_i32_s [{v}] -> [{res}]");
1796        resumable.stack.push_value(res.into())?;
1797        Ok(ControlFlow::Continue(()))
1798    }
1799}
1800
1801define_instruction_fn! {
1802    i64_extend_i32_u,
1803    fuel_check = flat(opcode::I64_EXTEND_I32_U),
1804    |Args { resumable, .. }| {
1805        let v: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
1806
1807        let res: i64 = v as u32 as i64;
1808
1809        trace!("Instruction: i64.extend_i32_u [{v}] -> [{res}]");
1810        resumable.stack.push_value(res.into())?;
1811        Ok(ControlFlow::Continue(()))
1812    }
1813}
1814
1815define_instruction_fn! {
1816    i64_trunc_f32_s,
1817    fuel_check = flat(opcode::I64_TRUNC_F32_S),
1818    |Args { resumable, .. }| {
1819        let v: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1820        if v.is_infinity() {
1821            return Err(TrapError::UnrepresentableResult.into());
1822        }
1823        if v.is_nan() {
1824            return Err(TrapError::BadConversionToInteger.into());
1825        }
1826        if v >= value::F32(9223372036854775808.0) || v <= value::F32(-9223373136366403584.0) {
1827            return Err(TrapError::UnrepresentableResult.into());
1828        }
1829
1830        let res: i64 = v.as_i64();
1831
1832        trace!("Instruction: i64.trunc_f32_s [{v:.7}] -> [{res}]");
1833        resumable.stack.push_value(res.into())?;
1834        Ok(ControlFlow::Continue(()))
1835    }
1836}
1837
1838define_instruction_fn! {
1839    i64_trunc_f32_u,
1840    fuel_check = flat(opcode::I64_TRUNC_F32_U),
1841    |Args { resumable, .. }| {
1842        let v: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1843        if v.is_infinity() {
1844            return Err(TrapError::UnrepresentableResult.into());
1845        }
1846        if v.is_nan() {
1847            return Err(TrapError::BadConversionToInteger.into());
1848        }
1849        if v >= value::F32(18446744073709551616.0) || v <= value::F32(-1.0) {
1850            return Err(TrapError::UnrepresentableResult.into());
1851        }
1852
1853        let res: i64 = v.as_u64() as i64;
1854
1855        trace!("Instruction: i64.trunc_f32_u [{v:.7}] -> [{res}]");
1856        resumable.stack.push_value(res.into())?;
1857        Ok(ControlFlow::Continue(()))
1858    }
1859}
1860
1861define_instruction_fn! {
1862    i64_trunc_f64_s,
1863    fuel_check = flat(opcode::I64_TRUNC_F64_S),
1864    |Args { resumable, .. }| {
1865        let v: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1866        if v.is_infinity() {
1867            return Err(TrapError::UnrepresentableResult.into());
1868        }
1869        if v.is_nan() {
1870            return Err(TrapError::BadConversionToInteger.into());
1871        }
1872        if v >= value::F64(9223372036854775808.0) || v <= value::F64(-9223372036854777856.0) {
1873            return Err(TrapError::UnrepresentableResult.into());
1874        }
1875
1876        let res: i64 = v.as_i64();
1877
1878        trace!("Instruction: i64.trunc_f64_s [{v:.17}] -> [{res}]");
1879        resumable.stack.push_value(res.into())?;
1880        Ok(ControlFlow::Continue(()))
1881    }
1882}
1883
1884define_instruction_fn! {
1885    i64_trunc_f64_u,
1886    fuel_check = flat(opcode::I64_TRUNC_F64_U),
1887    |Args { resumable, .. }| {
1888        let v: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1889        if v.is_infinity() {
1890            return Err(TrapError::UnrepresentableResult.into());
1891        }
1892        if v.is_nan() {
1893            return Err(TrapError::BadConversionToInteger.into());
1894        }
1895        if v >= value::F64(18446744073709551616.0) || v <= value::F64(-1.0) {
1896            return Err(TrapError::UnrepresentableResult.into());
1897        }
1898
1899        let res: i64 = v.as_u64() as i64;
1900
1901        trace!("Instruction: i64.trunc_f64_u [{v:.17}] -> [{res}]");
1902        resumable.stack.push_value(res.into())?;
1903        Ok(ControlFlow::Continue(()))
1904    }
1905}
1906
1907define_instruction_fn! {
1908    i64_reinterpret_f64,
1909    fuel_check = flat(opcode::I64_REINTERPRET_F64),
1910    |Args { resumable, .. }| {
1911        let v: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
1912        let res: i64 = v.reinterpret_as_i64();
1913
1914        trace!("Instruction: i64.reinterpret_f64 [{v:.17}] -> [{res}]");
1915        resumable.stack.push_value(res.into())?;
1916        Ok(ControlFlow::Continue(()))
1917    }
1918}
1919
1920define_instruction_fn! {
1921    i64_extend8_s,
1922    fuel_check = flat(opcode::I64_EXTEND8_S),
1923    |Args { resumable, .. }| {
1924        let mut v: u64 = resumable.stack.pop_value().try_into().unwrap_validated();
1925
1926        if v | 0xFF != 0xFF {
1927            trace!("Number v ({}) not contained in 8 bits, truncating", v);
1928            v &= 0xFF;
1929        }
1930
1931        let res = if v | 0x7F != 0x7F {
1932            v | 0xFFFFFFFF_FFFFFF00
1933        } else {
1934            v
1935        };
1936
1937        resumable.stack.push_value(res.into())?;
1938
1939        trace!("Instruction i64.extend8_s [{}] -> [{}]", v, res);
1940        Ok(ControlFlow::Continue(()))
1941    }
1942}
1943
1944define_instruction_fn! {
1945    i64_extend16_s,
1946    fuel_check = flat(opcode::I64_EXTEND16_S),
1947    |Args { resumable, .. }| {
1948        let mut v: u64 = resumable.stack.pop_value().try_into().unwrap_validated();
1949
1950        if v | 0xFFFF != 0xFFFF {
1951            trace!("Number v ({}) not contained in 16 bits, truncating", v);
1952            v &= 0xFFFF;
1953        }
1954
1955        let res = if v | 0x7FFF != 0x7FFF {
1956            v | 0xFFFFFFFF_FFFF0000
1957        } else {
1958            v
1959        };
1960
1961        resumable.stack.push_value(res.into())?;
1962
1963        trace!("Instruction i64.extend16_s [{}] -> [{}]", v, res);
1964        Ok(ControlFlow::Continue(()))
1965    }
1966}
1967
1968define_instruction_fn! {
1969    i64_extend32_s,
1970    fuel_check = flat(opcode::I64_EXTEND32_S),
1971    |Args { resumable, .. }| {
1972        let mut v: u64 = resumable.stack.pop_value().try_into().unwrap_validated();
1973
1974        if v | 0xFFFF_FFFF != 0xFFFF_FFFF {
1975            trace!("Number v ({}) not contained in 32 bits, truncating", v);
1976            v &= 0xFFFF_FFFF;
1977        }
1978
1979        let res = if v | 0x7FFF_FFFF != 0x7FFF_FFFF {
1980            v | 0xFFFFFFFF_00000000
1981        } else {
1982            v
1983        };
1984
1985        resumable.stack.push_value(res.into())?;
1986
1987        trace!("Instruction i64.extend32_s [{}] -> [{}]", v, res);
1988        Ok(ControlFlow::Continue(()))
1989    }
1990}
1991
1992define_instruction_fn! {
1993    i64_trunc_sat_f32_s,
1994    fuel_check = flat_fc(opcode::fc_extensions::I64_TRUNC_SAT_F32_S),
1995    |Args { resumable, .. }| {
1996        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
1997        let res = {
1998            if v1.is_nan() {
1999                0
2000            } else if v1.is_negative_infinity() {
2001                i64::MIN
2002            } else if v1.is_infinity() {
2003                i64::MAX
2004            } else {
2005                v1.as_i64()
2006            }
2007        };
2008
2009        trace!("Instruction: i64.trunc_sat_f32_s [{v1}] -> [{res}]");
2010        resumable.stack.push_value(res.into())?;
2011        Ok(ControlFlow::Continue(()))
2012    }
2013}
2014
2015define_instruction_fn! {
2016    i64_trunc_sat_f32_u,
2017    fuel_check = flat_fc(opcode::fc_extensions::I64_TRUNC_SAT_F32_U),
2018    |Args { resumable, .. }| {
2019        let v1: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
2020        let res = {
2021            if v1.is_nan() || v1.is_negative_infinity() {
2022                0
2023            } else if v1.is_infinity() {
2024                u64::MAX as i64
2025            } else {
2026                v1.as_u64() as i64
2027            }
2028        };
2029
2030        trace!("Instruction: i64.trunc_sat_f32_u [{v1}] -> [{res}]");
2031        resumable.stack.push_value(res.into())?;
2032        Ok(ControlFlow::Continue(()))
2033    }
2034}
2035
2036define_instruction_fn! {
2037    i64_trunc_sat_f64_s,
2038    fuel_check = flat_fc(opcode::fc_extensions::I64_TRUNC_SAT_F64_S),
2039    |Args { resumable, .. }| {
2040        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
2041        let res = {
2042            if v1.is_nan() {
2043                0
2044            } else if v1.is_negative_infinity() {
2045                i64::MIN
2046            } else if v1.is_infinity() {
2047                i64::MAX
2048            } else {
2049                v1.as_i64()
2050            }
2051        };
2052
2053        trace!("Instruction: i64.trunc_sat_f64_s [{v1}] -> [{res}]");
2054        resumable.stack.push_value(res.into())?;
2055        Ok(ControlFlow::Continue(()))
2056    }
2057}
2058
2059define_instruction_fn! {
2060    i64_trunc_sat_f64_u,
2061    fuel_check = flat_fc(opcode::fc_extensions::I64_TRUNC_SAT_F64_U),
2062    |Args { resumable, .. }| {
2063        let v1: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
2064        let res = {
2065            if v1.is_nan() || v1.is_negative_infinity() {
2066                0
2067            } else if v1.is_infinity() {
2068                u64::MAX as i64
2069            } else {
2070                v1.as_u64() as i64
2071            }
2072        };
2073
2074        trace!("Instruction: i64.trunc_sat_f64_u [{v1}] -> [{res}]");
2075        resumable.stack.push_value(res.into())?;
2076        Ok(ControlFlow::Continue(()))
2077    }
2078}
2079
2080// f32.cvtop
2081define_instruction_fn! {
2082    f32_convert_i32_s,
2083    fuel_check = flat(opcode::F32_CONVERT_I32_S),
2084    |Args { resumable, .. }| {
2085        let v: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
2086        let res: value::F32 = value::F32(v as f32);
2087
2088        trace!("Instruction: f32.convert_i32_s [{v}] -> [{res}]");
2089        resumable.stack.push_value(res.into())?;
2090        Ok(ControlFlow::Continue(()))
2091    }
2092}
2093
2094define_instruction_fn! {
2095    f32_convert_i32_u,
2096    fuel_check = flat(opcode::F32_CONVERT_I32_U),
2097    |Args { resumable, .. }| {
2098        let v: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
2099        let res: value::F32 = value::F32(v as u32 as f32);
2100
2101        trace!("Instruction: f32.convert_i32_u [{v}] -> [{res}]");
2102        resumable.stack.push_value(res.into())?;
2103        Ok(ControlFlow::Continue(()))
2104    }
2105}
2106
2107define_instruction_fn! {
2108    f32_convert_i64_s,
2109    fuel_check = flat(opcode::F32_CONVERT_I64_S),
2110    |Args { resumable, .. }| {
2111        let v: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
2112        let res: value::F32 = value::F32(v as f32);
2113
2114        trace!("Instruction: f32.convert_i64_s [{v}] -> [{res}]");
2115        resumable.stack.push_value(res.into())?;
2116        Ok(ControlFlow::Continue(()))
2117    }
2118}
2119
2120define_instruction_fn! {
2121    f32_convert_i64_u,
2122    fuel_check = flat(opcode::F32_CONVERT_I64_U),
2123    |Args { resumable, .. }| {
2124        let v: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
2125        let res: value::F32 = value::F32(v as u64 as f32);
2126
2127        trace!("Instruction: f32.convert_i64_u [{v}] -> [{res}]");
2128        resumable.stack.push_value(res.into())?;
2129        Ok(ControlFlow::Continue(()))
2130    }
2131}
2132
2133define_instruction_fn! {
2134    f32_demote_f64,
2135    fuel_check = flat(opcode::F32_DEMOTE_F64),
2136    |Args { resumable, .. }| {
2137        let v: value::F64 = resumable.stack.pop_value().try_into().unwrap_validated();
2138        let res: value::F32 = v.as_f32();
2139
2140        trace!("Instruction: f32.demote_f64 [{v:.17}] -> [{res:.7}]");
2141        resumable.stack.push_value(res.into())?;
2142        Ok(ControlFlow::Continue(()))
2143    }
2144}
2145
2146define_instruction_fn! {
2147    f32_reinterpret_i32,
2148    fuel_check = flat(opcode::F32_REINTERPRET_I32),
2149    |Args { resumable, .. }| {
2150        let v1: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
2151        let res: value::F32 = value::F32::from_bits(v1 as u32);
2152
2153        trace!("Instruction: f32.reinterpret_i32 [{v1}] -> [{res:.7}]");
2154        resumable.stack.push_value(res.into())?;
2155        Ok(ControlFlow::Continue(()))
2156    }
2157}
2158
2159// f64.cvtop
2160define_instruction_fn! {
2161    f64_convert_i32_s,
2162    fuel_check = flat(opcode::F64_CONVERT_I32_S),
2163    |Args { resumable, .. }| {
2164        let v: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
2165        let res: value::F64 = value::F64(v as f64);
2166
2167        trace!("Instruction: f64.convert_i32_s [{v}] -> [{res:.17}]");
2168        resumable.stack.push_value(res.into())?;
2169        Ok(ControlFlow::Continue(()))
2170    }
2171}
2172
2173define_instruction_fn! {
2174    f64_convert_i32_u,
2175    fuel_check = flat(opcode::F64_CONVERT_I32_U),
2176    |Args { resumable, .. }| {
2177        let v: i32 = resumable.stack.pop_value().try_into().unwrap_validated();
2178        let res: value::F64 = value::F64(v as u32 as f64);
2179
2180        trace!("Instruction: f64.convert_i32_u [{v}] -> [{res:.17}]");
2181        resumable.stack.push_value(res.into())?;
2182        Ok(ControlFlow::Continue(()))
2183    }
2184}
2185
2186define_instruction_fn! {
2187    f64_convert_i64_s,
2188    fuel_check = flat(opcode::F64_CONVERT_I64_S),
2189    |Args { resumable, .. }| {
2190        let v: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
2191        let res: value::F64 = value::F64(v as f64);
2192
2193        trace!("Instruction: f64.convert_i64_s [{v}] -> [{res:.17}]");
2194        resumable.stack.push_value(res.into())?;
2195        Ok(ControlFlow::Continue(()))
2196    }
2197}
2198
2199define_instruction_fn! {
2200    f64_convert_i64_u,
2201    fuel_check = flat(opcode::F64_CONVERT_I64_U),
2202    |Args { resumable, .. }| {
2203        let v: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
2204        let res: value::F64 = value::F64(v as u64 as f64);
2205
2206        trace!("Instruction: f64.convert_i64_u [{v}] -> [{res:.17}]");
2207        resumable.stack.push_value(res.into())?;
2208        Ok(ControlFlow::Continue(()))
2209    }
2210}
2211
2212define_instruction_fn! {
2213    f64_promote_f32,
2214    fuel_check = flat(opcode::F64_PROMOTE_F32),
2215    |Args { resumable, .. }| {
2216        let v: value::F32 = resumable.stack.pop_value().try_into().unwrap_validated();
2217        let res: value::F64 = v.as_f64();
2218
2219        trace!("Instruction: f64.promote_f32 [{v:.7}] -> [{res:.17}]");
2220        resumable.stack.push_value(res.into())?;
2221        Ok(ControlFlow::Continue(()))
2222    }
2223}
2224
2225define_instruction_fn! {
2226    f64_reinterpret_i64,
2227    fuel_check = flat(opcode::F64_REINTERPRET_I64),
2228    |Args { resumable, .. }| {
2229        let v1: i64 = resumable.stack.pop_value().try_into().unwrap_validated();
2230        let res: value::F64 = value::F64::from_bits(v1 as u64);
2231
2232        trace!("Instruction: f64.reinterpret_i64 [{v1}] -> [{res:.17}]");
2233        resumable.stack.push_value(res.into())?;
2234        Ok(ControlFlow::Continue(()))
2235    }
2236}