1 use lazy_static::lazy_static;
2 use lightbeam::{translate, ExecutableModule};
3 use quickcheck::quickcheck;
4 
translate_wat(wat: &str) -> ExecutableModule5 fn translate_wat(wat: &str) -> ExecutableModule {
6     let wasm = wat::parse_str(wat).unwrap();
7     let compiled = translate(&wasm).unwrap();
8     compiled
9 }
10 
11 mod op32 {
12     use super::{lazy_static, quickcheck, translate_wat, ExecutableModule};
13 
14     macro_rules! binop_test {
15         ($op:ident, $func:expr) => {
16             mod $op {
17                 use super::{lazy_static, quickcheck, translate_wat, ExecutableModule};
18                 use std::sync::Once;
19 
20                 const OP: &str = stringify!($op);
21 
22                 lazy_static! {
23                     static ref AS_PARAMS: ExecutableModule = translate_wat(&format!(
24                         "(module (func (param i32) (param i32) (result i32)
25                             (i32.{op} (get_local 0) (get_local 1))))",
26                         op = OP
27                     ));
28                 }
29 
30                 quickcheck! {
31                     fn as_params(a: i32, b: i32) -> bool {
32                          AS_PARAMS.execute_func::<(i32, i32), i32>(0, (a, b)) == Ok($func(a, b))
33                     }
34 
35                     fn lit_lit(a: i32, b: i32) -> bool {
36                         let translated = translate_wat(&format!("
37                             (module (func (result i32)
38                                 (i32.{op} (i32.const {left}) (i32.const {right}))))
39                         ", op = OP, left = a, right = b));
40                         static ONCE: Once = Once::new();
41                         ONCE.call_once(|| translated.disassemble());
42 
43                         translated.execute_func::<(), i32>(0, ()) == Ok($func(a, b))
44                     }
45 
46                     fn lit_reg(a: i32, b: i32) -> bool {
47                         let translated = translate_wat(&format!("
48                             (module (func (param i32) (result i32)
49                                 (i32.{op} (i32.const {left}) (get_local 0))))
50                         ", op = OP, left = a));
51                         static ONCE: Once = Once::new();
52                         ONCE.call_once(|| translated.disassemble());
53 
54                         translated.execute_func::<(i32,), i32>(0, (b,)) == Ok($func(a, b))
55                     }
56 
57                     fn reg_lit(a: i32, b: i32) -> bool {
58                         let translated = translate_wat(&format!("
59                             (module (func (param i32) (result i32)
60                                 (i32.{op} (get_local 0) (i32.const {right}))))
61                         ", op = OP, right = b));
62                         static ONCE: Once = Once::new();
63                         ONCE.call_once(|| translated.disassemble());
64 
65                         translated.execute_func::<(i32,), i32>(0, (a,)) == Ok($func(a, b))
66                     }
67                 }
68             }
69         };
70     }
71 
72     macro_rules! unop_test {
73         ($name:ident, $func:expr) => {
74             mod $name {
75                 use super::{lazy_static, quickcheck, translate_wat, ExecutableModule};
76                 use std::sync::Once;
77 
78                 lazy_static! {
79                     static ref AS_PARAM: ExecutableModule = translate_wat(concat!(
80                         "(module (func (param i32) (result i32)
81                             (i32.",
82                         stringify!($name),
83                         " (get_local 0))))"
84                     ),);
85                 }
86 
87                 quickcheck! {
88                     fn as_param(a: u32) -> bool {
89                          AS_PARAM.execute_func::<(u32,), u32>(0, (a,)) == Ok($func(a))
90                     }
91 
92                     fn lit(a: u32) -> bool {
93                         let translated = translate_wat(&format!(concat!("
94                             (module (func (result i32)
95                                 (i32.",stringify!($name)," (i32.const {val}))))
96                         "), val = a));
97                         static ONCE: Once = Once::new();
98                         ONCE.call_once(|| translated.disassemble());
99 
100                         translated.execute_func::<(), u32>(0, ()) == Ok($func(a))
101                     }
102                 }
103             }
104         };
105     }
106 
107     unop_test!(clz, u32::leading_zeros);
108     unop_test!(ctz, u32::trailing_zeros);
109     unop_test!(popcnt, u32::count_ones);
110     unop_test!(eqz, |a: u32| if a == 0 { 1 } else { 0 });
111 
112     binop_test!(add, i32::wrapping_add);
113     binop_test!(sub, i32::wrapping_sub);
114     binop_test!(and, std::ops::BitAnd::bitand);
115     binop_test!(or, std::ops::BitOr::bitor);
116     binop_test!(xor, std::ops::BitXor::bitxor);
117     binop_test!(mul, i32::wrapping_mul);
118     binop_test!(eq, |a, b| if a == b { 1 } else { 0 });
119     binop_test!(ne, |a, b| if a != b { 1 } else { 0 });
120     binop_test!(lt_u, |a, b| if (a as u32) < (b as u32) { 1 } else { 0 });
121     binop_test!(le_u, |a, b| if (a as u32) <= (b as u32) { 1 } else { 0 });
122     binop_test!(gt_u, |a, b| if (a as u32) > (b as u32) { 1 } else { 0 });
123     binop_test!(ge_u, |a, b| if (a as u32) >= (b as u32) { 1 } else { 0 });
124     binop_test!(lt_s, |a, b| if a < b { 1 } else { 0 });
125     binop_test!(le_s, |a, b| if a <= b { 1 } else { 0 });
126     binop_test!(gt_s, |a, b| if a > b { 1 } else { 0 });
127     binop_test!(ge_s, |a, b| if a >= b { 1 } else { 0 });
128     binop_test!(shl, |a, b| (a as i32).wrapping_shl(b as _));
129     binop_test!(shr_s, |a, b| (a as i32).wrapping_shr(b as _));
130     binop_test!(shr_u, |a, b| (a as u32).wrapping_shr(b as _) as i32);
131     binop_test!(rotl, |a, b| (a as u32).rotate_left(b as _) as i32);
132     binop_test!(rotr, |a, b| (a as u32).rotate_right(b as _) as i32);
133 }
134 
135 mod op64 {
136     use super::{lazy_static, quickcheck, translate_wat, ExecutableModule};
137 
138     macro_rules! binop_test {
139         ($op:ident, $func:expr) => {
140             binop_test!($op, $func, i64);
141         };
142         ($op:ident, $func:expr, $retty:ident) => {
143             mod $op {
144                 use super::{translate_wat, ExecutableModule, quickcheck, lazy_static};
145 
146                 const RETTY: &str = stringify!($retty);
147                 const OP: &str = stringify!($op);
148 
149                 lazy_static! {
150                     static ref AS_PARAMS: ExecutableModule = translate_wat(&format!("
151                         (module (func (param i64) (param i64) (result {retty})
152                             (i64.{op} (get_local 0) (get_local 1))))
153                     ", retty = RETTY, op = OP));
154                 }
155 
156                 quickcheck! {
157                     fn as_params(a: i64, b: i64) -> bool {
158                         AS_PARAMS.execute_func::<(i64, i64), $retty>(0, (a, b)) == Ok($func(a, b) as $retty)
159                     }
160 
161                     fn lit_lit(a: i64, b: i64) -> bool {
162                         translate_wat(&format!("
163                             (module (func (result {retty})
164                                 (i64.{op} (i64.const {left}) (i64.const {right}))))
165                         ", retty = RETTY, op = OP, left = a, right = b)).execute_func::<(), $retty>(0, ()) == Ok($func(a, b) as $retty)
166                     }
167 
168                     fn lit_reg(a: i64, b: i64) -> bool {
169                         use std::sync::Once;
170 
171                         let translated = translate_wat(&format!("
172                             (module (func (param i64) (result {retty})
173                                 (i64.{op} (i64.const {left}) (get_local 0))))
174                         ", retty = RETTY, op = OP, left = a));
175                         static ONCE: Once = Once::new();
176                         ONCE.call_once(|| translated.disassemble());
177 
178                         translated.execute_func::<(i64,), $retty>(0, (b,)) == Ok($func(a, b) as $retty)
179                     }
180 
181                     fn reg_lit(a: i64, b: i64) -> bool {
182                         use std::sync::Once;
183 
184                         let translated = translate_wat(&format!("
185                             (module (func (param i64) (result {retty})
186                                 (i64.{op} (get_local 0) (i64.const {right}))))
187                         ", retty = RETTY, op = OP, right = b));
188                         static ONCE: Once = Once::new();
189                         ONCE.call_once(|| translated.disassemble());
190 
191                         translated.execute_func::<(i64,), $retty>(0, (a,)) == Ok($func(a, b) as $retty)
192                     }
193                 }
194             }
195         };
196     }
197 
198     macro_rules! unop_test {
199         ($name:ident, $func:expr) => {
200             unop_test!($name, $func, i64);
201         };
202         ($name:ident, $func:expr, $out_ty:ty) => {
203             mod $name {
204                 use super::{lazy_static, quickcheck, translate_wat, ExecutableModule};
205                 use std::sync::Once;
206 
207                 lazy_static! {
208                     static ref AS_PARAM: ExecutableModule = translate_wat(concat!(
209                         "(module (func (param i64) (result ",
210                         stringify!($out_ty),
211                         ")
212                             (i64.",
213                         stringify!($name),
214                         " (get_local 0))))"
215                     ),);
216                 }
217 
218                 quickcheck! {
219                     fn as_param(a: u64) -> bool {
220                          AS_PARAM.execute_func::<(u64,), $out_ty>(0, (a,)) == Ok($func(a))
221                     }
222 
223                     fn lit(a: u64) -> bool {
224                                                 let translated = translate_wat(&format!(concat!("
225                             (module (func (result ",stringify!($out_ty),")
226                                 (i64.",stringify!($name)," (i64.const {val}))))
227                         "), val = a));
228                         static ONCE: Once = Once::new();
229                         ONCE.call_once(|| translated.disassemble());
230 
231                         translated.execute_func::<(), $out_ty>(0, ()) == Ok($func(a))
232                     }
233                 }
234             }
235         };
236     }
237 
238     unop_test!(clz, |a: u64| a.leading_zeros() as _);
239     unop_test!(ctz, |a: u64| a.trailing_zeros() as _);
240     unop_test!(popcnt, |a: u64| a.count_ones() as _);
241     unop_test!(eqz, |a: u64| if a == 0 { 1 } else { 0 }, i32);
242 
243     binop_test!(add, i64::wrapping_add);
244     binop_test!(sub, i64::wrapping_sub);
245     binop_test!(and, std::ops::BitAnd::bitand);
246     binop_test!(or, std::ops::BitOr::bitor);
247     binop_test!(xor, std::ops::BitXor::bitxor);
248     binop_test!(mul, i64::wrapping_mul);
249     binop_test!(eq, |a, b| if a == b { 1 } else { 0 }, i32);
250     binop_test!(ne, |a, b| if a != b { 1 } else { 0 }, i32);
251     binop_test!(
252         lt_u,
253         |a, b| if (a as u64) < (b as u64) { 1 } else { 0 },
254         i32
255     );
256     binop_test!(
257         le_u,
258         |a, b| if (a as u64) <= (b as u64) { 1 } else { 0 },
259         i32
260     );
261     binop_test!(
262         gt_u,
263         |a, b| if (a as u64) > (b as u64) { 1 } else { 0 },
264         i32
265     );
266     binop_test!(
267         ge_u,
268         |a, b| if (a as u64) >= (b as u64) { 1 } else { 0 },
269         i32
270     );
271     binop_test!(lt_s, |a, b| if a < b { 1 } else { 0 }, i32);
272     binop_test!(le_s, |a, b| if a <= b { 1 } else { 0 }, i32);
273     binop_test!(gt_s, |a, b| if a > b { 1 } else { 0 }, i32);
274     binop_test!(ge_s, |a, b| if a >= b { 1 } else { 0 }, i32);
275     binop_test!(shl, |a, b| (a as i64).wrapping_shl(b as _));
276     binop_test!(shr_s, |a, b| (a as i64).wrapping_shr(b as _));
277     binop_test!(shr_u, |a, b| (a as u64).wrapping_shr(b as _) as i64);
278     binop_test!(rotl, |a, b| (a as u64).rotate_left(b as _) as i64);
279     binop_test!(rotr, |a, b| (a as u64).rotate_right(b as _) as i64);
280 }
281 
282 mod opf32 {
283     use super::{lazy_static, quickcheck, translate_wat, ExecutableModule};
284 
285     macro_rules! binop_test {
286         ($op:ident, $func:expr) => {
287             binop_test!($op, $func, f32);
288         };
289         ($op:ident, $func:expr, $retty:ident) => {
290             mod $op {
291                 use super::{translate_wat, ExecutableModule, quickcheck, lazy_static};
292 
293                 const RETTY: &str = stringify!($retty);
294                 const OP: &str = stringify!($op);
295 
296                 lazy_static! {
297                     static ref AS_PARAMS: ExecutableModule = translate_wat(&format!("
298                         (module (func (param f32) (param f32) (result {retty})
299                             (f32.{op} (get_local 0) (get_local 1))))
300                     ", retty = RETTY, op = OP));
301                 }
302 
303                 quickcheck! {
304                     fn as_params(a: f32, b: f32) -> bool {
305                         AS_PARAMS.execute_func::<(f32, f32), $retty>(0, (a, b)) == Ok($func(a, b) as $retty)
306                     }
307 
308                     fn lit_lit(a: f32, b: f32) -> bool {
309                         translate_wat(&format!("
310                             (module (func (result {retty})
311                                 (f32.{op} (f32.const {left}) (f32.const {right}))))
312                         ", retty = RETTY, op = OP, left = a, right = b)).execute_func::<(), $retty>(0, ()) == Ok($func(a, b) as $retty)
313                     }
314 
315                     fn lit_reg(a: f32, b: f32) -> bool {
316                         use std::sync::Once;
317 
318                         let translated = translate_wat(&format!("
319                             (module (func (param f32) (result {retty})
320                                 (f32.{op} (f32.const {left}) (get_local 0))))
321                         ", retty = RETTY, op = OP, left = a));
322                         static ONCE: Once = Once::new();
323                         ONCE.call_once(|| translated.disassemble());
324 
325                         translated.execute_func::<(f32,), $retty>(0, (b,)) == Ok($func(a, b) as $retty)
326                     }
327 
328                     fn reg_lit(a: f32, b: f32) -> bool {
329                         use std::sync::Once;
330 
331                         let translated = translate_wat(&format!("
332                             (module (func (param f32) (result {retty})
333                                 (f32.{op} (get_local 0) (f32.const {right}))))
334                         ", retty = RETTY, op = OP, right = b));
335                         static ONCE: Once = Once::new();
336                         ONCE.call_once(|| translated.disassemble());
337 
338                         translated.execute_func::<(f32,), $retty>(0, (a,)) == Ok($func(a, b) as $retty)
339                     }
340                 }
341             }
342         };
343     }
344 
345     macro_rules! unop_test {
346         ($name:ident, $func:expr) => {
347             unop_test!($name, $func, f32);
348         };
349         ($name:ident, $func:expr, $out_ty:ty) => {
350             mod $name {
351                 use super::{lazy_static, quickcheck, translate_wat, ExecutableModule};
352                 use std::sync::Once;
353 
354                 lazy_static! {
355                     static ref AS_PARAM: ExecutableModule = translate_wat(concat!(
356                         "(module (func (param f32) (result ",
357                         stringify!($out_ty),
358                         ")
359                             (f32.",
360                         stringify!($name),
361                         " (get_local 0))))"
362                     ),);
363                 }
364 
365                 quickcheck! {
366                     fn as_param(a: f32) -> bool {
367                         static ONCE: Once = Once::new();
368                         ONCE.call_once(|| AS_PARAM.disassemble());
369                         AS_PARAM.execute_func::<(f32,), $out_ty>(0, (a,)) == Ok($func(a))
370                     }
371 
372                     fn lit(a: f32) -> bool {
373                                                 let translated = translate_wat(&format!(concat!("
374                             (module (func (result ",stringify!($out_ty),")
375                                 (f32.",stringify!($name)," (f32.const {val}))))
376                         "), val = a));
377                         static ONCE: Once = Once::new();
378                         ONCE.call_once(|| translated.disassemble());
379 
380                         translated.execute_func::<(), $out_ty>(0, ()) == Ok($func(a))
381                     }
382                 }
383             }
384         };
385     }
386 
387     binop_test!(add, |a, b| a + b);
388     binop_test!(mul, |a, b| a * b);
389     binop_test!(sub, |a, b| a - b);
390     binop_test!(gt, |a, b| a > b, i32);
391     binop_test!(lt, |a, b| a < b, i32);
392     binop_test!(ge, |a, b| a >= b, i32);
393     binop_test!(le, |a, b| a <= b, i32);
394 
395     unop_test!(neg, |a: f32| -a);
396     unop_test!(abs, |a: f32| a.abs());
397 }
398 
399 mod opf64 {
400     use super::{lazy_static, quickcheck, translate_wat, ExecutableModule};
401 
402     macro_rules! binop_test {
403         ($op:ident, $func:expr) => {
404             binop_test!($op, $func, f64);
405         };
406         ($op:ident, $func:expr, $retty:ident) => {
407             mod $op {
408                 use super::{translate_wat, ExecutableModule, quickcheck, lazy_static};
409 
410                 const RETTY: &str = stringify!($retty);
411                 const OP: &str = stringify!($op);
412 
413                 lazy_static! {
414                     static ref AS_PARAMS: ExecutableModule = translate_wat(&format!("
415                         (module (func (param f64) (param f64) (result {retty})
416                             (f64.{op} (get_local 0) (get_local 1))))
417                     ", retty = RETTY, op = OP));
418                 }
419 
420                 quickcheck! {
421                     fn as_params(a: f64, b: f64) -> bool {
422                         AS_PARAMS.execute_func::<(f64, f64), $retty>(0, (a, b)) == Ok($func(a, b) as $retty)
423                     }
424 
425                     fn lit_lit(a: f64, b: f64) -> bool {
426                         translate_wat(&format!("
427                             (module (func (result {retty})
428                                 (f64.{op} (f64.const {left}) (f64.const {right}))))
429                         ", retty = RETTY, op = OP, left = a, right = b)).execute_func::<(), $retty>(0, ()) == Ok($func(a, b) as $retty)
430                     }
431 
432                     fn lit_reg(a: f64, b: f64) -> bool {
433                         use std::sync::Once;
434 
435                         let translated = translate_wat(&format!("
436                             (module (func (param f64) (result {retty})
437                                 (f64.{op} (f64.const {left}) (get_local 0))))
438                         ", retty = RETTY, op = OP, left = a));
439                         static ONCE: Once = Once::new();
440                         ONCE.call_once(|| translated.disassemble());
441 
442                         translated.execute_func::<(f64,), $retty>(0, (b,)) == Ok($func(a, b) as $retty)
443                     }
444 
445                     fn reg_lit(a: f64, b: f64) -> bool {
446                         use std::sync::Once;
447 
448                         let translated = translate_wat(&format!("
449                             (module (func (param f64) (result {retty})
450                                 (f64.{op} (get_local 0) (f64.const {right}))))
451                         ", retty = RETTY, op = OP, right = b));
452                         static ONCE: Once = Once::new();
453                         ONCE.call_once(|| translated.disassemble());
454 
455                         translated.execute_func::<(f64,), $retty>(0, (a,)) == Ok($func(a, b) as $retty)
456                     }
457                 }
458             }
459         };
460     }
461 
462     macro_rules! unop_test {
463         ($name:ident, $func:expr) => {
464             unop_test!($name, $func, f64);
465         };
466         ($name:ident, $func:expr, $out_ty:ty) => {
467             mod $name {
468                 use super::{lazy_static, quickcheck, translate_wat, ExecutableModule};
469                 use std::sync::Once;
470 
471                 lazy_static! {
472                     static ref AS_PARAM: ExecutableModule = translate_wat(concat!(
473                         "(module (func (param f64) (result ",
474                         stringify!($out_ty),
475                         ")
476                             (f64.",
477                         stringify!($name),
478                         " (get_local 0))))"
479                     ),);
480                 }
481 
482                 quickcheck! {
483                     fn as_param(a: f64) -> bool {
484                         static ONCE: Once = Once::new();
485                         ONCE.call_once(|| AS_PARAM.disassemble());
486                         AS_PARAM.execute_func::<(f64,), $out_ty>(0, (a,)) == Ok($func(a))
487                     }
488 
489                     fn lit(a: f64) -> bool {
490                                                 let translated = translate_wat(&format!(concat!("
491                             (module (func (result ",stringify!($out_ty),")
492                                 (f64.",stringify!($name)," (f64.const {val}))))
493                         "), val = a));
494                         static ONCE: Once = Once::new();
495                         ONCE.call_once(|| translated.disassemble());
496 
497                         translated.execute_func::<(), $out_ty>(0, ()) == Ok($func(a))
498                     }
499                 }
500             }
501         };
502     }
503 
504     binop_test!(add, |a, b| a + b);
505     binop_test!(mul, |a, b| a * b);
506     binop_test!(sub, |a, b| a - b);
507     binop_test!(gt, |a, b| a > b, i32);
508     binop_test!(lt, |a, b| a < b, i32);
509     binop_test!(ge, |a, b| a >= b, i32);
510     binop_test!(le, |a, b| a <= b, i32);
511 
512     unop_test!(neg, |a: f64| -a);
513     unop_test!(abs, |a: f64| a.abs());
514 }
515 
516 quickcheck! {
517     fn if_then_else(a: u32, b: u32) -> bool {
518         const CODE: &str = r#"
519 (module
520   (func (param i32) (param i32) (result i32)
521     (if (result i32)
522       (i32.eq
523         (get_local 0)
524         (get_local 1)
525       )
526       (then (get_local 0))
527       (else (get_local 1))
528     )
529   )
530 )
531         "#;
532 
533         lazy_static! {
534             static ref TRANSLATED: ExecutableModule = {let out = translate_wat(CODE); out.disassemble(); out};
535         }
536 
537         let out = TRANSLATED.execute_func::<(u32, u32), u32>(0, (a, b));
538 
539         out == Ok(if a == b { a } else { b })
540     }
541 }
542 
543 quickcheck! {
544     #[test]
545     fn literals(a: i32, b: i64, c: i32, d: i64) -> bool {
546         let code = format!(r#"
547             (module
548               (func (result i32)
549                 (i32.const {})
550               )
551               (func (result i64)
552                 (i64.const {})
553               )
554               (func (result f32)
555                 (f32.const {})
556               )
557               (func (result f64)
558                 (f64.const {})
559               )
560             )
561         "#, a, b, c, d);
562 
563         let translated = translate_wat(&code);
564 
565         assert_eq!(translated.execute_func::<(), i32>(0, ()), Ok(a));
566         assert_eq!(translated.execute_func::<(), i64>(1, ()), Ok(b));
567         assert_eq!(translated.execute_func::<(), f32>(2, ()), Ok(c as _));
568         assert_eq!(translated.execute_func::<(), f64>(3, ()), Ok(d as _));
569 
570         true
571     }
572 }
573 
574 quickcheck! {
575     #[test]
576     fn params(a: i32, b: i64, c: i32, d: i64) -> bool {
577         let code = r#"
578             (module
579               (func (param i32) (param i64) (param f32) (param f64) (result i32)
580                 (get_local 0)
581               )
582               (func (param i32) (param i64) (param f32) (param f64) (result i64)
583                 (get_local 1)
584               )
585               (func (param i32) (param i64) (param f32) (param f64) (result f32)
586                 (get_local 2)
587               )
588               (func (param i32) (param i64) (param f32) (param f64) (result f64)
589                 (get_local 3)
590               )
591             )
592         "#;
593 
594         let c = c as f32;
595         let d = d as f64;
596 
597         let translated = translate_wat(&code);
598 
599         assert_eq!(translated.execute_func::<(i32, i64, f32, f64), i32>(0, (a, b, c, d)), Ok(a));
600         assert_eq!(translated.execute_func::<(i32, i64, f32, f64), i64>(1, (a, b, c, d)), Ok(b));
601         assert_eq!(translated.execute_func::<(i32, i64, f32, f64), f32>(2, (a, b, c, d)), Ok(c));
602         assert_eq!(translated.execute_func::<(i32, i64, f32, f64), f64>(3, (a, b, c, d)), Ok(d));
603 
604         true
605     }
606 }
607 
608 macro_rules! test_select {
609     ($name:ident, $ty:ident) => {
610         mod $name {
611             use super::{lazy_static, quickcheck, translate_wat, ExecutableModule};
612             use std::sync::Once;
613 
614             lazy_static! {
615                 static ref AS_PARAMS: ExecutableModule = translate_wat(&format!(
616                     "
617                     (module
618                         (func (param {ty}) (param {ty}) (param i32) (result {ty})
619                             (select (get_local 0) (get_local 1) (get_local 2))
620                         )
621                     )",
622                     ty = stringify!($ty)
623                 ));
624             }
625 
626             quickcheck! {
627                 fn as_param(cond: bool, then: $ty, else_: $ty) -> bool {
628                      let icond: i32 = if cond { 1 } else { 0 };
629                      AS_PARAMS.execute_func::<($ty, $ty, i32), $ty>(0, (then, else_, icond)) ==
630                         Ok(if cond { then } else { else_ })
631                 }
632 
633                 fn lit(cond: bool, then: $ty, else_: $ty) -> bool {
634                     let icond: i32 = if cond { 1 } else { 0 };
635                                                     let translated = translate_wat(&format!("
636                             (module (func (param {ty}) (param {ty}) (result {ty})
637                                 (select (get_local 0) (get_local 1) (i32.const {val}))))
638                         ",
639                         val = icond,
640                         ty = stringify!($ty)
641                     ));
642                     static ONCE: Once = Once::new();
643                     ONCE.call_once(|| translated.disassemble());
644 
645                     translated.execute_func::<($ty, $ty), $ty>(0, (then, else_)) ==
646                         Ok(if cond { then } else { else_ })
647                 }
648             }
649         }
650     };
651 }
652 
653 test_select!(select32, i32);
654 test_select!(select64, i64);
655