1use std::collections::{BTreeSet, HashMap, HashSet};
2
3use anyhow::Result;
4
5use crate::vmcodes::vm;
6
7use super::cfg::{BasicBlock, Cfg};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
10pub enum Var {
11 Reg(i32),
12 Flag,
13 Exception,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub struct VarId {
18 pub var: Var,
19 pub ver: u32,
20}
21
22#[derive(Debug, Clone)]
23pub struct Phi {
24 pub result: VarId,
25 pub var: Var,
26 pub args: Vec<(usize, VarId)>, }
28
29#[derive(Debug, Clone)]
30pub struct SsaInsn {
31 pub pc: usize,
32 pub op: i32,
33 pub mnemonic: &'static str,
34 pub raw_ops: Vec<i32>,
35 pub uses: Vec<VarId>,
36 pub defs: Vec<VarId>,
37}
38
39#[derive(Debug, Clone)]
40pub struct SsaBlock {
41 pub id: usize,
42 pub start_pc: usize,
43 pub pred: Vec<usize>,
44 pub succ: Vec<usize>,
45 pub phi: Vec<Phi>,
46 pub insns: Vec<SsaInsn>,
47}
48
49#[derive(Debug, Clone)]
50pub struct SsaProgram {
51 pub obj_index: usize,
52 pub blocks: Vec<SsaBlock>,
53 pub entry_block: usize,
54}
55
56impl SsaProgram {
57 pub fn from_cfg(cfg: &Cfg) -> Result<Self> {
58 let dom = DomInfo::compute(&cfg.blocks, cfg.entry_block);
59 let mut phi_sites = place_phi(&cfg.blocks, &dom);
60 let mut ssa_blocks = cfg
61 .blocks
62 .iter()
63 .map(|b| SsaBlock {
64 id: b.id,
65 start_pc: b.start_pc,
66 pred: b.pred.clone(),
67 succ: b.succ.clone(),
68 phi: Vec::new(),
69 insns: Vec::new(),
70 })
71 .collect::<Vec<_>>();
72
73 for (bid, vars) in phi_sites.drain() {
75 for v in vars {
76 ssa_blocks[bid].phi.push(Phi {
77 result: VarId { var: v, ver: 0 }, var: v,
79 args: Vec::new(),
80 });
81 }
82 ssa_blocks[bid].phi.sort_by_key(|p| p.var);
83 }
84
85 rename(cfg, &dom, &mut ssa_blocks);
86 Ok(Self {
87 obj_index: cfg.obj_index,
88 blocks: ssa_blocks,
89 entry_block: cfg.entry_block,
90 })
91 }
92
93 pub fn dump(&self) -> String {
94 let mut out = String::new();
95 for b in &self.blocks {
96 out.push_str(&format!(
97 "-- bb{} @{} (pred={:?}, succ={:?})\n",
98 b.id, b.start_pc, b.pred, b.succ
99 ));
100 for p in &b.phi {
101 out.push_str(&format!(
102 " {} = phi {}",
103 fmt_varid(p.result),
104 fmt_var(p.var)
105 ));
106 if !p.args.is_empty() {
107 out.push_str(" [");
108 for (i, (pred, v)) in p.args.iter().enumerate() {
109 if i != 0 {
110 out.push_str(", ");
111 }
112 out.push_str(&format!("bb{}: {}", pred, fmt_varid(*v)));
113 }
114 out.push(']');
115 }
116 out.push('\n');
117 }
118 for insn in &b.insns {
119 out.push_str(&format!(" {:04} {}", insn.pc, insn.mnemonic));
120 if !insn.raw_ops.is_empty() {
121 out.push(' ');
122 for (i, w) in insn.raw_ops.iter().enumerate() {
123 if i != 0 {
124 out.push_str(", ");
125 }
126 out.push_str(&format!("{}", w));
127 }
128 }
129 if !insn.defs.is_empty() {
130 out.push_str("\t; def=");
131 for (i, d) in insn.defs.iter().enumerate() {
132 if i != 0 {
133 out.push_str(",");
134 }
135 out.push_str(&fmt_varid(*d));
136 }
137 }
138 if !insn.uses.is_empty() {
139 out.push_str("\t; use=");
140 for (i, u) in insn.uses.iter().enumerate() {
141 if i != 0 {
142 out.push_str(",");
143 }
144 out.push_str(&fmt_varid(*u));
145 }
146 }
147 out.push('\n');
148 }
149 }
150 out
151 }
152}
153
154fn fmt_var(v: Var) -> &'static str {
155 match v {
156 Var::Reg(_) => "reg",
157 Var::Flag => "flag",
158 Var::Exception => "exception",
159 }
160}
161
162fn fmt_varid(id: VarId) -> String {
163 match id.var {
164 Var::Reg(r) => format!("r{}#{}", r, id.ver),
165 Var::Flag => format!("flag#{}", id.ver),
166 Var::Exception => format!("exc#{}", id.ver),
167 }
168}
169
170#[derive(Debug, Clone)]
171struct DomInfo {
172 idom: Vec<usize>,
173 children: Vec<Vec<usize>>,
174 df: Vec<HashSet<usize>>,
175}
176
177impl DomInfo {
178 fn compute(blocks: &[BasicBlock], entry: usize) -> Self {
179 let n = blocks.len();
180 let mut dom: Vec<HashSet<usize>> = vec![HashSet::new(); n];
181 for i in 0..n {
182 if i == entry {
183 dom[i].insert(i);
184 } else {
185 dom[i] = (0..n).collect();
186 }
187 }
188
189 let mut changed = true;
190 while changed {
191 changed = false;
192 for b in 0..n {
193 if b == entry {
194 continue;
195 }
196 let preds = &blocks[b].pred;
197 if preds.is_empty() {
198 continue;
199 }
200 let mut new_dom = dom[preds[0]].clone();
201 for &p in &preds[1..] {
202 new_dom = new_dom
203 .intersection(&dom[p])
204 .copied()
205 .collect::<HashSet<_>>();
206 }
207 new_dom.insert(b);
208 if new_dom != dom[b] {
209 dom[b] = new_dom;
210 changed = true;
211 }
212 }
213 }
214
215 let mut idom = vec![entry; n];
217 for b in 0..n {
218 if b == entry {
219 idom[b] = entry;
220 continue;
221 }
222 let mut best = entry;
223 let mut best_len = 0usize;
224 for &c in dom[b].iter() {
225 if c == b {
226 continue;
227 }
228 let l = dom[c].len();
229 if l > best_len {
230 best = c;
231 best_len = l;
232 }
233 }
234 idom[b] = best;
235 }
236
237 let mut children = vec![Vec::new(); n];
239 for b in 0..n {
240 if b == entry {
241 continue;
242 }
243 children[idom[b]].push(b);
244 }
245
246 let mut df: Vec<HashSet<usize>> = vec![HashSet::new(); n];
248 for b in 0..n {
249 for &s in &blocks[b].succ {
250 if idom[s] != b {
251 df[b].insert(s);
252 }
253 }
254 }
255 let mut order = Vec::new();
257 fn post(u: usize, ch: &[Vec<usize>], out: &mut Vec<usize>) {
258 for &v in &ch[u] {
259 post(v, ch, out);
260 }
261 out.push(u);
262 }
263 post(entry, &children, &mut order);
264 for &b in &order {
265 for &c in &children[b] {
266 let cdf: Vec<usize> = df[c].iter().copied().collect();
267 for w in cdf {
268 if idom[w] != b {
269 df[b].insert(w);
270 }
271 }
272 }
273 }
274
275 Self { idom, children, df }
276 }
277}
278
279fn collect_vars(blocks: &[BasicBlock]) -> BTreeSet<Var> {
280 let mut vars = BTreeSet::new();
281 vars.insert(Var::Flag);
282
283 for b in blocks {
284 for insn in &b.insns {
285 let (defs, uses) = effects_of(insn.op, insn.words.as_slice());
286 for r in defs {
287 vars.insert(r);
288 }
289 for r in uses {
290 vars.insert(r);
291 }
292 }
293 }
294 vars
295}
296
297fn place_phi(blocks: &[BasicBlock], dom: &DomInfo) -> HashMap<usize, BTreeSet<Var>> {
298 let vars = collect_vars(blocks);
299 let mut def_sites: HashMap<Var, HashSet<usize>> = HashMap::new();
300 for v in vars.iter().copied() {
301 def_sites.insert(v, HashSet::new());
302 }
303 for b in blocks {
304 for insn in &b.insns {
305 let (defs, _uses) = effects_of(insn.op, insn.words.as_slice());
306 for v in defs {
307 def_sites.entry(v).or_default().insert(b.id);
308 }
309 }
310 }
311
312 let mut phi: HashMap<usize, BTreeSet<Var>> = HashMap::new();
313 for (v, defs) in def_sites {
314 let mut work: Vec<usize> = defs.iter().copied().collect();
315 let mut has_phi: HashSet<usize> = HashSet::new();
316 while let Some(n) = work.pop() {
317 for &y in &dom.df[n] {
318 if !has_phi.contains(&y) {
319 has_phi.insert(y);
320 phi.entry(y).or_default().insert(v);
321 if !defs.contains(&y) {
322 work.push(y);
323 }
324 }
325 }
326 }
327 }
328 phi
329}
330
331fn rename(cfg: &Cfg, dom: &DomInfo, blocks: &mut [SsaBlock]) {
332 let mut counters: HashMap<Var, u32> = HashMap::new();
333 let mut stacks: HashMap<Var, Vec<VarId>> = HashMap::new();
334
335 for b in &cfg.blocks {
338 for insn in &b.insns {
339 let (defs, uses) = effects_of(insn.op, insn.words.as_slice());
340 for v in defs.into_iter().chain(uses.into_iter()) {
341 counters.entry(v).or_insert(0);
342 stacks.entry(v).or_default();
343 }
344 }
345 }
346 counters.entry(Var::Flag).or_insert(0);
347 stacks.entry(Var::Flag).or_default();
348 counters.entry(Var::Exception).or_insert(0);
350 stacks.entry(Var::Exception).or_default();
351
352 for (&v, st) in stacks.iter_mut() {
353 st.push(VarId { var: v, ver: 0 });
354 }
355
356 fn new_ver(
357 v: Var,
358 counters: &mut HashMap<Var, u32>,
359 stacks: &mut HashMap<Var, Vec<VarId>>,
360 ) -> VarId {
361 let c = counters.entry(v).or_insert(0);
362 *c += 1;
363 let id = VarId { var: v, ver: *c };
364 stacks.entry(v).or_default().push(id);
365 id
366 }
367
368 fn cur(v: Var, stacks: &HashMap<Var, Vec<VarId>>) -> VarId {
369 stacks
370 .get(&v)
371 .and_then(|s| s.last().copied())
372 .unwrap_or(VarId { var: v, ver: 0 })
373 }
374
375 fn dfs(
376 bid: usize,
377 cfg: &Cfg,
378 dom: &DomInfo,
379 blocks: &mut [SsaBlock],
380 counters: &mut HashMap<Var, u32>,
381 stacks: &mut HashMap<Var, Vec<VarId>>,
382 ) {
383 let mut pushed: Vec<Var> = Vec::new();
384
385 for p in blocks[bid].phi.iter_mut() {
387 let id = new_ver(p.var, counters, stacks);
388 p.result = id;
389 pushed.push(p.var);
390 }
391
392 if let Some(&ex_reg) = cfg.catch_sites.get(&blocks[bid].start_pc) {
394 let dst = Var::Reg(ex_reg);
395 let src = Var::Exception;
396 let dst_id = new_ver(dst, counters, stacks);
397 pushed.push(dst);
398 let src_id = cur(src, stacks);
399 blocks[bid].insns.push(SsaInsn {
400 pc: blocks[bid].start_pc,
401 op: -1,
402 mnemonic: "EXCDEF",
403 raw_ops: vec![ex_reg],
404 uses: vec![src_id],
405 defs: vec![dst_id],
406 });
407 }
408
409 for insn in &cfg.blocks[bid].insns {
411 let (defs, uses) = effects_of(insn.op, insn.words.as_slice());
412 let mut use_ids = Vec::new();
413 for v in uses {
414 use_ids.push(cur(v, stacks));
415 }
416
417 let mut def_ids = Vec::new();
418 for v in defs {
419 let id = new_ver(v, counters, stacks);
420 def_ids.push(id);
421 pushed.push(v);
422 }
423
424 blocks[bid].insns.push(SsaInsn {
425 pc: insn.pc,
426 op: insn.op,
427 mnemonic: insn.mnemonic(),
428 raw_ops: insn.operands().to_vec(),
429 uses: use_ids,
430 defs: def_ids,
431 });
432 }
433
434 let end_state: HashMap<Var, VarId> = stacks
436 .iter()
437 .map(|(&v, s)| (v, *s.last().unwrap()))
438 .collect();
439 for &succ in blocks[bid].succ.iter() {
440 for p in blocks[succ].phi.iter_mut() {
441 let val = *end_state
442 .get(&p.var)
443 .unwrap_or(&VarId { var: p.var, ver: 0 });
444 p.args.push((bid, val));
445 }
446 }
447
448 for &c in &dom.children[bid] {
450 dfs(c, cfg, dom, blocks, counters, stacks);
451 }
452
453 for v in pushed.into_iter().rev() {
455 if let Some(st) = stacks.get_mut(&v) {
456 let _ = st.pop();
457 }
458 }
459 }
460
461 dfs(
462 cfg.entry_block,
463 cfg,
464 dom,
465 blocks,
466 &mut counters,
467 &mut stacks,
468 );
469}
470
471fn effects_of(op: i32, words: &[i32]) -> (Vec<Var>, Vec<Var>) {
472 if words.is_empty() {
475 return (Vec::new(), Vec::new());
476 }
477 let ops = &words[1..];
478
479 let r = |i: usize| -> Var { Var::Reg(ops.get(i).copied().unwrap_or(0)) };
481 let def_r0 = |reg: i32| -> Vec<Var> {
482 if reg == 0 {
483 Vec::new()
484 } else {
485 vec![Var::Reg(reg)]
486 }
487 };
488
489 if matches!(
492 op,
493 vm::VM_CEQ
494 | vm::VM_CDEQ
495 | vm::VM_CLT
496 | vm::VM_CGT
497 | vm::VM_CHKINS
498 | vm::VM_TT
499 | vm::VM_TF
500 | vm::VM_NF
501 ) {
502 let mut uses = Vec::new();
503 match op {
504 x if x == vm::VM_TT || x == vm::VM_TF => {
505 uses.push(r(0));
506 }
507 x if x == vm::VM_CEQ
508 || x == vm::VM_CDEQ
509 || x == vm::VM_CLT
510 || x == vm::VM_CGT
511 || x == vm::VM_CHKINS =>
512 {
513 uses.push(r(0));
514 uses.push(r(1));
515 }
516 x if x == vm::VM_NF => {
517 uses.push(Var::Flag);
518 }
519 _ => {}
520 }
521 let defs = vec![Var::Flag];
522 return (defs, uses);
523 }
524
525 if op == vm::VM_JF || op == vm::VM_JNF {
527 return (Vec::new(), vec![Var::Flag]);
528 }
529
530 if op == vm::VM_SETF || op == vm::VM_SETNF {
532 let dst = ops.get(0).copied().unwrap_or(0);
533 return (vec![Var::Reg(dst)], Vec::new());
534 }
535
536 if op == vm::VM_CONST {
538 let dst = ops.get(0).copied().unwrap_or(0);
539 return (def_r0(dst), Vec::new());
540 }
541
542 if op == vm::VM_CP {
544 let dst = ops.get(0).copied().unwrap_or(0);
545 let src = ops.get(1).copied().unwrap_or(0);
546 return (def_r0(dst), vec![Var::Reg(src)]);
547 }
548
549 if op == vm::VM_CL {
551 let dst = ops.get(0).copied().unwrap_or(0);
552 return (def_r0(dst), Vec::new());
553 }
554
555 if op == vm::VM_CCL {
557 let r0 = ops.get(0).copied().unwrap_or(0);
558 let cnt = ops.get(1).copied().unwrap_or(0);
559 let mut defs = Vec::new();
560 for k in 0..cnt {
561 let rr = r0 + k;
562 if rr != 0 {
563 defs.push(Var::Reg(rr));
564 }
565 }
566 return (defs, Vec::new());
567 }
568
569 if op == vm::VM_GLOBAL {
571 let dst = ops.get(0).copied().unwrap_or(0);
572 return (def_r0(dst), Vec::new());
573 }
574
575 if op == vm::VM_SRV {
577 return (Vec::new(), vec![r(0)]);
578 }
579
580 if matches!(
583 op,
584 vm::VM_RET
585 | vm::VM_JMP
586 | vm::VM_NOP
587 | vm::VM_REGMEMBER
588 | vm::VM_DEBUGGER
589 | vm::VM_EXTRY
590 | vm::VM_ENTRY
591 ) {
592 return (Vec::new(), Vec::new());
593 }
594
595 if op == vm::VM_THROW {
597 return (Vec::new(), vec![r(0)]);
598 }
599
600 if op == vm::VM_SETP {
602 return (Vec::new(), vec![r(0), r(1)]);
604 }
605 if op == vm::VM_GETP {
606 let dst = ops.get(0).copied().unwrap_or(0);
608 return (def_r0(dst), vec![r(1)]);
609 }
610
611 if op == vm::VM_GPD || op == vm::VM_GPDS {
613 let dst = ops.get(0).copied().unwrap_or(0);
614 let obj = ops.get(1).copied().unwrap_or(0);
615 return (def_r0(dst), vec![Var::Reg(obj)]);
616 }
617 if op == vm::VM_GPI || op == vm::VM_GPIS {
619 let dst = ops.get(0).copied().unwrap_or(0);
620 let obj = ops.get(1).copied().unwrap_or(0);
621 let key = ops.get(2).copied().unwrap_or(0);
622 return (def_r0(dst), vec![Var::Reg(obj), Var::Reg(key)]);
623 }
624 if matches!(op, vm::VM_SPD | vm::VM_SPDE | vm::VM_SPDEH | vm::VM_SPDS) {
626 let obj = ops.get(0).copied().unwrap_or(0);
627 let val = ops.get(2).copied().unwrap_or(0);
628 return (Vec::new(), vec![Var::Reg(obj), Var::Reg(val)]);
629 }
630 if op == vm::VM_SPI || op == vm::VM_SPIE || op == vm::VM_SPIS {
632 let obj = ops.get(0).copied().unwrap_or(0);
633 let key = ops.get(1).copied().unwrap_or(0);
634 let val = ops.get(2).copied().unwrap_or(0);
635 return (
636 Vec::new(),
637 vec![Var::Reg(obj), Var::Reg(key), Var::Reg(val)],
638 );
639 }
640
641 if op == vm::VM_DELD {
644 let dst = ops.get(0).copied().unwrap_or(0);
645 let obj = ops.get(1).copied().unwrap_or(0);
646 return (vec![Var::Reg(dst)], vec![Var::Reg(obj)]);
647 }
648
649 if op == vm::VM_DELI {
651 let dst = ops.get(0).copied().unwrap_or(0);
652 let obj = ops.get(1).copied().unwrap_or(0);
653 let key = ops.get(2).copied().unwrap_or(0);
654 return (vec![Var::Reg(dst)], vec![Var::Reg(obj), Var::Reg(key)]);
655 }
656
657 if op == vm::VM_TYPEOFD {
658 let dst = ops.get(0).copied().unwrap_or(0);
659 let base = ops.get(1).copied().unwrap_or(0);
660 return (def_r0(dst), vec![Var::Reg(base)]);
661 }
662 if op == vm::VM_TYPEOFI {
663 let dst = ops.get(0).copied().unwrap_or(0);
664 let base = ops.get(1).copied().unwrap_or(0);
665 let key = ops.get(2).copied().unwrap_or(0);
666 return (def_r0(dst), vec![Var::Reg(base), Var::Reg(key)]);
667 }
668
669 if (vm::VM_INC..=vm::VM_INCP).contains(&op) {
671 return effects_op1_prop(ops, vm::VM_INC, op, true);
672 }
673 if (vm::VM_DEC..=vm::VM_DECP).contains(&op) {
674 return effects_op1_prop(ops, vm::VM_DEC, op, true);
675 }
676
677 if is_op2_prop(op) {
679 return effects_op2_prop(ops, op);
680 }
681
682 if op == vm::VM_CALL || op == vm::VM_NEW {
684 let dst = ops.get(0).copied().unwrap_or(0);
685 let callee = ops.get(1).copied().unwrap_or(0);
686 let argc = ops.get(2).copied().unwrap_or(0);
687 let mut uses = vec![Var::Reg(callee)];
688 uses.extend(call_arg_regs(argc, ops, 3));
689 return (def_r0(dst), uses);
690 }
691 if op == vm::VM_CALLD {
694 let dst = ops.get(0).copied().unwrap_or(0);
695 let obj = ops.get(1).copied().unwrap_or(0);
696 let argc = ops.get(3).copied().unwrap_or(0);
698 let mut uses = vec![Var::Reg(obj)];
699 uses.extend(call_arg_regs(argc, ops, 4));
700 return (def_r0(dst), uses);
701 }
702 if op == vm::VM_CALLI {
705 let dst = ops.get(0).copied().unwrap_or(0);
706 let obj = ops.get(1).copied().unwrap_or(0);
707 let member = ops.get(2).copied().unwrap_or(0);
708 let argc = ops.get(3).copied().unwrap_or(0);
709 let mut uses = vec![Var::Reg(obj), Var::Reg(member)];
710 uses.extend(call_arg_regs(argc, ops, 4));
711 return (def_r0(dst), uses);
712 }
713
714 if ops.len() == 1 {
716 let dst = ops.get(0).copied().unwrap_or(0);
717 if dst == 0 {
718 return (Vec::new(), Vec::new());
719 }
720 return (vec![Var::Reg(dst)], vec![Var::Reg(dst)]);
721 }
722
723 if ops.len() == 2 {
725 let a = ops.get(0).copied().unwrap_or(0);
726 let b = ops.get(1).copied().unwrap_or(0);
727 let defs = def_r0(a);
728 let mut uses = Vec::new();
729 uses.push(Var::Reg(a));
730 uses.push(Var::Reg(b));
731 return (defs, uses);
732 }
733
734 (Vec::new(), Vec::new())
736}
737
738fn call_arg_regs(argc: i32, ops: &[i32], start: usize) -> Vec<Var> {
739 let mut uses = Vec::new();
742 if argc == -1 {
743 return uses;
744 }
745 if argc == -2 {
746 let num = ops.get(start).copied().unwrap_or(0) as usize;
747 for j in 0..num {
749 let ty = ops.get(start + 1 + j * 2).copied().unwrap_or(0);
750 let val = ops.get(start + 1 + j * 2 + 1).copied().unwrap_or(0);
751 if ty == 0 || ty == 1 {
753 uses.push(Var::Reg(val));
754 }
755 }
756 return uses;
757 }
758
759 let n = argc.max(0) as usize;
760 for j in 0..n {
761 if let Some(&r) = ops.get(start + j) {
762 uses.push(Var::Reg(r));
763 }
764 }
765 uses
766}
767
768fn is_op2_prop(op: i32) -> bool {
769 matches!(
770 op,
771 _ if (vm::VM_LOR..=vm::VM_LOR + 3).contains(&op)
772 || (vm::VM_LAND..=vm::VM_LAND + 3).contains(&op)
773 || (vm::VM_BOR..=vm::VM_BOR + 3).contains(&op)
774 || (vm::VM_BXOR..=vm::VM_BXOR + 3).contains(&op)
775 || (vm::VM_BAND..=vm::VM_BAND + 3).contains(&op)
776 || (vm::VM_SAR..=vm::VM_SAR + 3).contains(&op)
777 || (vm::VM_SAL..=vm::VM_SAL + 3).contains(&op)
778 || (vm::VM_SR..=vm::VM_SR + 3).contains(&op)
779 || (vm::VM_ADD..=vm::VM_ADD + 3).contains(&op)
780 || (vm::VM_SUB..=vm::VM_SUB + 3).contains(&op)
781 || (vm::VM_MOD..=vm::VM_MOD + 3).contains(&op)
782 || (vm::VM_DIV..=vm::VM_DIV + 3).contains(&op)
783 || (vm::VM_IDIV..=vm::VM_IDIV + 3).contains(&op)
784 || (vm::VM_MUL..=vm::VM_MUL + 3).contains(&op)
785 )
786}
787
788fn op2_base(op: i32) -> i32 {
789 for base in [
790 vm::VM_LOR,
791 vm::VM_LAND,
792 vm::VM_BOR,
793 vm::VM_BXOR,
794 vm::VM_BAND,
795 vm::VM_SAR,
796 vm::VM_SAL,
797 vm::VM_SR,
798 vm::VM_ADD,
799 vm::VM_SUB,
800 vm::VM_MOD,
801 vm::VM_DIV,
802 vm::VM_IDIV,
803 vm::VM_MUL,
804 ] {
805 if (base..=base + 3).contains(&op) {
806 return base;
807 }
808 }
809 op
810}
811
812fn effects_op2_prop(ops: &[i32], op: i32) -> (Vec<Var>, Vec<Var>) {
813 let base = op2_base(op);
814 match op - base {
815 0 => {
816 let a = ops.get(0).copied().unwrap_or(0);
818 let b = ops.get(1).copied().unwrap_or(0);
819 let defs = if a == 0 {
820 Vec::new()
821 } else {
822 vec![Var::Reg(a)]
823 };
824 (defs, vec![Var::Reg(a), Var::Reg(b)])
825 }
826 1 => {
827 let res = ops.get(0).copied().unwrap_or(0);
829 let obj = ops.get(1).copied().unwrap_or(0);
830 let rhs = ops.get(3).copied().unwrap_or(0);
831 (
832 if res == 0 {
833 Vec::new()
834 } else {
835 vec![Var::Reg(res)]
836 },
837 vec![Var::Reg(obj), Var::Reg(rhs)],
838 )
839 }
840 2 => {
841 let res = ops.get(0).copied().unwrap_or(0);
843 let obj = ops.get(1).copied().unwrap_or(0);
844 let key = ops.get(2).copied().unwrap_or(0);
845 let rhs = ops.get(3).copied().unwrap_or(0);
846 (
847 if res == 0 {
848 Vec::new()
849 } else {
850 vec![Var::Reg(res)]
851 },
852 vec![Var::Reg(obj), Var::Reg(key), Var::Reg(rhs)],
853 )
854 }
855 3 => {
856 let res = ops.get(0).copied().unwrap_or(0);
858 let obj = ops.get(1).copied().unwrap_or(0);
859 let rhs = ops.get(2).copied().unwrap_or(0);
860 (
861 if res == 0 {
862 Vec::new()
863 } else {
864 vec![Var::Reg(res)]
865 },
866 vec![Var::Reg(obj), Var::Reg(rhs)],
867 )
868 }
869 _ => (Vec::new(), Vec::new()),
870 }
871}
872
873fn effects_op1_prop(ops: &[i32], base: i32, op: i32, rmw: bool) -> (Vec<Var>, Vec<Var>) {
874 match op - base {
875 0 => {
876 let r0 = ops.get(0).copied().unwrap_or(0);
878 if r0 == 0 {
879 return (Vec::new(), Vec::new());
880 }
881 if rmw {
882 (vec![Var::Reg(r0)], vec![Var::Reg(r0)])
883 } else {
884 (vec![Var::Reg(r0)], Vec::new())
885 }
886 }
887 1 => {
888 let res = ops.get(0).copied().unwrap_or(0);
890 let obj = ops.get(1).copied().unwrap_or(0);
891 (
892 if res == 0 {
893 Vec::new()
894 } else {
895 vec![Var::Reg(res)]
896 },
897 vec![Var::Reg(obj)],
898 )
899 }
900 2 => {
901 let res = ops.get(0).copied().unwrap_or(0);
903 let obj = ops.get(1).copied().unwrap_or(0);
904 let key = ops.get(2).copied().unwrap_or(0);
905 (
906 if res == 0 {
907 Vec::new()
908 } else {
909 vec![Var::Reg(res)]
910 },
911 vec![Var::Reg(obj), Var::Reg(key)],
912 )
913 }
914 3 => {
915 let res = ops.get(0).copied().unwrap_or(0);
917 let obj = ops.get(1).copied().unwrap_or(0);
918 (
919 if res == 0 {
920 Vec::new()
921 } else {
922 vec![Var::Reg(res)]
923 },
924 vec![Var::Reg(obj)],
925 )
926 }
927 _ => (Vec::new(), Vec::new()),
928 }
929}