1use std::collections::HashMap;
32
33use super::{BinOp, CmpOp, Elem, IId, INode, IType, IntGraph, Loop, UnOp};
34
35const MAGIC: &[u8; 4] = b"TIR1";
36
37#[derive(Clone, Copy, Debug)]
39pub struct Limits {
40 pub max_nodes: usize,
42 pub max_loops: usize,
44 pub max_depth: usize,
46 pub max_trip: u32,
48 pub max_cost: u64,
50}
51
52impl Default for Limits {
53 fn default() -> Self {
54 Self {
55 max_nodes: 1 << 14,
56 max_loops: 64,
57 max_depth: 4,
58 max_trip: 1 << 12,
59 max_cost: 1 << 20,
60 }
61 }
62}
63
64#[derive(Clone, Debug, PartialEq, Eq)]
66pub struct WireError(pub String);
67
68impl std::fmt::Display for WireError {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 write!(f, "int graph rejected: {}", self.0)
71 }
72}
73
74impl std::error::Error for WireError {}
75
76fn err<T>(m: impl Into<String>) -> Result<T, WireError> {
77 Err(WireError(m.into()))
78}
79
80fn put(o: &mut Vec<u8>, mut v: u64) {
81 loop {
82 let b = (v & 0x7f) as u8;
83 v >>= 7;
84 if v == 0 {
85 o.push(b);
86 return;
87 }
88 o.push(b | 0x80);
89 }
90}
91
92struct Rd<'a> {
93 b: &'a [u8],
94 p: usize,
95}
96
97impl Rd<'_> {
98 fn u8(&mut self) -> Result<u8, WireError> {
99 let v = *self.b.get(self.p).ok_or(WireError("truncated".into()))?;
100 self.p += 1;
101 Ok(v)
102 }
103 fn var(&mut self) -> Result<u64, WireError> {
104 let mut v = 0u64;
105 for s in 0..10 {
106 let b = self.u8()?;
107 v |= ((b & 0x7f) as u64) << (7 * s);
108 if b & 0x80 == 0 {
109 return Ok(v);
110 }
111 }
112 err("varint too long")
113 }
114 fn u32(&mut self) -> Result<u32, WireError> {
115 u32::try_from(self.var()?).or(err("value out of range"))
116 }
117 fn n(&mut self, max: usize, what: &str) -> Result<usize, WireError> {
118 let v = self.var()?;
119 if v > max as u64 {
120 return err(format!("too many {what}"));
121 }
122 Ok(v as usize)
123 }
124}
125
126fn ty_code(t: IType) -> u8 {
127 match t {
128 IType::I32 => 0,
129 IType::U32 => 1,
130 IType::Bool => 2,
131 }
132}
133
134fn ty_of(c: u8) -> Result<IType, WireError> {
135 Ok(match c {
136 0 => IType::I32,
137 1 => IType::U32,
138 2 => IType::Bool,
139 _ => return err("type"),
140 })
141}
142
143const BINS: [BinOp; 13] = [
144 BinOp::Add,
145 BinOp::Sub,
146 BinOp::Mul,
147 BinOp::MulHi,
148 BinOp::And,
149 BinOp::Or,
150 BinOp::Xor,
151 BinOp::Shl,
152 BinOp::Shr,
153 BinOp::Min,
154 BinOp::Max,
155 BinOp::Div,
156 BinOp::Rem,
157];
158const CMPS: [CmpOp; 4] = [CmpOp::Eq, CmpOp::Ne, CmpOp::Lt, CmpOp::Le];
159const UNS: [UnOp; 3] = [UnOp::Neg, UnOp::Not, UnOp::Clz];
160
161impl IntGraph {
162 pub fn to_bytes(&self, outputs: &[IId]) -> Vec<u8> {
165 let mut o = MAGIC.to_vec();
166 put(&mut o, self.loops.len() as u64);
167 for lp in &self.loops {
168 put(&mut o, lp.parent.map_or(0, |p| p as u64 + 1));
169 put(
170 &mut o,
171 lp.cap.expect("to_bytes: every loop needs a trip cap") as u64,
172 );
173 put(&mut o, lp.init.len() as u64);
174 for &x in &lp.init {
175 put(&mut o, x.0 as u64);
176 }
177 put(&mut o, lp.cond.0 as u64);
178 for &x in &lp.next {
179 put(&mut o, x.0 as u64);
180 }
181 }
182 put(&mut o, self.nodes.len() as u64);
183 for (i, n) in self.nodes.iter().enumerate() {
184 let d = |a: IId| (i - a.index()) as u64;
185 match *n {
186 INode::Const(t, v) => {
187 o.push(0);
188 o.push(ty_code(t));
189 let z = if t == IType::I32 {
191 ((v << 1) ^ (((v as i32) >> 31) as u32)) as u64
192 } else {
193 v as u64
194 };
195 put(&mut o, z);
196 }
197 INode::Param(p, t) => {
198 o.push(1);
199 put(&mut o, p as u64);
200 o.push(ty_code(t));
201 }
202 INode::Bin(op, a, b) => {
203 o.push(2);
204 o.push(BINS.iter().position(|&x| x == op).unwrap() as u8);
205 put(&mut o, d(a));
206 put(&mut o, d(b));
207 }
208 INode::Un(op, a) => {
209 o.push(3);
210 o.push(UNS.iter().position(|&x| x == op).unwrap() as u8);
211 put(&mut o, d(a));
212 }
213 INode::Cmp(op, a, b) => {
214 o.push(4);
215 o.push(CMPS.iter().position(|&x| x == op).unwrap() as u8);
216 put(&mut o, d(a));
217 put(&mut o, d(b));
218 }
219 INode::Select(c, a, b) => {
220 o.push(5);
221 put(&mut o, d(c));
222 put(&mut o, d(a));
223 put(&mut o, d(b));
224 }
225 INode::Bitcast(t, a) => {
226 o.push(6);
227 o.push(ty_code(t));
228 put(&mut o, d(a));
229 }
230 INode::Load(b, a) => {
231 o.push(7);
232 put(&mut o, b as u64);
233 put(&mut o, d(a));
234 }
235 INode::State(l, k) => {
236 o.push(8);
237 put(&mut o, l as u64);
238 put(&mut o, k as u64);
239 }
240 INode::LoopOut(l, k) => {
241 o.push(9);
242 put(&mut o, l as u64);
243 put(&mut o, k as u64);
244 }
245 INode::Tok => o.push(10),
246 INode::LoadRw(b, a, t) => {
247 o.push(11);
248 put(&mut o, b as u64);
249 put(&mut o, d(a));
250 put(&mut o, d(t));
251 }
252 INode::Store(b, a, v, t) => {
253 o.push(12);
254 put(&mut o, b as u64);
255 put(&mut o, d(a));
256 put(&mut o, d(v));
257 put(&mut o, d(t));
258 }
259 }
260 }
261 put(&mut o, outputs.len() as u64);
262 for x in outputs {
263 put(&mut o, x.0 as u64);
264 }
265 o
266 }
267
268 pub fn from_bytes(
272 bytes: &[u8],
273 params: &[IType],
274 bufs: &[(&str, Elem)],
275 outs: &[IType],
276 lim: &Limits,
277 ) -> Result<(IntGraph, Vec<IId>), WireError> {
278 Self::from_bytes_rw(bytes, params, bufs, &[], outs, lim)
279 }
280
281 pub fn from_bytes_rw(
284 bytes: &[u8],
285 params: &[IType],
286 bufs: &[(&str, Elem)],
287 rw: &[(&str, Elem, u32, &str)],
288 outs: &[IType],
289 lim: &Limits,
290 ) -> Result<(IntGraph, Vec<IId>), WireError> {
291 let mut r = Rd { b: bytes, p: 0 };
292 if bytes.len() < 4 || &bytes[..4] != MAGIC {
293 return err("magic");
294 }
295 r.p = 4;
296 let mut g = IntGraph::new();
297 for &(name, elem) in bufs {
298 g.buffer(name, elem);
299 }
300 for &(name, elem, len, base) in rw {
301 g.buffer_rw(name, elem, len, base);
302 }
303 let nbufs = g.bufs.len() as u64;
304 let nl = r.n(lim.max_loops, "loops")?;
305 let mut caps = Vec::with_capacity(nl);
306 for l in 0..nl {
307 let parent = match r.var()? {
308 0 => None,
309 p if p - 1 < l as u64 => Some((p - 1) as u32),
310 _ => return err("loop parent must be an earlier loop"),
311 };
312 let cap = r.u32()?;
313 if cap > lim.max_trip {
314 return err("loop trip cap over limit");
315 }
316 caps.push(cap);
317 let k = r.n(256, "loop states")?;
318 let ids = |r: &mut Rd, k: usize| {
319 (0..k)
320 .map(|_| r.u32().map(IId))
321 .collect::<Result<Vec<_>, _>>()
322 };
323 let init = ids(&mut r, k)?;
324 let cond = IId(r.u32()?);
325 let next = ids(&mut r, k)?;
326 let lp = Loop {
327 init,
328 cond,
329 next,
330 parent,
331 cap: Some(cap),
332 };
333 if g.depth(parent) + 1 > lim.max_depth {
334 return err("loops nest too deep");
335 }
336 g.loops.push(lp);
337 }
338 let nn = r.n(lim.max_nodes, "nodes")?;
339 let mut state_seen: Vec<Vec<bool>> =
340 g.loops.iter().map(|l| vec![false; l.init.len()]).collect();
341 let mut intern: HashMap<INode, IId> = HashMap::new();
342 for i in 0..nn {
343 let tag = r.u8()?;
344 let opnd = |r: &mut Rd| -> Result<IId, WireError> {
345 let d = r.var()?;
346 if d == 0 || d > i as u64 {
347 return err("operand must be an earlier node");
348 }
349 Ok(IId((i as u64 - d) as u32))
350 };
351 let lk = |r: &mut Rd, g: &IntGraph| -> Result<(u32, u16), WireError> {
352 let l = r.var()?;
353 let k = r.var()?;
354 if l >= g.loops.len() as u64 || k >= g.loops[l as usize].init.len() as u64 {
355 return err("loop reference");
356 }
357 Ok((l as u32, k as u16))
358 };
359 let n = match tag {
360 0 => {
361 let t = ty_of(r.u8()?)?;
362 let z = r.u32()?;
363 let v = if t == IType::I32 {
364 (z >> 1) ^ (z & 1).wrapping_neg()
365 } else {
366 z
367 };
368 if t == IType::Bool && v > 1 {
369 return err("bool constant");
370 }
371 INode::Const(t, v)
372 }
373 1 => {
374 let p = r.var()?;
375 let t = ty_of(r.u8()?)?;
376 if p >= params.len() as u64 || params[p as usize] != t {
377 return err("parameter not in the ABI");
378 }
379 INode::Param(p as u16, t)
380 }
381 2 => {
382 let op = *BINS.get(r.u8()? as usize).ok_or(WireError("op".into()))?;
383 INode::Bin(op, opnd(&mut r)?, opnd(&mut r)?)
384 }
385 3 => {
386 let op = *UNS.get(r.u8()? as usize).ok_or(WireError("op".into()))?;
387 INode::Un(op, opnd(&mut r)?)
388 }
389 4 => {
390 let op = *CMPS.get(r.u8()? as usize).ok_or(WireError("op".into()))?;
391 INode::Cmp(op, opnd(&mut r)?, opnd(&mut r)?)
392 }
393 5 => INode::Select(opnd(&mut r)?, opnd(&mut r)?, opnd(&mut r)?),
394 6 => {
395 let t = ty_of(r.u8()?)?;
396 INode::Bitcast(t, opnd(&mut r)?)
397 }
398 7 => {
399 let b = r.var()?;
400 if b >= nbufs {
401 return err("buffer not in the ABI");
402 }
403 INode::Load(b as u16, opnd(&mut r)?)
404 }
405 10 => INode::Tok,
406 11 | 12 => {
407 let b = r.var()?;
408 if b >= nbufs {
409 return err("buffer not in the ABI");
410 }
411 let i = opnd(&mut r)?;
412 if tag == 11 {
413 INode::LoadRw(b as u16, i, opnd(&mut r)?)
414 } else {
415 let v = opnd(&mut r)?;
416 INode::Store(b as u16, i, v, opnd(&mut r)?)
417 }
418 }
419 8 => {
420 let (l, k) = lk(&mut r, &g)?;
421 if g.loops[l as usize].init[k as usize].index() >= i {
422 return err("loop init must precede its state");
423 }
424 if std::mem::replace(&mut state_seen[l as usize][k as usize], true) {
425 return err("duplicate loop state");
426 }
427 INode::State(l, k)
428 }
429 9 => {
430 let (l, k) = lk(&mut r, &g)?;
431 let lp = &g.loops[l as usize];
432 if lp.cond.index() >= i || lp.next.iter().any(|x| x.index() >= i) {
433 return err("loop body must precede its result");
434 }
435 if !state_seen[l as usize].iter().all(|&s| s) {
436 return err("loop result before all its states");
437 }
438 INode::LoopOut(l, k)
439 }
440 _ => return err("node tag"),
441 };
442 let (ty, scope) = g.try_infer(n).map_err(|e| WireError(e.into()))?;
443 g.nodes.push(n);
444 g.types.push(ty);
445 g.scope.push(scope);
446 intern.entry(n).or_insert(IId(i as u32));
447 }
448 g.intern = intern;
449 let n = g.nodes.len();
450 for (l, seen) in state_seen.iter().enumerate().take(g.loops.len()) {
452 let lp = g.loops[l].clone();
453 if !seen.iter().all(|&s| s) {
454 return err("loop without all its states");
455 }
456 if lp.cond.index() >= n || lp.next.iter().chain(&lp.init).any(|x| x.index() >= n) {
457 return err("loop reference out of range");
458 }
459 if g.ty(lp.cond) != IType::Bool {
460 return err("loop condition must be bool");
461 }
462 let me = Some(l as u32);
463 let within = |g: &IntGraph, x: IId, s: Option<u32>| {
464 g.try_inner(g.scope[x.index()], s).ok() == Some(s)
465 };
466 if !within(&g, lp.cond, me) || !lp.next.iter().all(|&x| within(&g, x, me)) {
467 return err("loop body uses an unrelated scope");
468 }
469 if !lp.init.iter().all(|&x| within(&g, x, lp.parent)) {
470 return err("loop init outside the enclosing scope");
471 }
472 for (k, &x) in lp.next.iter().enumerate() {
473 if g.ty(x) != g.ty(lp.init[k]) {
474 return err("loop state changes type");
475 }
476 }
477 }
478 let mut cost = 0u64;
480 for i in 0..n {
481 let mut c = 1u64;
482 let mut s = g.scope[i];
483 while let Some(l) = s {
484 c = c.saturating_mul(caps[l as usize].max(1) as u64);
485 s = g.loops[l as usize].parent;
486 }
487 cost = cost.saturating_add(c);
488 }
489 if cost > lim.max_cost {
490 return err(format!("worst-case cost {cost} over limit"));
491 }
492 let no = r.n(outs.len(), "outputs")?;
493 if no != outs.len() {
494 return err("output count");
495 }
496 let mut o = Vec::with_capacity(no);
497 for &t in outs {
498 let x = r.u32()? as usize;
499 if x >= n || g.scope[x].is_some() || g.types[x] != t {
500 return err("output not a top-level value of the ABI type");
501 }
502 o.push(IId(x as u32));
503 }
504 if r.p != bytes.len() {
505 return err("trailing bytes");
506 }
507 Ok((g, o))
508 }
509}