1use serde::{Deserialize, Serialize};
8
9#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)]
12pub enum Region {
13 Arg(u16),
15 Stack,
17 Global,
20 Heapish,
22}
23
24#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)]
27pub enum ValueClass {
28 Input(u16),
30 InputDeriv(u16),
32 Zero,
33 SmallConst(i64),
35 BigConst,
37 Unknown,
38}
39
40#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
42pub enum CallTarget {
43 Sym(String),
45 Anon,
47}
48
49#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
51pub enum Effect {
52 Write {
54 region: Region,
55 off: i32,
56 val: ValueClass,
57 },
58 Read {
61 region: Region,
62 off: i32,
63 },
64 NewRegion(Region),
66 Call(CallTarget),
67 Syscall(u32),
68 Branch,
71 Ret(ValueClass),
72 Capped,
74}
75
76impl Effect {
77 pub fn token(&self) -> u64 {
81 let mut h = Fnv::new();
82 match self {
83 Effect::Write { region, off, val } => {
84 h.tag(1);
85 region.hash(&mut h);
86 h.i32(*off);
87 val.hash(&mut h);
88 }
89 Effect::Read { region, off } => {
90 h.tag(8);
91 region.hash(&mut h);
92 h.i32(*off);
93 }
94 Effect::NewRegion(r) => {
95 h.tag(2);
96 r.hash(&mut h);
97 }
98 Effect::Call(t) => {
99 h.tag(3);
100 match t {
101 CallTarget::Sym(s) => {
102 h.tag(1);
103 for b in s.as_bytes() {
104 h.byte(*b);
105 }
106 }
107 CallTarget::Anon => h.tag(2),
108 }
109 }
110 Effect::Syscall(n) => {
111 h.tag(4);
112 h.u64(*n as u64);
113 }
114 Effect::Branch => h.tag(5),
115 Effect::Ret(v) => {
116 h.tag(6);
117 v.hash(&mut h);
118 }
119 Effect::Capped => h.tag(7),
120 }
121 h.0
122 }
123}
124
125impl Region {
126 fn hash(&self, h: &mut Fnv) {
127 match self {
128 Region::Arg(k) => {
129 h.tag(10);
130 h.u64(*k as u64);
131 }
132 Region::Stack => h.tag(11),
133 Region::Global => h.tag(13),
134 Region::Heapish => h.tag(12),
135 }
136 }
137}
138
139impl ValueClass {
140 fn hash(&self, h: &mut Fnv) {
141 match self {
142 ValueClass::Input(k) => {
143 h.tag(20);
144 h.u64(*k as u64);
145 }
146 ValueClass::InputDeriv(k) => {
147 h.tag(21);
148 h.u64(*k as u64);
149 }
150 ValueClass::Zero => h.tag(22),
151 ValueClass::SmallConst(v) => {
152 h.tag(23);
153 h.u64(*v as u64);
154 }
155 ValueClass::BigConst => h.tag(24),
156 ValueClass::Unknown => h.tag(25),
157 }
158 }
159}
160
161#[derive(Clone, Debug, Serialize, Deserialize)]
163pub struct EffectTrace {
164 pub effects: Vec<Effect>,
165 pub instret: u64,
167 pub capped: bool,
169}
170
171impl EffectTrace {
172 pub fn tokens(&self) -> Vec<u64> {
173 self.effects.iter().map(|e| e.token()).collect()
174 }
175
176 pub fn complexity(&self) -> usize {
179 let mut c = 0usize;
181 for e in &self.effects {
182 c += match e {
183 Effect::Call(_) | Effect::Syscall(_) => 3,
184 Effect::Write { .. } | Effect::NewRegion(_) => 2,
185 Effect::Read { .. } => 1,
186 Effect::Ret(ValueClass::Input(_)) | Effect::Ret(ValueClass::InputDeriv(_)) => 1,
187 _ => 0,
188 };
189 }
190 c
191 }
192}
193
194pub struct Fnv(pub u64);
196impl Fnv {
197 pub fn new() -> Self {
198 Fnv(0xcbf29ce484222325)
199 }
200 #[inline]
201 pub fn byte(&mut self, b: u8) {
202 self.0 ^= b as u64;
203 self.0 = self.0.wrapping_mul(0x100000001b3);
204 }
205 pub fn tag(&mut self, t: u8) {
206 self.byte(t);
207 }
208 pub fn u64(&mut self, v: u64) {
209 for i in 0..8 {
210 self.byte((v >> (i * 8)) as u8);
211 }
212 }
213 pub fn i32(&mut self, v: i32) {
214 self.u64(v as i64 as u64);
215 }
216}
217impl Default for Fnv {
218 fn default() -> Self {
219 Self::new()
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn token_is_stable_and_distinct() {
229 let a = Effect::Write {
230 region: Region::Arg(0),
231 off: 8,
232 val: ValueClass::Input(1),
233 };
234 let b = Effect::Write {
235 region: Region::Arg(0),
236 off: 8,
237 val: ValueClass::Input(1),
238 };
239 let c = Effect::Write {
240 region: Region::Arg(0),
241 off: 16,
242 val: ValueClass::Input(1),
243 };
244 assert_eq!(a.token(), b.token());
245 assert_ne!(a.token(), c.token());
246 }
247
248 #[test]
249 fn complexity_withholds_tiny() {
250 let thunk = EffectTrace {
251 effects: vec![Effect::Ret(ValueClass::Input(0))],
252 instret: 2,
253 capped: false,
254 };
255 assert!(thunk.complexity() < 3);
256 let real = EffectTrace {
257 effects: vec![
258 Effect::NewRegion(Region::Arg(0)),
259 Effect::Write {
260 region: Region::Arg(0),
261 off: 0,
262 val: ValueClass::Input(1),
263 },
264 Effect::Call(CallTarget::Sym("memcpy".into())),
265 ],
266 instret: 40,
267 capped: false,
268 };
269 assert!(real.complexity() >= 3);
270 }
271}