tycho_vm/
cont.rs

1use std::mem::ManuallyDrop;
2use std::rc::Rc;
3
4#[cfg(feature = "tracing")]
5use tracing::instrument;
6use tycho_types::error::Error;
7use tycho_types::prelude::*;
8
9use crate::error::VmResult;
10use crate::saferc::{SafeDelete, SafeRc, SafeRcMakeMut};
11use crate::stack::{
12    RcStackValue, Stack, StackValue, StackValueType, Tuple, TupleExt, load_slice_as_stack_value,
13    store_slice_as_stack_value,
14};
15use crate::state::VmState;
16use crate::util::{OwnedCellSlice, Uint4, ensure_empty_slice};
17
18/// Total state of VM.
19#[derive(Debug, Default, Clone)]
20pub struct ControlData {
21    pub nargs: Option<u16>,
22    pub stack: Option<SafeRc<Stack>>,
23    pub save: ControlRegs,
24    pub cp: Option<u16>,
25}
26
27impl ControlData {
28    pub fn require_nargs(&self, copy: usize) -> VmResult<()> {
29        if matches!(self.nargs, Some(nargs) if (nargs as usize) < copy) {
30            vm_bail!(StackUnderflow(copy as _))
31        }
32        Ok(())
33    }
34}
35
36impl Store for ControlData {
37    fn store_into(
38        &self,
39        builder: &mut CellBuilder,
40        context: &dyn CellContext,
41    ) -> Result<(), Error> {
42        match self.nargs {
43            None => ok!(builder.store_bit_zero()),
44            Some(nargs) if nargs <= 0x1fff => {
45                ok!(builder.store_bit_one());
46                ok!(builder.store_uint(nargs as _, 13));
47            }
48            Some(_) => return Err(Error::IntOverflow),
49        }
50
51        ok!(self.stack.as_deref().store_into(builder, context));
52        ok!(self.save.store_into(builder, context));
53        ok!(self.cp.store_into(builder, context));
54        Ok(())
55    }
56}
57
58impl Load<'_> for ControlData {
59    fn load_from(slice: &mut CellSlice<'_>) -> Result<Self, Error> {
60        Ok(ControlData {
61            nargs: match ok!(slice.load_bit()) {
62                false => None,
63                true => Some(ok!(slice.load_uint(13)) as u16),
64            },
65            stack: match ok!(slice.load_bit()) {
66                false => None,
67                true => Some(ok!(SafeRc::<Stack>::load_from(slice))),
68            },
69            save: ok!(ControlRegs::load_from(slice)),
70            cp: ok!(Load::load_from(slice)),
71        })
72    }
73}
74
75/// Control registers page.
76#[derive(Default, Debug, Clone)]
77pub struct ControlRegs {
78    pub c: [Option<RcCont>; 4],
79    pub d: [Option<Cell>; 2],
80    pub c7: Option<SafeRc<Tuple>>,
81}
82
83impl ControlRegs {
84    const CONT_REG_COUNT: usize = 4;
85    const DATA_REG_OFFSET: usize = Self::CONT_REG_COUNT;
86    const DATA_REG_COUNT: usize = 2;
87    const DATA_REG_RANGE: std::ops::Range<usize> =
88        Self::DATA_REG_OFFSET..Self::DATA_REG_OFFSET + Self::DATA_REG_COUNT;
89
90    pub fn is_valid_idx(i: usize) -> bool {
91        i < Self::CONT_REG_COUNT || Self::DATA_REG_RANGE.contains(&i) || i == 7
92    }
93
94    pub fn merge(&mut self, other: &ControlRegs) {
95        for (c, other_c) in std::iter::zip(&mut self.c, &other.c) {
96            Self::merge_stack_value(c, other_c);
97        }
98        for (d, other_d) in std::iter::zip(&mut self.d, &other.d) {
99            Self::merge_cell_value(d, other_d)
100        }
101        Self::merge_stack_value(&mut self.c7, &other.c7)
102    }
103
104    pub fn preclear(&mut self, other: &ControlRegs) {
105        for (c, other_c) in std::iter::zip(&mut self.c, &other.c) {
106            if other_c.is_some() {
107                *c = None;
108            }
109        }
110        for (d, other_d) in std::iter::zip(&mut self.d, &other.d) {
111            if other_d.is_some() {
112                *d = None;
113            }
114        }
115        if other.c7.is_some() {
116            self.c7 = None;
117        }
118    }
119
120    // TODO: use `&dyn StackValue` for value?
121    pub fn set(&mut self, i: usize, value: RcStackValue) -> VmResult<()> {
122        if i < Self::CONT_REG_COUNT {
123            self.c[i] = Some(ok!(value.into_cont()));
124        } else if Self::DATA_REG_RANGE.contains(&i) {
125            let cell = ok!(value.into_cell());
126            self.d[i - Self::DATA_REG_OFFSET] = Some(SafeRc::unwrap_or_clone(cell));
127        } else if i == 7 {
128            self.c7 = Some(ok!(value.into_tuple()));
129        } else {
130            vm_bail!(ControlRegisterOutOfRange(i))
131        }
132        Ok(())
133    }
134
135    pub fn set_c(&mut self, i: usize, cont: RcCont) -> bool {
136        if i < Self::CONT_REG_COUNT {
137            self.c[i] = Some(cont);
138            true
139        } else {
140            false
141        }
142    }
143
144    pub fn set_d(&mut self, mut i: usize, cell: Cell) -> bool {
145        i = i.wrapping_sub(Self::DATA_REG_OFFSET);
146        if i < Self::DATA_REG_COUNT {
147            self.d[i] = Some(cell);
148            true
149        } else {
150            false
151        }
152    }
153
154    pub fn get_d(&self, mut i: usize) -> Option<Cell> {
155        i = i.wrapping_sub(Self::DATA_REG_OFFSET);
156        if i < Self::DATA_REG_COUNT {
157            self.d[i].clone()
158        } else {
159            None
160        }
161    }
162
163    pub fn set_c7(&mut self, tuple: SafeRc<Tuple>) {
164        self.c7 = Some(tuple);
165    }
166
167    pub fn get_as_stack_value(&self, i: usize) -> Option<RcStackValue> {
168        if i < Self::CONT_REG_COUNT {
169            self.c.get(i)?.clone().map(SafeRc::into_dyn_value)
170        } else if Self::DATA_REG_RANGE.contains(&i) {
171            self.d[i - Self::DATA_REG_OFFSET]
172                .clone()
173                .map(SafeRc::new_dyn_value)
174        } else if i == 7 {
175            self.c7.clone().map(SafeRc::into_dyn_value)
176        } else {
177            None
178        }
179    }
180
181    pub fn define_c0(&mut self, cont: &Option<RcCont>) {
182        if self.c[0].is_none() {
183            self.c[0].clone_from(cont)
184        }
185    }
186
187    pub fn define_c1(&mut self, cont: &Option<RcCont>) {
188        if self.c[1].is_none() {
189            self.c[1].clone_from(cont)
190        }
191    }
192
193    pub fn define_c2(&mut self, cont: &Option<RcCont>) {
194        if self.c[2].is_none() {
195            self.c[2].clone_from(cont)
196        }
197    }
198
199    pub fn define(&mut self, i: usize, value: RcStackValue) -> VmResult<()> {
200        if i < Self::CONT_REG_COUNT {
201            let cont = ok!(value.into_cont());
202            vm_ensure!(self.c[i].is_none(), ControlRegisterRedefined);
203            self.c[i] = Some(cont);
204        } else if Self::DATA_REG_RANGE.contains(&i) {
205            let cell = ok!(value.into_cell());
206            let d = &mut self.d[i - Self::DATA_REG_OFFSET];
207            vm_ensure!(d.is_none(), ControlRegisterRedefined);
208            *d = Some(SafeRc::unwrap_or_clone(cell));
209        } else if i == 7 {
210            let tuple = ok!(value.into_tuple());
211
212            // NOTE: Value is ignored on redefinition
213            if self.c7.is_none() {
214                self.c7 = Some(tuple);
215            }
216        } else {
217            vm_bail!(ControlRegisterOutOfRange(i))
218        }
219        Ok(())
220    }
221
222    pub fn get_c7_params(&self) -> VmResult<&[RcStackValue]> {
223        let Some(c7) = self.c7.as_ref() else {
224            vm_bail!(ControlRegisterOutOfRange(7))
225        };
226
227        c7.try_get_tuple_range(0, 0..=255)
228    }
229
230    fn merge_cell_value(lhs: &mut Option<Cell>, rhs: &Option<Cell>) {
231        if let Some(rhs) = rhs {
232            if let Some(lhs) = lhs {
233                let lhs = lhs.as_ref() as *const _ as *const ();
234                let rhs = rhs.as_ref() as *const _ as *const ();
235                if std::ptr::eq(lhs, rhs) {
236                    return;
237                }
238            }
239            *lhs = Some(rhs.clone())
240        }
241    }
242
243    fn merge_stack_value<T: SafeDelete + ?Sized>(
244        lhs: &mut Option<SafeRc<T>>,
245        rhs: &Option<SafeRc<T>>,
246    ) {
247        if let Some(rhs) = rhs {
248            if let Some(lhs) = lhs
249                && SafeRc::ptr_eq(lhs, rhs)
250            {
251                return;
252            }
253            *lhs = Some(rhs.clone())
254        }
255    }
256}
257
258impl Store for ControlRegs {
259    fn store_into(
260        &self,
261        builder: &mut CellBuilder,
262        context: &dyn CellContext,
263    ) -> Result<(), Error> {
264        #[repr(transparent)]
265        struct AsDictValue<'a>(&'a dyn StackValue);
266
267        impl Store for AsDictValue<'_> {
268            #[inline]
269            fn store_into(
270                &self,
271                builder: &mut CellBuilder,
272                context: &dyn CellContext,
273            ) -> Result<(), Error> {
274                self.0.store_as_stack_value(builder, context)
275            }
276        }
277
278        // TODO: optimize by building dict manually
279
280        let mut dict = Dict::<Uint4, AsDictValue>::new();
281
282        for (i, c) in self.c.iter().enumerate() {
283            if let Some(c) = c {
284                ok!(dict.set_ext(Uint4(i), AsDictValue(c.as_stack_value()), context));
285            }
286        }
287        for (i, d) in self.d.iter().enumerate() {
288            if let Some(d) = d {
289                ok!(dict.set_ext(Uint4(i + Self::DATA_REG_OFFSET), AsDictValue(d), context));
290            }
291        }
292        if let Some(c7) = &self.c7 {
293            ok!(dict.set_ext(Uint4(7), AsDictValue(c7.as_ref()), context));
294        }
295
296        dict.store_into(builder, context)
297    }
298}
299
300impl Load<'_> for ControlRegs {
301    fn load_from(slice: &mut CellSlice<'_>) -> Result<Self, Error> {
302        let dict = ok!(Dict::<Uint4, CellSlice<'_>>::load_from(slice));
303
304        let mut result = ControlRegs::default();
305        for entry in dict.iter() {
306            let (key, ref mut slice) = ok!(entry);
307            let value = ok!(Stack::load_stack_value(slice));
308            ok!(ensure_empty_slice(slice));
309            if result.set(key.0, value).is_err() {
310                return Err(Error::InvalidData);
311            }
312        }
313
314        Ok(result)
315    }
316}
317
318/// Continuation interface.
319pub trait Cont: Store + SafeDelete + dyn_clone::DynClone + std::fmt::Debug {
320    fn rc_into_dyn(self: Rc<Self>) -> Rc<dyn StackValue>;
321
322    fn as_stack_value(&self) -> &dyn StackValue;
323
324    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
325
326    fn jump(self: Rc<Self>, state: &mut VmState, exit_code: &mut i32) -> VmResult<Option<RcCont>>;
327
328    fn get_control_data(&self) -> Option<&ControlData> {
329        None
330    }
331
332    fn get_control_data_mut(&mut self) -> Option<&mut ControlData> {
333        None
334    }
335}
336
337impl<T: Cont + 'static> StackValue for T {
338    #[inline]
339    fn rc_into_dyn(self: Rc<Self>) -> Rc<dyn StackValue> {
340        self
341    }
342
343    fn raw_ty(&self) -> u8 {
344        StackValueType::Cont as _
345    }
346
347    fn store_as_stack_value(
348        &self,
349        builder: &mut CellBuilder,
350        context: &dyn CellContext,
351    ) -> Result<(), Error> {
352        ok!(builder.store_u8(0x06));
353        self.store_into(builder, context)
354    }
355
356    fn fmt_dump(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
357        ok!(f.write_str("Cont{{"));
358        ok!(<T as Cont>::fmt(self, f));
359        f.write_str("}}")
360    }
361
362    fn as_cont(&self) -> Option<&dyn Cont> {
363        Some(self)
364    }
365
366    fn rc_into_cont(self: Rc<Self>) -> VmResult<Rc<dyn Cont>> {
367        Ok(self)
368    }
369}
370
371/// Continuation.
372pub type RcCont = SafeRc<dyn Cont>;
373
374impl<'a> Load<'a> for RcCont {
375    #[inline]
376    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
377        load_cont(slice)
378    }
379}
380
381impl dyn Cont {
382    pub fn has_c0(&self) -> bool {
383        if let Some(control) = self.get_control_data() {
384            control.save.c[0].is_some()
385        } else {
386            false
387        }
388    }
389}
390
391impl SafeRcMakeMut for dyn Cont {
392    #[inline]
393    fn rc_make_mut(rc: &mut Rc<Self>) -> &mut Self {
394        dyn_clone::rc_make_mut(rc)
395    }
396}
397
398impl<T: Cont + 'static> SafeRc<T> {
399    #[inline]
400    pub fn into_dyn_cont(self) -> RcCont {
401        let value = SafeRc::into_inner(self);
402        SafeRc(ManuallyDrop::new(value))
403    }
404}
405
406impl<T: Cont + 'static> From<T> for RcCont {
407    #[inline]
408    fn from(value: T) -> Self {
409        Self(ManuallyDrop::new(Rc::new(value)))
410    }
411}
412
413impl<T: Cont + 'static> From<Rc<T>> for RcCont {
414    #[inline]
415    fn from(value: Rc<T>) -> Self {
416        Self(ManuallyDrop::new(value))
417    }
418}
419
420pub(crate) fn load_cont(slice: &mut CellSlice) -> Result<RcCont, Error> {
421    #[allow(clippy::unusual_byte_groupings)]
422    const MASK: u64 = 0x1_007_01_1_1_0001_0001;
423
424    // Prefetch slice prefix aligned to 6 bits
425    let slice_bits = slice.size_bits();
426    let n = if slice_bits < 6 {
427        ok!(slice.get_small_uint(0, slice_bits)) << (6 - slice_bits)
428    } else {
429        ok!(slice.get_small_uint(0, 6))
430    };
431
432    // Count ones in first N bits of mask
433    let n = (MASK & (2u64 << n).wrapping_sub(1)).count_ones() - 1;
434
435    // Match bit count with tag ranges
436    Ok(match n {
437        // 00xxxx -> 0 (16)
438        0 => SafeRc::from(ok!(OrdCont::load_from(slice))),
439        // 01xxxx -> 1 (16)
440        1 => SafeRc::from(ok!(ArgContExt::load_from(slice))),
441        // 1000xx -> 2 (4)
442        2 => SafeRc::from(ok!(QuitCont::load_from(slice))),
443        // 1001xx -> 3 (4)
444        3 => SafeRc::from(ok!(ExcQuitCont::load_from(slice))),
445        // 10100x -> 4 (2)
446        4 => SafeRc::from(ok!(RepeatCont::load_from(slice))),
447        // 110000 -> 5 (1)
448        5 => SafeRc::from(ok!(UntilCont::load_from(slice))),
449        // 110001 -> 6 (1)
450        6 => SafeRc::from(ok!(AgainCont::load_from(slice))),
451        // 11001x -> 7 (2)
452        7 => SafeRc::from(ok!(WhileCont::load_from(slice))),
453        // 1111xx -> 8 (4)
454        8 => SafeRc::from(ok!(PushIntCont::load_from(slice))),
455        // all other
456        _ => return Err(Error::InvalidTag),
457    })
458}
459
460/// Continuation that represents the end of work of TVM.
461#[derive(Debug, Copy, Clone)]
462pub struct QuitCont {
463    pub exit_code: i32,
464}
465
466impl QuitCont {
467    const TAG: u8 = 0b1000;
468}
469
470impl Cont for QuitCont {
471    #[inline]
472    fn rc_into_dyn(self: Rc<Self>) -> Rc<dyn StackValue> {
473        self
474    }
475
476    fn as_stack_value(&self) -> &dyn StackValue {
477        self
478    }
479
480    #[inline]
481    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
482        f.write_str("vmc_quit")
483    }
484
485    #[cfg_attr(
486        feature = "tracing",
487        instrument(level = "trace", name = "quit_cont", skip_all)
488    )]
489    fn jump(self: Rc<Self>, _: &mut VmState, exit_code: &mut i32) -> VmResult<Option<RcCont>> {
490        *exit_code = !self.exit_code;
491        Ok(None)
492    }
493}
494
495impl Store for QuitCont {
496    fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
497        ok!(builder.store_small_uint(Self::TAG, 4));
498        builder.store_u32(self.exit_code as u32)
499    }
500}
501
502impl Load<'_> for QuitCont {
503    fn load_from(slice: &mut CellSlice<'_>) -> Result<Self, Error> {
504        if ok!(slice.load_small_uint(4)) != Self::TAG {
505            return Err(Error::InvalidTag);
506        }
507
508        Ok(Self {
509            exit_code: ok!(slice.load_u32()) as i32,
510        })
511    }
512}
513
514/// Default exception handler continuation.
515#[derive(Debug, Copy, Clone)]
516pub struct ExcQuitCont;
517
518impl ExcQuitCont {
519    const TAG: u8 = 0b1001;
520}
521
522impl Cont for ExcQuitCont {
523    #[inline]
524    fn rc_into_dyn(self: Rc<Self>) -> Rc<dyn StackValue> {
525        self
526    }
527
528    fn as_stack_value(&self) -> &dyn StackValue {
529        self
530    }
531
532    #[inline]
533    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
534        f.write_str("vmc_quit_exc")
535    }
536
537    #[cfg_attr(
538        feature = "tracing",
539        instrument(level = "trace", name = "exc_quit_cont", skip_all)
540    )]
541    fn jump(self: Rc<Self>, state: &mut VmState, exit_code: &mut i32) -> VmResult<Option<RcCont>> {
542        let n = SafeRc::make_mut(&mut state.stack)
543            .pop_smallint_range(0, 0xffff)
544            .unwrap_or_else(|e| e.as_exception() as u32);
545        vm_log_trace!("terminating vm in the default exception handler: n={n}");
546        *exit_code = !(n as i32);
547        Ok(None)
548    }
549}
550
551impl Store for ExcQuitCont {
552    fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
553        builder.store_small_uint(Self::TAG, 4)
554    }
555}
556
557impl Load<'_> for ExcQuitCont {
558    #[inline]
559    fn load_from(slice: &mut CellSlice<'_>) -> Result<Self, Error> {
560        if ok!(slice.load_small_uint(4)) == Self::TAG {
561            Ok(Self)
562        } else {
563            Err(Error::InvalidTag)
564        }
565    }
566}
567
568/// Continuation that pushes a single integer to the stack.
569#[derive(Debug, Clone)]
570pub struct PushIntCont {
571    pub value: i32,
572    pub next: RcCont,
573}
574
575impl PushIntCont {
576    const TAG: u8 = 0b1111;
577}
578
579impl Cont for PushIntCont {
580    #[inline]
581    fn rc_into_dyn(self: Rc<Self>) -> Rc<dyn StackValue> {
582        self
583    }
584
585    fn as_stack_value(&self) -> &dyn StackValue {
586        self
587    }
588
589    #[inline]
590    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
591        f.write_str("vmc_pushint")
592    }
593
594    #[cfg_attr(
595        feature = "tracing",
596        instrument(
597            level = "trace",
598            name = "push_int_cont",
599            fields(value = self.value),
600            skip_all,
601        )
602    )]
603    fn jump(self: Rc<Self>, state: &mut VmState, _: &mut i32) -> VmResult<Option<RcCont>> {
604        ok!(SafeRc::make_mut(&mut state.stack).push_int(self.value));
605        Ok(Some(match Rc::try_unwrap(self) {
606            Ok(this) => this.next,
607            Err(this) => this.next.clone(),
608        }))
609    }
610}
611
612impl Store for PushIntCont {
613    fn store_into(
614        &self,
615        builder: &mut CellBuilder,
616        context: &dyn CellContext,
617    ) -> Result<(), Error> {
618        ok!(builder.store_small_uint(Self::TAG, 4));
619        ok!(builder.store_u32(self.value as u32));
620        builder.store_reference(ok!(CellBuilder::build_from_ext(&*self.next, context)))
621    }
622}
623
624impl Load<'_> for PushIntCont {
625    fn load_from(slice: &mut CellSlice<'_>) -> Result<Self, Error> {
626        if ok!(slice.load_small_uint(4)) != Self::TAG {
627            return Err(Error::InvalidTag);
628        }
629
630        Ok(Self {
631            value: ok!(slice.load_u32()) as i32,
632            next: ok!(load_cont(slice)),
633        })
634    }
635}
636
637/// Continuation that takes an integer `n` and a continuation `c`,
638/// and executes `c` `n` times.
639#[derive(Debug, Clone)]
640pub struct RepeatCont {
641    pub count: u64,
642    pub body: RcCont,
643    pub after: RcCont,
644}
645
646impl RepeatCont {
647    const TAG: u8 = 0b1010;
648    const MAX_COUNT: u64 = 0x8000000000000000;
649}
650
651impl Cont for RepeatCont {
652    #[inline]
653    fn rc_into_dyn(self: Rc<Self>) -> Rc<dyn StackValue> {
654        self
655    }
656
657    fn as_stack_value(&self) -> &dyn StackValue {
658        self
659    }
660
661    #[inline]
662    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
663        f.write_str("vmc_repeat")
664    }
665
666    #[cfg_attr(
667        feature = "tracing",
668        instrument(
669            level = "trace",
670            name = "repeat_cont",
671            fields(value = self.count),
672            skip_all,
673        )
674    )]
675    fn jump(mut self: Rc<Self>, state: &mut VmState, _: &mut i32) -> VmResult<Option<RcCont>> {
676        if self.count == 0 {
677            return Ok(Some(self.after.clone()));
678        }
679        if self.body.has_c0() {
680            return Ok(Some(self.body.clone()));
681        }
682
683        let body = self.body.clone();
684        match Rc::get_mut(&mut self) {
685            Some(this) => {
686                this.count -= 1;
687                state.set_c0(RcCont::from(self))
688            }
689            None => state.set_c0(SafeRc::from(RepeatCont {
690                count: self.count - 1,
691                body: self.body.clone(),
692                after: self.after.clone(),
693            })),
694        }
695
696        Ok(Some(body))
697    }
698}
699
700impl Store for RepeatCont {
701    fn store_into(
702        &self,
703        builder: &mut CellBuilder,
704        context: &dyn CellContext,
705    ) -> Result<(), Error> {
706        if self.count >= Self::MAX_COUNT {
707            return Err(Error::IntOverflow);
708        }
709        ok!(builder.store_small_uint(Self::TAG, 4));
710        ok!(builder.store_u64(self.count));
711        ok!(builder.store_reference(ok!(CellBuilder::build_from_ext(&*self.body, context))));
712        builder.store_reference(ok!(CellBuilder::build_from_ext(&*self.after, context)))
713    }
714}
715
716impl Load<'_> for RepeatCont {
717    fn load_from(slice: &mut CellSlice<'_>) -> Result<Self, Error> {
718        if ok!(slice.load_small_uint(4)) != Self::TAG {
719            return Err(Error::InvalidTag);
720        }
721
722        Ok(Self {
723            count: ok!(slice.load_u64()),
724            body: ok!(load_cont(slice)),
725            after: ok!(load_cont(slice)),
726        })
727    }
728}
729
730/// Continuation that executes its body infinitely many times.
731///
732/// A `RET` only begins a new iteration of the infinite loop, which can
733/// be exited only by an exception, or a `RETALT` (or an explicit `JMPX`).
734#[derive(Debug, Clone)]
735pub struct AgainCont {
736    pub body: RcCont,
737}
738
739impl AgainCont {
740    const TAG: u8 = 0b110001;
741}
742
743impl Cont for AgainCont {
744    #[inline]
745    fn rc_into_dyn(self: Rc<Self>) -> Rc<dyn StackValue> {
746        self
747    }
748
749    fn as_stack_value(&self) -> &dyn StackValue {
750        self
751    }
752
753    #[inline]
754    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
755        f.write_str("vmc_again")
756    }
757
758    #[cfg_attr(
759        feature = "tracing",
760        instrument(level = "trace", name = "again_cont", skip_all)
761    )]
762    fn jump(self: Rc<Self>, state: &mut VmState, _: &mut i32) -> VmResult<Option<RcCont>> {
763        if !self.body.has_c0() {
764            state.set_c0(SafeRc::from(self.clone()))
765        }
766        Ok(Some(self.body.clone()))
767    }
768}
769
770impl Store for AgainCont {
771    fn store_into(
772        &self,
773        builder: &mut CellBuilder,
774        context: &dyn CellContext,
775    ) -> Result<(), Error> {
776        ok!(builder.store_small_uint(Self::TAG, 6));
777        builder.store_reference(ok!(CellBuilder::build_from_ext(&*self.body, context)))
778    }
779}
780
781impl Load<'_> for AgainCont {
782    fn load_from(slice: &mut CellSlice<'_>) -> Result<Self, Error> {
783        if ok!(slice.load_small_uint(6)) != Self::TAG {
784            return Err(Error::InvalidTag);
785        }
786
787        Ok(Self {
788            body: ok!(load_cont(slice)),
789        })
790    }
791}
792
793/// Continuation of a loop with postcondition.
794#[derive(Debug, Clone)]
795pub struct UntilCont {
796    pub body: RcCont,
797    pub after: RcCont,
798}
799
800impl UntilCont {
801    const TAG: u8 = 0b110000;
802}
803
804impl Cont for UntilCont {
805    #[inline]
806    fn rc_into_dyn(self: Rc<Self>) -> Rc<dyn StackValue> {
807        self
808    }
809
810    fn as_stack_value(&self) -> &dyn StackValue {
811        self
812    }
813
814    #[inline]
815    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
816        f.write_str("vmc_until")
817    }
818
819    #[cfg_attr(
820        feature = "tracing",
821        instrument(level = "trace", name = "until_cont", skip_all)
822    )]
823    fn jump(self: Rc<Self>, state: &mut VmState, _: &mut i32) -> VmResult<Option<RcCont>> {
824        vm_log_trace!("until loop condition end");
825        let terminated = ok!(SafeRc::make_mut(&mut state.stack).pop_bool());
826        if terminated {
827            vm_log_trace!("until loop terminated");
828            return Ok(Some(self.after.clone()));
829        }
830        if !self.body.has_c0() {
831            state.set_c0(RcCont::from(self.clone()));
832        }
833        Ok(Some(self.body.clone()))
834    }
835}
836
837impl Store for UntilCont {
838    fn store_into(
839        &self,
840        builder: &mut CellBuilder,
841        context: &dyn CellContext,
842    ) -> Result<(), Error> {
843        ok!(builder.store_small_uint(Self::TAG, 6));
844        ok!(builder.store_reference(ok!(CellBuilder::build_from_ext(&*self.body, context))));
845        builder.store_reference(ok!(CellBuilder::build_from_ext(&*self.after, context)))
846    }
847}
848
849impl Load<'_> for UntilCont {
850    fn load_from(slice: &mut CellSlice<'_>) -> Result<Self, Error> {
851        if ok!(slice.load_small_uint(6)) != Self::TAG {
852            return Err(Error::InvalidTag);
853        }
854
855        Ok(Self {
856            body: ok!(load_cont(slice)),
857            after: ok!(load_cont(slice)),
858        })
859    }
860}
861
862/// Continuation of a loop with precondition.
863#[derive(Debug, Clone)]
864pub struct WhileCont {
865    pub check_cond: bool,
866    pub cond: RcCont,
867    pub body: RcCont,
868    pub after: RcCont,
869}
870
871impl WhileCont {
872    const TAG: u8 = 0b11001;
873}
874
875impl Cont for WhileCont {
876    #[inline]
877    fn rc_into_dyn(self: Rc<Self>) -> Rc<dyn StackValue> {
878        self
879    }
880
881    fn as_stack_value(&self) -> &dyn StackValue {
882        self
883    }
884
885    #[inline]
886    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
887        f.write_str(if self.check_cond {
888            "vmc_while_cond"
889        } else {
890            "vmc_while_body"
891        })
892    }
893
894    #[cfg_attr(
895        feature = "tracing",
896        instrument(
897            level = "trace",
898            name = "while_cont",
899            fields(check_cond = self.check_cond),
900            skip_all,
901        )
902    )]
903    fn jump(mut self: Rc<Self>, state: &mut VmState, _: &mut i32) -> VmResult<Option<RcCont>> {
904        let next = if self.check_cond {
905            vm_log_trace!("while loop condition end");
906            if !ok!(SafeRc::make_mut(&mut state.stack).pop_bool()) {
907                vm_log_trace!("while loop terminated");
908                return Ok(Some(self.after.clone()));
909            }
910            self.body.clone()
911        } else {
912            vm_log_trace!("while loop body end");
913            self.cond.clone()
914        };
915
916        if !next.has_c0() {
917            match Rc::get_mut(&mut self) {
918                Some(this) => {
919                    this.check_cond = !this.check_cond;
920                    state.set_c0(RcCont::from(self));
921                }
922                None => state.set_c0(SafeRc::from(WhileCont {
923                    check_cond: !self.check_cond,
924                    cond: self.cond.clone(),
925                    body: self.body.clone(),
926                    after: self.after.clone(),
927                })),
928            }
929        }
930
931        Ok(Some(next))
932    }
933}
934
935impl Store for WhileCont {
936    fn store_into(
937        &self,
938        builder: &mut CellBuilder,
939        context: &dyn CellContext,
940    ) -> Result<(), Error> {
941        let tag = (Self::TAG << 1) | !self.check_cond as u8;
942        ok!(builder.store_small_uint(tag, 6));
943        ok!(builder.store_reference(ok!(CellBuilder::build_from_ext(&*self.cond, context))));
944        ok!(builder.store_reference(ok!(CellBuilder::build_from_ext(&*self.body, context))));
945        builder.store_reference(ok!(CellBuilder::build_from_ext(&*self.after, context)))
946    }
947}
948
949impl Load<'_> for WhileCont {
950    fn load_from(slice: &mut CellSlice<'_>) -> Result<Self, Error> {
951        if ok!(slice.load_small_uint(5)) != Self::TAG {
952            return Err(Error::InvalidTag);
953        }
954
955        Ok(Self {
956            check_cond: ok!(slice.load_bit()),
957            cond: ok!(load_cont(slice)),
958            body: ok!(load_cont(slice)),
959            after: ok!(load_cont(slice)),
960        })
961    }
962}
963
964/// Continuation with control data (arguments).
965#[derive(Debug, Clone)]
966pub struct ArgContExt {
967    pub data: ControlData,
968    pub ext: RcCont,
969}
970
971impl ArgContExt {
972    const TAG: u8 = 0b01;
973}
974
975impl Cont for ArgContExt {
976    #[inline]
977    fn rc_into_dyn(self: Rc<Self>) -> Rc<dyn StackValue> {
978        self
979    }
980
981    fn as_stack_value(&self) -> &dyn StackValue {
982        self
983    }
984
985    #[inline]
986    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
987        f.write_str("vmc_envelope")
988    }
989
990    #[cfg_attr(
991        feature = "tracing",
992        instrument(level = "trace", name = "arg_cont", skip_all)
993    )]
994    fn jump(self: Rc<Self>, state: &mut VmState, _: &mut i32) -> VmResult<Option<RcCont>> {
995        state.adjust_cr(&self.data.save);
996        if let Some(cp) = self.data.cp {
997            ok!(state.force_cp(cp));
998        }
999
1000        Ok(Some(match Rc::try_unwrap(self) {
1001            Ok(this) => this.ext,
1002            Err(this) => this.ext.clone(),
1003        }))
1004    }
1005
1006    fn get_control_data(&self) -> Option<&ControlData> {
1007        Some(&self.data)
1008    }
1009
1010    fn get_control_data_mut(&mut self) -> Option<&mut ControlData> {
1011        Some(&mut self.data)
1012    }
1013}
1014
1015impl Store for ArgContExt {
1016    fn store_into(
1017        &self,
1018        builder: &mut CellBuilder,
1019        context: &dyn CellContext,
1020    ) -> Result<(), Error> {
1021        ok!(builder.store_small_uint(Self::TAG, 2));
1022        self.ext.store_into(builder, context)
1023    }
1024}
1025
1026impl Load<'_> for ArgContExt {
1027    fn load_from(slice: &mut CellSlice<'_>) -> Result<Self, Error> {
1028        if ok!(slice.load_small_uint(2)) != Self::TAG {
1029            return Err(Error::InvalidTag);
1030        }
1031
1032        Ok(Self {
1033            data: ok!(ControlData::load_from(slice)),
1034            ext: ok!(load_cont(slice)),
1035        })
1036    }
1037}
1038
1039/// Ordinary continuation.
1040#[derive(Debug, Clone)]
1041pub struct OrdCont {
1042    pub data: ControlData,
1043    pub code: OwnedCellSlice,
1044}
1045
1046impl OrdCont {
1047    const TAG: u8 = 0b00;
1048
1049    pub fn simple(code: OwnedCellSlice, cp: u16) -> Self {
1050        Self {
1051            data: ControlData {
1052                cp: Some(cp),
1053                ..Default::default()
1054            },
1055            code,
1056        }
1057    }
1058}
1059
1060impl Cont for OrdCont {
1061    #[inline]
1062    fn rc_into_dyn(self: Rc<Self>) -> Rc<dyn StackValue> {
1063        self
1064    }
1065
1066    fn as_stack_value(&self) -> &dyn StackValue {
1067        self
1068    }
1069
1070    #[inline]
1071    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1072        f.write_str("vmc_std")
1073    }
1074
1075    #[cfg_attr(
1076        feature = "tracing",
1077        instrument(level = "trace", name = "ord_cont", skip_all)
1078    )]
1079    fn jump(self: Rc<Self>, state: &mut VmState, _: &mut i32) -> VmResult<Option<RcCont>> {
1080        state.adjust_cr(&self.data.save);
1081        let Some(cp) = self.data.cp else {
1082            vm_bail!(InvalidOpcode);
1083        };
1084        ok!(state.set_code(self.code.clone(), cp));
1085        Ok(None)
1086    }
1087
1088    fn get_control_data(&self) -> Option<&ControlData> {
1089        Some(&self.data)
1090    }
1091
1092    fn get_control_data_mut(&mut self) -> Option<&mut ControlData> {
1093        Some(&mut self.data)
1094    }
1095}
1096
1097impl Store for OrdCont {
1098    fn store_into(
1099        &self,
1100        builder: &mut CellBuilder,
1101        context: &dyn CellContext,
1102    ) -> Result<(), Error> {
1103        ok!(builder.store_zeros(2));
1104        ok!(self.data.store_into(builder, context));
1105        store_slice_as_stack_value(&self.code, builder)
1106    }
1107}
1108
1109impl Load<'_> for OrdCont {
1110    fn load_from(slice: &mut CellSlice<'_>) -> Result<Self, Error> {
1111        if ok!(slice.load_small_uint(2)) != Self::TAG {
1112            return Err(Error::InvalidTag);
1113        }
1114
1115        Ok(Self {
1116            data: ok!(ControlData::load_from(slice)),
1117            code: ok!(load_slice_as_stack_value(slice)),
1118        })
1119    }
1120}