1use crate::term::{BV, Bool};
8use synth_core::WasmOp;
9
10pub struct WasmSemantics {
14 #[allow(dead_code)]
17 memory: Vec<BV>,
18}
19
20impl Default for WasmSemantics {
21 fn default() -> Self {
22 Self::new()
23 }
24}
25
26impl WasmSemantics {
27 pub fn new() -> Self {
29 let memory = (0..256)
31 .map(|i| BV::new_const(format!("mem_{}", i), 32))
32 .collect();
33
34 Self { memory }
35 }
36
37 pub fn new_with_memory(memory: Vec<BV>) -> Self {
39 Self { memory }
40 }
41
42 pub fn encode_op(&self, op: &WasmOp, inputs: &[BV]) -> BV {
47 match op {
48 WasmOp::I32Add => {
50 assert_eq!(inputs.len(), 2, "I32Add requires 2 inputs");
51 inputs[0].bvadd(&inputs[1])
52 }
53
54 WasmOp::I32Sub => {
55 assert_eq!(inputs.len(), 2, "I32Sub requires 2 inputs");
56 inputs[0].bvsub(&inputs[1])
57 }
58
59 WasmOp::I32Mul => {
60 assert_eq!(inputs.len(), 2, "I32Mul requires 2 inputs");
61 inputs[0].bvmul(&inputs[1])
62 }
63
64 WasmOp::I32DivS => {
65 assert_eq!(inputs.len(), 2, "I32DivS requires 2 inputs");
66 inputs[0].bvsdiv(&inputs[1])
67 }
68
69 WasmOp::I32DivU => {
70 assert_eq!(inputs.len(), 2, "I32DivU requires 2 inputs");
71 inputs[0].bvudiv(&inputs[1])
72 }
73
74 WasmOp::I32RemS => {
75 assert_eq!(inputs.len(), 2, "I32RemS requires 2 inputs");
76 inputs[0].bvsrem(&inputs[1])
77 }
78
79 WasmOp::I32RemU => {
80 assert_eq!(inputs.len(), 2, "I32RemU requires 2 inputs");
81 inputs[0].bvurem(&inputs[1])
82 }
83
84 WasmOp::I32And => {
86 assert_eq!(inputs.len(), 2, "I32And requires 2 inputs");
87 inputs[0].bvand(&inputs[1])
88 }
89
90 WasmOp::I32Or => {
91 assert_eq!(inputs.len(), 2, "I32Or requires 2 inputs");
92 inputs[0].bvor(&inputs[1])
93 }
94
95 WasmOp::I32Xor => {
96 assert_eq!(inputs.len(), 2, "I32Xor requires 2 inputs");
97 inputs[0].bvxor(&inputs[1])
98 }
99
100 WasmOp::I32Shl => {
101 assert_eq!(inputs.len(), 2, "I32Shl requires 2 inputs");
102 let shift_mod = inputs[1].bvurem(BV::from_i64(32, 32));
104 inputs[0].bvshl(&shift_mod)
105 }
106
107 WasmOp::I32ShrS => {
108 assert_eq!(inputs.len(), 2, "I32ShrS requires 2 inputs");
109 let shift_mod = inputs[1].bvurem(BV::from_i64(32, 32));
111 inputs[0].bvashr(&shift_mod)
112 }
113
114 WasmOp::I32ShrU => {
115 assert_eq!(inputs.len(), 2, "I32ShrU requires 2 inputs");
116 let shift_mod = inputs[1].bvurem(BV::from_i64(32, 32));
118 inputs[0].bvlshr(&shift_mod)
119 }
120
121 WasmOp::I32Rotl => {
122 assert_eq!(inputs.len(), 2, "I32Rotl requires 2 inputs");
123 let shift_mod = inputs[1].bvurem(BV::from_i64(32, 32));
125 inputs[0].bvrotl(&shift_mod)
126 }
127
128 WasmOp::I32Rotr => {
129 assert_eq!(inputs.len(), 2, "I32Rotr requires 2 inputs");
130 let shift_mod = inputs[1].bvurem(BV::from_i64(32, 32));
132 inputs[0].bvrotr(&shift_mod)
133 }
134
135 WasmOp::I32Clz => {
136 assert_eq!(inputs.len(), 1, "I32Clz requires 1 input");
137 self.encode_clz(&inputs[0])
139 }
140
141 WasmOp::I32Ctz => {
142 assert_eq!(inputs.len(), 1, "I32Ctz requires 1 input");
143 self.encode_ctz(&inputs[0])
145 }
146
147 WasmOp::I32Popcnt => {
148 assert_eq!(inputs.len(), 1, "I32Popcnt requires 1 input");
149 self.encode_popcnt(&inputs[0])
151 }
152
153 WasmOp::I32Const(value) => {
155 assert_eq!(inputs.len(), 0, "I32Const requires 0 inputs");
156 BV::from_i64(*value as i64, 32)
157 }
158
159 WasmOp::I32Eqz => {
161 assert_eq!(inputs.len(), 1, "I32Eqz requires 1 input");
162 let zero = BV::from_i64(0, 32);
163 let cond = inputs[0].eq(&zero);
164 self.bool_to_bv32(&cond)
165 }
166
167 WasmOp::I32Eq => {
168 assert_eq!(inputs.len(), 2, "I32Eq requires 2 inputs");
169 let cond = inputs[0].eq(&inputs[1]);
170 self.bool_to_bv32(&cond)
171 }
172
173 WasmOp::I32Ne => {
174 assert_eq!(inputs.len(), 2, "I32Ne requires 2 inputs");
175 let cond = inputs[0].eq(&inputs[1]).not();
176 self.bool_to_bv32(&cond)
177 }
178
179 WasmOp::I32LtS => {
180 assert_eq!(inputs.len(), 2, "I32LtS requires 2 inputs");
181 let cond = inputs[0].bvslt(&inputs[1]);
182 self.bool_to_bv32(&cond)
183 }
184
185 WasmOp::I32LtU => {
186 assert_eq!(inputs.len(), 2, "I32LtU requires 2 inputs");
187 let cond = inputs[0].bvult(&inputs[1]);
188 self.bool_to_bv32(&cond)
189 }
190
191 WasmOp::I32LeS => {
192 assert_eq!(inputs.len(), 2, "I32LeS requires 2 inputs");
193 let cond = inputs[0].bvsle(&inputs[1]);
194 self.bool_to_bv32(&cond)
195 }
196
197 WasmOp::I32LeU => {
198 assert_eq!(inputs.len(), 2, "I32LeU requires 2 inputs");
199 let cond = inputs[0].bvule(&inputs[1]);
200 self.bool_to_bv32(&cond)
201 }
202
203 WasmOp::I32GtS => {
204 assert_eq!(inputs.len(), 2, "I32GtS requires 2 inputs");
205 let cond = inputs[0].bvsgt(&inputs[1]);
206 self.bool_to_bv32(&cond)
207 }
208
209 WasmOp::I32GtU => {
210 assert_eq!(inputs.len(), 2, "I32GtU requires 2 inputs");
211 let cond = inputs[0].bvugt(&inputs[1]);
212 self.bool_to_bv32(&cond)
213 }
214
215 WasmOp::I32GeS => {
216 assert_eq!(inputs.len(), 2, "I32GeS requires 2 inputs");
217 let cond = inputs[0].bvsge(&inputs[1]);
218 self.bool_to_bv32(&cond)
219 }
220
221 WasmOp::I32GeU => {
222 assert_eq!(inputs.len(), 2, "I32GeU requires 2 inputs");
223 let cond = inputs[0].bvuge(&inputs[1]);
224 self.bool_to_bv32(&cond)
225 }
226
227 WasmOp::Select => {
229 assert_eq!(inputs.len(), 3, "Select requires 3 inputs");
230 let zero = BV::from_i64(0, 32);
233 let cond = inputs[2].eq(&zero).not(); cond.ite(&inputs[0], &inputs[1])
235 }
236
237 WasmOp::Drop => {
238 assert_eq!(inputs.len(), 1, "Drop requires 1 input");
239 BV::from_i64(0, 32)
242 }
243
244 WasmOp::I32Load { offset, .. } => {
246 assert_eq!(inputs.len(), 1, "I32Load requires 1 input (address)");
247 let address = inputs[0].clone();
250 let offset_bv = BV::from_u64(*offset as u64, 32);
251 let _effective_addr = address.bvadd(&offset_bv);
252
253 BV::new_const(format!("load_{}_{}", offset, address), 32)
256 }
257
258 WasmOp::I32Store { offset, .. } => {
259 assert_eq!(
260 inputs.len(),
261 2,
262 "I32Store requires 2 inputs (address, value)"
263 );
264 let _address = inputs[0].clone();
267 let value = inputs[1].clone();
268 let _offset_bv = BV::from_u64(*offset as u64, 32);
269
270 value
272 }
273
274 WasmOp::LocalGet(index) => {
276 assert_eq!(inputs.len(), 0, "LocalGet requires 0 inputs");
277 BV::new_const(format!("local_{}", index), 32)
279 }
280
281 WasmOp::LocalSet(_index) => {
282 assert_eq!(inputs.len(), 1, "LocalSet requires 1 input");
283 inputs[0].clone()
286 }
287
288 WasmOp::LocalTee(_index) => {
289 assert_eq!(inputs.len(), 1, "LocalTee requires 1 input");
290 inputs[0].clone()
292 }
293
294 WasmOp::GlobalGet(index) => {
295 assert_eq!(inputs.len(), 0, "GlobalGet requires 0 inputs");
296 BV::new_const(format!("global_{}", index), 32)
298 }
299
300 WasmOp::GlobalSet(_index) => {
301 assert_eq!(inputs.len(), 1, "GlobalSet requires 1 input");
302 inputs[0].clone()
305 }
306
307 WasmOp::Nop => {
309 assert_eq!(inputs.len(), 0, "Nop requires 0 inputs");
310 BV::from_i64(0, 32)
312 }
313
314 WasmOp::Unreachable => {
315 assert_eq!(inputs.len(), 0, "Unreachable requires 0 inputs");
316 BV::new_const("unreachable_trap", 32)
318 }
319
320 WasmOp::Block => {
322 assert_eq!(inputs.len(), 0, "Block requires 0 inputs");
323 BV::from_i64(0, 32)
325 }
326
327 WasmOp::Loop => {
328 assert_eq!(inputs.len(), 0, "Loop requires 0 inputs");
329 BV::from_i64(0, 32)
331 }
332
333 WasmOp::End => {
334 assert_eq!(inputs.len(), 0, "End requires 0 inputs");
335 BV::from_i64(0, 32)
337 }
338
339 WasmOp::Br(label) => {
340 assert_eq!(inputs.len(), 0, "Br requires 0 inputs");
341 BV::new_const(format!("br_{}", label), 32)
343 }
344
345 WasmOp::BrIf(label) => {
346 assert_eq!(inputs.len(), 1, "BrIf requires 1 input (condition)");
347 let _cond = inputs[0].clone();
350 BV::new_const(format!("br_if_{}", label), 32)
351 }
352
353 WasmOp::Return => {
354 assert_eq!(inputs.len(), 0, "Return requires 0 inputs");
355 BV::new_const("return", 32)
357 }
358
359 WasmOp::If => {
360 if !inputs.is_empty() {
363 let _cond = inputs[0].clone();
364 }
365 BV::from_i64(0, 32)
366 }
367
368 WasmOp::Else => {
369 assert_eq!(inputs.len(), 0, "Else requires 0 inputs");
370 BV::from_i64(0, 32)
372 }
373
374 WasmOp::BrTable { targets, default } => {
375 if inputs.is_empty() {
380 return BV::from_i64(0, 32);
382 }
383 let _index = inputs[0].clone();
384
385 BV::new_const(format!("br_table_{}_{}", targets.len(), default), 32)
388 }
389
390 WasmOp::Call(func_idx) => {
391 BV::new_const(format!("call_{}", func_idx), 32)
395 }
396
397 WasmOp::CallIndirect { type_index, .. } => {
398 if inputs.is_empty() {
401 return BV::from_i64(0, 32);
403 }
404 let _table_index = inputs[0].clone();
407 BV::new_const(format!("call_indirect_{}", type_index), 32)
408 }
409
410 WasmOp::I64Const(value) => {
417 assert_eq!(inputs.len(), 0, "I64Const requires 0 inputs");
418 let low32 = (*value as i32) as i64;
421 BV::from_i64(low32, 32)
422 }
423
424 WasmOp::I64Add => {
425 assert_eq!(inputs.len(), 2, "I64Add requires 2 inputs");
426 inputs[0].bvadd(&inputs[1])
429 }
430
431 WasmOp::I64Eqz => {
432 assert_eq!(inputs.len(), 1, "I64Eqz requires 1 input");
433 let zero = BV::from_i64(0, 32);
436 let cond = inputs[0].eq(&zero);
437 self.bool_to_bv32(&cond)
438 }
439
440 WasmOp::I32WrapI64 => {
441 assert_eq!(inputs.len(), 1, "I32WrapI64 requires 1 input");
442 inputs[0].clone()
445 }
446
447 WasmOp::I64ExtendI32S => {
448 assert_eq!(inputs.len(), 1, "I64ExtendI32S requires 1 input");
449 inputs[0].clone()
453 }
454
455 WasmOp::I64ExtendI32U => {
456 assert_eq!(inputs.len(), 1, "I64ExtendI32U requires 1 input");
457 inputs[0].clone()
460 }
461
462 WasmOp::I64Load { offset, .. } => {
464 assert_eq!(inputs.len(), 1, "I64Load requires 1 input (address)");
465 let address = inputs[0].clone();
469 let offset_bv = BV::from_u64(*offset as u64, 32);
470 let _effective_addr = address.bvadd(&offset_bv);
471
472 BV::new_const(format!("i64load_{}_{}", offset, address), 32)
474 }
475
476 WasmOp::I64Store { offset, .. } => {
477 assert_eq!(
478 inputs.len(),
479 2,
480 "I64Store requires 2 inputs (address, value)"
481 );
482 let _address = inputs[0].clone();
485 let value = inputs[1].clone();
486 let _offset_bv = BV::from_u64(*offset as u64, 32);
487
488 value
490 }
491
492 WasmOp::F32Const(value) => {
497 let bits = value.to_bits() as i64;
500 BV::from_i64(bits, 32)
501 }
502
503 WasmOp::F32Add => {
504 assert_eq!(inputs.len(), 2, "F32Add requires 2 inputs");
505 BV::new_const("f32_add_result", 32)
507 }
508
509 WasmOp::F32Sub => {
510 assert_eq!(inputs.len(), 2, "F32Sub requires 2 inputs");
511 BV::new_const("f32_sub_result", 32)
513 }
514
515 WasmOp::F32Mul => {
516 assert_eq!(inputs.len(), 2, "F32Mul requires 2 inputs");
517 BV::new_const("f32_mul_result", 32)
519 }
520
521 WasmOp::F32Div => {
522 assert_eq!(inputs.len(), 2, "F32Div requires 2 inputs");
523 BV::new_const("f32_div_result", 32)
525 }
526
527 WasmOp::F32Abs => {
528 assert_eq!(inputs.len(), 1, "F32Abs requires 1 input");
529 let val = inputs[0].clone();
531 let mask = BV::from_u64(0x7FFFFFFF, 32);
532 val.bvand(&mask)
533 }
534
535 WasmOp::F32Neg => {
536 assert_eq!(inputs.len(), 1, "F32Neg requires 1 input");
537 let val = inputs[0].clone();
539 let mask = BV::from_u64(0x80000000, 32);
540 val.bvxor(&mask)
541 }
542
543 WasmOp::F32Sqrt => {
544 assert_eq!(inputs.len(), 1, "F32Sqrt requires 1 input");
545 BV::new_const("f32_sqrt_result", 32)
547 }
548
549 WasmOp::F32Min => {
550 assert_eq!(inputs.len(), 2, "F32Min requires 2 inputs");
551 BV::new_const("f32_min_result", 32)
553 }
554
555 WasmOp::F32Max => {
556 assert_eq!(inputs.len(), 2, "F32Max requires 2 inputs");
557 BV::new_const("f32_max_result", 32)
559 }
560
561 WasmOp::F32Copysign => {
562 assert_eq!(inputs.len(), 2, "F32Copysign requires 2 inputs");
563 let val_n = inputs[0].clone();
565 let val_m = inputs[1].clone();
566
567 let mag_mask = BV::from_u64(0x7FFFFFFF, 32);
569 let magnitude = val_n.bvand(&mag_mask);
570
571 let sign_mask = BV::from_u64(0x80000000, 32);
573 let sign = val_m.bvand(&sign_mask);
574
575 magnitude.bvor(&sign)
577 }
578
579 WasmOp::F32Load {
580 offset: _,
581 align: _,
582 } => {
583 assert_eq!(inputs.len(), 1, "F32Load requires 1 input (address)");
584 BV::new_const("f32_load_result", 32)
586 }
587
588 WasmOp::F32Eq => {
590 assert_eq!(inputs.len(), 2, "F32Eq requires 2 inputs");
591 BV::new_const("f32_eq_result", 32)
593 }
594
595 WasmOp::F32Ne => {
596 assert_eq!(inputs.len(), 2, "F32Ne requires 2 inputs");
597 BV::new_const("f32_ne_result", 32)
599 }
600
601 WasmOp::F32Lt => {
602 assert_eq!(inputs.len(), 2, "F32Lt requires 2 inputs");
603 BV::new_const("f32_lt_result", 32)
605 }
606
607 WasmOp::F32Le => {
608 assert_eq!(inputs.len(), 2, "F32Le requires 2 inputs");
609 BV::new_const("f32_le_result", 32)
611 }
612
613 WasmOp::F32Gt => {
614 assert_eq!(inputs.len(), 2, "F32Gt requires 2 inputs");
615 BV::new_const("f32_gt_result", 32)
617 }
618
619 WasmOp::F32Ge => {
620 assert_eq!(inputs.len(), 2, "F32Ge requires 2 inputs");
621 BV::new_const("f32_ge_result", 32)
623 }
624
625 WasmOp::F32Store {
626 offset: _,
627 align: _,
628 } => {
629 assert_eq!(
630 inputs.len(),
631 2,
632 "F32Store requires 2 inputs (address, value)"
633 );
634 BV::from_i64(0, 32)
637 }
638
639 WasmOp::F32Ceil => {
641 assert_eq!(inputs.len(), 1, "F32Ceil requires 1 input");
642 BV::new_const("f32_ceil_result", 32)
644 }
645
646 WasmOp::F32Floor => {
647 assert_eq!(inputs.len(), 1, "F32Floor requires 1 input");
648 BV::new_const("f32_floor_result", 32)
650 }
651
652 WasmOp::F32Trunc => {
653 assert_eq!(inputs.len(), 1, "F32Trunc requires 1 input");
654 BV::new_const("f32_trunc_result", 32)
656 }
657
658 WasmOp::F32Nearest => {
659 assert_eq!(inputs.len(), 1, "F32Nearest requires 1 input");
660 BV::new_const("f32_nearest_result", 32)
662 }
663
664 WasmOp::F32ConvertI32S => {
666 assert_eq!(inputs.len(), 1, "F32ConvertI32S requires 1 input");
667 BV::new_const("f32_convert_i32s_result", 32)
669 }
670
671 WasmOp::F32ConvertI32U => {
672 assert_eq!(inputs.len(), 1, "F32ConvertI32U requires 1 input");
673 BV::new_const("f32_convert_i32u_result", 32)
675 }
676
677 WasmOp::F32ConvertI64S => {
678 assert_eq!(inputs.len(), 1, "F32ConvertI64S requires 1 input");
679 BV::new_const("f32_convert_i64s_result", 32)
681 }
682
683 WasmOp::F32ConvertI64U => {
684 assert_eq!(inputs.len(), 1, "F32ConvertI64U requires 1 input");
685 BV::new_const("f32_convert_i64u_result", 32)
687 }
688
689 WasmOp::F32DemoteF64 => {
691 assert_eq!(inputs.len(), 1, "F32DemoteF64 requires 1 input");
692 BV::new_const("f32_demote_f64_result", 32)
694 }
695
696 WasmOp::F32ReinterpretI32 => {
698 assert_eq!(inputs.len(), 1, "F32ReinterpretI32 requires 1 input");
699 inputs[0].clone()
701 }
702
703 WasmOp::I32ReinterpretF32 => {
704 assert_eq!(inputs.len(), 1, "I32ReinterpretF32 requires 1 input");
705 inputs[0].clone()
707 }
708
709 WasmOp::F64Const(value) => {
713 let bits = value.to_bits() as i64;
716 BV::from_i64(bits, 64)
717 }
718
719 WasmOp::F64Add => {
720 assert_eq!(inputs.len(), 2, "F64Add requires 2 inputs");
721 BV::new_const("f64_add_result", 64)
723 }
724
725 WasmOp::F64Sub => {
726 assert_eq!(inputs.len(), 2, "F64Sub requires 2 inputs");
727 BV::new_const("f64_sub_result", 64)
729 }
730
731 WasmOp::F64Mul => {
732 assert_eq!(inputs.len(), 2, "F64Mul requires 2 inputs");
733 BV::new_const("f64_mul_result", 64)
735 }
736
737 WasmOp::F64Div => {
738 assert_eq!(inputs.len(), 2, "F64Div requires 2 inputs");
739 BV::new_const("f64_div_result", 64)
741 }
742
743 WasmOp::F64Abs => {
744 assert_eq!(inputs.len(), 1, "F64Abs requires 1 input");
745 let val = inputs[0].clone();
747 let sign_mask = BV::from_u64(0x7FFF_FFFF_FFFF_FFFF, 64);
748 val.bvand(&sign_mask)
749 }
750
751 WasmOp::F64Neg => {
752 assert_eq!(inputs.len(), 1, "F64Neg requires 1 input");
753 let val = inputs[0].clone();
755 let sign_bit = BV::from_u64(0x8000_0000_0000_0000, 64);
756 val.bvxor(&sign_bit)
757 }
758
759 WasmOp::F64Sqrt => {
760 assert_eq!(inputs.len(), 1, "F64Sqrt requires 1 input");
761 BV::new_const("f64_sqrt_result", 64)
763 }
764
765 WasmOp::F64Min => {
766 assert_eq!(inputs.len(), 2, "F64Min requires 2 inputs");
767 BV::new_const("f64_min_result", 64)
769 }
770
771 WasmOp::F64Max => {
772 assert_eq!(inputs.len(), 2, "F64Max requires 2 inputs");
773 BV::new_const("f64_max_result", 64)
775 }
776
777 WasmOp::F64Copysign => {
778 assert_eq!(inputs.len(), 2, "F64Copysign requires 2 inputs");
779 let val_n = inputs[0].clone();
781 let val_m = inputs[1].clone();
782
783 let magnitude_mask = BV::from_u64(0x7FFF_FFFF_FFFF_FFFF, 64);
785 let magnitude = val_n.bvand(&magnitude_mask);
786
787 let sign_mask = BV::from_u64(0x8000_0000_0000_0000, 64);
789 let sign = val_m.bvand(&sign_mask);
790
791 magnitude.bvor(&sign)
793 }
794
795 WasmOp::F64Load {
796 offset: _,
797 align: _,
798 } => {
799 assert_eq!(inputs.len(), 1, "F64Load requires 1 input (address)");
801 BV::new_const("f64_load_result", 64)
802 }
803
804 WasmOp::F64Eq => {
805 assert_eq!(inputs.len(), 2, "F64Eq requires 2 inputs");
806 BV::new_const("f64_eq_result", 32)
808 }
809
810 WasmOp::F64Ne => {
811 assert_eq!(inputs.len(), 2, "F64Ne requires 2 inputs");
812 BV::new_const("f64_ne_result", 32)
814 }
815
816 WasmOp::F64Lt => {
817 assert_eq!(inputs.len(), 2, "F64Lt requires 2 inputs");
818 BV::new_const("f64_lt_result", 32)
820 }
821
822 WasmOp::F64Le => {
823 assert_eq!(inputs.len(), 2, "F64Le requires 2 inputs");
824 BV::new_const("f64_le_result", 32)
826 }
827
828 WasmOp::F64Gt => {
829 assert_eq!(inputs.len(), 2, "F64Gt requires 2 inputs");
830 BV::new_const("f64_gt_result", 32)
832 }
833
834 WasmOp::F64Ge => {
835 assert_eq!(inputs.len(), 2, "F64Ge requires 2 inputs");
836 BV::new_const("f64_ge_result", 32)
838 }
839
840 WasmOp::F64Store {
841 offset: _,
842 align: _,
843 } => {
844 assert_eq!(
846 inputs.len(),
847 2,
848 "F64Store requires 2 inputs (value, address)"
849 );
850 BV::from_i64(0, 32)
852 }
853
854 WasmOp::F64Ceil => {
855 assert_eq!(inputs.len(), 1, "F64Ceil requires 1 input");
856 BV::new_const("f64_ceil_result", 64)
858 }
859
860 WasmOp::F64Floor => {
861 assert_eq!(inputs.len(), 1, "F64Floor requires 1 input");
862 BV::new_const("f64_floor_result", 64)
864 }
865
866 WasmOp::F64Trunc => {
867 assert_eq!(inputs.len(), 1, "F64Trunc requires 1 input");
868 BV::new_const("f64_trunc_result", 64)
870 }
871
872 WasmOp::F64Nearest => {
873 assert_eq!(inputs.len(), 1, "F64Nearest requires 1 input");
874 BV::new_const("f64_nearest_result", 64)
876 }
877
878 WasmOp::F64ConvertI32S => {
880 assert_eq!(inputs.len(), 1, "F64ConvertI32S requires 1 input");
881 BV::new_const("f64_convert_i32s_result", 64)
883 }
884
885 WasmOp::F64ConvertI32U => {
886 assert_eq!(inputs.len(), 1, "F64ConvertI32U requires 1 input");
887 BV::new_const("f64_convert_i32u_result", 64)
889 }
890
891 WasmOp::F64ConvertI64S => {
892 assert_eq!(inputs.len(), 1, "F64ConvertI64S requires 1 input");
893 BV::new_const("f64_convert_i64s_result", 64)
895 }
896
897 WasmOp::F64ConvertI64U => {
898 assert_eq!(inputs.len(), 1, "F64ConvertI64U requires 1 input");
899 BV::new_const("f64_convert_i64u_result", 64)
901 }
902
903 WasmOp::F64PromoteF32 => {
904 assert_eq!(inputs.len(), 1, "F64PromoteF32 requires 1 input");
905 BV::new_const("f64_promote_f32_result", 64)
908 }
909
910 WasmOp::F64ReinterpretI64 => {
911 assert_eq!(inputs.len(), 1, "F64ReinterpretI64 requires 1 input");
912 inputs[0].clone()
914 }
915
916 WasmOp::I64ReinterpretF64 => {
917 assert_eq!(inputs.len(), 1, "I64ReinterpretF64 requires 1 input");
918 inputs[0].clone()
920 }
921
922 WasmOp::I64TruncF64S => {
923 assert_eq!(inputs.len(), 1, "I64TruncF64S requires 1 input");
924 BV::new_const("i64_trunc_f64s_result", 64)
926 }
927
928 WasmOp::I64TruncF64U => {
929 assert_eq!(inputs.len(), 1, "I64TruncF64U requires 1 input");
930 BV::new_const("i64_trunc_f64u_result", 64)
932 }
933
934 WasmOp::I64TruncF32S => {
940 assert_eq!(inputs.len(), 1, "I64TruncF32S requires 1 input");
941 BV::new_const("i64_trunc_f32s_result", 64)
942 }
943
944 WasmOp::I64TruncF32U => {
945 assert_eq!(inputs.len(), 1, "I64TruncF32U requires 1 input");
946 BV::new_const("i64_trunc_f32u_result", 64)
947 }
948
949 WasmOp::I32TruncF64S => {
950 assert_eq!(inputs.len(), 1, "I32TruncF64S requires 1 input");
951 BV::new_const("i32_trunc_f64s_result", 32)
953 }
954
955 WasmOp::I32TruncF64U => {
956 assert_eq!(inputs.len(), 1, "I32TruncF64U requires 1 input");
957 BV::new_const("i32_trunc_f64u_result", 32)
959 }
960
961 _ => {
963 BV::new_const(format!("unsupported_{:?}", op), 32)
966 }
967 }
968 }
969
970 fn bool_to_bv32(&self, b: &Bool) -> BV {
972 let zero = BV::from_i64(0, 32);
973 let one = BV::from_i64(1, 32);
974 b.ite(&one, &zero)
975 }
976
977 fn encode_clz(&self, input: &BV) -> BV {
986 let zero = BV::from_i64(0, 32);
987
988 let all_zero = input.eq(&zero);
990 let result_if_zero = BV::from_i64(32, 32);
991
992 let mut count = BV::from_i64(0, 32);
994 let mut remaining = input.clone();
995
996 let mask_16 = BV::from_u64(0xFFFF0000, 32);
998 let top_16 = remaining.bvand(&mask_16);
999 let top_16_zero = top_16.eq(&zero);
1000
1001 count = top_16_zero.ite(count.bvadd(BV::from_i64(16, 32)), &count);
1003 remaining = top_16_zero.ite(remaining.bvshl(BV::from_i64(16, 32)), &remaining);
1004
1005 let mask_8 = BV::from_u64(0xFF000000, 32);
1007 let top_8 = remaining.bvand(&mask_8);
1008 let top_8_zero = top_8.eq(&zero);
1009
1010 count = top_8_zero.ite(count.bvadd(BV::from_i64(8, 32)), &count);
1011 remaining = top_8_zero.ite(remaining.bvshl(BV::from_i64(8, 32)), &remaining);
1012
1013 let mask_4 = BV::from_u64(0xF0000000, 32);
1015 let top_4 = remaining.bvand(&mask_4);
1016 let top_4_zero = top_4.eq(&zero);
1017
1018 count = top_4_zero.ite(count.bvadd(BV::from_i64(4, 32)), &count);
1019 remaining = top_4_zero.ite(remaining.bvshl(BV::from_i64(4, 32)), &remaining);
1020
1021 let mask_2 = BV::from_u64(0xC0000000, 32);
1023 let top_2 = remaining.bvand(&mask_2);
1024 let top_2_zero = top_2.eq(&zero);
1025
1026 count = top_2_zero.ite(count.bvadd(BV::from_i64(2, 32)), &count);
1027 remaining = top_2_zero.ite(remaining.bvshl(BV::from_i64(2, 32)), &remaining);
1028
1029 let mask_1 = BV::from_u64(0x80000000, 32);
1031 let top_1 = remaining.bvand(&mask_1);
1032 let top_1_zero = top_1.eq(&zero);
1033
1034 count = top_1_zero.ite(count.bvadd(BV::from_i64(1, 32)), &count);
1035
1036 all_zero.ite(&result_if_zero, &count)
1038 }
1039
1040 fn encode_ctz(&self, input: &BV) -> BV {
1045 let zero = BV::from_i64(0, 32);
1046
1047 let all_zero = input.eq(&zero);
1049 let result_if_zero = BV::from_i64(32, 32);
1050
1051 let mut count = BV::from_i64(0, 32);
1053 let mut remaining = input.clone();
1054
1055 let mask_16 = BV::from_u64(0x0000FFFF, 32);
1057 let bottom_16 = remaining.bvand(&mask_16);
1058 let bottom_16_zero = bottom_16.eq(&zero);
1059
1060 count = bottom_16_zero.ite(count.bvadd(BV::from_i64(16, 32)), &count);
1061 remaining = bottom_16_zero.ite(remaining.bvlshr(BV::from_i64(16, 32)), &remaining);
1062
1063 let mask_8 = BV::from_u64(0x000000FF, 32);
1065 let bottom_8 = remaining.bvand(&mask_8);
1066 let bottom_8_zero = bottom_8.eq(&zero);
1067
1068 count = bottom_8_zero.ite(count.bvadd(BV::from_i64(8, 32)), &count);
1069 remaining = bottom_8_zero.ite(remaining.bvlshr(BV::from_i64(8, 32)), &remaining);
1070
1071 let mask_4 = BV::from_u64(0x0000000F, 32);
1073 let bottom_4 = remaining.bvand(&mask_4);
1074 let bottom_4_zero = bottom_4.eq(&zero);
1075
1076 count = bottom_4_zero.ite(count.bvadd(BV::from_i64(4, 32)), &count);
1077 remaining = bottom_4_zero.ite(remaining.bvlshr(BV::from_i64(4, 32)), &remaining);
1078
1079 let mask_2 = BV::from_u64(0x00000003, 32);
1081 let bottom_2 = remaining.bvand(&mask_2);
1082 let bottom_2_zero = bottom_2.eq(&zero);
1083
1084 count = bottom_2_zero.ite(count.bvadd(BV::from_i64(2, 32)), &count);
1085 remaining = bottom_2_zero.ite(remaining.bvlshr(BV::from_i64(2, 32)), &remaining);
1086
1087 let mask_1 = BV::from_u64(0x00000001, 32);
1089 let bottom_1 = remaining.bvand(&mask_1);
1090 let bottom_1_zero = bottom_1.eq(&zero);
1091
1092 count = bottom_1_zero.ite(count.bvadd(BV::from_i64(1, 32)), &count);
1093
1094 all_zero.ite(&result_if_zero, &count)
1096 }
1097
1098 fn encode_popcnt(&self, input: &BV) -> BV {
1109 let mut x = input.clone();
1110
1111 let mask1 = BV::from_u64(0x55555555, 32);
1115 let masked = x.bvand(&mask1);
1116 let shifted = x.bvlshr(BV::from_i64(1, 32));
1117 let shifted_masked = shifted.bvand(&mask1);
1118 x = masked.bvadd(&shifted_masked);
1119
1120 let mask2 = BV::from_u64(0x33333333, 32);
1124 let masked = x.bvand(&mask2);
1125 let shifted = x.bvlshr(BV::from_i64(2, 32));
1126 let shifted_masked = shifted.bvand(&mask2);
1127 x = masked.bvadd(&shifted_masked);
1128
1129 let mask3 = BV::from_u64(0x0F0F0F0F, 32);
1133 let masked = x.bvand(&mask3);
1134 let shifted = x.bvlshr(BV::from_i64(4, 32));
1135 let shifted_masked = shifted.bvand(&mask3);
1136 x = masked.bvadd(&shifted_masked);
1137
1138 let multiplier = BV::from_u64(0x01010101, 32);
1142 x = x.bvmul(&multiplier);
1143 x = x.bvlshr(BV::from_i64(24, 32));
1144
1145 x
1146 }
1147}
1148
1149#[cfg(test)]
1150mod tests {
1151 use super::*;
1152 use crate::with_verification_context;
1153
1154 #[test]
1155 fn test_wasm_add_encoding() {
1156 with_verification_context(|| {
1157 let encoder = WasmSemantics::new();
1158 let a = BV::new_const("a", 32);
1159 let b = BV::new_const("b", 32);
1160 let result = encoder.encode_op(&WasmOp::I32Add, &[a, b]);
1161 assert!(result.to_string().contains("bvadd"));
1162 });
1163 }
1164
1165 #[test]
1166 fn test_wasm_sub_encoding() {
1167 with_verification_context(|| {
1168 let encoder = WasmSemantics::new();
1169 let a = BV::new_const("a", 32);
1170 let b = BV::new_const("b", 32);
1171 let result = encoder.encode_op(&WasmOp::I32Sub, &[a, b]);
1172 assert!(result.to_string().contains("bvsub"));
1173 });
1174 }
1175
1176 #[test]
1177 fn test_wasm_const_encoding() {
1178 with_verification_context(|| {
1179 let encoder = WasmSemantics::new();
1180 let result = encoder.encode_op(&WasmOp::I32Const(42), &[]);
1181 assert_eq!(result.simplify().as_i64(), Some(42));
1182 });
1183 }
1184
1185 #[test]
1186 fn test_wasm_comparison() {
1187 with_verification_context(|| {
1188 let encoder = WasmSemantics::new();
1189 let a = BV::from_i64(5, 32);
1190 let b = BV::from_i64(10, 32);
1191 let result = encoder.encode_op(&WasmOp::I32LtS, &[a, b]);
1192 assert_eq!(result.simplify().as_i64(), Some(1));
1193 });
1194 }
1195
1196 #[test]
1197 fn test_wasm_bitwise_ops() {
1198 with_verification_context(|| {
1199 let encoder = WasmSemantics::new();
1200 let a = BV::from_i64(0b1010, 32);
1201 let b = BV::from_i64(0b1100, 32);
1202
1203 let and_result = encoder.encode_op(&WasmOp::I32And, &[a.clone(), b.clone()]);
1204 assert_eq!(and_result.simplify().as_i64(), Some(0b1000));
1205
1206 let or_result = encoder.encode_op(&WasmOp::I32Or, &[a.clone(), b.clone()]);
1207 assert_eq!(or_result.simplify().as_i64(), Some(0b1110));
1208
1209 let xor_result = encoder.encode_op(&WasmOp::I32Xor, &[a, b]);
1210 assert_eq!(xor_result.simplify().as_i64(), Some(0b0110));
1211 });
1212 }
1213
1214 #[test]
1215 fn test_wasm_shift_ops() {
1216 with_verification_context(|| {
1217 let encoder = WasmSemantics::new();
1218 let value = BV::from_i64(8, 32);
1219 let shift = BV::from_i64(2, 32);
1220
1221 let shl_result = encoder.encode_op(&WasmOp::I32Shl, &[value.clone(), shift.clone()]);
1222 assert_eq!(shl_result.simplify().as_i64(), Some(32));
1223
1224 let shr_result = encoder.encode_op(&WasmOp::I32ShrU, &[value, shift]);
1225 assert_eq!(shr_result.simplify().as_i64(), Some(2));
1226 });
1227 }
1228
1229 #[test]
1230 fn test_wasm_shift_modulo() {
1231 with_verification_context(|| {
1232 let encoder = WasmSemantics::new();
1233 let value = BV::from_i64(0xFF, 32);
1234 let shift = BV::from_i64(33, 32);
1235 let shl_result = encoder.encode_op(&WasmOp::I32Shl, &[value.clone(), shift.clone()]);
1236 assert_eq!(shl_result.simplify().as_i64(), Some(0x1FE));
1237 });
1238 }
1239
1240 #[test]
1241 fn test_wasm_rem_ops() {
1242 with_verification_context(|| {
1243 let encoder = WasmSemantics::new();
1244 let a = BV::from_i64(17, 32);
1245 let b = BV::from_i64(5, 32);
1246
1247 let rem_s = encoder.encode_op(&WasmOp::I32RemS, &[a.clone(), b.clone()]);
1248 assert_eq!(rem_s.simplify().as_i64(), Some(2));
1249
1250 let rem_u = encoder.encode_op(&WasmOp::I32RemU, &[a, b]);
1251 assert_eq!(rem_u.simplify().as_i64(), Some(2));
1252 });
1253 }
1254
1255 #[test]
1256 fn test_wasm_rotation_ops() {
1257 with_verification_context(|| {
1258 let encoder = WasmSemantics::new();
1259 let value = BV::from_i64(0x12345678, 32);
1260 let rotate = BV::from_i64(8, 32);
1261
1262 let rotr_result = encoder.encode_op(&WasmOp::I32Rotr, &[value.clone(), rotate.clone()]);
1263 assert_eq!(rotr_result.simplify().as_i64(), Some(0x78123456));
1264
1265 let rotl_result = encoder.encode_op(&WasmOp::I32Rotl, &[value, rotate]);
1266 assert_eq!(rotl_result.simplify().as_i64(), Some(0x34567812));
1267 });
1268 }
1269
1270 #[test]
1271 fn test_wasm_clz_comprehensive() {
1272 with_verification_context(|| {
1273 let encoder = WasmSemantics::new();
1274
1275 let zero = BV::from_i64(0, 32);
1276 assert_eq!(
1277 encoder
1278 .encode_op(&WasmOp::I32Clz, &[zero])
1279 .simplify()
1280 .as_i64(),
1281 Some(32)
1282 );
1283
1284 let one = BV::from_i64(1, 32);
1285 assert_eq!(
1286 encoder
1287 .encode_op(&WasmOp::I32Clz, &[one])
1288 .simplify()
1289 .as_i64(),
1290 Some(31)
1291 );
1292
1293 let msb_set = BV::from_u64(0x80000000, 32);
1294 assert_eq!(
1295 encoder
1296 .encode_op(&WasmOp::I32Clz, &[msb_set])
1297 .simplify()
1298 .as_i64(),
1299 Some(0)
1300 );
1301
1302 let val1 = BV::from_u64(0x00FF0000, 32);
1303 assert_eq!(
1304 encoder
1305 .encode_op(&WasmOp::I32Clz, &[val1])
1306 .simplify()
1307 .as_i64(),
1308 Some(8)
1309 );
1310
1311 let val2 = BV::from_u64(0x00001000, 32);
1312 assert_eq!(
1313 encoder
1314 .encode_op(&WasmOp::I32Clz, &[val2])
1315 .simplify()
1316 .as_i64(),
1317 Some(19)
1318 );
1319
1320 let all_ones = BV::from_u64(0xFFFFFFFF, 32);
1321 assert_eq!(
1322 encoder
1323 .encode_op(&WasmOp::I32Clz, &[all_ones])
1324 .simplify()
1325 .as_i64(),
1326 Some(0)
1327 );
1328
1329 let val3 = BV::from_u64(0x00000100, 32);
1330 assert_eq!(
1331 encoder
1332 .encode_op(&WasmOp::I32Clz, &[val3])
1333 .simplify()
1334 .as_i64(),
1335 Some(23)
1336 );
1337 });
1338 }
1339
1340 #[test]
1341 fn test_wasm_ctz_comprehensive() {
1342 with_verification_context(|| {
1343 let encoder = WasmSemantics::new();
1344
1345 assert_eq!(
1346 encoder
1347 .encode_op(&WasmOp::I32Ctz, &[BV::from_i64(0, 32)])
1348 .simplify()
1349 .as_i64(),
1350 Some(32)
1351 );
1352 assert_eq!(
1353 encoder
1354 .encode_op(&WasmOp::I32Ctz, &[BV::from_i64(1, 32)])
1355 .simplify()
1356 .as_i64(),
1357 Some(0)
1358 );
1359 assert_eq!(
1360 encoder
1361 .encode_op(&WasmOp::I32Ctz, &[BV::from_i64(2, 32)])
1362 .simplify()
1363 .as_i64(),
1364 Some(1)
1365 );
1366 assert_eq!(
1367 encoder
1368 .encode_op(&WasmOp::I32Ctz, &[BV::from_u64(0x80000000, 32)])
1369 .simplify()
1370 .as_i64(),
1371 Some(31)
1372 );
1373 assert_eq!(
1374 encoder
1375 .encode_op(&WasmOp::I32Ctz, &[BV::from_u64(0x00FF0000, 32)])
1376 .simplify()
1377 .as_i64(),
1378 Some(16)
1379 );
1380 assert_eq!(
1381 encoder
1382 .encode_op(&WasmOp::I32Ctz, &[BV::from_u64(0x00001000, 32)])
1383 .simplify()
1384 .as_i64(),
1385 Some(12)
1386 );
1387 assert_eq!(
1388 encoder
1389 .encode_op(&WasmOp::I32Ctz, &[BV::from_u64(0xFFFFFFFF, 32)])
1390 .simplify()
1391 .as_i64(),
1392 Some(0)
1393 );
1394 assert_eq!(
1395 encoder
1396 .encode_op(&WasmOp::I32Ctz, &[BV::from_u64(0x00000100, 32)])
1397 .simplify()
1398 .as_i64(),
1399 Some(8)
1400 );
1401 assert_eq!(
1402 encoder
1403 .encode_op(&WasmOp::I32Ctz, &[BV::from_i64(12, 32)])
1404 .simplify()
1405 .as_i64(),
1406 Some(2)
1407 );
1408 });
1409 }
1410
1411 #[test]
1412 fn test_wasm_popcnt() {
1413 with_verification_context(|| {
1414 let encoder = WasmSemantics::new();
1415
1416 assert_eq!(
1417 encoder
1418 .encode_op(&WasmOp::I32Popcnt, &[BV::from_i64(0, 32)])
1419 .simplify()
1420 .as_i64(),
1421 Some(0)
1422 );
1423 assert_eq!(
1424 encoder
1425 .encode_op(&WasmOp::I32Popcnt, &[BV::from_i64(1, 32)])
1426 .simplify()
1427 .as_i64(),
1428 Some(1)
1429 );
1430 assert_eq!(
1431 encoder
1432 .encode_op(&WasmOp::I32Popcnt, &[BV::from_u64(0xFFFFFFFF, 32)])
1433 .simplify()
1434 .as_i64(),
1435 Some(32)
1436 );
1437 assert_eq!(
1438 encoder
1439 .encode_op(&WasmOp::I32Popcnt, &[BV::from_u64(0x0F0F0F0F, 32)])
1440 .simplify()
1441 .as_i64(),
1442 Some(16)
1443 );
1444 assert_eq!(
1445 encoder
1446 .encode_op(&WasmOp::I32Popcnt, &[BV::from_i64(7, 32)])
1447 .simplify()
1448 .as_i64(),
1449 Some(3)
1450 );
1451 assert_eq!(
1452 encoder
1453 .encode_op(&WasmOp::I32Popcnt, &[BV::from_u64(0xAAAAAAAA, 32)])
1454 .simplify()
1455 .as_i64(),
1456 Some(16)
1457 );
1458 });
1459 }
1460
1461 #[test]
1462 fn test_wasm_select() {
1463 with_verification_context(|| {
1464 let encoder = WasmSemantics::new();
1465
1466 let val1 = BV::from_i64(10, 32);
1467 let val2 = BV::from_i64(20, 32);
1468 let cond_true = BV::from_i64(1, 32);
1469 let result =
1470 encoder.encode_op(&WasmOp::Select, &[val1.clone(), val2.clone(), cond_true]);
1471 assert_eq!(result.simplify().as_i64(), Some(10));
1472
1473 let cond_false = BV::from_i64(0, 32);
1474 let result =
1475 encoder.encode_op(&WasmOp::Select, &[val1.clone(), val2.clone(), cond_false]);
1476 assert_eq!(result.simplify().as_i64(), Some(20));
1477
1478 let val3 = BV::from_i64(42, 32);
1479 let val4 = BV::from_i64(99, 32);
1480 let cond_neg = BV::from_i64(-1, 32);
1481 let result = encoder.encode_op(&WasmOp::Select, &[val3, val4, cond_neg]);
1482 assert_eq!(result.simplify().as_i64(), Some(42));
1483 });
1484 }
1485}