1use majit_ir::{InputArg, Op, OpCode, OpRef};
9
10pub type History = TreeLoop;
16
17#[derive(Clone, Debug)]
19pub struct TreeLoop {
20 pub inputargs: Vec<InputArg>,
22 pub ops: Vec<Op>,
24 pub snapshots: Vec<crate::recorder::Snapshot>,
27}
28
29impl TreeLoop {
30 pub fn new(inputargs: Vec<InputArg>, ops: Vec<Op>) -> Self {
32 TreeLoop {
33 inputargs,
34 ops,
35 snapshots: Vec::new(),
36 }
37 }
38
39 pub fn with_snapshots(
41 inputargs: Vec<InputArg>,
42 ops: Vec<Op>,
43 snapshots: Vec<crate::recorder::Snapshot>,
44 ) -> Self {
45 TreeLoop {
46 inputargs,
47 ops,
48 snapshots,
49 }
50 }
51
52 pub fn num_ops(&self) -> usize {
54 self.ops.len()
55 }
56
57 pub fn num_inputargs(&self) -> usize {
59 self.inputargs.len()
60 }
61
62 pub fn is_loop(&self) -> bool {
64 self.ops.last().is_some_and(|op| op.opcode == OpCode::Jump)
65 }
66
67 pub fn is_finished(&self) -> bool {
69 self.ops
70 .last()
71 .is_some_and(|op| op.opcode == OpCode::Finish)
72 }
73
74 pub fn get_op(&self, opref: OpRef) -> Option<&Op> {
76 self.ops.get(opref.0 as usize)
77 }
78
79 pub fn iter_ops(&self) -> impl Iterator<Item = &Op> {
81 self.ops.iter()
82 }
83
84 pub fn iter_guards(&self) -> impl Iterator<Item = &Op> {
86 self.ops.iter().filter(|op| op.opcode.is_guard())
87 }
88
89 pub fn num_guards(&self) -> usize {
91 self.ops.iter().filter(|op| op.opcode.is_guard()).count()
92 }
93
94 pub fn get_final_op(&self) -> Option<&Op> {
96 self.ops.last().filter(|op| op.opcode.is_final())
97 }
98
99 pub fn find_label(&self) -> Option<usize> {
101 self.ops.iter().position(|op| op.opcode == OpCode::Label)
102 }
103
104 pub fn split_at_label(&self) -> (&[Op], &[Op]) {
107 match self.find_label() {
108 Some(pos) => (&self.ops[..pos], &self.ops[pos..]),
109 None => (&self.ops, &[]),
110 }
111 }
112
113 pub fn inputarg_types(&self) -> Vec<majit_ir::Type> {
115 self.inputargs.iter().map(|ia| ia.tp).collect()
116 }
117
118 pub fn get_iter(&self) -> crate::opencoder::TraceIterator<'_> {
120 crate::opencoder::TraceIterator::new(&self.ops)
121 }
122
123 pub fn check_consistency(&self) -> bool {
126 if self.ops.is_empty() {
127 return true;
128 }
129 let last = self.ops.last().unwrap();
131 if !last.opcode.is_final() {
132 return false;
133 }
134 let mut seen = std::collections::HashSet::new();
136 for op in &self.ops {
137 if !op.pos.is_none() {
138 if !seen.insert(op.pos) {
139 return false; }
141 }
142 }
143 true
144 }
145
146 pub fn free_vars(&self) -> Vec<OpRef> {
149 let defined: std::collections::HashSet<OpRef> = self
150 .ops
151 .iter()
152 .filter(|op| !op.pos.is_none())
153 .map(|op| op.pos)
154 .collect();
155 let mut free = std::collections::HashSet::new();
156 for op in &self.ops {
157 for arg in &op.args {
158 if !arg.is_none() && !defined.contains(arg) {
159 free.insert(*arg);
160 }
161 }
162 }
163 let mut result: Vec<OpRef> = free.into_iter().collect();
164 result.sort_by_key(|r| r.0);
165 result
166 }
167
168 pub fn count_by_category(&self) -> (usize, usize, usize, usize) {
170 let mut guards = 0;
171 let mut pure = 0;
172 let mut calls = 0;
173 let mut other = 0;
174 for op in &self.ops {
175 if op.opcode.is_guard() {
176 guards += 1;
177 } else if op.opcode.is_always_pure() {
178 pure += 1;
179 } else if op.opcode.is_call() {
180 calls += 1;
181 } else {
182 other += 1;
183 }
184 }
185 (guards, pure, calls, other)
186 }
187
188 pub fn cut_trace_from(
194 &self,
195 start: crate::recorder::TracePosition,
196 original_boxes: &[OpRef],
197 original_box_types: &[majit_ir::Type],
198 ) -> TreeLoop {
199 use std::collections::{HashMap, HashSet, VecDeque};
200
201 let num_original_inputargs = self.inputargs.len() as u32;
202 let cut_ops = &self.ops[start.ops_len..];
203
204 let mut remap: HashMap<OpRef, OpRef> = HashMap::new();
206 let original_set: HashSet<OpRef> = original_boxes.iter().copied().collect();
207 for (i, &old_ref) in original_boxes.iter().enumerate() {
208 remap.insert(old_ref, OpRef(i as u32));
209 }
210
211 let defined_after_cut: HashSet<OpRef> = cut_ops
213 .iter()
214 .filter(|op| !op.pos.is_none())
215 .map(|op| op.pos)
216 .collect();
217
218 let is_pre_cut_ref = |r: &OpRef| -> bool {
222 !r.is_none()
223 && r.0 < 10_000
224 && !original_set.contains(r)
225 && !defined_after_cut.contains(r)
226 };
227
228 let mut escaped_set: HashSet<OpRef> = HashSet::new();
229 let mut queue: VecDeque<OpRef> = VecDeque::new();
230
231 for op in cut_ops {
236 for arg in &op.args {
237 if is_pre_cut_ref(arg) && escaped_set.insert(*arg) {
238 queue.push_back(*arg);
239 }
240 }
241 }
242
243 while let Some(esc_ref) = queue.pop_front() {
245 if esc_ref.0 < num_original_inputargs {
246 continue;
249 }
250 let op_idx = (esc_ref.0 - num_original_inputargs) as usize;
251 if let Some(op) = self.ops.get(op_idx) {
252 for arg in &op.args {
253 if is_pre_cut_ref(arg) && escaped_set.insert(*arg) {
254 queue.push_back(*arg);
255 }
256 }
257 }
258 }
259
260 let mut orig_inputarg_escaped: Vec<OpRef> = Vec::new();
265 let mut op_escaped: Vec<OpRef> = Vec::new();
266 for &r in &escaped_set {
267 if r.0 < num_original_inputargs {
268 orig_inputarg_escaped.push(r);
269 } else {
270 op_escaped.push(r);
271 }
272 }
273 orig_inputarg_escaped.sort_by_key(|r| r.0);
274 op_escaped.sort_by_key(|r| r.0); let mut new_ia_boxes = original_boxes.to_vec();
278 let mut new_ia_types = original_box_types.to_vec();
279 for &r in &orig_inputarg_escaped {
280 remap.insert(r, OpRef(new_ia_boxes.len() as u32));
281 new_ia_boxes.push(r);
282 new_ia_types.push(self.inputargs[r.0 as usize].tp);
283 }
284 let new_inputargs_count = new_ia_boxes.len() as u32;
285
286 let new_inputargs: Vec<InputArg> = new_ia_types
287 .iter()
288 .enumerate()
289 .map(|(i, &tp)| InputArg {
290 index: i as u32,
291 tp,
292 })
293 .collect();
294
295 let mut next_ref = new_inputargs_count;
297 for &r in &op_escaped {
298 remap.insert(r, OpRef(next_ref));
299 next_ref += 1;
300 }
301
302 let prefix_count = op_escaped.len() as u32;
304 for (i, op) in cut_ops.iter().enumerate() {
305 if !op.pos.is_none() {
306 remap.insert(op.pos, OpRef(new_inputargs_count + prefix_count + i as u32));
307 }
308 }
309
310 let remap_ref = |r: &OpRef| -> OpRef {
311 if r.is_none() || r.0 >= 10_000 {
312 *r
313 } else if let Some(&new_ref) = remap.get(r) {
314 new_ref
315 } else {
316 OpRef::NONE
317 }
318 };
319
320 let mut prefix_ops: Vec<Op> = Vec::with_capacity(op_escaped.len());
322 for (pi, &r) in op_escaped.iter().enumerate() {
323 let op_idx = (r.0 - num_original_inputargs) as usize;
324 let orig_op = &self.ops[op_idx];
325 let mut new_op = orig_op.clone();
326 new_op.pos = OpRef(new_inputargs_count + pi as u32);
327 for arg in new_op.args.iter_mut() {
328 *arg = remap_ref(arg);
329 }
330 new_op.fail_args = None;
332 prefix_ops.push(new_op);
333 }
334
335 let mut new_ops: Vec<Op> = Vec::with_capacity(prefix_ops.len() + cut_ops.len());
337 new_ops.extend(prefix_ops);
338 for (i, op) in cut_ops.iter().enumerate() {
339 let mut new_op = op.clone();
340 new_op.pos = OpRef(new_inputargs_count + prefix_count + i as u32);
341 for arg in new_op.args.iter_mut() {
342 *arg = remap_ref(arg);
343 }
344 if let Some(ref mut fa) = new_op.fail_args {
345 for arg in fa.iter_mut() {
346 *arg = remap_ref(arg);
347 }
348 }
349 new_ops.push(new_op);
350 }
351
352 TreeLoop::with_snapshots(new_inputargs, new_ops, self.snapshots.clone())
359 }
360}
361
362#[cfg(test)]
363mod tests {
364 use super::*;
365 use majit_ir::Type;
366
367 #[test]
368 fn test_empty_trace() {
369 let trace = TreeLoop::new(vec![], vec![]);
370 assert_eq!(trace.num_ops(), 0);
371 assert_eq!(trace.num_inputargs(), 0);
372 assert!(!trace.is_loop());
373 assert!(!trace.is_finished());
374 }
375
376 #[test]
377 fn test_trace_with_jump() {
378 let inputargs = vec![InputArg::new_int(0)];
379 let ops = vec![
380 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)]),
381 Op::new(OpCode::Jump, &[OpRef(1)]),
382 ];
383 let trace = TreeLoop::new(inputargs, ops);
384 assert!(trace.is_loop());
385 assert!(!trace.is_finished());
386 assert_eq!(trace.num_ops(), 2);
387 assert_eq!(trace.num_inputargs(), 1);
388 }
389
390 #[test]
391 fn test_trace_with_finish() {
392 let inputargs = vec![InputArg::new_int(0)];
393 let ops = vec![
394 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)]),
395 Op::new(OpCode::Finish, &[OpRef(1)]),
396 ];
397 let trace = TreeLoop::new(inputargs, ops);
398 assert!(!trace.is_loop());
399 assert!(trace.is_finished());
400 }
401
402 #[test]
403 fn test_inputarg_types() {
404 let inputargs = vec![
405 InputArg::new_int(0),
406 InputArg::new_ref(1),
407 InputArg::new_float(2),
408 ];
409 let trace = TreeLoop::new(inputargs, vec![]);
410 assert_eq!(trace.inputargs[0].tp, Type::Int);
411 assert_eq!(trace.inputargs[1].tp, Type::Ref);
412 assert_eq!(trace.inputargs[2].tp, Type::Float);
413 }
414
415 #[test]
421 fn test_trace_structure_inputargs_and_ops() {
422 let inputargs = vec![InputArg::new_int(0), InputArg::new_int(1)];
424 let ops = vec![
425 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]),
426 Op::new(OpCode::IntSub, &[OpRef(2), OpRef(0)]),
427 Op::new(OpCode::Jump, &[OpRef(3), OpRef(1)]),
428 ];
429 let trace = TreeLoop::new(inputargs, ops);
430
431 assert_eq!(trace.num_inputargs(), 2);
432 assert_eq!(trace.num_ops(), 3);
433 assert!(trace.is_loop());
434 }
435
436 #[test]
437 fn test_trace_guards_can_have_fail_args() {
438 let inputargs = vec![InputArg::new_int(0), InputArg::new_int(1)];
440 let mut guard = Op::new(OpCode::GuardTrue, &[OpRef(0)]);
441 guard.fail_args = Some(smallvec::smallvec![OpRef(0), OpRef(1)]);
442
443 let ops = vec![
444 guard,
445 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]),
446 Op::new(OpCode::Jump, &[OpRef(2), OpRef(1)]),
447 ];
448 let trace = TreeLoop::new(inputargs, ops);
449
450 let guards: Vec<_> = trace.iter_guards().collect();
451 assert_eq!(guards.len(), 1);
452 let fa = guards[0].fail_args.as_ref().unwrap();
453 assert_eq!(fa.len(), 2);
454 assert_eq!(fa[0], OpRef(0));
455 assert_eq!(fa[1], OpRef(1));
456 }
457
458 #[test]
459 fn test_trace_get_op() {
460 let inputargs = vec![InputArg::new_int(0)];
462 let ops = vec![
463 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)]),
464 Op::new(OpCode::Jump, &[OpRef(1)]),
465 ];
466 let trace = TreeLoop::new(inputargs, ops);
467
468 let op0 = trace.get_op(OpRef(0)).unwrap();
469 assert_eq!(op0.opcode, OpCode::IntAdd);
470
471 let op1 = trace.get_op(OpRef(1)).unwrap();
472 assert_eq!(op1.opcode, OpCode::Jump);
473
474 assert!(trace.get_op(OpRef(99)).is_none());
475 }
476
477 #[test]
478 fn test_trace_iter_guards_filters_correctly() {
479 let inputargs = vec![InputArg::new_int(0), InputArg::new_int(1)];
481 let ops = vec![
482 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]),
483 Op::new(OpCode::GuardTrue, &[OpRef(2)]),
484 Op::new(OpCode::IntSub, &[OpRef(2), OpRef(0)]),
485 Op::new(OpCode::GuardFalse, &[OpRef(3)]),
486 Op::new(OpCode::Jump, &[OpRef(3), OpRef(1)]),
487 ];
488 let trace = TreeLoop::new(inputargs, ops);
489
490 let guards: Vec<_> = trace.iter_guards().collect();
491 assert_eq!(guards.len(), 2);
492 assert_eq!(guards[0].opcode, OpCode::GuardTrue);
493 assert_eq!(guards[1].opcode, OpCode::GuardFalse);
494 }
495
496 #[test]
497 fn test_trace_not_loop_not_finished() {
498 let inputargs = vec![InputArg::new_int(0)];
500 let ops = vec![Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)])];
501 let trace = TreeLoop::new(inputargs, ops);
502 assert!(!trace.is_loop());
503 assert!(!trace.is_finished());
504 }
505
506 #[test]
507 fn test_trace_loop_vs_finish_exclusive() {
508 let inputargs = vec![InputArg::new_int(0)];
510
511 let loop_trace = TreeLoop::new(
512 inputargs.clone(),
513 vec![
514 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)]),
515 Op::new(OpCode::Jump, &[OpRef(1)]),
516 ],
517 );
518 assert!(loop_trace.is_loop());
519 assert!(!loop_trace.is_finished());
520
521 let finish_trace = TreeLoop::new(
522 inputargs,
523 vec![
524 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)]),
525 Op::new(OpCode::Finish, &[OpRef(1)]),
526 ],
527 );
528 assert!(!finish_trace.is_loop());
529 assert!(finish_trace.is_finished());
530 }
531
532 #[test]
533 fn test_trace_mixed_type_inputargs() {
534 let inputargs = vec![
536 InputArg::new_int(0),
537 InputArg::new_ref(1),
538 InputArg::new_float(2),
539 ];
540 let ops = vec![Op::new(OpCode::Jump, &[OpRef(0), OpRef(1), OpRef(2)])];
541 let trace = TreeLoop::new(inputargs, ops);
542
543 assert_eq!(trace.num_inputargs(), 3);
544 assert_eq!(trace.inputargs[0].tp, Type::Int);
545 assert_eq!(trace.inputargs[1].tp, Type::Ref);
546 assert_eq!(trace.inputargs[2].tp, Type::Float);
547 assert!(trace.is_loop());
548 }
549
550 #[test]
551 fn test_trace_multiple_guards_with_different_fail_args() {
552 let inputargs = vec![InputArg::new_int(0), InputArg::new_int(1)];
554
555 let mut g0 = Op::new(OpCode::GuardTrue, &[OpRef(0)]);
556 g0.fail_args = Some(smallvec::smallvec![OpRef(0)]);
557
558 let mut g1 = Op::new(OpCode::GuardFalse, &[OpRef(1)]);
559 g1.fail_args = Some(smallvec::smallvec![OpRef(0), OpRef(1)]);
560
561 let ops = vec![
562 g0,
563 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]),
564 g1,
565 Op::new(OpCode::Jump, &[OpRef(2), OpRef(1)]),
566 ];
567 let trace = TreeLoop::new(inputargs, ops);
568
569 let guards: Vec<_> = trace.iter_guards().collect();
570 assert_eq!(guards.len(), 2);
571
572 assert_eq!(guards[0].fail_args.as_ref().unwrap().len(), 1);
573 assert_eq!(guards[1].fail_args.as_ref().unwrap().len(), 2);
574 }
575
576 #[test]
577 fn test_trace_guard_without_fail_args() {
578 let inputargs = vec![InputArg::new_int(0)];
580 let ops = vec![
581 Op::new(OpCode::GuardTrue, &[OpRef(0)]),
582 Op::new(OpCode::Jump, &[OpRef(0)]),
583 ];
584 let trace = TreeLoop::new(inputargs, ops);
585
586 let guards: Vec<_> = trace.iter_guards().collect();
587 assert_eq!(guards.len(), 1);
588 assert!(guards[0].fail_args.is_none());
589 }
590
591 #[test]
592 fn test_trace_ops_have_correct_opcodes() {
593 let inputargs = vec![InputArg::new_int(0), InputArg::new_int(1)];
595 let ops = vec![
596 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]),
597 Op::new(OpCode::IntMul, &[OpRef(2), OpRef(0)]),
598 Op::new(OpCode::IntSub, &[OpRef(3), OpRef(1)]),
599 Op::new(OpCode::Jump, &[OpRef(4), OpRef(1)]),
600 ];
601 let trace = TreeLoop::new(inputargs, ops);
602
603 let opcodes: Vec<_> = trace.iter_ops().map(|op| op.opcode).collect();
604 assert_eq!(
605 opcodes,
606 vec![OpCode::IntAdd, OpCode::IntMul, OpCode::IntSub, OpCode::Jump]
607 );
608 }
609
610 #[test]
615 fn test_trace_ops_with_descrs() {
616 use majit_ir::DescrRef;
618 use std::sync::Arc;
619
620 #[derive(Debug)]
621 struct TestDescr(u32);
622 impl majit_ir::Descr for TestDescr {
623 fn index(&self) -> u32 {
624 self.0
625 }
626 }
627
628 let inputargs = vec![InputArg::new_int(0), InputArg::new_int(1)];
629 let descr: DescrRef = Arc::new(TestDescr(42));
630 let ops = vec![
631 Op::with_descr(OpCode::CallI, &[OpRef(0)], descr.clone()),
632 Op::with_descr(OpCode::GuardTrue, &[OpRef(0)], descr.clone()),
633 Op::new(OpCode::Jump, &[OpRef(0), OpRef(1)]),
634 ];
635 let trace = TreeLoop::new(inputargs, ops);
636
637 assert!(trace.ops[0].descr.is_some());
639 assert_eq!(trace.ops[0].descr.as_ref().unwrap().index(), 42);
640 assert!(trace.ops[1].descr.is_some());
642 assert_eq!(trace.ops[1].descr.as_ref().unwrap().index(), 42);
643 assert!(trace.ops[2].descr.is_none());
645 }
646
647 #[test]
648 fn test_trace_iteration_order_matches_recording() {
649 let inputargs = vec![InputArg::new_int(0)];
651 let expected_opcodes = vec![
652 OpCode::IntAdd,
653 OpCode::IntSub,
654 OpCode::IntMul,
655 OpCode::IntNeg,
656 OpCode::IntLt,
657 OpCode::Jump,
658 ];
659 let ops = vec![
660 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)]),
661 Op::new(OpCode::IntSub, &[OpRef(1), OpRef(0)]),
662 Op::new(OpCode::IntMul, &[OpRef(2), OpRef(0)]),
663 Op::new(OpCode::IntNeg, &[OpRef(3)]),
664 Op::new(OpCode::IntLt, &[OpRef(4), OpRef(0)]),
665 Op::new(OpCode::Jump, &[OpRef(4)]),
666 ];
667 let trace = TreeLoop::new(inputargs, ops);
668
669 let actual: Vec<_> = trace.iter_ops().map(|op| op.opcode).collect();
670 assert_eq!(actual, expected_opcodes);
671 }
672
673 #[test]
674 fn test_trace_is_immutable_snapshot() {
675 let inputargs = vec![InputArg::new_int(0)];
678 let ops = vec![
679 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)]),
680 Op::new(OpCode::Jump, &[OpRef(1)]),
681 ];
682 let trace = TreeLoop::new(inputargs, ops);
683 let trace2 = trace.clone();
684
685 assert_eq!(trace.num_ops(), trace2.num_ops());
686 assert_eq!(trace.num_inputargs(), trace2.num_inputargs());
687 assert_eq!(trace.is_loop(), trace2.is_loop());
688 }
689
690 #[test]
691 fn test_trace_stress_100_ops() {
692 let inputargs = vec![InputArg::new_int(0)];
694 let mut ops = Vec::new();
695 let mut prev = OpRef(0);
696 for i in 0..100 {
697 let mut op = Op::new(OpCode::IntAdd, &[prev, OpRef(0)]);
698 op.pos = OpRef(i + 1);
699 ops.push(op);
700 prev = OpRef(i + 1);
701 }
702 ops.push(Op::new(OpCode::Jump, &[prev]));
703 let trace = TreeLoop::new(inputargs, ops);
704
705 assert_eq!(trace.num_ops(), 101); assert!(trace.is_loop());
707
708 assert_eq!(trace.ops[0].opcode, OpCode::IntAdd);
710 assert_eq!(trace.ops[99].opcode, OpCode::IntAdd);
711 assert_eq!(trace.ops[100].opcode, OpCode::Jump);
712
713 for op in &trace.ops[..100] {
715 assert_eq!(op.opcode, OpCode::IntAdd);
716 }
717 }
718
719 #[test]
720 fn test_trace_guard_fail_args_reference_valid_refs() {
721 let inputargs = vec![InputArg::new_int(0), InputArg::new_int(1)];
723
724 let add_op = Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]);
725 let mut guard_op = Op::new(OpCode::GuardTrue, &[OpRef(2)]);
726 guard_op.fail_args = Some(smallvec::smallvec![OpRef(0), OpRef(1), OpRef(2)]);
728
729 let ops = vec![
730 add_op,
731 guard_op,
732 Op::new(OpCode::Jump, &[OpRef(2), OpRef(1)]),
733 ];
734 let trace = TreeLoop::new(inputargs, ops);
735
736 let guard = trace.iter_guards().next().unwrap();
737 let fa = guard.fail_args.as_ref().unwrap();
738 assert!(fa.iter().all(|r| r.0 <= 2));
740 assert_eq!(fa.len(), 3);
741 }
742
743 #[test]
744 fn test_trace_many_guards_with_varying_fail_args() {
745 let inputargs = vec![InputArg::new_int(0), InputArg::new_int(1)];
747
748 let mut g0 = Op::new(OpCode::GuardTrue, &[OpRef(0)]);
749 g0.fail_args = Some(smallvec::smallvec![]);
750
751 let mut g1 = Op::new(OpCode::GuardFalse, &[OpRef(1)]);
752 g1.fail_args = Some(smallvec::smallvec![OpRef(0)]);
753
754 let add = Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]);
755
756 let mut g2 = Op::new(OpCode::GuardTrue, &[OpRef(0)]);
757 g2.fail_args = Some(smallvec::smallvec![OpRef(0), OpRef(1), OpRef(2)]);
758
759 let ops = vec![
760 g0,
761 g1,
762 add,
763 g2,
764 Op::new(OpCode::Jump, &[OpRef(0), OpRef(1)]),
765 ];
766 let trace = TreeLoop::new(inputargs, ops);
767
768 let guards: Vec<_> = trace.iter_guards().collect();
769 assert_eq!(guards.len(), 3);
770 assert_eq!(guards[0].fail_args.as_ref().unwrap().len(), 0);
771 assert_eq!(guards[1].fail_args.as_ref().unwrap().len(), 1);
772 assert_eq!(guards[2].fail_args.as_ref().unwrap().len(), 3);
773 }
774
775 #[test]
776 fn test_trace_get_op_returns_correct_positions() {
777 let inputargs = vec![InputArg::new_int(0)];
779 let ops = vec![
780 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)]),
781 Op::new(OpCode::IntSub, &[OpRef(1), OpRef(0)]),
782 Op::new(OpCode::IntMul, &[OpRef(2), OpRef(1)]),
783 Op::new(OpCode::Jump, &[OpRef(3)]),
784 ];
785 let trace = TreeLoop::new(inputargs, ops);
786
787 assert_eq!(trace.get_op(OpRef(0)).unwrap().opcode, OpCode::IntAdd);
788 assert_eq!(trace.get_op(OpRef(1)).unwrap().opcode, OpCode::IntSub);
789 assert_eq!(trace.get_op(OpRef(2)).unwrap().opcode, OpCode::IntMul);
790 assert_eq!(trace.get_op(OpRef(3)).unwrap().opcode, OpCode::Jump);
791 assert!(trace.get_op(OpRef(4)).is_none());
792 assert!(trace.get_op(OpRef(100)).is_none());
793 }
794
795 #[test]
796 fn test_trace_clone_independence() {
797 let inputargs = vec![InputArg::new_int(0)];
799 let ops = vec![
800 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)]),
801 Op::new(OpCode::Jump, &[OpRef(1)]),
802 ];
803 let trace = TreeLoop::new(inputargs, ops);
804 let mut trace2 = trace.clone();
805
806 trace2
807 .ops
808 .push(Op::new(OpCode::IntSub, &[OpRef(0), OpRef(0)]));
809 assert_eq!(trace.num_ops(), 2);
810 assert_eq!(trace2.num_ops(), 3);
811 }
812
813 #[test]
814 fn test_trace_only_guards_in_iter_guards() {
815 let inputargs = vec![InputArg::new_int(0), InputArg::new_int(1)];
817 let ops = vec![
818 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]),
819 Op::new(OpCode::IntSub, &[OpRef(0), OpRef(1)]),
820 Op::new(OpCode::GuardTrue, &[OpRef(2)]),
821 Op::new(OpCode::IntMul, &[OpRef(2), OpRef(3)]),
822 Op::new(OpCode::IntNeg, &[OpRef(4)]),
823 Op::new(OpCode::GuardFalse, &[OpRef(5)]),
824 Op::new(OpCode::IntLt, &[OpRef(4), OpRef(5)]),
825 Op::new(OpCode::GuardNoException, &[]),
826 Op::new(OpCode::Jump, &[OpRef(4), OpRef(5)]),
827 ];
828 let trace = TreeLoop::new(inputargs, ops);
829
830 let guard_opcodes: Vec<_> = trace.iter_guards().map(|op| op.opcode).collect();
831 assert_eq!(
832 guard_opcodes,
833 vec![
834 OpCode::GuardTrue,
835 OpCode::GuardFalse,
836 OpCode::GuardNoException
837 ]
838 );
839 }
840
841 #[test]
842 fn test_check_consistency_valid() {
843 let ops = vec![
844 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]),
845 Op::new(OpCode::Jump, &[OpRef(0)]),
846 ];
847 let trace = TreeLoop::new(vec![InputArg::new_int(0)], ops);
848 assert!(trace.check_consistency());
849 }
850
851 #[test]
852 fn test_check_consistency_no_final() {
853 let ops = vec![Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)])];
854 let trace = TreeLoop::new(vec![], ops);
855 assert!(!trace.check_consistency());
856 }
857
858 #[test]
859 fn test_free_vars() {
860 let mut ops = vec![
861 Op::new(OpCode::IntAdd, &[OpRef(100), OpRef(101)]),
862 Op::new(OpCode::Finish, &[OpRef(0)]),
863 ];
864 ops[0].pos = OpRef(0);
865 ops[1].pos = OpRef(1);
866 let trace = TreeLoop::new(vec![], ops);
867 let free = trace.free_vars();
868 assert!(free.contains(&OpRef(100)));
870 assert!(free.contains(&OpRef(101)));
871 assert!(!free.contains(&OpRef(0))); }
873
874 #[test]
875 fn test_count_by_category() {
876 let ops = vec![
877 Op::new(OpCode::GuardTrue, &[OpRef(0)]),
878 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]),
879 Op::new(OpCode::IntMul, &[OpRef(0), OpRef(1)]),
880 Op::new(OpCode::CallI, &[OpRef(0)]),
881 Op::new(OpCode::Finish, &[]),
882 ];
883 let trace = TreeLoop::new(vec![], ops);
884 let (guards, pure, calls, other) = trace.count_by_category();
885 assert_eq!(guards, 1);
886 assert_eq!(pure, 2);
887 assert_eq!(calls, 1);
888 assert_eq!(other, 1); }
890
891 #[test]
892 fn test_split_at_label() {
893 let ops = vec![
894 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]),
895 Op::new(OpCode::Label, &[OpRef(0)]),
896 Op::new(OpCode::IntMul, &[OpRef(0), OpRef(1)]),
897 Op::new(OpCode::Jump, &[OpRef(0)]),
898 ];
899 let trace = TreeLoop::new(vec![], ops);
900 let (preamble, body) = trace.split_at_label();
901 assert_eq!(preamble.len(), 1);
902 assert_eq!(body.len(), 3); }
904
905 #[test]
906 fn test_num_guards() {
907 let ops = vec![
908 Op::new(OpCode::GuardTrue, &[OpRef(0)]),
909 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]),
910 Op::new(OpCode::GuardNonnull, &[OpRef(0)]),
911 Op::new(OpCode::GuardClass, &[OpRef(0), OpRef(1)]),
912 Op::new(OpCode::Finish, &[]),
913 ];
914 let trace = TreeLoop::new(vec![], ops);
915 assert_eq!(trace.num_guards(), 3);
916 }
917
918 #[test]
919 fn test_get_final_op() {
920 let ops = vec![
921 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]),
922 Op::new(OpCode::Finish, &[OpRef(0)]),
923 ];
924 let trace = TreeLoop::new(vec![], ops);
925 let final_op = trace.get_final_op().unwrap();
926 assert_eq!(final_op.opcode, OpCode::Finish);
927 }
928
929 #[test]
930 fn test_get_iter() {
931 let ops = vec![
932 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]),
933 Op::new(OpCode::IntMul, &[OpRef(0), OpRef(1)]),
934 Op::new(OpCode::Jump, &[OpRef(0)]),
935 ];
936 let trace = TreeLoop::new(vec![], ops);
937 let mut iter = trace.get_iter();
938 assert_eq!(iter.num_ops(), 3);
939 assert!(!iter.done());
940 iter.next_op();
941 assert_eq!(iter.position(), 1);
942 }
943
944 #[test]
945 fn test_inputarg_types_all() {
946 let inputargs = vec![
947 InputArg {
948 index: 0,
949 tp: Type::Int,
950 },
951 InputArg {
952 index: 1,
953 tp: Type::Ref,
954 },
955 InputArg {
956 index: 2,
957 tp: Type::Float,
958 },
959 ];
960 let trace = TreeLoop::new(inputargs, vec![Op::new(OpCode::Finish, &[])]);
961 let types = trace.inputarg_types();
962 assert_eq!(types, vec![Type::Int, Type::Ref, Type::Float]);
963 }
964
965 #[test]
970 fn test_cut_trace_from_no_escaped_refs() {
971 let inputargs = vec![InputArg::new_int(0), InputArg::new_int(1)];
974 let mut ops = Vec::new();
975 let mut op0 = Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]);
977 op0.pos = OpRef(2);
978 ops.push(op0);
979 let mut op1 = Op::new(OpCode::IntMul, &[OpRef(0), OpRef(1)]);
981 op1.pos = OpRef(3);
982 ops.push(op1);
983 let mut op2 = Op::new(OpCode::Jump, &[OpRef(3)]);
984 op2.pos = OpRef(4);
985 ops.push(op2);
986 let trace = TreeLoop::new(inputargs, ops);
987
988 let start = crate::recorder::TracePosition {
989 op_count: 3,
990 ops_len: 1, };
992 let original_boxes = vec![OpRef(0), OpRef(1)];
993 let original_box_types = vec![Type::Int, Type::Int];
994
995 let cut = trace.cut_trace_from(start, &original_boxes, &original_box_types);
996 assert_eq!(cut.inputargs.len(), 2);
997 assert_eq!(cut.ops.len(), 2); assert_eq!(cut.ops[0].opcode, OpCode::IntMul);
999 assert_eq!(cut.ops[0].args[0], OpRef(0)); assert_eq!(cut.ops[0].args[1], OpRef(1)); assert_eq!(cut.ops[1].opcode, OpCode::Jump);
1002 assert_eq!(cut.ops[1].args[0], OpRef(2)); }
1004
1005 #[test]
1006 fn test_cut_trace_from_with_escaped_op() {
1007 let inputargs = vec![InputArg::new_int(0), InputArg::new_int(1)];
1010 let mut ops = Vec::new();
1011 let mut op0 = Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(1)]);
1013 op0.pos = OpRef(2);
1014 ops.push(op0);
1015 let mut op1 = Op::new(OpCode::IntMul, &[OpRef(2), OpRef(0)]);
1017 op1.pos = OpRef(3);
1018 ops.push(op1);
1019 let mut op2 = Op::new(OpCode::Jump, &[OpRef(3)]);
1020 op2.pos = OpRef(4);
1021 ops.push(op2);
1022 let trace = TreeLoop::new(inputargs, ops);
1023
1024 let start = crate::recorder::TracePosition {
1025 op_count: 3,
1026 ops_len: 1, };
1028 let original_boxes = vec![OpRef(0)];
1030 let original_box_types = vec![Type::Int];
1031
1032 let cut = trace.cut_trace_from(start, &original_boxes, &original_box_types);
1033 assert_eq!(cut.inputargs.len(), 2); assert_eq!(cut.ops.len(), 3); }
1039
1040 #[test]
1041 fn test_cut_trace_from_constants_preserved() {
1042 let inputargs = vec![InputArg::new_int(0)];
1044 let mut ops = Vec::new();
1045 let mut op0 = Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)]);
1047 op0.pos = OpRef(1);
1048 ops.push(op0);
1049 let mut op1 = Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(10_000)]);
1051 op1.pos = OpRef(2);
1052 ops.push(op1);
1053 let mut op2 = Op::new(OpCode::Jump, &[OpRef(2)]);
1054 op2.pos = OpRef(3);
1055 ops.push(op2);
1056 let trace = TreeLoop::new(inputargs, ops);
1057
1058 let start = crate::recorder::TracePosition {
1059 op_count: 2,
1060 ops_len: 1,
1061 };
1062 let original_boxes = vec![OpRef(0)];
1063 let original_box_types = vec![Type::Int];
1064
1065 let cut = trace.cut_trace_from(start, &original_boxes, &original_box_types);
1066 assert_eq!(cut.ops.len(), 2);
1067 assert_eq!(cut.ops[0].args[1], OpRef(10_000));
1069 }
1070
1071 #[test]
1072 fn test_cut_trace_from_transitive_escaped() {
1073 let inputargs = vec![InputArg::new_int(0)];
1075 let mut ops = Vec::new();
1076 let mut op0 = Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)]);
1078 op0.pos = OpRef(1);
1079 ops.push(op0);
1080 let mut op1 = Op::new(OpCode::IntMul, &[OpRef(1), OpRef(0)]);
1082 op1.pos = OpRef(2);
1083 ops.push(op1);
1084 let mut op2 = Op::new(OpCode::IntSub, &[OpRef(2), OpRef(0)]);
1086 op2.pos = OpRef(3);
1087 ops.push(op2);
1088 let mut op3 = Op::new(OpCode::Jump, &[OpRef(3)]);
1089 op3.pos = OpRef(4);
1090 ops.push(op3);
1091 let trace = TreeLoop::new(inputargs, ops);
1092
1093 let start = crate::recorder::TracePosition {
1094 op_count: 3,
1095 ops_len: 2, };
1097 let original_boxes = vec![OpRef(0)];
1098 let original_box_types = vec![Type::Int];
1099
1100 let cut = trace.cut_trace_from(start, &original_boxes, &original_box_types);
1101 assert_eq!(cut.inputargs.len(), 1);
1103 assert_eq!(cut.ops.len(), 4);
1104 assert_eq!(cut.ops[0].opcode, OpCode::IntAdd); assert_eq!(cut.ops[1].opcode, OpCode::IntMul); assert_eq!(cut.ops[2].opcode, OpCode::IntSub);
1107 assert_eq!(cut.ops[3].opcode, OpCode::Jump);
1108 assert_eq!(cut.ops[1].args[0], OpRef(1)); }
1111}