midenc_codegen_masm/
stack.rs

1use core::{
2    fmt,
3    ops::{Index, IndexMut},
4};
5
6use miden_core::{Felt, FieldElement};
7use midenc_hir::{AttributeValue, Immediate, Type, ValueRef};
8use smallvec::{smallvec, SmallVec};
9
10/// This represents a constraint an operand's usage at
11/// a given program point, namely when used as an instruction
12/// or block argument.
13#[derive(Debug, Copy, Clone)]
14pub enum Constraint {
15    /// The operand should be moved, consuming it
16    /// from the stack and making it unavailable for
17    /// further use.
18    Move,
19    /// The operand should be copied, preserving the
20    /// original value for later use.
21    Copy,
22}
23
24/// Represents the type of operand represented on the operand stack
25pub enum OperandType {
26    /// The operand is a literal, unassociated with any value in the IR
27    Const(Box<dyn AttributeValue>),
28    /// The operand is an SSA value of known type
29    Value(ValueRef),
30    /// The operand is an intermediate runtime value of a known type, but
31    /// unassociated with any value in the IR
32    Type(Type),
33}
34impl Clone for OperandType {
35    fn clone(&self) -> Self {
36        match self {
37            Self::Const(value) => Self::Const(value.clone_value()),
38            Self::Value(value) => Self::Value(*value),
39            Self::Type(ty) => Self::Type(ty.clone()),
40        }
41    }
42}
43impl OperandType {
44    /// Get the type representation of this operand
45    pub fn ty(&self) -> Type {
46        match self {
47            Self::Const(imm) => {
48                imm.downcast_ref::<Immediate>().expect("unexpected constant value type").ty()
49            }
50            Self::Value(value) => value.borrow().ty().clone(),
51            Self::Type(ref ty) => ty.clone(),
52        }
53    }
54}
55impl fmt::Debug for OperandType {
56    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57        match self {
58            Self::Const(value) => write!(f, "Const({value:?})"),
59            Self::Value(value) => write!(f, "Value({value})"),
60            Self::Type(ty) => write!(f, "Type({ty})"),
61        }
62    }
63}
64impl Eq for OperandType {}
65impl PartialEq for OperandType {
66    fn eq(&self, other: &Self) -> bool {
67        match (self, other) {
68            (Self::Value(a), Self::Value(b)) => a == b,
69            (Self::Value(_), _) | (_, Self::Value(_)) => false,
70            (Self::Const(ref a), Self::Const(ref b)) => a == b,
71            (Self::Const(_), _) | (_, Self::Const(_)) => false,
72            (Self::Type(ref a), Self::Type(ref b)) => a == b,
73        }
74    }
75}
76impl PartialEq<Type> for OperandType {
77    fn eq(&self, other: &Type) -> bool {
78        match self {
79            Self::Type(a) => a == other,
80            _ => false,
81        }
82    }
83}
84impl PartialEq<Immediate> for OperandType {
85    fn eq(&self, other: &Immediate) -> bool {
86        match self {
87            Self::Const(a) => a.downcast_ref::<Immediate>().is_some_and(|a| a == other),
88            _ => false,
89        }
90    }
91}
92impl PartialEq<dyn AttributeValue> for OperandType {
93    fn eq(&self, other: &dyn AttributeValue) -> bool {
94        match self {
95            Self::Const(a) => a.as_ref() == other,
96            _ => false,
97        }
98    }
99}
100impl PartialEq<ValueRef> for OperandType {
101    fn eq(&self, other: &ValueRef) -> bool {
102        match self {
103            Self::Value(this) => this == other,
104            _ => false,
105        }
106    }
107}
108impl From<ValueRef> for OperandType {
109    fn from(value: ValueRef) -> Self {
110        Self::Value(value)
111    }
112}
113impl From<Type> for OperandType {
114    fn from(ty: Type) -> Self {
115        Self::Type(ty)
116    }
117}
118impl From<Immediate> for OperandType {
119    fn from(value: Immediate) -> Self {
120        Self::Const(Box::new(value))
121    }
122}
123impl From<Box<dyn AttributeValue>> for OperandType {
124    fn from(value: Box<dyn AttributeValue>) -> Self {
125        Self::Const(value)
126    }
127}
128
129/// This type represents a logical operand on the stack, which may consist
130/// of one or more "parts", up to a word in size, on the actual stack.
131///
132/// The [OperandStack] operates in terms of [Operand], but when emitting
133/// Miden Assembly, we must know how to translate operand-oriented operations
134/// into equivalent element-/word-oriented operations. This is accomplished
135/// by tracking the low-level representation of a given operand in this struct.
136#[derive(Debug, Clone, PartialEq, Eq)]
137pub struct Operand {
138    /// The section of stack corresponding to this operand, containing
139    /// up to a full word of elements. No chunk will ever exceed a word
140    /// in size. This field behaves like a miniature [OperandStack], i.e.
141    /// elements are pushed and popped off the end to modify it.
142    ///
143    /// An operand is encoded on this stack in order of lowest
144    /// addressed bytes first. For example, given a struct operand,
145    /// the first field of the struct will be closest to the top of
146    /// the stack.
147    word: SmallVec<[Type; 4]>,
148    /// The high-level operand represented by this item.
149    ///
150    /// If the operand stack is manipulated in such a way that the operand
151    /// is torn apart, say one field of a struct is popped; then this will
152    /// be set to a `Type` operand, representing what high-level information
153    /// we have about the remaining parts of the original operand on the stack.
154    operand: OperandType,
155}
156impl Default for Operand {
157    fn default() -> Self {
158        Self {
159            word: smallvec![Type::Felt],
160            operand: OperandType::Const(Box::new(Immediate::Felt(Felt::ZERO))),
161        }
162    }
163}
164impl PartialEq<ValueRef> for Operand {
165    #[inline(always)]
166    fn eq(&self, other: &ValueRef) -> bool {
167        self.operand.eq(other)
168    }
169}
170impl PartialEq<dyn AttributeValue> for Operand {
171    #[inline(always)]
172    fn eq(&self, other: &dyn AttributeValue) -> bool {
173        self.operand.eq(other)
174    }
175}
176impl PartialEq<Immediate> for Operand {
177    #[inline(always)]
178    fn eq(&self, other: &Immediate) -> bool {
179        self.operand.eq(other)
180    }
181}
182impl PartialEq<Immediate> for &Operand {
183    #[inline(always)]
184    fn eq(&self, other: &Immediate) -> bool {
185        self.operand.eq(other)
186    }
187}
188impl PartialEq<Type> for Operand {
189    #[inline(always)]
190    fn eq(&self, other: &Type) -> bool {
191        self.operand.eq(other)
192    }
193}
194impl PartialEq<Type> for &Operand {
195    #[inline(always)]
196    fn eq(&self, other: &Type) -> bool {
197        self.operand.eq(other)
198    }
199}
200impl From<Immediate> for Operand {
201    #[inline]
202    fn from(imm: Immediate) -> Self {
203        Self::new(imm.into())
204    }
205}
206impl From<u32> for Operand {
207    #[inline]
208    fn from(imm: u32) -> Self {
209        Self::new(Immediate::U32(imm).into())
210    }
211}
212impl TryFrom<&Operand> for ValueRef {
213    type Error = ();
214
215    fn try_from(operand: &Operand) -> Result<Self, Self::Error> {
216        match operand.operand {
217            OperandType::Value(value) => Ok(value),
218            _ => Err(()),
219        }
220    }
221}
222#[cfg(test)]
223impl TryFrom<&Operand> for Immediate {
224    type Error = ();
225
226    fn try_from(operand: &Operand) -> Result<Self, Self::Error> {
227        match &operand.operand {
228            OperandType::Const(value) => value.downcast_ref::<Immediate>().copied().ok_or(()),
229            _ => Err(()),
230        }
231    }
232}
233#[cfg(test)]
234impl TryFrom<&Operand> for Type {
235    type Error = ();
236
237    fn try_from(operand: &Operand) -> Result<Self, Self::Error> {
238        match operand.operand {
239            OperandType::Type(ref ty) => Ok(ty.clone()),
240            _ => Err(()),
241        }
242    }
243}
244#[cfg(test)]
245impl TryFrom<Operand> for Type {
246    type Error = ();
247
248    fn try_from(operand: Operand) -> Result<Self, Self::Error> {
249        match operand.operand {
250            OperandType::Type(ty) => Ok(ty),
251            _ => Err(()),
252        }
253    }
254}
255impl From<Type> for Operand {
256    #[inline]
257    fn from(ty: Type) -> Self {
258        Self::new(OperandType::Type(ty))
259    }
260}
261impl From<ValueRef> for Operand {
262    #[inline]
263    fn from(value: ValueRef) -> Self {
264        Self::new(OperandType::Value(value))
265    }
266}
267impl Operand {
268    pub fn new(operand: OperandType) -> Self {
269        let ty = operand.ty();
270        let mut word = ty.to_raw_parts().expect("invalid operand type");
271        assert!(!word.is_empty(), "invalid operand: must be a sized type");
272        assert!(word.len() <= 4, "invalid operand: must be smaller than or equal to a word");
273        if word.len() > 1 {
274            word.reverse();
275        }
276        Self { word, operand }
277    }
278
279    /// Get the size of this operand in field elements
280    pub fn size(&self) -> usize {
281        self.word.len()
282    }
283
284    /// Get the [OperandType] representing the value of this operand
285    #[inline(always)]
286    pub fn value(&self) -> &OperandType {
287        &self.operand
288    }
289
290    /// Get this operand as a [Value]
291    #[inline]
292    pub fn as_value(&self) -> Option<ValueRef> {
293        self.try_into().ok()
294    }
295
296    /// Get the [Type] of this operand
297    #[inline]
298    pub fn ty(&self) -> Type {
299        self.operand.ty()
300    }
301}
302
303/// This structure emulates the state of the VM's operand stack while
304/// generating code from the SSA representation of a function.
305///
306/// In order to emit efficient and correct stack manipulation code, we must be able to
307/// reason about where values are on the operand stack at a given program point. This
308/// structure tracks what SSA values have been pushed on the operand stack, where they are
309/// on the stack relative to the top, and whether a given stack slot aliases multiple
310/// values.
311///
312/// In addition to the state tracked, this structure also has an API that mimics the
313/// stack manipulation instructions we can emit in the code generator, so that as we
314/// emit instructions and modify this structure at the same time, 1:1.
315#[derive(Clone, PartialEq, Eq)]
316pub struct OperandStack {
317    stack: Vec<Operand>,
318}
319impl Default for OperandStack {
320    fn default() -> Self {
321        Self {
322            stack: Vec::with_capacity(16),
323        }
324    }
325}
326impl OperandStack {
327    /// Renames the `n`th operand from the top of the stack to `value`
328    ///
329    /// The type is assumed to remain unchanged
330    pub fn rename(&mut self, n: usize, value: ValueRef) {
331        match &mut self[n].operand {
332            OperandType::Value(ref mut prev_value) => {
333                *prev_value = value;
334            }
335            prev => {
336                *prev = OperandType::Value(value);
337            }
338        }
339    }
340
341    /// Searches for the position on the stack containing the operand corresponding to `value`.
342    ///
343    /// NOTE: This function will panic if `value` is not on the stack
344    pub fn find(&self, value: &ValueRef) -> Option<usize> {
345        self.stack.iter().rev().position(|v| v == value)
346    }
347
348    /// Returns true if the operand stack is empty
349    #[allow(unused)]
350    #[inline(always)]
351    pub fn is_empty(&self) -> bool {
352        self.stack.is_empty()
353    }
354
355    /// Returns the number of field elements on the stack
356    #[inline]
357    pub fn raw_len(&self) -> usize {
358        self.stack.iter().map(|operand| operand.size()).sum()
359    }
360
361    /// Returns the index in the actual runtime stack which corresponds to
362    /// the first element of the operand at `index`.
363    #[track_caller]
364    pub fn effective_index(&self, index: usize) -> usize {
365        assert!(
366            index < self.stack.len(),
367            "expected {} to be less than {}",
368            index,
369            self.stack.len()
370        );
371
372        self.stack.iter().rev().take(index).map(|o| o.size()).sum()
373    }
374
375    /// Returns the index in the actual runtime stack which corresponds to
376    /// the last element of the operand at `index`.
377    #[track_caller]
378    pub fn effective_index_inclusive(&self, index: usize) -> usize {
379        assert!(index < self.stack.len());
380
381        self.stack.iter().rev().take(index + 1).map(|o| o.size()).sum::<usize>() - 1
382    }
383
384    /// Returns the number of operands on the stack
385    #[inline]
386    pub fn len(&self) -> usize {
387        self.stack.len()
388    }
389
390    /// Returns the operand on top of the stack, without consuming it
391    #[inline]
392    pub fn peek(&self) -> Option<&Operand> {
393        self.stack.last()
394    }
395
396    /// Pushes an operand on top of the stack
397    #[inline]
398    pub fn push<V: Into<Operand>>(&mut self, value: V) {
399        self.stack.push(value.into());
400    }
401
402    /// Pops the operand on top of the stack
403    #[inline]
404    #[track_caller]
405    pub fn pop(&mut self) -> Option<Operand> {
406        self.stack.pop()
407    }
408
409    /// Drops the top operand on the stack
410    #[allow(clippy::should_implement_trait)]
411    #[track_caller]
412    pub fn drop(&mut self) {
413        self.stack.pop().expect("operand stack is empty");
414    }
415
416    /// Drops the top `n` operands on the stack
417    #[inline]
418    #[track_caller]
419    pub fn dropn(&mut self, n: usize) {
420        let len = self.stack.len();
421        assert!(n <= len, "unable to drop {} operands, operand stack only has {}", n, len);
422        self.stack.truncate(len - n);
423    }
424
425    /// Duplicates the operand in the `n`th position on the stack
426    ///
427    /// If `n` is 0, duplicates the top of the stack.
428    #[track_caller]
429    pub fn dup(&mut self, n: usize) {
430        let operand = self[n].clone();
431        self.stack.push(operand);
432    }
433
434    /// Swaps the `n`th operand from the top of the stack, with the top of the stack
435    ///
436    /// If `n` is 1, it swaps the first two operands on the stack.
437    ///
438    /// NOTE: This function will panic if `n` is 0, or out of bounds.
439    #[track_caller]
440    pub fn swap(&mut self, n: usize) {
441        assert_ne!(n, 0, "invalid swap, index must be in the range 1..=15");
442        let len = self.stack.len();
443        assert!(
444            n < len,
445            "invalid operand stack index ({}), only {} operands are available",
446            n,
447            len
448        );
449        let a = len - 1;
450        let b = a - n;
451        self.stack.swap(a, b);
452    }
453
454    /// Moves the `n`th operand to the top of the stack
455    ///
456    /// If `n` is 1, this is equivalent to `swap(1)`.
457    ///
458    /// NOTE: This function will panic if `n` is 0, or out of bounds.
459    pub fn movup(&mut self, n: usize) {
460        assert_ne!(n, 0, "invalid move, index must be in the range 1..=15");
461        let len = self.stack.len();
462        assert!(
463            n < len,
464            "invalid operand stack index ({}), only {} operands are available",
465            n,
466            len
467        );
468        // Pick the midpoint by counting backwards from the end
469        let mid = len - (n + 1);
470        // Split the stack, and rotate the half that
471        // contains our desired value to place it on top.
472        let (_, r) = self.stack.split_at_mut(mid);
473        r.rotate_left(1);
474    }
475
476    /// Makes the operand on top of the stack, the `n`th operand on the stack
477    ///
478    /// If `n` is 1, this is equivalent to `swap(1)`.
479    ///
480    /// NOTE: This function will panic if `n` is 0, or out of bounds.
481    pub fn movdn(&mut self, n: usize) {
482        assert_ne!(n, 0, "invalid move, index must be in the range 1..=15");
483        let len = self.stack.len();
484        assert!(
485            n < len,
486            "invalid operand stack index ({}), only {} operands are available",
487            n,
488            len
489        );
490        // Split the stack so that the desired position is in the top half
491        let mid = len - (n + 1);
492        let (_, r) = self.stack.split_at_mut(mid);
493        // Move all elements above the `n`th position up by one, moving the top element to the `n`th
494        // position
495        r.rotate_right(1);
496    }
497
498    #[allow(unused)]
499    #[inline(always)]
500    pub fn iter(&self) -> impl DoubleEndedIterator<Item = &Operand> {
501        self.stack.iter()
502    }
503}
504impl Index<usize> for OperandStack {
505    type Output = Operand;
506
507    #[track_caller]
508    fn index(&self, index: usize) -> &Self::Output {
509        let len = self.stack.len();
510        assert!(
511            index < len,
512            "invalid operand stack index ({}): only {} operands are available",
513            index,
514            len
515        );
516        let effective_len: usize = self.stack.iter().rev().take(index + 1).map(|o| o.size()).sum();
517        assert!(
518            effective_len <= 16,
519            "invalid operand stack index ({}): requires access to more than 16 elements, which is \
520             not supported in Miden",
521            index
522        );
523        &self.stack[len - index - 1]
524    }
525}
526impl IndexMut<usize> for OperandStack {
527    #[track_caller]
528    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
529        let len = self.stack.len();
530        assert!(
531            index < len,
532            "invalid operand stack index ({}): only {} elements are available",
533            index,
534            len
535        );
536        let effective_len: usize = self.stack.iter().rev().take(index + 1).map(|o| o.size()).sum();
537        assert!(
538            effective_len <= 16,
539            "invalid operand stack index ({}): requires access to more than 16 elements, which is \
540             not supported in Miden",
541            index
542        );
543        &mut self.stack[len - index - 1]
544    }
545}
546
547impl fmt::Debug for OperandStack {
548    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
549        let mut builder = f.debug_list();
550        for (index, value) in self.stack.iter().rev().enumerate() {
551            builder.entry_with(|f| write!(f, "{index} => {value:?}"));
552        }
553        builder.finish()
554    }
555}
556
557#[cfg(test)]
558mod tests {
559    use alloc::rc::Rc;
560
561    use midenc_hir::{ArrayType, BuilderExt, Context, PointerType, StructType};
562
563    use super::*;
564
565    #[test]
566    fn operand_stack_homogenous_operand_sizes_test() {
567        let mut stack = OperandStack::default();
568
569        let zero = Immediate::U32(0);
570        let one = Immediate::U32(1);
571        let two = Immediate::U32(2);
572        let three = Immediate::U32(3);
573
574        #[inline]
575        #[allow(unused)]
576        fn as_imms(word: [Operand; 4]) -> [Immediate; 4] {
577            [
578                (&word[0]).try_into().unwrap(),
579                (&word[1]).try_into().unwrap(),
580                (&word[2]).try_into().unwrap(),
581                (&word[3]).try_into().unwrap(),
582            ]
583        }
584
585        #[inline]
586        fn as_imm(operand: Operand) -> Immediate {
587            (&operand).try_into().unwrap()
588        }
589
590        // push
591        stack.push(zero);
592        stack.push(one);
593        stack.push(two);
594        stack.push(three);
595        assert_eq!(stack.len(), 4);
596        assert_eq!(stack[0], three);
597        assert_eq!(stack[1], two);
598        assert_eq!(stack[2], one);
599        assert_eq!(stack[3], zero);
600
601        // peek
602        assert_eq!(stack.peek().unwrap(), three);
603
604        // dup
605        stack.dup(0);
606        assert_eq!(stack.len(), 5);
607        assert_eq!(stack[0], three);
608        assert_eq!(stack[1], three);
609        assert_eq!(stack[2], two);
610        assert_eq!(stack[3], one);
611        assert_eq!(stack[4], zero);
612
613        stack.dup(3);
614        assert_eq!(stack.len(), 6);
615        assert_eq!(stack[0], one);
616        assert_eq!(stack[1], three);
617        assert_eq!(stack[2], three);
618        assert_eq!(stack[3], two);
619        assert_eq!(stack[4], one);
620        assert_eq!(stack[5], zero);
621
622        // drop
623        stack.drop();
624        assert_eq!(stack.len(), 5);
625        assert_eq!(stack[0], three);
626        assert_eq!(stack[1], three);
627        assert_eq!(stack[2], two);
628        assert_eq!(stack[3], one);
629        assert_eq!(stack[4], zero);
630
631        // swap
632        stack.swap(2);
633        assert_eq!(stack.len(), 5);
634        assert_eq!(stack[0], two);
635        assert_eq!(stack[1], three);
636        assert_eq!(stack[2], three);
637        assert_eq!(stack[3], one);
638        assert_eq!(stack[4], zero);
639
640        stack.swap(1);
641        assert_eq!(stack.len(), 5);
642        assert_eq!(stack[0], three);
643        assert_eq!(stack[1], two);
644        assert_eq!(stack[2], three);
645        assert_eq!(stack[3], one);
646        assert_eq!(stack[4], zero);
647
648        // movup
649        stack.movup(2);
650        assert_eq!(stack.len(), 5);
651        assert_eq!(stack[0], three);
652        assert_eq!(stack[1], three);
653        assert_eq!(stack[2], two);
654        assert_eq!(stack[3], one);
655        assert_eq!(stack[4], zero);
656
657        // movdn
658        stack.movdn(3);
659        assert_eq!(stack.len(), 5);
660        assert_eq!(stack[0], three);
661        assert_eq!(stack[1], two);
662        assert_eq!(stack[2], one);
663        assert_eq!(stack[3], three);
664        assert_eq!(stack[4], zero);
665
666        // pop
667        assert_eq!(stack.pop().map(as_imm), Some(three));
668        assert_eq!(stack.len(), 4);
669        assert_eq!(stack[0], two);
670        assert_eq!(stack[1], one);
671        assert_eq!(stack[2], three);
672        assert_eq!(stack[3], zero);
673
674        // dropn
675        stack.dropn(2);
676        assert_eq!(stack.len(), 2);
677        assert_eq!(stack[0], three);
678        assert_eq!(stack[1], zero);
679    }
680
681    #[test]
682    fn operand_stack_values_test() {
683        use midenc_dialect_hir::Load;
684
685        let mut stack = OperandStack::default();
686        let context = Rc::new(Context::default());
687
688        let ptr_u8 = Type::from(PointerType::new(Type::U8));
689        let array_u8 = Type::from(ArrayType::new(Type::U8, 4));
690        let struct_ty = Type::from(StructType::new([Type::U64, Type::U8]));
691        let block = context.create_block_with_params([ptr_u8, array_u8, Type::U32, struct_ty]);
692        let block = block.borrow();
693        let values = block.arguments();
694
695        let zero = values[0] as ValueRef;
696        let one = values[1] as ValueRef;
697        let two = values[2] as ValueRef;
698        let three = values[3] as ValueRef;
699
700        drop(block);
701
702        // push
703        stack.push(zero);
704        stack.push(one);
705        stack.push(two);
706        stack.push(three);
707        assert_eq!(stack.len(), 4);
708        assert_eq!(stack.raw_len(), 6);
709
710        assert_eq!(stack.find(&zero), Some(3));
711        assert_eq!(stack.find(&one), Some(2));
712        assert_eq!(stack.find(&two), Some(1));
713        assert_eq!(stack.find(&three), Some(0));
714
715        // dup
716        stack.dup(0);
717        assert_eq!(stack.find(&three), Some(0));
718
719        stack.dup(3);
720        assert_eq!(stack.find(&one), Some(0));
721
722        // drop
723        stack.drop();
724        assert_eq!(stack.find(&one), Some(3));
725        assert_eq!(stack.find(&three), Some(0));
726        assert_eq!(stack[1], three);
727
728        // padw
729        stack.push(Immediate::Felt(Felt::ZERO));
730        stack.push(Immediate::Felt(Felt::ZERO));
731        stack.push(Immediate::Felt(Felt::ZERO));
732        stack.push(Immediate::Felt(Felt::ZERO));
733        assert_eq!(stack.find(&one), Some(7));
734        assert_eq!(stack.find(&three), Some(4));
735
736        // rename
737        let four = {
738            let mut builder = midenc_hir::OpBuilder::new(context.clone());
739            let load_builder = builder.create::<Load, _>(Default::default());
740            let load = load_builder(zero).unwrap();
741            load.borrow().result().as_value_ref()
742        };
743        stack.rename(1, four);
744        assert_eq!(stack.find(&four), Some(1));
745        assert_eq!(stack.find(&three), Some(4));
746
747        // pop
748        let top = stack.pop().unwrap();
749        assert_eq!((&top).try_into(), Ok(Immediate::Felt(Felt::ZERO)));
750        assert_eq!(stack.find(&four), Some(0));
751        assert_eq!(stack[1], Immediate::Felt(Felt::ZERO));
752        assert_eq!(stack[2], Immediate::Felt(Felt::ZERO));
753        assert_eq!(stack.find(&three), Some(3));
754
755        // dropn
756        stack.dropn(3);
757        assert_eq!(stack.find(&four), None);
758        assert_eq!(stack.find(&three), Some(0));
759        assert_eq!(stack[1], three);
760        assert_eq!(stack.find(&two), Some(2));
761        assert_eq!(stack.find(&one), Some(3));
762        assert_eq!(stack.find(&zero), Some(4));
763
764        // swap
765        stack.swap(3);
766        assert_eq!(stack.find(&one), Some(0));
767        assert_eq!(stack.find(&three), Some(1));
768        assert_eq!(stack.find(&two), Some(2));
769        assert_eq!(stack[3], three);
770
771        stack.swap(1);
772        assert_eq!(stack.find(&three), Some(0));
773        assert_eq!(stack.find(&one), Some(1));
774        assert_eq!(stack.find(&two), Some(2));
775        assert_eq!(stack.find(&zero), Some(4));
776
777        // movup
778        stack.movup(2);
779        assert_eq!(stack.find(&two), Some(0));
780        assert_eq!(stack.find(&three), Some(1));
781        assert_eq!(stack.find(&one), Some(2));
782        assert_eq!(stack.find(&zero), Some(4));
783
784        // movdn
785        stack.movdn(3);
786        assert_eq!(stack.find(&three), Some(0));
787        assert_eq!(stack.find(&one), Some(1));
788        assert_eq!(stack[2], three);
789        assert_eq!(stack.find(&two), Some(3));
790        assert_eq!(stack.find(&zero), Some(4));
791    }
792
793    #[test]
794    fn operand_stack_heterogenous_operand_sizes_test() {
795        let mut stack = OperandStack::default();
796
797        let zero = Immediate::U32(0);
798        let one = Immediate::U32(1);
799        let two = Type::U64;
800        let three = Type::U64;
801        let struct_a = Type::from(StructType::new([
802            Type::from(PointerType::new(Type::U8)),
803            Type::U16,
804            Type::U32,
805        ]));
806
807        // push
808        stack.push(zero);
809        stack.push(one);
810        stack.push(two.clone());
811        stack.push(three.clone());
812        stack.push(struct_a.clone());
813        assert_eq!(stack.len(), 5);
814        assert_eq!(stack.raw_len(), 9);
815        assert_eq!(stack[0], struct_a);
816        assert_eq!(stack[1], three);
817        assert_eq!(stack[2], two);
818        assert_eq!(stack[3], one);
819        assert_eq!(stack[4], zero);
820
821        // peek
822        assert_eq!(stack.peek().unwrap(), struct_a);
823
824        // dup
825        stack.dup(0);
826        assert_eq!(stack.len(), 6);
827        assert_eq!(stack.raw_len(), 12);
828        assert_eq!(stack[0], struct_a);
829        assert_eq!(stack[1], struct_a);
830        assert_eq!(stack[2], three);
831        assert_eq!(stack[3], two);
832        assert_eq!(stack[4], one);
833        assert_eq!(stack[5], zero);
834        assert_eq!(stack.effective_index(3), 8);
835
836        stack.dup(3);
837        assert_eq!(stack.len(), 7);
838        assert_eq!(stack.raw_len(), 14);
839        assert_eq!(stack[0], two);
840        assert_eq!(stack[1], struct_a);
841        assert_eq!(stack[2], struct_a);
842
843        // drop
844        stack.drop();
845        assert_eq!(stack.len(), 6);
846        assert_eq!(stack.raw_len(), 12);
847        assert_eq!(stack[0], struct_a);
848        assert_eq!(stack[1], struct_a);
849        assert_eq!(stack[2], three);
850        assert_eq!(stack[3], two);
851        assert_eq!(stack[4], one);
852        assert_eq!(stack[5], zero);
853
854        // swap
855        stack.swap(2);
856        assert_eq!(stack.len(), 6);
857        assert_eq!(stack.raw_len(), 12);
858        assert_eq!(stack[0], three);
859        assert_eq!(stack[1], struct_a);
860        assert_eq!(stack[2], struct_a);
861        assert_eq!(stack[3], two);
862        assert_eq!(stack[4], one);
863
864        stack.swap(1);
865        assert_eq!(stack.len(), 6);
866        assert_eq!(stack.raw_len(), 12);
867        assert_eq!(stack[0], struct_a);
868        assert_eq!(stack[1], three);
869        assert_eq!(stack[2], struct_a);
870        assert_eq!(stack[3], two);
871        assert_eq!(stack[4], one);
872        assert_eq!(stack[5], zero);
873
874        // movup
875        stack.movup(4);
876        assert_eq!(stack.len(), 6);
877        assert_eq!(stack.raw_len(), 12);
878        assert_eq!(stack[0], one);
879        assert_eq!(stack[1], struct_a);
880        assert_eq!(stack[2], three);
881        assert_eq!(stack[3], struct_a);
882        assert_eq!(stack[4], two);
883        assert_eq!(stack[5], zero);
884
885        // movdn
886        stack.movdn(3);
887        assert_eq!(stack.len(), 6);
888        assert_eq!(stack.raw_len(), 12);
889        assert_eq!(stack[0], struct_a);
890        assert_eq!(stack[1], three);
891        assert_eq!(stack[2], struct_a);
892        assert_eq!(stack[3], one);
893        assert_eq!(stack[4], two);
894
895        // pop
896        let operand: Type = stack.pop().unwrap().try_into().unwrap();
897        assert_eq!(operand, struct_a);
898        assert_eq!(stack.len(), 5);
899        assert_eq!(stack.raw_len(), 9);
900        assert_eq!(stack[0], three);
901        assert_eq!(stack[1], struct_a);
902        assert_eq!(stack[2], one);
903        assert_eq!(stack[3], two);
904
905        // dropn
906        stack.dropn(2);
907        assert_eq!(stack.len(), 3);
908        assert_eq!(stack.raw_len(), 4);
909        assert_eq!(stack[0], one);
910        assert_eq!(stack[1], two);
911        assert_eq!(stack[2], zero);
912    }
913}