1#![allow(
42 clippy::match_same_arms,
43 clippy::must_use_candidate,
44 clippy::manual_assert,
45 clippy::missing_panics_doc,
46 clippy::uninlined_format_args,
47 clippy::implicit_hasher
48)]
49
50use std::collections::HashMap;
51
52use crate::pvm::{Instruction, Opcode};
53use crate::translate::ImportAction;
54use crate::{CompileOptions, Error, Result, SpiProgram, compile, compile_with_options};
55
56pub fn wat_to_wasm(wat: &str) -> Result<Vec<u8>> {
58 wat::parse_str(wat).map_err(|e| Error::Internal(format!("WAT parse error: {e}")))
59}
60
61pub fn compile_wat(wat: &str) -> Result<SpiProgram> {
63 let wasm = wat_to_wasm(wat)?;
64 compile(&wasm)
65}
66
67pub fn compile_wat_with_imports(
69 wat: &str,
70 import_map: HashMap<String, ImportAction>,
71) -> Result<SpiProgram> {
72 let wasm = wat_to_wasm(wat)?;
73 compile_with_options(
74 &wasm,
75 &CompileOptions {
76 import_map: Some(import_map),
77 ..CompileOptions::default()
78 },
79 )
80}
81
82pub fn compile_wat_with_options(wat: &str, options: &CompileOptions) -> Result<SpiProgram> {
84 let wasm = wat_to_wasm(wat)?;
85 compile_with_options(&wasm, options)
86}
87
88pub fn extract_instructions(program: &SpiProgram) -> Vec<Instruction> {
90 program.code().instructions().to_vec()
91}
92
93#[derive(Debug, Clone)]
95pub enum Pat<T> {
96 Any,
98 Exact(T),
100 Predicate(fn(&T) -> bool),
102}
103
104impl<T: PartialEq> Pat<T> {
105 pub fn matches(&self, value: &T) -> bool {
107 match self {
108 Pat::Any => true,
109 Pat::Exact(expected) => value == expected,
110 Pat::Predicate(pred) => pred(value),
111 }
112 }
113}
114
115#[derive(Debug, Clone)]
120pub enum InstructionPattern {
121 Any,
123 LoadImm {
125 reg: Pat<u8>,
126 value: Pat<i32>,
127 },
128 LoadImm64 {
129 reg: Pat<u8>,
130 value: Pat<u64>,
131 },
132 Add32 {
133 dst: Pat<u8>,
134 src1: Pat<u8>,
135 src2: Pat<u8>,
136 },
137 Sub32 {
138 dst: Pat<u8>,
139 src1: Pat<u8>,
140 src2: Pat<u8>,
141 },
142 Mul32 {
143 dst: Pat<u8>,
144 src1: Pat<u8>,
145 src2: Pat<u8>,
146 },
147 DivU32 {
148 dst: Pat<u8>,
149 src1: Pat<u8>,
150 src2: Pat<u8>,
151 },
152 DivS32 {
153 dst: Pat<u8>,
154 src1: Pat<u8>,
155 src2: Pat<u8>,
156 },
157 RemU32 {
158 dst: Pat<u8>,
159 src1: Pat<u8>,
160 src2: Pat<u8>,
161 },
162 RemS32 {
163 dst: Pat<u8>,
164 src1: Pat<u8>,
165 src2: Pat<u8>,
166 },
167 Add64 {
168 dst: Pat<u8>,
169 src1: Pat<u8>,
170 src2: Pat<u8>,
171 },
172 Sub64 {
173 dst: Pat<u8>,
174 src1: Pat<u8>,
175 src2: Pat<u8>,
176 },
177 Mul64 {
178 dst: Pat<u8>,
179 src1: Pat<u8>,
180 src2: Pat<u8>,
181 },
182 DivU64 {
183 dst: Pat<u8>,
184 src1: Pat<u8>,
185 src2: Pat<u8>,
186 },
187 DivS64 {
188 dst: Pat<u8>,
189 src1: Pat<u8>,
190 src2: Pat<u8>,
191 },
192 RemU64 {
193 dst: Pat<u8>,
194 src1: Pat<u8>,
195 src2: Pat<u8>,
196 },
197 RemS64 {
198 dst: Pat<u8>,
199 src1: Pat<u8>,
200 src2: Pat<u8>,
201 },
202 And {
203 dst: Pat<u8>,
204 src1: Pat<u8>,
205 src2: Pat<u8>,
206 },
207 Or {
208 dst: Pat<u8>,
209 src1: Pat<u8>,
210 src2: Pat<u8>,
211 },
212 Xor {
213 dst: Pat<u8>,
214 src1: Pat<u8>,
215 src2: Pat<u8>,
216 },
217 ShloL32 {
218 dst: Pat<u8>,
219 src1: Pat<u8>,
220 src2: Pat<u8>,
221 },
222 ShloR32 {
223 dst: Pat<u8>,
224 src1: Pat<u8>,
225 src2: Pat<u8>,
226 },
227 SharR32 {
228 dst: Pat<u8>,
229 src1: Pat<u8>,
230 src2: Pat<u8>,
231 },
232 ShloL64 {
233 dst: Pat<u8>,
234 src1: Pat<u8>,
235 src2: Pat<u8>,
236 },
237 ShloR64 {
238 dst: Pat<u8>,
239 src1: Pat<u8>,
240 src2: Pat<u8>,
241 },
242 SharR64 {
243 dst: Pat<u8>,
244 src1: Pat<u8>,
245 src2: Pat<u8>,
246 },
247 SetLtU {
248 dst: Pat<u8>,
249 src1: Pat<u8>,
250 src2: Pat<u8>,
251 },
252 SetLtS {
253 dst: Pat<u8>,
254 src1: Pat<u8>,
255 src2: Pat<u8>,
256 },
257 SetLtUImm {
258 dst: Pat<u8>,
259 src: Pat<u8>,
260 value: Pat<i32>,
261 },
262 SetLtSImm {
263 dst: Pat<u8>,
264 src: Pat<u8>,
265 value: Pat<i32>,
266 },
267 Jump {
268 offset: Pat<i32>,
269 },
270 JumpInd {
271 reg: Pat<u8>,
272 offset: Pat<i32>,
273 },
274 AddImm32 {
275 dst: Pat<u8>,
276 src: Pat<u8>,
277 value: Pat<i32>,
278 },
279 AddImm64 {
280 dst: Pat<u8>,
281 src: Pat<u8>,
282 value: Pat<i32>,
283 },
284 BranchLtU {
285 reg1: Pat<u8>,
286 reg2: Pat<u8>,
287 offset: Pat<i32>,
288 },
289 BranchGeU {
290 reg1: Pat<u8>,
291 reg2: Pat<u8>,
292 offset: Pat<i32>,
293 },
294 BranchEqImm {
295 reg: Pat<u8>,
296 value: Pat<i32>,
297 offset: Pat<i32>,
298 },
299 BranchNeImm {
300 reg: Pat<u8>,
301 value: Pat<i32>,
302 offset: Pat<i32>,
303 },
304 BranchGeSImm {
305 reg: Pat<u8>,
306 value: Pat<i32>,
307 offset: Pat<i32>,
308 },
309 LoadIndU8 {
310 dst: Pat<u8>,
311 base: Pat<u8>,
312 offset: Pat<i32>,
313 },
314 LoadIndI8 {
315 dst: Pat<u8>,
316 base: Pat<u8>,
317 offset: Pat<i32>,
318 },
319 LoadIndU16 {
320 dst: Pat<u8>,
321 base: Pat<u8>,
322 offset: Pat<i32>,
323 },
324 LoadIndI16 {
325 dst: Pat<u8>,
326 base: Pat<u8>,
327 offset: Pat<i32>,
328 },
329 LoadIndU32 {
330 dst: Pat<u8>,
331 base: Pat<u8>,
332 offset: Pat<i32>,
333 },
334 LoadIndU64 {
335 dst: Pat<u8>,
336 base: Pat<u8>,
337 offset: Pat<i32>,
338 },
339 StoreIndU8 {
340 base: Pat<u8>,
341 src: Pat<u8>,
342 offset: Pat<i32>,
343 },
344 StoreIndU16 {
345 base: Pat<u8>,
346 src: Pat<u8>,
347 offset: Pat<i32>,
348 },
349 StoreIndU32 {
350 base: Pat<u8>,
351 src: Pat<u8>,
352 offset: Pat<i32>,
353 },
354 StoreIndU64 {
355 base: Pat<u8>,
356 src: Pat<u8>,
357 offset: Pat<i32>,
358 },
359 CountSetBits32 {
360 dst: Pat<u8>,
361 src: Pat<u8>,
362 },
363 CountSetBits64 {
364 dst: Pat<u8>,
365 src: Pat<u8>,
366 },
367 LeadingZeroBits32 {
368 dst: Pat<u8>,
369 src: Pat<u8>,
370 },
371 LeadingZeroBits64 {
372 dst: Pat<u8>,
373 src: Pat<u8>,
374 },
375 TrailingZeroBits32 {
376 dst: Pat<u8>,
377 src: Pat<u8>,
378 },
379 TrailingZeroBits64 {
380 dst: Pat<u8>,
381 src: Pat<u8>,
382 },
383 SignExtend8 {
384 dst: Pat<u8>,
385 src: Pat<u8>,
386 },
387 SignExtend16 {
388 dst: Pat<u8>,
389 src: Pat<u8>,
390 },
391 ZeroExtend16 {
392 dst: Pat<u8>,
393 src: Pat<u8>,
394 },
395 CmovIzImm {
396 cond: Pat<u8>,
397 dst: Pat<u8>,
398 value: Pat<i32>,
399 },
400 CmovNzImm {
401 cond: Pat<u8>,
402 dst: Pat<u8>,
403 value: Pat<i32>,
404 },
405 StoreImmU8 {
406 address: Pat<i32>,
407 value: Pat<i32>,
408 },
409 StoreImmU16 {
410 address: Pat<i32>,
411 value: Pat<i32>,
412 },
413 StoreImmU32 {
414 address: Pat<i32>,
415 value: Pat<i32>,
416 },
417 StoreImmU64 {
418 address: Pat<i32>,
419 value: Pat<i32>,
420 },
421 Trap,
422 Fallthrough,
423 Ecalli {
424 index: Pat<u32>,
425 },
426}
427
428impl InstructionPattern {
429 pub fn matches(&self, instr: &Instruction) -> bool {
431 use InstructionPattern as P;
432
433 match (self, instr) {
434 (P::Any, _) => true,
435 (P::Trap, Instruction::Trap) => true,
436 (P::Fallthrough, Instruction::Fallthrough) => true,
437 (
438 P::LoadImm {
439 reg: r_pat,
440 value: v_pat,
441 },
442 Instruction::LoadImm { reg, value },
443 ) => r_pat.matches(reg) && v_pat.matches(value),
444 (
445 P::LoadImm64 {
446 reg: r_pat,
447 value: v_pat,
448 },
449 Instruction::LoadImm64 { reg, value },
450 ) => r_pat.matches(reg) && v_pat.matches(value),
451 (
452 P::Add32 {
453 dst: d_pat,
454 src1: s1_pat,
455 src2: s2_pat,
456 },
457 Instruction::Add32 { dst, src1, src2 },
458 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
459 (
460 P::Sub32 {
461 dst: d_pat,
462 src1: s1_pat,
463 src2: s2_pat,
464 },
465 Instruction::Sub32 { dst, src1, src2 },
466 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
467 (
468 P::Mul32 {
469 dst: d_pat,
470 src1: s1_pat,
471 src2: s2_pat,
472 },
473 Instruction::Mul32 { dst, src1, src2 },
474 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
475 (
476 P::DivU32 {
477 dst: d_pat,
478 src1: s1_pat,
479 src2: s2_pat,
480 },
481 Instruction::DivU32 { dst, src1, src2 },
482 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
483 (
484 P::DivS32 {
485 dst: d_pat,
486 src1: s1_pat,
487 src2: s2_pat,
488 },
489 Instruction::DivS32 { dst, src1, src2 },
490 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
491 (
492 P::RemU32 {
493 dst: d_pat,
494 src1: s1_pat,
495 src2: s2_pat,
496 },
497 Instruction::RemU32 { dst, src1, src2 },
498 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
499 (
500 P::RemS32 {
501 dst: d_pat,
502 src1: s1_pat,
503 src2: s2_pat,
504 },
505 Instruction::RemS32 { dst, src1, src2 },
506 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
507 (
508 P::Add64 {
509 dst: d_pat,
510 src1: s1_pat,
511 src2: s2_pat,
512 },
513 Instruction::Add64 { dst, src1, src2 },
514 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
515 (
516 P::Sub64 {
517 dst: d_pat,
518 src1: s1_pat,
519 src2: s2_pat,
520 },
521 Instruction::Sub64 { dst, src1, src2 },
522 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
523 (
524 P::Mul64 {
525 dst: d_pat,
526 src1: s1_pat,
527 src2: s2_pat,
528 },
529 Instruction::Mul64 { dst, src1, src2 },
530 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
531 (
532 P::DivU64 {
533 dst: d_pat,
534 src1: s1_pat,
535 src2: s2_pat,
536 },
537 Instruction::DivU64 { dst, src1, src2 },
538 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
539 (
540 P::DivS64 {
541 dst: d_pat,
542 src1: s1_pat,
543 src2: s2_pat,
544 },
545 Instruction::DivS64 { dst, src1, src2 },
546 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
547 (
548 P::RemU64 {
549 dst: d_pat,
550 src1: s1_pat,
551 src2: s2_pat,
552 },
553 Instruction::RemU64 { dst, src1, src2 },
554 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
555 (
556 P::RemS64 {
557 dst: d_pat,
558 src1: s1_pat,
559 src2: s2_pat,
560 },
561 Instruction::RemS64 { dst, src1, src2 },
562 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
563 (
564 P::And {
565 dst: d_pat,
566 src1: s1_pat,
567 src2: s2_pat,
568 },
569 Instruction::And { dst, src1, src2 },
570 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
571 (
572 P::Or {
573 dst: d_pat,
574 src1: s1_pat,
575 src2: s2_pat,
576 },
577 Instruction::Or { dst, src1, src2 },
578 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
579 (
580 P::Xor {
581 dst: d_pat,
582 src1: s1_pat,
583 src2: s2_pat,
584 },
585 Instruction::Xor { dst, src1, src2 },
586 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
587 (
588 P::ShloL32 {
589 dst: d_pat,
590 src1: s1_pat,
591 src2: s2_pat,
592 },
593 Instruction::ShloL32 { dst, src1, src2 },
594 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
595 (
596 P::ShloR32 {
597 dst: d_pat,
598 src1: s1_pat,
599 src2: s2_pat,
600 },
601 Instruction::ShloR32 { dst, src1, src2 },
602 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
603 (
604 P::SharR32 {
605 dst: d_pat,
606 src1: s1_pat,
607 src2: s2_pat,
608 },
609 Instruction::SharR32 { dst, src1, src2 },
610 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
611 (
612 P::ShloL64 {
613 dst: d_pat,
614 src1: s1_pat,
615 src2: s2_pat,
616 },
617 Instruction::ShloL64 { dst, src1, src2 },
618 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
619 (
620 P::ShloR64 {
621 dst: d_pat,
622 src1: s1_pat,
623 src2: s2_pat,
624 },
625 Instruction::ShloR64 { dst, src1, src2 },
626 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
627 (
628 P::SharR64 {
629 dst: d_pat,
630 src1: s1_pat,
631 src2: s2_pat,
632 },
633 Instruction::SharR64 { dst, src1, src2 },
634 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
635 (
636 P::SetLtU {
637 dst: d_pat,
638 src1: s1_pat,
639 src2: s2_pat,
640 },
641 Instruction::SetLtU { dst, src1, src2 },
642 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
643 (
644 P::SetLtS {
645 dst: d_pat,
646 src1: s1_pat,
647 src2: s2_pat,
648 },
649 Instruction::SetLtS { dst, src1, src2 },
650 ) => d_pat.matches(dst) && s1_pat.matches(src1) && s2_pat.matches(src2),
651 (
652 P::SetLtUImm {
653 dst: d_pat,
654 src: s_pat,
655 value: v_pat,
656 },
657 Instruction::SetLtUImm { dst, src, value },
658 ) => d_pat.matches(dst) && s_pat.matches(src) && v_pat.matches(value),
659 (
660 P::SetLtSImm {
661 dst: d_pat,
662 src: s_pat,
663 value: v_pat,
664 },
665 Instruction::SetLtSImm { dst, src, value },
666 ) => d_pat.matches(dst) && s_pat.matches(src) && v_pat.matches(value),
667 (P::Jump { offset: o_pat }, Instruction::Jump { offset }) => o_pat.matches(offset),
668 (
669 P::JumpInd {
670 reg: r_pat,
671 offset: o_pat,
672 },
673 Instruction::JumpInd { reg, offset },
674 ) => r_pat.matches(reg) && o_pat.matches(offset),
675 (
676 P::AddImm32 {
677 dst: d_pat,
678 src: s_pat,
679 value: v_pat,
680 },
681 Instruction::AddImm32 { dst, src, value },
682 ) => d_pat.matches(dst) && s_pat.matches(src) && v_pat.matches(value),
683 (
684 P::AddImm64 {
685 dst: d_pat,
686 src: s_pat,
687 value: v_pat,
688 },
689 Instruction::AddImm64 { dst, src, value },
690 ) => d_pat.matches(dst) && s_pat.matches(src) && v_pat.matches(value),
691 (
692 P::BranchLtU {
693 reg1: r1_pat,
694 reg2: r2_pat,
695 offset: o_pat,
696 },
697 Instruction::BranchLtU { reg1, reg2, offset },
698 ) => r1_pat.matches(reg1) && r2_pat.matches(reg2) && o_pat.matches(offset),
699 (
700 P::BranchGeU {
701 reg1: r1_pat,
702 reg2: r2_pat,
703 offset: o_pat,
704 },
705 Instruction::BranchGeU { reg1, reg2, offset },
706 ) => r1_pat.matches(reg1) && r2_pat.matches(reg2) && o_pat.matches(offset),
707 (
708 P::BranchEqImm {
709 reg: r_pat,
710 value: v_pat,
711 offset: o_pat,
712 },
713 Instruction::BranchEqImm { reg, value, offset },
714 ) => r_pat.matches(reg) && v_pat.matches(value) && o_pat.matches(offset),
715 (
716 P::BranchNeImm {
717 reg: r_pat,
718 value: v_pat,
719 offset: o_pat,
720 },
721 Instruction::BranchNeImm { reg, value, offset },
722 ) => r_pat.matches(reg) && v_pat.matches(value) && o_pat.matches(offset),
723 (
724 P::BranchGeSImm {
725 reg: r_pat,
726 value: v_pat,
727 offset: o_pat,
728 },
729 Instruction::BranchGeSImm { reg, value, offset },
730 ) => r_pat.matches(reg) && v_pat.matches(value) && o_pat.matches(offset),
731 (
732 P::LoadIndU8 {
733 dst: d_pat,
734 base: b_pat,
735 offset: o_pat,
736 },
737 Instruction::LoadIndU8 { dst, base, offset },
738 ) => d_pat.matches(dst) && b_pat.matches(base) && o_pat.matches(offset),
739 (
740 P::LoadIndI8 {
741 dst: d_pat,
742 base: b_pat,
743 offset: o_pat,
744 },
745 Instruction::LoadIndI8 { dst, base, offset },
746 ) => d_pat.matches(dst) && b_pat.matches(base) && o_pat.matches(offset),
747 (
748 P::LoadIndU16 {
749 dst: d_pat,
750 base: b_pat,
751 offset: o_pat,
752 },
753 Instruction::LoadIndU16 { dst, base, offset },
754 ) => d_pat.matches(dst) && b_pat.matches(base) && o_pat.matches(offset),
755 (
756 P::LoadIndI16 {
757 dst: d_pat,
758 base: b_pat,
759 offset: o_pat,
760 },
761 Instruction::LoadIndI16 { dst, base, offset },
762 ) => d_pat.matches(dst) && b_pat.matches(base) && o_pat.matches(offset),
763 (
764 P::LoadIndU32 {
765 dst: d_pat,
766 base: b_pat,
767 offset: o_pat,
768 },
769 Instruction::LoadIndU32 { dst, base, offset },
770 ) => d_pat.matches(dst) && b_pat.matches(base) && o_pat.matches(offset),
771 (
772 P::LoadIndU64 {
773 dst: d_pat,
774 base: b_pat,
775 offset: o_pat,
776 },
777 Instruction::LoadIndU64 { dst, base, offset },
778 ) => d_pat.matches(dst) && b_pat.matches(base) && o_pat.matches(offset),
779 (
780 P::StoreIndU8 {
781 base: b_pat,
782 src: s_pat,
783 offset: o_pat,
784 },
785 Instruction::StoreIndU8 { base, src, offset },
786 ) => b_pat.matches(base) && s_pat.matches(src) && o_pat.matches(offset),
787 (
788 P::StoreIndU16 {
789 base: b_pat,
790 src: s_pat,
791 offset: o_pat,
792 },
793 Instruction::StoreIndU16 { base, src, offset },
794 ) => b_pat.matches(base) && s_pat.matches(src) && o_pat.matches(offset),
795 (
796 P::StoreIndU32 {
797 base: b_pat,
798 src: s_pat,
799 offset: o_pat,
800 },
801 Instruction::StoreIndU32 { base, src, offset },
802 ) => b_pat.matches(base) && s_pat.matches(src) && o_pat.matches(offset),
803 (
804 P::StoreIndU64 {
805 base: b_pat,
806 src: s_pat,
807 offset: o_pat,
808 },
809 Instruction::StoreIndU64 { base, src, offset },
810 ) => b_pat.matches(base) && s_pat.matches(src) && o_pat.matches(offset),
811 (
812 P::CountSetBits32 {
813 dst: d_pat,
814 src: s_pat,
815 },
816 Instruction::CountSetBits32 { dst, src },
817 ) => d_pat.matches(dst) && s_pat.matches(src),
818 (
819 P::CountSetBits64 {
820 dst: d_pat,
821 src: s_pat,
822 },
823 Instruction::CountSetBits64 { dst, src },
824 ) => d_pat.matches(dst) && s_pat.matches(src),
825 (
826 P::LeadingZeroBits32 {
827 dst: d_pat,
828 src: s_pat,
829 },
830 Instruction::LeadingZeroBits32 { dst, src },
831 ) => d_pat.matches(dst) && s_pat.matches(src),
832 (
833 P::LeadingZeroBits64 {
834 dst: d_pat,
835 src: s_pat,
836 },
837 Instruction::LeadingZeroBits64 { dst, src },
838 ) => d_pat.matches(dst) && s_pat.matches(src),
839 (
840 P::TrailingZeroBits32 {
841 dst: d_pat,
842 src: s_pat,
843 },
844 Instruction::TrailingZeroBits32 { dst, src },
845 ) => d_pat.matches(dst) && s_pat.matches(src),
846 (
847 P::TrailingZeroBits64 {
848 dst: d_pat,
849 src: s_pat,
850 },
851 Instruction::TrailingZeroBits64 { dst, src },
852 ) => d_pat.matches(dst) && s_pat.matches(src),
853 (
854 P::SignExtend8 {
855 dst: d_pat,
856 src: s_pat,
857 },
858 Instruction::SignExtend8 { dst, src },
859 ) => d_pat.matches(dst) && s_pat.matches(src),
860 (
861 P::SignExtend16 {
862 dst: d_pat,
863 src: s_pat,
864 },
865 Instruction::SignExtend16 { dst, src },
866 ) => d_pat.matches(dst) && s_pat.matches(src),
867 (
868 P::ZeroExtend16 {
869 dst: d_pat,
870 src: s_pat,
871 },
872 Instruction::ZeroExtend16 { dst, src },
873 ) => d_pat.matches(dst) && s_pat.matches(src),
874 (
875 P::CmovIzImm {
876 cond: c_pat,
877 dst: d_pat,
878 value: v_pat,
879 },
880 Instruction::CmovIzImm { cond, dst, value },
881 ) => c_pat.matches(cond) && d_pat.matches(dst) && v_pat.matches(value),
882 (
883 P::CmovNzImm {
884 cond: c_pat,
885 dst: d_pat,
886 value: v_pat,
887 },
888 Instruction::CmovNzImm { cond, dst, value },
889 ) => c_pat.matches(cond) && d_pat.matches(dst) && v_pat.matches(value),
890 (
891 P::StoreImmU8 {
892 address: a_pat,
893 value: v_pat,
894 },
895 Instruction::StoreImmU8 { address, value },
896 ) => a_pat.matches(address) && v_pat.matches(value),
897 (
898 P::StoreImmU16 {
899 address: a_pat,
900 value: v_pat,
901 },
902 Instruction::StoreImmU16 { address, value },
903 ) => a_pat.matches(address) && v_pat.matches(value),
904 (
905 P::StoreImmU32 {
906 address: a_pat,
907 value: v_pat,
908 },
909 Instruction::StoreImmU32 { address, value },
910 ) => a_pat.matches(address) && v_pat.matches(value),
911 (
912 P::StoreImmU64 {
913 address: a_pat,
914 value: v_pat,
915 },
916 Instruction::StoreImmU64 { address, value },
917 ) => a_pat.matches(address) && v_pat.matches(value),
918 (P::Ecalli { index: i_pat }, Instruction::Ecalli { index }) => i_pat.matches(index),
919 _ => false,
920 }
921 }
922}
923
924pub fn find_pattern(instructions: &[Instruction], pattern: &[InstructionPattern]) -> Option<usize> {
928 if pattern.is_empty() {
929 return Some(0);
930 }
931
932 'outer: for start in 0..=instructions.len().saturating_sub(pattern.len()) {
933 for (i, pat) in pattern.iter().enumerate() {
934 if !pat.matches(&instructions[start + i]) {
935 continue 'outer;
936 }
937 }
938 return Some(start);
939 }
940 None
941}
942
943pub fn assert_has_pattern(instructions: &[Instruction], pattern: &[InstructionPattern]) {
947 if find_pattern(instructions, pattern).is_none() {
948 panic!(
949 "Pattern not found in instruction sequence.\n\nExpected pattern:\n{}\n\nActual instructions:\n{}",
950 format_patterns(pattern),
951 format_instructions(instructions)
952 );
953 }
954}
955
956pub fn assert_matches(instructions: &[Instruction], pattern: &[InstructionPattern]) {
960 if instructions.len() != pattern.len() {
961 panic!(
962 "Instruction count mismatch: expected {}, got {}.\n\nExpected pattern:\n{}\n\nActual instructions:\n{}",
963 pattern.len(),
964 instructions.len(),
965 format_patterns(pattern),
966 format_instructions(instructions)
967 );
968 }
969
970 for (i, (instr, pat)) in instructions.iter().zip(pattern.iter()).enumerate() {
971 if !pat.matches(instr) {
972 panic!(
973 "Instruction mismatch at index {}:\nExpected: {:?}\nActual: {:?}\n\nFull pattern:\n{}\n\nFull instructions:\n{}",
974 i,
975 pat,
976 instr,
977 format_patterns(pattern),
978 format_instructions(instructions)
979 );
980 }
981 }
982}
983
984fn format_patterns(patterns: &[InstructionPattern]) -> String {
986 patterns
987 .iter()
988 .map(|p| format!(" {:?}", p))
989 .collect::<Vec<_>>()
990 .join("\n")
991}
992
993fn format_instructions(instructions: &[Instruction]) -> String {
995 instructions
996 .iter()
997 .map(|i| format!(" {:?}", i))
998 .collect::<Vec<_>>()
999 .join("\n")
1000}
1001
1002pub fn count_opcode(instructions: &[Instruction], opcode: Opcode) -> usize {
1004 instructions
1005 .iter()
1006 .filter(|i| i.opcode() == Some(opcode))
1007 .count()
1008}
1009
1010pub fn has_opcode(instructions: &[Instruction], opcode: Opcode) -> bool {
1012 instructions.iter().any(|i| i.opcode() == Some(opcode))
1013}
1014
1015pub fn filter_by_opcode(instructions: &[Instruction], opcode: Opcode) -> Vec<&Instruction> {
1017 instructions
1018 .iter()
1019 .filter(|i| i.opcode() == Some(opcode))
1020 .collect()
1021}
1022
1023pub trait InstructionExt {
1025 fn opcode(&self) -> Option<Opcode>;
1027}
1028
1029impl InstructionExt for Instruction {
1030 fn opcode(&self) -> Option<Opcode> {
1031 match self {
1032 Instruction::Trap => Some(Opcode::Trap),
1033 Instruction::Fallthrough => Some(Opcode::Fallthrough),
1034 Instruction::LoadImm64 { .. } => Some(Opcode::LoadImm64),
1035 Instruction::LoadImm { .. } => Some(Opcode::LoadImm),
1036 Instruction::Add32 { .. } => Some(Opcode::Add32),
1037 Instruction::Sub32 { .. } => Some(Opcode::Sub32),
1038 Instruction::Mul32 { .. } => Some(Opcode::Mul32),
1039 Instruction::DivU32 { .. } => Some(Opcode::DivU32),
1040 Instruction::DivS32 { .. } => Some(Opcode::DivS32),
1041 Instruction::RemU32 { .. } => Some(Opcode::RemU32),
1042 Instruction::RemS32 { .. } => Some(Opcode::RemS32),
1043 Instruction::Add64 { .. } => Some(Opcode::Add64),
1044 Instruction::Sub64 { .. } => Some(Opcode::Sub64),
1045 Instruction::Mul64 { .. } => Some(Opcode::Mul64),
1046 Instruction::DivU64 { .. } => Some(Opcode::DivU64),
1047 Instruction::DivS64 { .. } => Some(Opcode::DivS64),
1048 Instruction::RemU64 { .. } => Some(Opcode::RemU64),
1049 Instruction::RemS64 { .. } => Some(Opcode::RemS64),
1050 Instruction::ShloL64 { .. } => Some(Opcode::ShloL64),
1051 Instruction::ShloR64 { .. } => Some(Opcode::ShloR64),
1052 Instruction::SharR64 { .. } => Some(Opcode::SharR64),
1053 Instruction::AddImm32 { .. } => Some(Opcode::AddImm32),
1054 Instruction::AddImm64 { .. } => Some(Opcode::AddImm64),
1055 Instruction::AndImm { .. } => Some(Opcode::AndImm),
1056 Instruction::XorImm { .. } => Some(Opcode::XorImm),
1057 Instruction::OrImm { .. } => Some(Opcode::OrImm),
1058 Instruction::MulImm32 { .. } => Some(Opcode::MulImm32),
1059 Instruction::MulImm64 { .. } => Some(Opcode::MulImm64),
1060 Instruction::ShloLImm32 { .. } => Some(Opcode::ShloLImm32),
1061 Instruction::ShloRImm32 { .. } => Some(Opcode::ShloRImm32),
1062 Instruction::SharRImm32 { .. } => Some(Opcode::SharRImm32),
1063 Instruction::ShloLImm64 { .. } => Some(Opcode::ShloLImm64),
1064 Instruction::ShloRImm64 { .. } => Some(Opcode::ShloRImm64),
1065 Instruction::SharRImm64 { .. } => Some(Opcode::SharRImm64),
1066 Instruction::NegAddImm32 { .. } => Some(Opcode::NegAddImm32),
1067 Instruction::NegAddImm64 { .. } => Some(Opcode::NegAddImm64),
1068 Instruction::SetGtUImm { .. } => Some(Opcode::SetGtUImm),
1069 Instruction::SetGtSImm { .. } => Some(Opcode::SetGtSImm),
1070 Instruction::Jump { .. } => Some(Opcode::Jump),
1071 Instruction::LoadImmJump { .. } => Some(Opcode::LoadImmJump),
1072 Instruction::JumpInd { .. } => Some(Opcode::JumpInd),
1073 Instruction::LoadIndU32 { .. } => Some(Opcode::LoadIndU32),
1074 Instruction::StoreIndU32 { .. } => Some(Opcode::StoreIndU32),
1075 Instruction::LoadIndU64 { .. } => Some(Opcode::LoadIndU64),
1076 Instruction::StoreIndU64 { .. } => Some(Opcode::StoreIndU64),
1077 Instruction::BranchNeImm { .. } => Some(Opcode::BranchNeImm),
1078 Instruction::BranchEqImm { .. } => Some(Opcode::BranchEqImm),
1079 Instruction::BranchGeSImm { .. } => Some(Opcode::BranchGeSImm),
1080 Instruction::BranchLtUImm { .. } => Some(Opcode::BranchLtUImm),
1081 Instruction::BranchLeUImm { .. } => Some(Opcode::BranchLeUImm),
1082 Instruction::BranchGeUImm { .. } => Some(Opcode::BranchGeUImm),
1083 Instruction::BranchGtUImm { .. } => Some(Opcode::BranchGtUImm),
1084 Instruction::BranchLtSImm { .. } => Some(Opcode::BranchLtSImm),
1085 Instruction::BranchLeSImm { .. } => Some(Opcode::BranchLeSImm),
1086 Instruction::BranchGtSImm { .. } => Some(Opcode::BranchGtSImm),
1087 Instruction::MoveReg { .. } => Some(Opcode::MoveReg),
1088 Instruction::BranchEq { .. } => Some(Opcode::BranchEq),
1089 Instruction::BranchNe { .. } => Some(Opcode::BranchNe),
1090 Instruction::BranchGeU { .. } => Some(Opcode::BranchGeU),
1091 Instruction::BranchLtU { .. } => Some(Opcode::BranchLtU),
1092 Instruction::BranchLtS { .. } => Some(Opcode::BranchLtS),
1093 Instruction::BranchGeS { .. } => Some(Opcode::BranchGeS),
1094 Instruction::SetLtU { .. } => Some(Opcode::SetLtU),
1095 Instruction::SetLtS { .. } => Some(Opcode::SetLtS),
1096 Instruction::CmovIz { .. } => Some(Opcode::CmovIz),
1097 Instruction::CmovNz { .. } => Some(Opcode::CmovNz),
1098 Instruction::And { .. } => Some(Opcode::And),
1099 Instruction::Xor { .. } => Some(Opcode::Xor),
1100 Instruction::Or { .. } => Some(Opcode::Or),
1101 Instruction::SetLtUImm { .. } => Some(Opcode::SetLtUImm),
1102 Instruction::SetLtSImm { .. } => Some(Opcode::SetLtSImm),
1103 Instruction::ShloL32 { .. } => Some(Opcode::ShloL32),
1104 Instruction::ShloR32 { .. } => Some(Opcode::ShloR32),
1105 Instruction::SharR32 { .. } => Some(Opcode::SharR32),
1106 Instruction::Sbrk { .. } => Some(Opcode::Sbrk),
1107 Instruction::CountSetBits64 { .. } => Some(Opcode::CountSetBits64),
1108 Instruction::CountSetBits32 { .. } => Some(Opcode::CountSetBits32),
1109 Instruction::LeadingZeroBits64 { .. } => Some(Opcode::LeadingZeroBits64),
1110 Instruction::LeadingZeroBits32 { .. } => Some(Opcode::LeadingZeroBits32),
1111 Instruction::TrailingZeroBits64 { .. } => Some(Opcode::TrailingZeroBits64),
1112 Instruction::TrailingZeroBits32 { .. } => Some(Opcode::TrailingZeroBits32),
1113 Instruction::SignExtend8 { .. } => Some(Opcode::SignExtend8),
1114 Instruction::SignExtend16 { .. } => Some(Opcode::SignExtend16),
1115 Instruction::ZeroExtend16 { .. } => Some(Opcode::ZeroExtend16),
1116 Instruction::LoadIndU8 { .. } => Some(Opcode::LoadIndU8),
1117 Instruction::LoadIndI8 { .. } => Some(Opcode::LoadIndI8),
1118 Instruction::LoadIndU16 { .. } => Some(Opcode::LoadIndU16),
1119 Instruction::LoadIndI16 { .. } => Some(Opcode::LoadIndI16),
1120 Instruction::StoreIndU8 { .. } => Some(Opcode::StoreIndU8),
1121 Instruction::StoreIndU16 { .. } => Some(Opcode::StoreIndU16),
1122 Instruction::CmovIzImm { .. } => Some(Opcode::CmovIzImm),
1123 Instruction::CmovNzImm { .. } => Some(Opcode::CmovNzImm),
1124 Instruction::StoreImmIndU8 { .. } => Some(Opcode::StoreImmIndU8),
1125 Instruction::StoreImmIndU16 { .. } => Some(Opcode::StoreImmIndU16),
1126 Instruction::StoreImmIndU32 { .. } => Some(Opcode::StoreImmIndU32),
1127 Instruction::StoreImmIndU64 { .. } => Some(Opcode::StoreImmIndU64),
1128 Instruction::Ecalli { .. } => Some(Opcode::Ecalli),
1129 Instruction::StoreImmU8 { .. } => Some(Opcode::StoreImmU8),
1130 Instruction::StoreImmU16 { .. } => Some(Opcode::StoreImmU16),
1131 Instruction::StoreImmU32 { .. } => Some(Opcode::StoreImmU32),
1132 Instruction::StoreImmU64 { .. } => Some(Opcode::StoreImmU64),
1133 Instruction::LoadU8 { .. } => Some(Opcode::LoadU8),
1134 Instruction::LoadI8 { .. } => Some(Opcode::LoadI8),
1135 Instruction::LoadU16 { .. } => Some(Opcode::LoadU16),
1136 Instruction::LoadI16 { .. } => Some(Opcode::LoadI16),
1137 Instruction::LoadU32 { .. } => Some(Opcode::LoadU32),
1138 Instruction::LoadI32 { .. } => Some(Opcode::LoadI32),
1139 Instruction::LoadU64 { .. } => Some(Opcode::LoadU64),
1140 Instruction::StoreU8 { .. } => Some(Opcode::StoreU8),
1141 Instruction::StoreU16 { .. } => Some(Opcode::StoreU16),
1142 Instruction::StoreU32 { .. } => Some(Opcode::StoreU32),
1143 Instruction::StoreU64 { .. } => Some(Opcode::StoreU64),
1144 Instruction::LoadIndI32 { .. } => Some(Opcode::LoadIndI32),
1145 Instruction::ReverseBytes { .. } => Some(Opcode::ReverseBytes),
1146 Instruction::ShloLImmAlt32 { .. } => Some(Opcode::ShloLImmAlt32),
1147 Instruction::ShloRImmAlt32 { .. } => Some(Opcode::ShloRImmAlt32),
1148 Instruction::SharRImmAlt32 { .. } => Some(Opcode::SharRImmAlt32),
1149 Instruction::ShloLImmAlt64 { .. } => Some(Opcode::ShloLImmAlt64),
1150 Instruction::ShloRImmAlt64 { .. } => Some(Opcode::ShloRImmAlt64),
1151 Instruction::SharRImmAlt64 { .. } => Some(Opcode::SharRImmAlt64),
1152 Instruction::RotRImm64 { .. } => Some(Opcode::RotRImm64),
1153 Instruction::RotRImmAlt64 { .. } => Some(Opcode::RotRImmAlt64),
1154 Instruction::RotRImm32 { .. } => Some(Opcode::RotRImm32),
1155 Instruction::RotRImmAlt32 { .. } => Some(Opcode::RotRImmAlt32),
1156 Instruction::LoadImmJumpInd { .. } => Some(Opcode::LoadImmJumpInd),
1157 Instruction::MulUpperSS { .. } => Some(Opcode::MulUpperSS),
1158 Instruction::MulUpperUU { .. } => Some(Opcode::MulUpperUU),
1159 Instruction::MulUpperSU { .. } => Some(Opcode::MulUpperSU),
1160 Instruction::RotL64 { .. } => Some(Opcode::RotL64),
1161 Instruction::RotL32 { .. } => Some(Opcode::RotL32),
1162 Instruction::RotR64 { .. } => Some(Opcode::RotR64),
1163 Instruction::RotR32 { .. } => Some(Opcode::RotR32),
1164 Instruction::AndInv { .. } => Some(Opcode::AndInv),
1165 Instruction::OrInv { .. } => Some(Opcode::OrInv),
1166 Instruction::Xnor { .. } => Some(Opcode::Xnor),
1167 Instruction::Max { .. } => Some(Opcode::Max),
1168 Instruction::MaxU { .. } => Some(Opcode::MaxU),
1169 Instruction::Min { .. } => Some(Opcode::Min),
1170 Instruction::MinU { .. } => Some(Opcode::MinU),
1171 Instruction::Unknown { .. } => None,
1172 }
1173 }
1174}
1175
1176#[cfg(test)]
1177mod tests {
1178 use super::*;
1179
1180 #[test]
1181 fn test_wat_to_wasm_simple() {
1182 let wat = r#"
1183 (module
1184 (func (export "main") (result i32)
1185 i32.const 42
1186 )
1187 )
1188 "#;
1189 let wasm = wat_to_wasm(wat).expect("Failed to parse WAT");
1190 assert!(!wasm.is_empty());
1191 assert_eq!(&wasm[0..4], &[0x00, 0x61, 0x73, 0x6d]);
1193 }
1194
1195 #[test]
1196 fn test_instruction_pattern_matching() {
1197 let instr = Instruction::Add32 {
1198 dst: 5,
1199 src1: 2,
1200 src2: 3,
1201 };
1202
1203 let pattern_exact = InstructionPattern::Add32 {
1205 dst: Pat::Exact(5),
1206 src1: Pat::Exact(2),
1207 src2: Pat::Exact(3),
1208 };
1209 assert!(pattern_exact.matches(&instr));
1210
1211 let pattern_wildcard = InstructionPattern::Add32 {
1213 dst: Pat::Any,
1214 src1: Pat::Any,
1215 src2: Pat::Any,
1216 };
1217 assert!(pattern_wildcard.matches(&instr));
1218
1219 let pattern_wrong = InstructionPattern::Sub32 {
1221 dst: Pat::Any,
1222 src1: Pat::Any,
1223 src2: Pat::Any,
1224 };
1225 assert!(!pattern_wrong.matches(&instr));
1226 }
1227
1228 #[test]
1229 fn test_find_pattern() {
1230 let instructions = vec![
1231 Instruction::LoadImm { reg: 2, value: 5 },
1232 Instruction::LoadImm { reg: 3, value: 7 },
1233 Instruction::Add32 {
1234 dst: 4,
1235 src1: 2,
1236 src2: 3,
1237 },
1238 Instruction::Trap,
1239 ];
1240
1241 let pattern = vec![
1242 InstructionPattern::LoadImm {
1243 reg: Pat::Any,
1244 value: Pat::Any,
1245 },
1246 InstructionPattern::Add32 {
1247 dst: Pat::Any,
1248 src1: Pat::Exact(2),
1249 src2: Pat::Any,
1250 },
1251 ];
1252
1253 assert_eq!(find_pattern(&instructions, &pattern), Some(1));
1254 }
1255
1256 #[test]
1257 fn test_count_opcode() {
1258 let instructions = vec![
1259 Instruction::LoadImm { reg: 2, value: 5 },
1260 Instruction::LoadImm { reg: 3, value: 7 },
1261 Instruction::Add32 {
1262 dst: 4,
1263 src1: 2,
1264 src2: 3,
1265 },
1266 Instruction::Trap,
1267 ];
1268
1269 assert_eq!(count_opcode(&instructions, Opcode::LoadImm), 2);
1270 assert_eq!(count_opcode(&instructions, Opcode::Add32), 1);
1271 assert_eq!(count_opcode(&instructions, Opcode::Trap), 1);
1272 assert_eq!(count_opcode(&instructions, Opcode::Sub32), 0);
1273 }
1274
1275 #[test]
1276 fn test_pat_predicate() {
1277 let is_positive = |v: &i32| *v > 0;
1278 let pat = Pat::Predicate(is_positive);
1279
1280 assert!(pat.matches(&5));
1281 assert!(!pat.matches(&-1));
1282 assert!(!pat.matches(&0));
1283 }
1284
1285 #[test]
1286 fn test_compile_simple_wat() {
1287 let wat = r#"
1288 (module
1289 (func (export "main") (param i32 i32) (result i32)
1290 local.get 0
1291 local.get 1
1292 i32.add
1293 )
1294 )
1295 "#;
1296
1297 let program = compile_wat(wat).expect("Failed to compile");
1298 let code = program.code();
1299 let instructions = code.instructions();
1300
1301 assert!(!instructions.is_empty());
1303
1304 assert!(has_opcode(instructions, Opcode::Add32));
1306 }
1307}