evmil/util/interval_stack.rs
1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12use std::{cmp, fmt, mem};
13use std::fmt::{Debug,Display};
14use crate::util::{Bottom, Interval, Join, JoinInto, JoinLattice};
15
16// ============================================================================
17// Disassembly Context
18// ============================================================================
19
20#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
21pub struct IntervalStack<T: PartialEq> {
22 // The lower segment of an abstract stack represents a variable
23 // number of unknown values. An interval is used for a compact
24 // representation. So, for example, `0..1` represents two
25 // possible lower segments: `[]` and `[??]`.
26 lower: Interval<usize>,
27 // The upper segment represents zero or more concrete values on
28 // the stack.
29 upper: Vec<T>,
30}
31
32impl<T> IntervalStack<T>
33where
34 T: PartialEq + Clone + JoinLattice,
35{
36 pub fn new(lower: impl Into<Interval<usize>>, upper: Vec<T>) -> Self {
37 let lower_iv = lower.into();
38 // Done
39 Self {
40 lower: lower_iv,
41 upper,
42 }
43 }
44 /// Construct an empty stack.
45 pub fn empty() -> Self {
46 Self::new(0, Vec::new())
47 }
48 /// Determine possible lengths of the stack as an interval
49 pub fn len(&self) -> Interval<usize> {
50 self.lower.add(self.upper.len().into())
51 }
52 /// Peek nth item on the stack (where `0` is top).
53 pub fn peek(&self, n: usize) -> T {
54 // Should never be called on bottom
55 assert!(self != &Self::BOTTOM);
56 // Get the nth value!
57 if n < self.upper.len() {
58 // Determine stack index
59 let i = self.upper.len() - (1 + n);
60 // Extract value
61 self.upper[i].clone()
62 } else {
63 T::TOP
64 }
65 }
66 /// Push an iterm onto this stack.
67 pub fn push(&mut self, val: T) -> &mut Self {
68 // Should never be called on bottom
69 assert!(self != &Self::BOTTOM);
70 //
71 if val == T::TOP && self.upper.is_empty() {
72 self.lower = self.lower.add(1.into());
73 } else {
74 // Pop target address off the stack.
75 self.upper.push(val);
76 }
77 self
78 }
79 // Pop a single item off the stack
80 pub fn pop(&mut self) -> &mut Self {
81 // Should never be called on bottom
82 assert!(self != &Self::BOTTOM);
83 // Pop target address off the stack.
84 if self.upper.is_empty() {
85 self.lower = self.lower.sub(1.into());
86 } else {
87 self.upper.pop();
88 }
89 self
90 }
91
92 /// Determine the minimum length of any stack represented by this
93 /// abstract stack.
94 pub fn min_len(&self) -> usize {
95 self.lower.start + self.upper.len()
96 }
97 /// Determine the maximum length of any stack represented by this
98 /// abstract stack.
99 pub fn max_len(&self) -> usize {
100 self.lower.end + self.upper.len()
101 }
102 /// Access the array of concrete values represented by this stack
103 /// (i.e. the _upper_ portion of the stack).
104 pub fn values(&self) -> &[T] {
105 &self.upper
106 }
107
108 /// Set `ith` item from the top on this stack. Thus, `0` is the
109 /// top of the stack, etc.
110 pub fn set(mut self, n: usize, val: T) -> Self {
111 // Should never be called on bottom
112 assert!(self != Self::BOTTOM);
113 // NOTE: inefficient when putting unknown value into lower
114 // portion.
115 self.ensure_upper(n + 1);
116 // Determine stack index
117 let i = self.upper.len() - (1 + n);
118 // Set value
119 self.upper[i] = val;
120 // Rebalance (which can be necessary is val unknown)
121 self.rebalance()
122 }
123
124 /// Join two abstract stacks together.
125 pub fn join(self, other: &IntervalStack<T>) -> Self {
126 let slen = self.upper.len();
127 let olen = other.upper.len();
128 // Determine common upper length
129 let n = cmp::min(slen, olen);
130 // Normalise lower segments
131 let lself = self.lower.add(Interval::from(slen - n));
132 let lother = other.lower.add(Interval::from(olen - n));
133 let mut merger = IntervalStack::new(lself.union(&lother), Vec::new());
134 // Push merged items from upper segment
135 for i in (0..n).rev() {
136 let ithself = self.peek(i);
137 let ithother = other.peek(i);
138 merger.push(ithself.join(&ithother));
139 }
140 // Done
141 merger
142 }
143
144 /// Rebalance the stack if necessary. This is necessary when the
145 /// upper portion contains unknown values which can be shifted
146 /// into the lower portion.
147 fn rebalance(mut self) -> Self {
148 let mut i = 0;
149 // Determine whether any rebalancing necessary.
150 while i < self.upper.len() {
151 if self.upper[i] != T::TOP {
152 break;
153 }
154 i += 1;
155 }
156 // Rebalance only if necessary
157 if i > 0 {
158 // Increase lower portion
159 self.lower = self.lower.add(i.into());
160 // Decrease upper portion
161 self.upper.drain(0..i);
162 }
163 //
164 self
165 }
166
167 /// Ensure the upper portion has space for at least `n` elements.
168 fn ensure_upper(&mut self, n: usize) {
169 // FIXME: inefficient!!
170 while n > self.upper.len() {
171 self.upper.insert(0, T::TOP);
172 self.lower = self.lower.sub(1.into());
173 }
174 }
175}
176
177// ==================================================================
178// Standard Traits
179// ==================================================================
180
181impl<T: PartialEq + Clone + JoinLattice> Default for IntervalStack<T> {
182 fn default() -> Self {
183 Self::empty()
184 }
185}
186
187impl<T> Clone for IntervalStack<T>
188where
189 T: PartialEq + Clone,
190{
191 fn clone(&self) -> Self {
192 IntervalStack {
193 lower: self.lower,
194 upper: self.upper.clone(),
195 }
196 }
197}
198
199impl<T> Display for IntervalStack<T>
200where
201 T: Clone + Display + PartialEq + Bottom,
202{
203 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
204 if self == &Self::BOTTOM {
205 write!(f, "_|_")
206 } else {
207 write!(f, "({})[", self.lower)?;
208 for i in 0..self.upper.len() {
209 write!(f, "{}", self.upper[i])?;
210 }
211 write!(f, "]")
212 }
213 }
214}
215
216// ==================================================================
217// Lattice Traits
218// ==================================================================
219
220impl<T> Bottom for IntervalStack<T>
221where
222 T: PartialEq + Clone + Bottom,
223{
224 const BOTTOM: Self = Self {
225 lower: Interval::BOTTOM,
226 upper: Vec::new(),
227 };
228}
229
230impl<T> JoinInto for IntervalStack<T>
231where
232 T: PartialEq + Clone + JoinLattice,
233{
234 /// Merge an abstract stack into this stack, whilst reporting
235 /// whether this stack changed or not.
236 fn join_into(&mut self, other: &IntervalStack<T>) -> bool {
237 // NOTE: this could be done more efficiently.
238 let old = self.clone();
239 let mut tmp = Self::empty();
240 // Install dummy value to keep self alive
241 mem::swap(self, &mut tmp);
242 // Perform merge
243 *self = tmp.join(other);
244 // Check for change
245 *self != old
246 }
247}