Skip to main content

riscv_etrace/types/
stack.rs

1// Copyright (C) 2025, 2026 FZI Forschungszentrum Informatik
2// SPDX-License-Identifier: Apache-2.0
3//! Return stack utiltities for infering function returns
4
5/// Return stack
6///
7/// A stack of return addresses with a predefined maximal depth.
8pub trait ReturnStack: Sized {
9    /// Create a new return stack with the given maximum depth
10    ///
11    /// Returns [`None`] if `max_depth` is greater than the value supported by
12    /// the implementaiton, or if the stack could not be created due to some
13    /// other reason.
14    fn new(max_depth: usize) -> Option<Self>;
15
16    /// Push a new return address on the stack
17    ///
18    /// If the maximal depth is reached, the bottom address will be evicted from
19    /// the stack and thus no longer be obtainable via a [`pop`][Self::pop].
20    fn push(&mut self, addr: u64);
21
22    /// Retrieve and remove the topmost return address
23    fn pop(&mut self) -> Option<u64>;
24
25    /// Get the current stack depth
26    fn depth(&self) -> usize;
27
28    /// Get the maximum stack depth
29    fn max_depth(&self) -> usize;
30}
31
32/// Statically allocated [`ReturnStack`]
33///
34/// This [`ReturnStack`] keeps data in an array of size `N`. It supports maximum
35/// depths up to that size.
36#[derive(Clone, Debug)]
37pub struct StaticStack<const N: usize> {
38    data: [u64; N],
39    max_depth: usize,
40    depth: usize,
41    base: usize,
42}
43
44impl<const N: usize> ReturnStack for StaticStack<N> {
45    fn new(max_depth: usize) -> Option<Self> {
46        if max_depth > N {
47            None
48        } else {
49            Some(Self {
50                data: [0; N],
51                max_depth,
52                depth: 0,
53                base: 0,
54            })
55        }
56    }
57
58    fn push(&mut self, addr: u64) {
59        let depth = self.depth;
60        self.data[(self.base + depth) % N] = addr;
61
62        if depth < self.max_depth {
63            self.depth = depth.saturating_add(1);
64        } else {
65            let base = self.base + 1;
66            if base < N {
67                self.base = base;
68            } else {
69                self.base = 0;
70            }
71        }
72    }
73
74    fn pop(&mut self) -> Option<u64> {
75        let depth = self.depth.checked_sub(1)?;
76        self.depth = depth;
77        Some(self.data[(self.base + depth) % N])
78    }
79
80    fn depth(&self) -> usize {
81        self.depth
82    }
83
84    fn max_depth(&self) -> usize {
85        self.max_depth
86    }
87}
88
89/// Dummy [`ReturnStack`] with zero depth
90///
91/// This [`ReturnStack`] does not hold any data. It only supports a maximum
92/// depth of zero.
93pub struct NoStack;
94
95impl ReturnStack for NoStack {
96    fn new(max_depth: usize) -> Option<Self> {
97        (max_depth == 0).then_some(Self)
98    }
99
100    fn push(&mut self, _: u64) {}
101
102    fn pop(&mut self) -> Option<u64> {
103        None
104    }
105
106    fn depth(&self) -> usize {
107        0
108    }
109
110    fn max_depth(&self) -> usize {
111        0
112    }
113}
114
115#[cfg(feature = "alloc")]
116use alloc::collections::VecDeque;
117#[derive(Clone, Debug)]
118#[cfg(feature = "alloc")]
119pub struct VecStack {
120    data: VecDeque<u64>,
121    max_depth: usize,
122}
123
124#[cfg(feature = "alloc")]
125impl ReturnStack for VecStack {
126    fn new(max_size: usize) -> Option<Self> {
127        Some(Self {
128            data: VecDeque::with_capacity(max_size),
129            max_depth: max_size,
130        })
131    }
132
133    fn depth(&self) -> usize {
134        self.data.len()
135    }
136
137    // ReturnStack Aliases
138    fn push(&mut self, addr: u64) {
139        self.push_back(addr);
140    }
141    fn pop(&mut self) -> Option<u64> {
142        self.pop_back()
143    }
144    fn max_depth(&self) -> usize {
145        self.max_depth
146    }
147}
148
149#[cfg(feature = "alloc")]
150impl VecStack {
151    // Pushes to top of stack, like push in vector
152    pub fn push_back(&mut self, addr: u64) {
153        if self.max_depth == 0 {
154            return;
155        }
156        if self.data.len() == self.max_depth {
157            self.data.pop_front();
158        }
159        self.data.push_back(addr);
160    }
161
162    // Pushes to bottom of stack
163    pub fn push_front(&mut self, addr: u64) {
164        if self.max_depth == 0 {
165            return;
166        }
167        if self.data.len() == self.max_depth {
168            self.data.pop_back();
169        }
170        self.data.push_front(addr);
171    }
172
173    // Pops top element of stack
174    pub fn pop_back(&mut self) -> Option<u64> {
175        self.data.pop_back()
176    }
177
178    // Pops most bottom element of stack
179    pub fn pop_front(&mut self) -> Option<u64> {
180        self.data.pop_front()
181    }
182}
183
184#[cfg(feature = "alloc")]
185use alloc::{boxed::Box, vec};
186#[derive(Clone, Debug)]
187#[cfg(feature = "alloc")]
188pub struct BoxStack {
189    data: Box<[u64]>,
190    depth: usize,
191    base: usize,
192}
193
194#[cfg(feature = "alloc")]
195impl ReturnStack for BoxStack {
196    fn new(max_depth: usize) -> Option<Self> {
197        if max_depth == 0 {
198            return None;
199        }
200        Some(Self {
201            data: vec![0u64; max_depth].into_boxed_slice(),
202            depth: 0,
203            base: 0,
204        })
205    }
206
207    fn push(&mut self, addr: u64) {
208        let max_len = self.data.len();
209        let index = (self.depth + self.base) % max_len;
210        self.data[index] = addr;
211
212        if self.depth < max_len {
213            self.depth += 1;
214        } else {
215            self.base = (self.base + 1) % max_len;
216        }
217    }
218
219    fn pop(&mut self) -> Option<u64> {
220        let depth = self.depth.checked_sub(1)?;
221        self.depth = depth;
222        Some(self.data[(self.base + depth) % self.data.len()])
223    }
224
225    fn depth(&self) -> usize {
226        self.depth
227    }
228
229    fn max_depth(&self) -> usize {
230        self.data.len()
231    }
232}