1use std::collections::HashMap;
8
9use majit_ir::{InputArg, OPCODE_COUNT, Op, OpCode, OpRef, Type};
10
11use crate::history::TreeLoop;
12
13pub fn encode_varint(buf: &mut Vec<u8>, mut value: u64) {
15 loop {
16 let byte = (value & 0x7F) as u8;
17 value >>= 7;
18 if value == 0 {
19 buf.push(byte);
20 return;
21 }
22 buf.push(byte | 0x80);
23 }
24}
25
26pub fn decode_varint(buf: &[u8]) -> (u64, usize) {
32 let mut value: u64 = 0;
33 let mut shift = 0;
34 for (i, &byte) in buf.iter().enumerate() {
35 value |= ((byte & 0x7F) as u64) << shift;
36 if byte & 0x80 == 0 {
37 return (value, i + 1);
38 }
39 shift += 7;
40 }
41 panic!("truncated varint");
42}
43
44fn type_to_u8(tp: Type) -> u8 {
45 match tp {
46 Type::Int => 0,
47 Type::Ref => 1,
48 Type::Float => 2,
49 Type::Void => 3,
50 }
51}
52
53fn u8_to_type(v: u8) -> Type {
54 match v {
55 0 => Type::Int,
56 1 => Type::Ref,
57 2 => Type::Float,
58 3 => Type::Void,
59 _ => panic!("invalid type byte: {v}"),
60 }
61}
62
63fn u16_to_opcode(v: u16) -> OpCode {
64 assert!(
65 (v as usize) < OPCODE_COUNT,
66 "invalid opcode discriminant: {v}"
67 );
68 unsafe { std::mem::transmute(v) }
70}
71
72pub fn encode_trace(trace: &TreeLoop) -> Vec<u8> {
74 let mut buf = Vec::new();
75
76 encode_varint(&mut buf, trace.inputargs.len() as u64);
78 for arg in &trace.inputargs {
79 buf.push(type_to_u8(arg.tp));
80 }
81
82 encode_varint(&mut buf, trace.ops.len() as u64);
84
85 for op in &trace.ops {
87 encode_varint(&mut buf, op.opcode as u16 as u64);
88 encode_varint(&mut buf, op.args.len() as u64);
89 for arg in &op.args {
90 encode_varint(&mut buf, arg.0 as u64);
91 }
92 if let Some(ref descr) = op.descr {
94 let idx = descr.index();
95 if idx != u32::MAX {
96 encode_varint(&mut buf, (idx as u64) + 1);
97 } else {
98 buf.push(1); }
100 } else {
101 buf.push(0);
102 }
103 if let Some(ref fa) = op.fail_args {
105 encode_varint(&mut buf, fa.len() as u64 + 1); for arg in fa.iter() {
107 encode_varint(&mut buf, arg.0 as u64);
108 }
109 } else {
110 buf.push(0);
111 }
112 }
113
114 buf
115}
116
117pub fn decode_trace(buf: &[u8]) -> TreeLoop {
124 let mut pos = 0;
125
126 let (num_inputargs, n) = decode_varint(&buf[pos..]);
128 pos += n;
129 let num_inputargs = num_inputargs as usize;
130
131 let mut inputargs = Vec::with_capacity(num_inputargs);
132 for i in 0..num_inputargs {
133 let tp = u8_to_type(buf[pos]);
134 pos += 1;
135 inputargs.push(InputArg::from_type(tp, i as u32));
136 }
137
138 let (num_ops, n) = decode_varint(&buf[pos..]);
140 pos += n;
141 let num_ops = num_ops as usize;
142
143 let mut ops = Vec::with_capacity(num_ops);
144 for _ in 0..num_ops {
145 let (opcode_raw, n) = decode_varint(&buf[pos..]);
146 pos += n;
147 let opcode = u16_to_opcode(opcode_raw as u16);
148
149 let (num_args, n) = decode_varint(&buf[pos..]);
150 pos += n;
151 let num_args = num_args as usize;
152
153 let mut args = Vec::with_capacity(num_args);
154 for _ in 0..num_args {
155 let (arg_ref, n) = decode_varint(&buf[pos..]);
156 pos += n;
157 args.push(OpRef(arg_ref as u32));
158 }
159
160 let (descr_marker, n) = decode_varint(&buf[pos..]);
162 pos += n;
163 let _descr_index = if descr_marker > 0 {
164 Some((descr_marker - 1) as u32)
165 } else {
166 None
167 };
168
169 let (fa_marker, n) = decode_varint(&buf[pos..]);
171 pos += n;
172 let fail_args = if fa_marker > 0 {
173 let fa_len = (fa_marker - 1) as usize;
174 let mut fa = Vec::with_capacity(fa_len);
175 for _ in 0..fa_len {
176 let (arg_ref, n) = decode_varint(&buf[pos..]);
177 pos += n;
178 fa.push(OpRef(arg_ref as u32));
179 }
180 Some(fa)
181 } else {
182 None
183 };
184
185 let mut op = Op::new(opcode, &args);
186 if let Some(fa) = fail_args {
187 op.fail_args = Some(fa.into());
188 }
189 ops.push(op);
190 }
191
192 TreeLoop::new(inputargs, ops)
193}
194
195pub fn encode_varint_signed(buf: &mut Vec<u8>, value: i64) {
200 let zigzag = ((value << 1) ^ (value >> 63)) as u64;
202 encode_varint(buf, zigzag);
203}
204
205pub fn decode_varint_signed(buf: &[u8]) -> (i64, usize) {
208 let (zigzag, consumed) = decode_varint(buf);
209 let value = ((zigzag >> 1) as i64) ^ -((zigzag & 1) as i64);
210 (value, consumed)
211}
212
213pub const TAGINT: u8 = 0; pub const TAGCONSTPTR: u8 = 1; pub const TAGCONSTOTHER: u8 = 2; pub const TAGBOX: u8 = 3; pub const TAG_SHIFT: u32 = 2;
224pub const TAG_MASK: u8 = (1 << TAG_SHIFT) - 1;
226pub const TAGMASK: u8 = TAG_MASK;
228pub const TAGSHIFT: u32 = TAG_SHIFT;
229
230pub const INIT_SIZE: usize = 4096;
232
233pub const MIN_VALUE: i64 = -(1 << 30);
235pub const MAX_VALUE: i64 = (1 << 30) - 1;
236
237pub const SMALL_INT_START: i64 = -(1 << 28);
239pub const SMALL_INT_STOP: i64 = 1 << 28;
240
241pub const SNAPSHOT_PREV_NEEDS_PATCHING: i32 = -3;
243pub const SNAPSHOT_PREV_NONE: i32 = -2;
244pub const SNAPSHOT_PREV_COMES_NEXT: i32 = -1;
245
246#[inline]
247pub fn skip_varint_signed(buf: &[u8], mut index: usize, skip: usize) -> usize {
248 assert!(skip > 0);
249 for _ in 0..skip {
250 let byte = buf[index];
251 if byte & 0x80 != 0 {
252 index += 2;
253 } else {
254 index += 1;
255 }
256 }
257 index
258}
259
260#[inline]
261pub fn varint_only_decode(buf: &[u8], index: usize, skip: usize) -> i64 {
262 let start = if skip > 0 {
263 skip_varint_signed(buf, index, skip)
264 } else {
265 index
266 };
267 decode_varint_signed(&buf[start..]).0
268}
269
270#[inline]
271pub fn combine_uint(index1: u32, index2: u32) -> u32 {
272 (index1 << 16) | index2
273}
274
275#[inline]
276pub fn unpack_uint(packed: u32) -> (u32, u32) {
277 ((packed >> 16) & 0xffff, packed & 0xffff)
278}
279
280pub fn tag(kind: u8, value: u32) -> u32 {
283 (value << TAG_SHIFT) | kind as u32
284}
285
286pub fn untag(tagged: u32) -> (u8, u32) {
289 let kind = (tagged & TAG_MASK as u32) as u8;
290 let value = tagged >> TAG_SHIFT;
291 (kind, value)
292}
293
294#[derive(Clone, Debug)]
301pub struct Snapshot {
302 pub values: Vec<u32>,
304 pub prev: Option<usize>,
306 pub jitcode_index: u32,
308 pub pc: u32,
310}
311
312#[derive(Clone, Debug)]
315pub struct TopSnapshot {
316 pub snapshot: Snapshot,
318 pub vable_array_index: Option<usize>,
320 pub vref_array_index: Option<usize>,
322}
323
324pub struct SnapshotIterator<'a> {
327 storage: &'a SnapshotStorage,
328 current_snapshot_idx: Option<usize>,
329}
330
331impl<'a> SnapshotIterator<'a> {
332 pub fn new(storage: &'a SnapshotStorage, top_snapshot_idx: usize) -> Self {
333 let start = if top_snapshot_idx < storage.top_snapshots.len() {
334 let _top = &storage.top_snapshots[top_snapshot_idx];
335 Some(storage.snapshots.len() - 1) } else {
337 None
338 };
339 SnapshotIterator {
340 storage,
341 current_snapshot_idx: start,
342 }
343 }
344
345 pub fn current(&self) -> Option<&'a Snapshot> {
347 self.current_snapshot_idx
348 .and_then(|idx| self.storage.snapshots.get(idx))
349 }
350
351 pub fn next_frame(&mut self) -> Option<&'a Snapshot> {
353 let idx = self.current_snapshot_idx?;
354 let snap = self.storage.snapshots.get(idx)?;
355 self.current_snapshot_idx = snap.prev;
356 self.current()
357 }
358
359 pub fn decode_value(&self, tagged: u32) -> DecodedSnapshotValue {
361 let (kind, value) = untag(tagged);
362 match kind {
363 TAGINT => DecodedSnapshotValue::SmallInt(value as i64),
364 TAGCONSTPTR => DecodedSnapshotValue::ConstPtr(
365 self.storage
366 .const_refs
367 .get(value as usize)
368 .copied()
369 .unwrap_or(0),
370 ),
371 TAGCONSTOTHER => DecodedSnapshotValue::ConstOther(value),
372 TAGBOX => DecodedSnapshotValue::Box(majit_ir::OpRef(value)),
373 _ => DecodedSnapshotValue::SmallInt(0),
374 }
375 }
376}
377
378#[derive(Clone, Debug)]
380pub enum DecodedSnapshotValue {
381 SmallInt(i64),
383 ConstPtr(u64),
385 ConstOther(u32),
387 Box(majit_ir::OpRef),
389}
390
391pub struct TopDownSnapshotIterator<'a> {
396 storage: &'a SnapshotStorage,
397 snapshot_index: usize,
398 vable_array_index: Option<usize>,
399 vref_array_index: Option<usize>,
400 frames: Vec<usize>,
402 pos: usize,
403}
404
405impl<'a> TopDownSnapshotIterator<'a> {
406 pub fn new(storage: &'a SnapshotStorage, top_idx: usize) -> Self {
408 let mut frames = Vec::new();
409 let mut vable_array_index = None;
410 let mut vref_array_index = None;
411 if top_idx < storage.top_snapshots.len() {
412 vable_array_index = storage.top_snapshots[top_idx].vable_array_index;
413 vref_array_index = storage.top_snapshots[top_idx].vref_array_index;
414 let first_snap_idx = storage
416 .top_snapshots
417 .get(top_idx)
418 .and_then(|top| top.snapshot.prev)
419 .unwrap_or(0);
420 let mut idx = Some(first_snap_idx);
421 while let Some(i) = idx {
422 if i < storage.snapshots.len() {
423 frames.push(i);
424 idx = storage.snapshots[i].prev;
425 } else {
426 break;
427 }
428 }
429 frames.reverse();
431 }
432 TopDownSnapshotIterator {
433 storage,
434 snapshot_index: top_idx,
435 vable_array_index,
436 vref_array_index,
437 frames,
438 pos: 0,
439 }
440 }
441
442 fn snapshot_values(&self, index: usize) -> &'a [u32] {
443 self.storage
444 .snapshots
445 .get(index)
446 .map(|snapshot| snapshot.values.as_slice())
447 .unwrap_or(&[])
448 }
449
450 pub fn iter_vable_array(&self) -> BoxArrayIter<'a> {
452 match self.vable_array_index {
453 Some(index) => BoxArrayIter::new(self.snapshot_values(index)),
454 None => BoxArrayIter::empty(),
455 }
456 }
457
458 pub fn iter_vref_array(&self) -> BoxArrayIter<'a> {
460 match self.vref_array_index {
461 Some(index) => BoxArrayIter::new(self.snapshot_values(index)),
462 None => BoxArrayIter::empty(),
463 }
464 }
465
466 pub fn iter_array(&self, snapshot_index: usize) -> BoxArrayIter<'a> {
468 BoxArrayIter::new(self.snapshot_values(snapshot_index))
469 }
470
471 pub fn length(&self, snapshot_index: usize) -> usize {
473 self.snapshot_values(snapshot_index).len()
474 }
475
476 pub fn prev(&mut self, snapshot_index: usize) -> i32 {
478 self.storage
479 .snapshots
480 .get(snapshot_index)
481 .and_then(|snapshot| snapshot.prev)
482 .map_or(SNAPSHOT_PREV_NONE, |prev| prev as i32)
483 }
484
485 pub fn unpack_jitcode_pc(&self, snapshot_index: usize) -> (u32, u32) {
487 self.storage
488 .snapshots
489 .get(snapshot_index)
490 .map(|snapshot| (snapshot.jitcode_index, snapshot.pc))
491 .unwrap_or((u32::MAX, u32::MAX))
492 }
493
494 pub fn is_empty_snapshot(&self, snapshot_index: usize) -> bool {
496 self.storage
497 .snapshots
498 .get(snapshot_index)
499 .is_none_or(|snapshot| snapshot.values.is_empty())
500 }
501
502 pub fn decode_snapshot_int(&mut self) -> i64 {
504 self.snapshot_index as i64
505 }
506
507 pub fn next_frame(&mut self) -> Option<&'a Snapshot> {
509 if self.pos < self.frames.len() {
510 let idx = self.frames[self.pos];
511 self.pos += 1;
512 self.storage.snapshots.get(idx)
513 } else {
514 None
515 }
516 }
517
518 pub fn num_frames(&self) -> usize {
520 self.frames.len()
521 }
522
523 pub fn done(&self) -> bool {
525 self.pos >= self.frames.len()
526 }
527}
528
529impl<'a> Iterator for TopDownSnapshotIterator<'a> {
530 type Item = usize;
531
532 fn next(&mut self) -> Option<Self::Item> {
533 if self.pos < self.frames.len() {
534 let idx = self.frames[self.pos];
535 self.pos += 1;
536 Some(idx)
537 } else {
538 None
539 }
540 }
541}
542
543#[derive(Debug)]
550pub struct SnapshotStorage {
551 pub snapshots: Vec<Snapshot>,
553 pub top_snapshots: Vec<TopSnapshot>,
555 pub const_refs: Vec<u64>,
558 pub const_bigints: Vec<i64>,
560 pub const_floats: Vec<f64>,
562 rooted_ref_indices: Vec<(usize, usize)>,
566 shadow_stack_base: usize,
568}
569
570impl Default for SnapshotStorage {
571 fn default() -> Self {
572 SnapshotStorage {
573 snapshots: Vec::new(),
574 top_snapshots: Vec::new(),
575 const_refs: Vec::new(),
576 const_bigints: Vec::new(),
577 const_floats: Vec::new(),
578 rooted_ref_indices: Vec::new(),
579 shadow_stack_base: majit_gc::shadow_stack::depth(),
580 }
581 }
582}
583
584impl SnapshotStorage {
585 pub fn new() -> Self {
586 Self::default()
587 }
588
589 pub fn add_snapshot(&mut self, snapshot: Snapshot) -> usize {
591 let idx = self.snapshots.len();
592 self.snapshots.push(snapshot);
593 idx
594 }
595
596 pub fn add_top_snapshot(&mut self, top: TopSnapshot) -> usize {
598 let idx = self.top_snapshots.len();
599 self.top_snapshots.push(top);
600 idx
601 }
602
603 pub fn add_const_ref(&mut self, ptr: u64) -> u32 {
606 let cr_idx = self.const_refs.len();
607 self.const_refs.push(ptr);
608 if ptr != 0 {
609 let ss_idx = majit_gc::shadow_stack::push(majit_ir::GcRef(ptr as usize));
610 self.rooted_ref_indices.push((cr_idx, ss_idx));
611 }
612 cr_idx as u32
613 }
614
615 pub fn add_const_bigint(&mut self, value: i64) -> u32 {
617 let idx = self.const_bigints.len() as u32;
618 self.const_bigints.push(value);
619 idx
620 }
621
622 pub fn add_const_float(&mut self, value: f64) -> u32 {
624 let idx = self.const_floats.len() as u32;
625 self.const_floats.push(value);
626 idx
627 }
628
629 pub fn num_snapshots(&self) -> usize {
631 self.snapshots.len()
632 }
633
634 pub fn refresh_from_gc(&mut self) {
637 for &(cr_idx, ss_idx) in &self.rooted_ref_indices {
638 self.const_refs[cr_idx] = majit_gc::shadow_stack::get(ss_idx).0 as u64;
639 }
640 }
641
642 fn release_roots(&mut self) {
644 if !self.rooted_ref_indices.is_empty() {
645 majit_gc::shadow_stack::pop_to(self.shadow_stack_base);
646 self.rooted_ref_indices.clear();
647 }
648 }
649}
650
651impl Drop for SnapshotStorage {
652 fn drop(&mut self) {
653 self.release_roots();
654 }
655}
656
657pub struct TraceIterator<'a> {
666 ops: &'a [majit_ir::Op],
668 pos: usize,
670 live_range_end: Vec<usize>,
673 ranges_computed: bool,
675}
676
677impl<'a> TraceIterator<'a> {
678 pub fn new(ops: &'a [majit_ir::Op]) -> Self {
680 TraceIterator {
681 ops,
682 pos: 0,
683 live_range_end: Vec::new(),
684 ranges_computed: false,
685 }
686 }
687
688 pub fn next_op(&mut self) -> Option<&'a majit_ir::Op> {
690 if self.pos < self.ops.len() {
691 let op = &self.ops[self.pos];
692 self.pos += 1;
693 Some(op)
694 } else {
695 None
696 }
697 }
698
699 pub fn position(&self) -> usize {
701 self.pos
702 }
703
704 pub fn done(&self) -> bool {
706 self.pos >= self.ops.len()
707 }
708
709 pub fn num_ops(&self) -> usize {
711 self.ops.len()
712 }
713
714 pub fn reset(&mut self) {
716 self.pos = 0;
717 }
718
719 pub fn compute_dead_ranges(&mut self) -> &[usize] {
722 if self.ranges_computed {
723 return &self.live_range_end;
724 }
725
726 let max_ref = self
728 .ops
729 .iter()
730 .flat_map(|op| {
731 op.args
732 .iter()
733 .chain(op.fail_args.iter().flat_map(|fa| fa.iter()))
734 .map(|r| r.0 as usize)
735 })
736 .max()
737 .unwrap_or(0);
738
739 self.live_range_end = vec![0; max_ref + 1];
740
741 for (i, op) in self.ops.iter().enumerate().rev() {
743 for arg in &op.args {
744 let idx = arg.0 as usize;
745 if idx < self.live_range_end.len() && self.live_range_end[idx] == 0 {
746 self.live_range_end[idx] = i;
747 }
748 }
749 if let Some(ref fa) = op.fail_args {
750 for arg in fa.iter() {
751 let idx = arg.0 as usize;
752 if idx < self.live_range_end.len() && self.live_range_end[idx] == 0 {
753 self.live_range_end[idx] = i;
754 }
755 }
756 }
757 }
758
759 self.ranges_computed = true;
760 &self.live_range_end
761 }
762
763 pub fn is_dead_at(&self, opref: majit_ir::OpRef, pos: usize) -> bool {
766 let idx = opref.0 as usize;
767 if idx >= self.live_range_end.len() {
768 return true; }
770 self.live_range_end[idx] < pos
771 }
772}
773
774#[derive(Clone, Debug)]
777pub struct CutTrace {
778 pub cut_at: usize,
780 pub num_inputargs: usize,
782}
783
784impl CutTrace {
785 pub fn new(cut_at: usize, num_inputargs: usize) -> Self {
786 CutTrace {
787 cut_at,
788 num_inputargs,
789 }
790 }
791}
792
793pub struct BoxArrayIter<'a> {
796 values: &'a [u32],
797 pos: usize,
798}
799
800impl<'a> BoxArrayIter<'a> {
801 pub fn empty() -> Self {
802 Self::new(&[])
803 }
804
805 pub fn new(values: &'a [u32]) -> Self {
806 BoxArrayIter { values, pos: 0 }
807 }
808
809 pub fn next_value(&mut self) -> Option<u32> {
811 if self.pos < self.values.len() {
812 let val = self.values[self.pos];
813 self.pos += 1;
814 Some(val)
815 } else {
816 None
817 }
818 }
819
820 pub fn next_decoded(&mut self) -> Option<(u8, u32)> {
822 self.next_value().map(untag)
823 }
824
825 pub fn remaining(&self) -> usize {
827 self.values.len() - self.pos
828 }
829
830 pub fn done(&self) -> bool {
832 self.pos >= self.values.len()
833 }
834}
835
836impl<'a> Iterator for BoxArrayIter<'a> {
837 type Item = u32;
838
839 fn next(&mut self) -> Option<Self::Item> {
840 self.next_value()
841 }
842}
843
844pub type Trace = TraceRecordBuffer;
851pub struct TraceRecordBuffer {
852 data: Vec<u8>,
854 num_inputargs: usize,
856 num_ops: usize,
858 snapshots: SnapshotStorage,
860 cut_points: Vec<usize>,
862 overflow: bool,
864 max_size: usize,
866 const_int_cache: HashMap<i64, u32>,
868 const_ptr_cache: HashMap<u64, u32>,
870}
871
872impl TraceRecordBuffer {
873 pub fn new(num_inputargs: usize) -> Self {
875 TraceRecordBuffer {
876 data: Vec::with_capacity(4096),
877 num_inputargs,
878 num_ops: 0,
879 snapshots: SnapshotStorage::new(),
880 cut_points: Vec::new(),
881 overflow: false,
882 max_size: 1 << 20, const_int_cache: HashMap::new(),
884 const_ptr_cache: HashMap::new(),
885 }
886 }
887
888 pub fn tag_overflow_imminent(&self) -> bool {
891 self.data.len() > self.max_size * 9 / 10
892 }
893
894 pub fn tracing_done(&mut self) -> bool {
897 if self.data.len() > self.max_size {
898 self.overflow = true;
899 }
900 !self.overflow
901 }
902
903 pub fn record_op(&mut self, opcode: u16, num_args: u8) {
905 encode_varint(&mut self.data, opcode as u64);
906 self.data.push(num_args);
907 self.num_ops += 1;
908 }
909
910 pub fn _op_start(&self) -> usize {
912 self.data.len()
913 }
914
915 pub fn _op_end(&self) -> usize {
917 self.data.len()
918 }
919
920 #[inline]
925 pub fn _encode(&self, kind: u8, value: u32) -> u32 {
926 tag(kind, value)
927 }
928
929 pub fn _cached_const_int(&mut self, value: i64) -> u32 {
932 if let Some(idx) = self.const_int_cache.get(&value) {
933 return *idx;
934 }
935 let idx = self.snapshots.add_const_bigint(value);
936 self.const_int_cache.insert(value, idx);
937 idx
938 }
939
940 pub fn _cached_const_ptr(&mut self, ptr: u64) -> u32 {
943 if let Some(idx) = self.const_ptr_cache.get(&ptr) {
944 return *idx;
945 }
946 let idx = self.snapshots.add_const_ref(ptr);
947 self.const_ptr_cache.insert(ptr, idx);
948 idx
949 }
950
951 pub fn _encode_descr(&self, descr: u32) -> u32 {
953 descr
954 }
955
956 pub fn _add_box_to_storage(&mut self, value: u32) -> usize {
960 self.snapshots.const_refs.push(value as u64);
961 self.snapshots.const_refs.len() - 1
962 }
963
964 pub fn append_byte(&mut self, byte: u8) {
966 self.data.push(byte);
967 }
968
969 pub fn append_int(&mut self, value: u32) {
971 self.record_arg(value);
972 }
973
974 pub fn append_snapshot_array_data_int(data: &mut Vec<u32>, value: i32) {
976 data.push(value as u32);
977 }
978
979 pub fn append_snapshot_data_int(data: &mut Vec<u32>, value: i32) {
981 data.push(value as u32);
982 }
983
984 pub fn _encode_snapshot(snapshot: &Snapshot) -> Vec<u32> {
986 snapshot.values.clone()
987 }
988
989 pub fn create_snapshot(&self, values: Vec<u32>) -> Snapshot {
991 Snapshot {
992 values,
993 prev: None,
994 jitcode_index: 0,
995 pc: 0,
996 }
997 }
998
999 pub fn snapshot_add_prev(snapshot: &mut Snapshot, prev: Option<usize>) {
1001 snapshot.prev = prev;
1002 }
1003
1004 pub fn record_op0(&mut self, opcode: u16) {
1007 self.record_op(opcode, 0);
1008 }
1009
1010 pub fn record_op1(&mut self, opcode: u16, arg0: u32) {
1013 self.record_op(opcode, 1);
1014 self.record_arg(arg0);
1015 }
1016
1017 pub fn record_op2(&mut self, opcode: u16, arg0: u32, arg1: u32) {
1020 self.record_op(opcode, 2);
1021 self.record_arg(arg0);
1022 self.record_arg(arg1);
1023 }
1024
1025 pub fn record_op3(&mut self, opcode: u16, arg0: u32, arg1: u32, arg2: u32) {
1028 self.record_op(opcode, 3);
1029 self.record_arg(arg0);
1030 self.record_arg(arg1);
1031 self.record_arg(arg2);
1032 }
1033
1034 pub fn _list_of_boxes(&self, boxes: &[u32]) -> Vec<u32> {
1036 boxes.iter().map(|&b| self._encode(TAGBOX, b)).collect()
1037 }
1038
1039 pub fn _list_of_boxes_virtualizable(&self, boxes: &[u32]) -> Vec<u32> {
1041 boxes.iter().map(|&b| self._encode(TAGBOX, b)).collect()
1042 }
1043
1044 pub fn new_array(&self, items: &[u32]) -> Vec<u32> {
1046 items.to_vec()
1047 }
1048
1049 pub fn record_arg(&mut self, value: u32) {
1051 encode_varint(&mut self.data, value as u64);
1052 }
1053
1054 pub fn cut_point(&mut self) {
1057 self.cut_points.push(self.data.len());
1058 }
1059
1060 pub fn num_ops(&self) -> usize {
1062 self.num_ops
1063 }
1064
1065 pub fn overflowed(&self) -> bool {
1067 self.overflow
1068 }
1069
1070 pub fn snapshots(&self) -> &SnapshotStorage {
1072 &self.snapshots
1073 }
1074
1075 pub fn snapshots_mut(&mut self) -> &mut SnapshotStorage {
1077 &mut self.snapshots
1078 }
1079
1080 pub fn capture_resumedata(&mut self, snapshot: Snapshot) -> usize {
1083 self.snapshots.add_snapshot(snapshot)
1084 }
1085
1086 pub fn create_top_snapshot(
1089 &mut self,
1090 snapshot: Snapshot,
1091 vable_array_index: Option<usize>,
1092 vref_array_index: Option<usize>,
1093 ) -> usize {
1094 let _snap_idx = self.snapshots.add_snapshot(snapshot.clone());
1095 self.snapshots.add_top_snapshot(TopSnapshot {
1096 snapshot,
1097 vable_array_index,
1098 vref_array_index,
1099 })
1100 }
1101
1102 pub fn create_empty_top_snapshot(
1105 &mut self,
1106 vable_array_index: Option<usize>,
1107 vref_array_index: Option<usize>,
1108 ) -> usize {
1109 let empty_snap = Snapshot {
1110 values: Vec::new(),
1111 prev: None,
1112 jitcode_index: 0,
1113 pc: 0,
1114 };
1115 self.create_top_snapshot(empty_snap, vable_array_index, vref_array_index)
1116 }
1117
1118 pub fn get_live_ranges(&self, ops: &[majit_ir::Op]) -> Vec<usize> {
1123 let mut iter = TraceIterator::new(ops);
1124 iter.compute_dead_ranges().to_vec()
1125 }
1126
1127 pub fn data_len(&self) -> usize {
1131 self.data.len()
1132 }
1133
1134 pub fn max_size(&self) -> usize {
1136 self.max_size
1137 }
1138
1139 pub fn set_max_size(&mut self, size: usize) {
1141 self.max_size = size;
1142 }
1143
1144 pub fn capture_resumedata_framestack(
1149 &mut self,
1150 frame_pcs: &[u64],
1151 frame_slots: &[Vec<u32>],
1152 _virtualizable_boxes: &[u32],
1153 _virtualref_boxes: &[u32],
1154 ) -> usize {
1155 if frame_pcs.is_empty() {
1156 let empty_snap = Snapshot {
1157 values: vec![],
1158 prev: None,
1159 jitcode_index: 0,
1160 pc: 0,
1161 };
1162 let top = TopSnapshot {
1163 snapshot: empty_snap,
1164 vable_array_index: None,
1165 vref_array_index: None,
1166 };
1167 return self.snapshots.add_top_snapshot(top);
1168 }
1169
1170 let n = frame_pcs.len() - 1;
1171
1172 let mut parent_idx: Option<usize> = None;
1174 for i in 0..n {
1175 let snapshot = Snapshot {
1176 values: if i < frame_slots.len() {
1177 frame_slots[i].clone()
1178 } else {
1179 vec![]
1180 },
1181 prev: parent_idx,
1182 jitcode_index: 0,
1183 pc: frame_pcs[i] as u32,
1184 };
1185 parent_idx = Some(self.snapshots.add_snapshot(snapshot));
1186 }
1187
1188 let top_snap = Snapshot {
1190 values: if n < frame_slots.len() {
1191 frame_slots[n].clone()
1192 } else {
1193 vec![]
1194 },
1195 prev: parent_idx,
1196 jitcode_index: 0,
1197 pc: frame_pcs[n] as u32,
1198 };
1199 let top = TopSnapshot {
1200 snapshot: top_snap,
1201 vable_array_index: None,
1202 vref_array_index: None,
1203 };
1204 self.snapshots.add_top_snapshot(top)
1205 }
1206
1207 pub fn get_dead_ranges(&self, ops: &[majit_ir::Op]) -> Vec<usize> {
1213 let live_ranges = self.get_live_ranges(ops);
1214 let mut dead_ranges = vec![0usize; live_ranges.len() + 2];
1215 for (i, &last_use) in live_ranges.iter().enumerate() {
1216 if last_use > 0 && last_use + 1 < dead_ranges.len() {
1217 let mut pos = last_use + 1;
1220 while pos < dead_ranges.len() && dead_ranges[pos] != 0 {
1221 pos += 1;
1222 }
1223 if pos < dead_ranges.len() {
1224 dead_ranges[pos] = i;
1225 }
1226 }
1227 }
1228 dead_ranges
1229 }
1230
1231 pub fn unpack(&self, encoded: &[u32]) -> Vec<(u8, u32)> {
1233 encoded.iter().copied().map(untag).collect()
1234 }
1235}
1236
1237#[cfg(test)]
1238mod tests {
1239 use super::*;
1240
1241 #[test]
1242 fn test_varint_roundtrip() {
1243 let values = [0u64, 1, 127, 128, 255, 256, 65535, 0xFFFF_FFFF, u64::MAX];
1244 for &val in &values {
1245 let mut buf = Vec::new();
1246 encode_varint(&mut buf, val);
1247 let (decoded, consumed) = decode_varint(&buf);
1248 assert_eq!(decoded, val, "roundtrip failed for {val}");
1249 assert_eq!(consumed, buf.len());
1250 }
1251 }
1252
1253 #[test]
1254 fn test_varint_small_values() {
1255 for val in 0..=127u64 {
1257 let mut buf = Vec::new();
1258 encode_varint(&mut buf, val);
1259 assert_eq!(buf.len(), 1, "value {val} should be 1 byte");
1260 assert_eq!(buf[0], val as u8);
1261 }
1262 }
1263
1264 #[test]
1265 fn test_varint_128_is_two_bytes() {
1266 let mut buf = Vec::new();
1267 encode_varint(&mut buf, 128);
1268 assert_eq!(buf.len(), 2);
1269 let (decoded, consumed) = decode_varint(&buf);
1270 assert_eq!(decoded, 128);
1271 assert_eq!(consumed, 2);
1272 }
1273
1274 #[test]
1275 fn test_empty_trace_roundtrip() {
1276 let trace = TreeLoop::new(vec![], vec![]);
1277 let encoded = encode_trace(&trace);
1278 let decoded = decode_trace(&encoded);
1279 assert_eq!(decoded.num_inputargs(), 0);
1280 assert_eq!(decoded.num_ops(), 0);
1281 }
1282
1283 #[test]
1284 fn test_trace_roundtrip() {
1285 let inputargs = vec![
1286 InputArg::new_int(0),
1287 InputArg::new_ref(1),
1288 InputArg::new_float(2),
1289 ];
1290 let ops = vec![
1291 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)]),
1292 Op::new(OpCode::GuardTrue, &[OpRef(1)]),
1293 Op::new(OpCode::Jump, &[OpRef(2)]),
1294 ];
1295 let trace = TreeLoop::new(inputargs, ops);
1296
1297 let encoded = encode_trace(&trace);
1298 let decoded = decode_trace(&encoded);
1299
1300 assert_eq!(decoded.num_inputargs(), 3);
1301 assert_eq!(decoded.inputargs[0].tp, Type::Int);
1302 assert_eq!(decoded.inputargs[0].index, 0);
1303 assert_eq!(decoded.inputargs[1].tp, Type::Ref);
1304 assert_eq!(decoded.inputargs[1].index, 1);
1305 assert_eq!(decoded.inputargs[2].tp, Type::Float);
1306 assert_eq!(decoded.inputargs[2].index, 2);
1307
1308 assert_eq!(decoded.num_ops(), 3);
1309 assert_eq!(decoded.ops[0].opcode, OpCode::IntAdd);
1310 assert_eq!(decoded.ops[0].args.as_slice(), &[OpRef(0), OpRef(0)]);
1311 assert_eq!(decoded.ops[1].opcode, OpCode::GuardTrue);
1312 assert_eq!(decoded.ops[1].args.as_slice(), &[OpRef(1)]);
1313 assert_eq!(decoded.ops[2].opcode, OpCode::Jump);
1314 assert_eq!(decoded.ops[2].args.as_slice(), &[OpRef(2)]);
1315
1316 assert!(decoded.is_loop());
1317 }
1318
1319 #[test]
1320 fn test_trace_with_many_ops() {
1321 let inputargs = vec![InputArg::new_int(0)];
1322 let mut ops = Vec::new();
1323
1324 for i in 0..100u32 {
1326 ops.push(Op::new(OpCode::IntAdd, &[OpRef(i), OpRef(0)]));
1327 }
1328 ops.push(Op::new(OpCode::Jump, &[OpRef(100)]));
1329
1330 let trace = TreeLoop::new(inputargs, ops);
1331 let encoded = encode_trace(&trace);
1332 let decoded = decode_trace(&encoded);
1333
1334 assert_eq!(decoded.num_ops(), 101);
1335 for i in 0..100 {
1336 assert_eq!(decoded.ops[i].opcode, OpCode::IntAdd);
1337 assert_eq!(decoded.ops[i].args[0], OpRef(i as u32));
1338 assert_eq!(decoded.ops[i].args[1], OpRef(0));
1339 }
1340 assert_eq!(decoded.ops[100].opcode, OpCode::Jump);
1341 assert!(decoded.is_loop());
1342 }
1343
1344 #[test]
1345 fn test_trace_with_finish() {
1346 let inputargs = vec![InputArg::new_int(0)];
1347 let ops = vec![
1348 Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)]),
1349 Op::new(OpCode::Finish, &[OpRef(1)]),
1350 ];
1351 let trace = TreeLoop::new(inputargs, ops);
1352 let encoded = encode_trace(&trace);
1353 let decoded = decode_trace(&encoded);
1354
1355 assert!(decoded.is_finished());
1356 assert!(!decoded.is_loop());
1357 }
1358
1359 #[test]
1360 fn test_trace_preserves_descr_flag() {
1361 let ops = vec![Op::new(OpCode::IntAdd, &[OpRef(0), OpRef(0)])];
1364 let trace = TreeLoop::new(vec![InputArg::new_int(0)], ops);
1365
1366 let encoded = encode_trace(&trace);
1367 assert_eq!(*encoded.last().unwrap(), 0);
1369 }
1370
1371 #[test]
1372 fn test_varint_multiple_in_buffer() {
1373 let mut buf = Vec::new();
1374 encode_varint(&mut buf, 42);
1375 encode_varint(&mut buf, 12345);
1376 encode_varint(&mut buf, 0);
1377
1378 let (v1, n1) = decode_varint(&buf);
1379 assert_eq!(v1, 42);
1380 let (v2, n2) = decode_varint(&buf[n1..]);
1381 assert_eq!(v2, 12345);
1382 let (v3, _n3) = decode_varint(&buf[n1 + n2..]);
1383 assert_eq!(v3, 0);
1384 }
1385
1386 #[test]
1387 fn test_signed_varint_roundtrip() {
1388 let values = [0i64, 1, -1, 127, -128, 1000, -1000, i64::MAX, i64::MIN];
1389 for &val in &values {
1390 let mut buf = Vec::new();
1391 encode_varint_signed(&mut buf, val);
1392 let (decoded, _consumed) = decode_varint_signed(&buf);
1393 assert_eq!(decoded, val, "signed varint roundtrip failed for {val}");
1394 }
1395 }
1396
1397 #[test]
1398 fn test_tag_untag_roundtrip() {
1399 for kind in [TAGINT, TAGCONSTPTR, TAGCONSTOTHER, TAGBOX] {
1400 for value in [0u32, 1, 42, 255, 1000] {
1401 let tagged = tag(kind, value);
1402 let (k, v) = untag(tagged);
1403 assert_eq!(k, kind, "tag kind mismatch");
1404 assert_eq!(v, value, "tag value mismatch");
1405 }
1406 }
1407 }
1408
1409 #[test]
1410 fn test_snapshot_storage() {
1411 let mut storage = SnapshotStorage::new();
1412 let snap = Snapshot {
1413 values: vec![tag(TAGINT, 42), tag(TAGBOX, 0)],
1414 prev: None,
1415 jitcode_index: 0,
1416 pc: 10,
1417 };
1418 let idx = storage.add_snapshot(snap);
1419 assert_eq!(idx, 0);
1420 assert_eq!(storage.num_snapshots(), 1);
1421
1422 let const_idx = storage.add_const_ref(0x1000);
1423 assert_eq!(const_idx, 0);
1424 let float_idx = storage.add_const_float(3.14);
1425 assert_eq!(float_idx, 0);
1426 }
1427
1428 #[test]
1429 fn test_trace_iterator_dead_ranges() {
1430 let ops = vec![
1431 majit_ir::Op::new(majit_ir::OpCode::IntAdd, &[OpRef(100), OpRef(101)]),
1432 majit_ir::Op::new(majit_ir::OpCode::IntAdd, &[OpRef(0), OpRef(101)]),
1433 majit_ir::Op::new(majit_ir::OpCode::Finish, &[OpRef(1)]),
1434 ];
1435 let mut iter = TraceIterator::new(&ops);
1436 assert_eq!(iter.num_ops(), 3);
1437 assert!(!iter.done());
1438
1439 let ranges = iter.compute_dead_ranges();
1440 assert!(ranges.len() > 100);
1443
1444 assert!(iter.next_op().is_some());
1446 assert_eq!(iter.position(), 1);
1447 }
1448
1449 #[test]
1450 fn test_box_array_iter() {
1451 let values = vec![tag(TAGINT, 42), tag(TAGBOX, 10), tag(TAGCONSTPTR, 0)];
1452 let mut iter = BoxArrayIter::new(&values);
1453 assert_eq!(iter.remaining(), 3);
1454
1455 let (k, v) = iter.next_decoded().unwrap();
1456 assert_eq!(k, TAGINT);
1457 assert_eq!(v, 42);
1458
1459 let (k, v) = iter.next_decoded().unwrap();
1460 assert_eq!(k, TAGBOX);
1461 assert_eq!(v, 10);
1462
1463 assert_eq!(iter.remaining(), 1);
1464 assert!(!iter.done());
1465 iter.next_value();
1466 assert!(iter.done());
1467 }
1468
1469 #[test]
1470 fn test_trace_record_buffer() {
1471 let mut buf = TraceRecordBuffer::new(2);
1472 assert_eq!(buf.num_ops(), 0);
1473 assert!(!buf.overflowed());
1474 assert!(!buf.tag_overflow_imminent());
1475
1476 buf.record_op(OpCode::IntAdd as u16, 2);
1477 buf.record_arg(100);
1478 buf.record_arg(101);
1479 assert_eq!(buf.num_ops(), 1);
1480
1481 buf.cut_point();
1482 assert!(buf.tracing_done());
1483 }
1484
1485 #[test]
1486 fn test_trace_record_snapshot() {
1487 let mut buf = TraceRecordBuffer::new(1);
1488 let snap = Snapshot {
1489 values: vec![tag(TAGINT, 42)],
1490 prev: None,
1491 jitcode_index: 0,
1492 pc: 5,
1493 };
1494 let idx = buf.capture_resumedata(snap);
1495 assert_eq!(idx, 0);
1496 assert_eq!(buf.snapshots().num_snapshots(), 1);
1497 }
1498}