1use crossbeam::deque::{Injector, Steal, Stealer, Worker};
35use crossbeam::queue::SegQueue;
36use crossbeam::thread;
37use std::collections::{HashMap, HashSet};
38use std::mem;
39use std::sync::atomic::{AtomicBool, Ordering};
40use std::sync::mpsc;
41use std::sync::mpsc::{Receiver, Sender};
42use std::sync::{Arc, RwLock};
43use std::thread::sleep;
44use std::time::{Duration, Instant};
45
46use crate::bitvector::BV;
47use crate::error::ExecError;
48use crate::ir::*;
49use crate::log;
50use crate::memory::Memory;
51use crate::primop;
52use crate::probe;
53use crate::smt::*;
54use crate::zencode;
55
56pub fn symbolic<B: BV>(
62 ty: &Ty<Name>,
63 shared_state: &SharedState<B>,
64 solver: &mut Solver<B>,
65) -> Result<Val<B>, ExecError> {
66 let smt_ty = match ty {
67 Ty::Unit => return Ok(Val::Unit),
68 Ty::Bits(0) => return Ok(Val::Bits(B::zeros(0))),
69
70 Ty::I64 => smtlib::Ty::BitVec(64),
71 Ty::I128 => smtlib::Ty::BitVec(128),
72 Ty::Bits(sz) => smtlib::Ty::BitVec(*sz),
73 Ty::Bool => smtlib::Ty::Bool,
74 Ty::Bit => smtlib::Ty::BitVec(1),
75
76 Ty::Struct(name) => {
77 if let Some(field_types) = shared_state.structs.get(name) {
78 let field_values = field_types
79 .iter()
80 .map(|(f, ty)| match symbolic(ty, shared_state, solver) {
81 Ok(value) => Ok((*f, value)),
82 Err(error) => Err(error),
83 })
84 .collect::<Result<_, _>>()?;
85 return Ok(Val::Struct(field_values));
86 } else {
87 let name = zencode::decode(shared_state.symtab.to_str(*name));
88 return Err(ExecError::Unreachable(format!("Struct {} does not appear to exist!", name)));
89 }
90 }
91
92 Ty::Enum(name) => {
93 let enum_size = shared_state.enums.get(name).unwrap().len();
94 let enum_id = solver.get_enum(enum_size);
95 return solver.declare_const(smtlib::Ty::Enum(enum_id)).into();
96 }
97
98 Ty::FixedVector(sz, ty) => {
99 let values = (0..*sz).map(|_| symbolic(ty, shared_state, solver)).collect::<Result<_, _>>()?;
100 return Ok(Val::Vector(values));
101 }
102
103 _ => return Ok(Val::Poison),
106 };
107
108 solver.declare_const(smt_ty).into()
109}
110
111#[derive(Clone)]
112struct LocalState<'ir, B> {
113 vars: Bindings<'ir, B>,
114 regs: Bindings<'ir, B>,
115 lets: Bindings<'ir, B>,
116}
117
118fn get_and_initialize<'ir, B: BV>(
131 v: Name,
132 vars: &mut Bindings<'ir, B>,
133 shared_state: &SharedState<'ir, B>,
134 solver: &mut Solver<B>,
135) -> Result<Option<Val<B>>, ExecError> {
136 Ok(match vars.get(&v) {
137 Some(UVal::Uninit(ty)) => {
138 let sym = symbolic(ty, shared_state, solver)?;
139 vars.insert(v, UVal::Init(sym.clone()));
140 Some(sym)
141 }
142 Some(UVal::Init(value)) => Some(value.clone()),
143 None => None,
144 })
145}
146
147fn get_id_and_initialize<'ir, B: BV>(
148 id: Name,
149 local_state: &mut LocalState<'ir, B>,
150 shared_state: &SharedState<'ir, B>,
151 solver: &mut Solver<B>,
152 accessor: &mut Vec<Accessor>,
153) -> Result<Val<B>, ExecError> {
154 Ok(match get_and_initialize(id, &mut local_state.vars, shared_state, solver)? {
155 Some(value) => value,
156 None => match get_and_initialize(id, &mut local_state.regs, shared_state, solver)? {
157 Some(value) => {
158 let symbol = zencode::decode(shared_state.symtab.to_str(id));
159 if symbol != "_TLB" {
161 solver.add_event(Event::ReadReg(id, accessor.to_vec(), value.clone()));
163 }
164 value
165 }
166 None => match get_and_initialize(id, &mut local_state.lets, shared_state, solver)? {
167 Some(value) => value,
168 None => match shared_state.enum_members.get(&id) {
169 Some((member, enum_size)) => {
170 let enum_id = solver.get_enum(*enum_size);
171 Val::Enum(EnumMember { enum_id, member: *member })
172 }
173 None => panic!("Symbol {} ({:?}) not found", zencode::decode(shared_state.symtab.to_str(id)), id),
174 },
175 },
176 },
177 })
178}
179
180fn get_loc_and_initialize<'ir, B: BV>(
181 loc: &Loc<Name>,
182 local_state: &mut LocalState<'ir, B>,
183 shared_state: &SharedState<'ir, B>,
184 solver: &mut Solver<B>,
185 accessor: &mut Vec<Accessor>,
186) -> Result<Val<B>, ExecError> {
187 Ok(match loc {
188 Loc::Id(id) => get_id_and_initialize(*id, local_state, shared_state, solver, accessor)?,
189 Loc::Field(loc, field) => {
190 accessor.push(Accessor::Field(*field));
191 if let Val::Struct(members) = get_loc_and_initialize(loc, local_state, shared_state, solver, accessor)? {
192 match members.get(field) {
193 Some(field_value) => field_value.clone(),
194 None => panic!("No field {:?}", shared_state.symtab.to_str(*field)),
195 }
196 } else {
197 panic!("Struct expression did not evaluate to a struct")
198 }
199 }
200 _ => panic!("Cannot get_loc_and_initialize"),
201 })
202}
203
204fn eval_exp_with_accessor<'ir, B: BV>(
205 exp: &Exp<Name>,
206 local_state: &mut LocalState<'ir, B>,
207 shared_state: &SharedState<'ir, B>,
208 solver: &mut Solver<B>,
209 accessor: &mut Vec<Accessor>,
210) -> Result<Val<B>, ExecError> {
211 use Exp::*;
212 Ok(match exp {
213 Id(id) => get_id_and_initialize(*id, local_state, shared_state, solver, accessor)?,
214
215 I64(i) => Val::I64(*i),
216 I128(i) => Val::I128(*i),
217 Unit => Val::Unit,
218 Bool(b) => Val::Bool(*b),
219 Bits(bv) => Val::Bits(B::new(bv.lower_u64(), bv.len())),
221 String(s) => Val::String(s.clone()),
222
223 Undefined(ty) => symbolic(ty, shared_state, solver)?,
224
225 Call(op, args) => {
226 let args: Vec<Val<B>> =
227 args.iter().map(|arg| eval_exp(arg, local_state, shared_state, solver)).collect::<Result<_, _>>()?;
228 match op {
229 Op::Lt => primop::op_lt(args[0].clone(), args[1].clone(), solver)?,
230 Op::Gt => primop::op_gt(args[0].clone(), args[1].clone(), solver)?,
231 Op::Lteq => primop::op_lteq(args[0].clone(), args[1].clone(), solver)?,
232 Op::Gteq => primop::op_gteq(args[0].clone(), args[1].clone(), solver)?,
233 Op::Eq => primop::op_eq(args[0].clone(), args[1].clone(), solver)?,
234 Op::Neq => primop::op_neq(args[0].clone(), args[1].clone(), solver)?,
235 Op::Add => primop::op_add(args[0].clone(), args[1].clone(), solver)?,
236 Op::Sub => primop::op_sub(args[0].clone(), args[1].clone(), solver)?,
237 Op::Bvnot => primop::not_bits(args[0].clone(), solver)?,
238 Op::Bvor => primop::or_bits(args[0].clone(), args[1].clone(), solver)?,
239 Op::Bvxor => primop::xor_bits(args[0].clone(), args[1].clone(), solver)?,
240 Op::Bvand => primop::and_bits(args[0].clone(), args[1].clone(), solver)?,
241 Op::Bvadd => primop::add_bits(args[0].clone(), args[1].clone(), solver)?,
242 Op::Bvsub => primop::sub_bits(args[0].clone(), args[1].clone(), solver)?,
243 Op::Bvaccess => primop::vector_access(args[0].clone(), args[1].clone(), solver)?,
244 Op::Concat => primop::append(args[0].clone(), args[1].clone(), solver)?,
245 Op::Not => primop::not_bool(args[0].clone(), solver)?,
246 Op::And => primop::and_bool(args[0].clone(), args[1].clone(), solver)?,
247 Op::Or => primop::or_bool(args[0].clone(), args[1].clone(), solver)?,
248 Op::Slice(len) => primop::op_slice(args[0].clone(), args[1].clone(), *len, solver)?,
249 Op::SetSlice => primop::op_set_slice(args[0].clone(), args[1].clone(), args[2].clone(), solver)?,
250 Op::Unsigned(_) => primop::op_unsigned(args[0].clone(), solver)?,
251 Op::Signed(_) => primop::op_signed(args[0].clone(), solver)?,
252 Op::Head => primop::op_head(args[0].clone(), solver)?,
253 Op::Tail => primop::op_tail(args[0].clone(), solver)?,
254 Op::ZeroExtend(len) => primop::op_zero_extend(args[0].clone(), *len, solver)?,
255 }
256 }
257
258 Kind(ctor_a, exp) => {
259 let v = eval_exp(exp, local_state, shared_state, solver)?;
260 match v {
261 Val::Ctor(ctor_b, _) => Val::Bool(*ctor_a != ctor_b),
262 _ => return Err(ExecError::Type(format!("Kind check on non-constructor {:?}", &v))),
263 }
264 }
265
266 Unwrap(ctor_a, exp) => {
267 let v = eval_exp(exp, local_state, shared_state, solver)?;
268 match v {
269 Val::Ctor(ctor_b, v) => {
270 if *ctor_a == ctor_b {
271 *v
272 } else {
273 return Err(ExecError::Type(format!("Constructors did not match in unwrap {:?}", &v)));
274 }
275 }
276 _ => return Err(ExecError::Type(format!("Tried to unwrap non-constructor {:?}", &v))),
277 }
278 }
279
280 Field(exp, field) => {
281 accessor.push(Accessor::Field(*field));
282 if let Val::Struct(struct_value) = eval_exp_with_accessor(exp, local_state, shared_state, solver, accessor)?
283 {
284 match struct_value.get(field) {
285 Some(field_value) => field_value.clone(),
286 None => panic!("No field {:?}", shared_state.symtab.to_str(*field)),
287 }
288 } else {
289 panic!("Struct expression did not evaluate to a struct")
290 }
291 }
292
293 Ref(reg) => Val::Ref(*reg),
294
295 _ => panic!("Could not evaluate expression {:?}", exp),
296 })
297}
298
299fn eval_exp<'ir, B: BV>(
300 exp: &Exp<Name>,
301 local_state: &mut LocalState<'ir, B>,
302 shared_state: &SharedState<'ir, B>,
303 solver: &mut Solver<B>,
304) -> Result<Val<B>, ExecError> {
305 eval_exp_with_accessor(exp, local_state, shared_state, solver, &mut Vec::new())
306}
307
308fn assign_with_accessor<'ir, B: BV>(
309 loc: &Loc<Name>,
310 v: Val<B>,
311 local_state: &mut LocalState<'ir, B>,
312 shared_state: &SharedState<'ir, B>,
313 solver: &mut Solver<B>,
314 accessor: &mut Vec<Accessor>,
315) -> Result<(), ExecError> {
316 match loc {
317 Loc::Id(id) => {
318 if local_state.vars.contains_key(id) || *id == RETURN {
319 local_state.vars.insert(*id, UVal::Init(v));
320 } else if local_state.lets.contains_key(id) {
321 local_state.lets.insert(*id, UVal::Init(v));
322 } else {
323 let symbol = zencode::decode(shared_state.symtab.to_str(*id));
324 if symbol != "_TLB" {
326 solver.add_event(Event::WriteReg(*id, accessor.to_vec(), v.clone()))
327 }
328 local_state.regs.insert(*id, UVal::Init(v));
329 }
330 }
331
332 Loc::Field(loc, field) => {
333 let mut accessor = Vec::new();
334 accessor.push(Accessor::Field(*field));
335 if let Val::Struct(field_values) =
336 get_loc_and_initialize(loc, local_state, shared_state, solver, &mut accessor)?
337 {
338 match field_values.get(field) {
340 Some(_) => {
341 let mut field_values = field_values.clone();
342 field_values.insert(*field, v);
343 assign_with_accessor(
344 loc,
345 Val::Struct(field_values),
346 local_state,
347 shared_state,
348 solver,
349 &mut accessor,
350 )?;
351 }
352 None => panic!("Invalid field assignment"),
353 }
354 } else {
355 panic!(
356 "Cannot assign struct to non-struct {:?}.{:?} ({:?})",
357 loc,
358 field,
359 get_loc_and_initialize(loc, local_state, shared_state, solver, &mut accessor)
360 )
361 }
362 }
363
364 Loc::Addr(loc) => {
365 if let Val::Ref(reg) = get_loc_and_initialize(loc, local_state, shared_state, solver, accessor)? {
366 assign_with_accessor(&Loc::Id(reg), v, local_state, shared_state, solver, accessor)?
367 } else {
368 panic!("Cannot get address of non-reference {:?}", loc)
369 }
370 }
371 };
372 Ok(())
373}
374
375fn assign<'ir, B: BV>(
376 tid: usize,
377 loc: &Loc<Name>,
378 v: Val<B>,
379 local_state: &mut LocalState<'ir, B>,
380 shared_state: &SharedState<'ir, B>,
381 solver: &mut Solver<B>,
382) -> Result<(), ExecError> {
383 let id = loc.id();
384 if shared_state.probes.contains(&id) {
385 let mut symbol = String::from(shared_state.symtab.to_str(id));
386 if symbol.starts_with('z') {
387 symbol = zencode::decode(&symbol);
388 }
389 log_from!(tid, log::PROBE, &format!("Assigning {}[{:?}] <- {:?}", symbol, id, v))
390 }
391
392 assign_with_accessor(loc, v, local_state, shared_state, solver, &mut Vec::new())
393}
394
395type Stack<'ir, B> = Option<
399 Arc<
400 dyn 'ir
401 + Send
402 + Sync
403 + Fn(Val<B>, &mut LocalFrame<'ir, B>, &SharedState<'ir, B>, &mut Solver<B>) -> Result<(), ExecError>,
404 >,
405>;
406
407pub type Backtrace = Vec<(Name, usize)>;
408
409#[derive(Clone)]
412pub struct Frame<'ir, B> {
413 function_name: Name,
414 pc: usize,
415 forks: u32,
416 backjumps: u32,
417 local_state: Arc<LocalState<'ir, B>>,
418 memory: Arc<Memory<B>>,
419 instrs: &'ir [Instr<Name, B>],
420 stack_vars: Arc<Vec<Bindings<'ir, B>>>,
421 stack_call: Stack<'ir, B>,
422 backtrace: Arc<Backtrace>,
423}
424
425pub struct LocalFrame<'ir, B> {
429 function_name: Name,
430 pc: usize,
431 forks: u32,
432 backjumps: u32,
433 local_state: LocalState<'ir, B>,
434 memory: Memory<B>,
435 instrs: &'ir [Instr<Name, B>],
436 stack_vars: Vec<Bindings<'ir, B>>,
437 stack_call: Stack<'ir, B>,
438 backtrace: Backtrace,
439}
440
441pub fn unfreeze_frame<'ir, B: BV>(frame: &Frame<'ir, B>) -> LocalFrame<'ir, B> {
442 LocalFrame {
443 function_name: frame.function_name,
444 pc: frame.pc,
445 forks: frame.forks,
446 backjumps: frame.backjumps,
447 local_state: (*frame.local_state).clone(),
448 memory: (*frame.memory).clone(),
449 instrs: frame.instrs,
450 stack_vars: (*frame.stack_vars).clone(),
451 stack_call: frame.stack_call.clone(),
452 backtrace: (*frame.backtrace).clone(),
453 }
454}
455
456pub fn freeze_frame<'ir, B: BV>(frame: &LocalFrame<'ir, B>) -> Frame<'ir, B> {
457 Frame {
458 function_name: frame.function_name,
459 pc: frame.pc,
460 forks: frame.forks,
461 backjumps: frame.backjumps,
462 local_state: Arc::new(frame.local_state.clone()),
463 memory: Arc::new(frame.memory.clone()),
464 instrs: frame.instrs,
465 stack_vars: Arc::new(frame.stack_vars.clone()),
466 stack_call: frame.stack_call.clone(),
467 backtrace: Arc::new(frame.backtrace.clone()),
468 }
469}
470
471impl<'ir, B: BV> LocalFrame<'ir, B> {
472 pub fn vars_mut(&mut self) -> &mut Bindings<'ir, B> {
473 &mut self.local_state.vars
474 }
475
476 pub fn vars(&self) -> &Bindings<'ir, B> {
477 &self.local_state.vars
478 }
479
480 pub fn regs_mut(&mut self) -> &mut Bindings<'ir, B> {
481 &mut self.local_state.regs
482 }
483
484 pub fn regs(&self) -> &Bindings<'ir, B> {
485 &self.local_state.regs
486 }
487
488 pub fn add_regs(&mut self, regs: &Bindings<'ir, B>) -> &mut Self {
489 for (k, v) in regs.iter() {
490 self.local_state.regs.insert(*k, v.clone());
491 }
492 self
493 }
494
495 pub fn lets_mut(&mut self) -> &mut Bindings<'ir, B> {
496 &mut self.local_state.lets
497 }
498
499 pub fn lets(&self) -> &Bindings<'ir, B> {
500 &self.local_state.lets
501 }
502
503 pub fn add_lets(&mut self, lets: &Bindings<'ir, B>) -> &mut Self {
504 for (k, v) in lets.iter() {
505 self.local_state.lets.insert(*k, v.clone());
506 }
507 self
508 }
509
510 pub fn get_exception(&self) -> Option<(&Val<B>, &str)> {
511 if let Some(UVal::Init(Val::Bool(true))) = self.lets().get(&HAVE_EXCEPTION) {
512 if let Some(UVal::Init(val)) = self.lets().get(&CURRENT_EXCEPTION) {
513 let loc = match self.lets().get(&THROW_LOCATION) {
514 Some(UVal::Init(Val::String(s))) => s,
515 Some(UVal::Init(_)) => "location has wrong type",
516 _ => "missing location",
517 };
518 Some((val, loc))
519 } else {
520 None
521 }
522 } else {
523 None
524 }
525 }
526
527 pub fn memory(&self) -> &Memory<B> {
528 &self.memory
529 }
530
531 pub fn memory_mut(&mut self) -> &mut Memory<B> {
532 &mut self.memory
533 }
534
535 pub fn set_memory(&mut self, memory: Memory<B>) -> &mut Self {
536 self.memory = memory;
537 self
538 }
539
540 pub fn new(
541 name: Name,
542 args: &[(Name, &'ir Ty<Name>)],
543 vals: Option<&[Val<B>]>,
544 instrs: &'ir [Instr<Name, B>],
545 ) -> Self {
546 let mut vars = HashMap::new();
547 match vals {
548 Some(vals) => {
549 for ((id, _), val) in args.iter().zip(vals) {
550 vars.insert(*id, UVal::Init(val.clone()));
551 }
552 }
553 None => {
554 for (id, ty) in args {
555 vars.insert(*id, UVal::Uninit(ty));
556 }
557 }
558 }
559
560 let mut lets = HashMap::new();
561 lets.insert(HAVE_EXCEPTION, UVal::Init(Val::Bool(false)));
562 lets.insert(CURRENT_EXCEPTION, UVal::Uninit(&Ty::Union(SAIL_EXCEPTION)));
563 lets.insert(THROW_LOCATION, UVal::Uninit(&Ty::String));
564 lets.insert(NULL, UVal::Init(Val::List(Vec::new())));
565
566 let regs = HashMap::new();
567
568 LocalFrame {
569 function_name: name,
570 pc: 0,
571 forks: 0,
572 backjumps: 0,
573 local_state: LocalState { vars, regs, lets },
574 memory: Memory::new(),
575 instrs,
576 stack_vars: Vec::new(),
577 stack_call: None,
578 backtrace: Vec::new(),
579 }
580 }
581
582 pub fn new_call(
583 &self,
584 name: Name,
585 args: &[(Name, &'ir Ty<Name>)],
586 vals: Option<&[Val<B>]>,
587 instrs: &'ir [Instr<Name, B>],
588 ) -> Self {
589 let mut new_frame = LocalFrame::new(name, args, vals, instrs);
590 new_frame.forks = self.forks;
591 new_frame.local_state.regs = self.local_state.regs.clone();
592 new_frame.local_state.lets = self.local_state.lets.clone();
593 new_frame.memory = self.memory.clone();
594 new_frame
595 }
596
597 pub fn task_with_checkpoint<'task>(
598 &self,
599 task_id: usize,
600 state: &'task TaskState<B>,
601 checkpoint: Checkpoint<B>,
602 ) -> Task<'ir, 'task, B> {
603 Task { id: task_id, frame: freeze_frame(&self), checkpoint, fork_cond: None, state, stop_functions: None }
604 }
605
606 pub fn task<'task>(&self, task_id: usize, state: &'task TaskState<B>) -> Task<'ir, 'task, B> {
607 self.task_with_checkpoint(task_id, state, Checkpoint::new())
608 }
609}
610
611fn push_call_stack<'ir, B: BV>(frame: &mut LocalFrame<'ir, B>) {
612 let mut vars = Box::new(HashMap::new());
613 mem::swap(&mut *vars, frame.vars_mut());
614 frame.stack_vars.push(*vars)
615}
616
617fn pop_call_stack<'ir, B: BV>(frame: &mut LocalFrame<'ir, B>) {
618 if let Some(mut vars) = frame.stack_vars.pop() {
619 mem::swap(&mut vars, frame.vars_mut())
620 }
621}
622
623#[derive(Copy, Clone, Debug)]
624struct Timeout {
625 start_time: Instant,
626 duration: Option<Duration>,
627}
628
629impl Timeout {
630 fn unlimited() -> Self {
631 Timeout { start_time: Instant::now(), duration: None }
632 }
633
634 fn timed_out(&self) -> bool {
635 self.duration.is_some() && self.start_time.elapsed() > self.duration.unwrap()
636 }
637}
638
639fn run<'ir, 'task, B: BV>(
640 tid: usize,
641 task_id: usize,
642 timeout: Timeout,
643 stop_functions: Option<&'task HashSet<Name>>,
644 queue: &Worker<Task<'ir, 'task, B>>,
645 frame: &Frame<'ir, B>,
646 task_state: &'task TaskState<B>,
647 shared_state: &SharedState<'ir, B>,
648 solver: &mut Solver<B>,
649) -> Result<(Val<B>, LocalFrame<'ir, B>), (ExecError, Backtrace)> {
650 let mut frame = unfreeze_frame(frame);
651 match run_loop(tid, task_id, timeout, stop_functions, queue, &mut frame, task_state, shared_state, solver) {
652 Ok(v) => Ok((v, frame)),
653 Err(err) => {
654 frame.backtrace.push((frame.function_name, frame.pc));
655 Err((err, frame.backtrace))
656 }
657 }
658}
659
660fn run_loop<'ir, 'task, B: BV>(
661 tid: usize,
662 task_id: usize,
663 timeout: Timeout,
664 stop_functions: Option<&'task HashSet<Name>>,
665 queue: &Worker<Task<'ir, 'task, B>>,
666 frame: &mut LocalFrame<'ir, B>,
667 task_state: &'task TaskState<B>,
668 shared_state: &SharedState<'ir, B>,
669 solver: &mut Solver<B>,
670) -> Result<Val<B>, ExecError> {
671 loop {
672 if frame.pc >= frame.instrs.len() {
673 log_from!(tid, log::VERBOSE, "Fell from end of instruction list");
675 return Ok(Val::Unit);
676 }
677
678 if timeout.timed_out() {
679 return Err(ExecError::Timeout);
680 }
681
682 match &frame.instrs[frame.pc] {
683 Instr::Decl(v, ty) => {
684 frame.vars_mut().insert(*v, UVal::Uninit(ty));
685 frame.pc += 1;
686 }
687
688 Instr::Init(var, _, exp) => {
689 let value = eval_exp(exp, &mut frame.local_state, shared_state, solver)?;
690 frame.vars_mut().insert(*var, UVal::Init(value));
691 frame.pc += 1;
692 }
693
694 Instr::Jump(exp, target, loc) => {
695 let value = eval_exp(exp, &mut frame.local_state, shared_state, solver)?;
696 match value {
697 Val::Symbolic(v) => {
698 use smtlib::Def::*;
699 use smtlib::Exp::*;
700
701 let test_true = Var(v);
702 let test_false = Not(Box::new(Var(v)));
703 let can_be_true = solver.check_sat_with(&test_true).is_sat()?;
704 let can_be_false = solver.check_sat_with(&test_false).is_sat()?;
705
706 if can_be_true && can_be_false {
707 if_logging!(log::FORK, {
708 log_from!(tid, log::FORK, loc);
709 probe::taint_info(log::FORK, v, Some(shared_state), solver)
710 });
711
712 solver.add_event(Event::Fork(frame.forks, v, loc.clone()));
715 frame.forks += 1;
716
717 let point = checkpoint(solver);
718 let frozen = Frame { pc: frame.pc + 1, ..freeze_frame(&frame) };
719 queue.push(Task {
720 id: task_id,
721 frame: frozen,
722 checkpoint: point,
723 fork_cond: Some(Assert(test_false)),
724 state: task_state,
725 stop_functions,
726 });
727 solver.add(Assert(test_true));
728 frame.pc = *target
729 } else if can_be_true {
730 solver.add(Assert(test_true));
731 frame.pc = *target
732 } else if can_be_false {
733 solver.add(Assert(test_false));
734 frame.pc += 1
735 } else {
736 return Err(ExecError::Dead);
737 }
738 }
739 Val::Bool(jump) => {
740 if jump {
741 frame.pc = *target
742 } else {
743 frame.pc += 1
744 }
745 }
746 _ => {
747 return Err(ExecError::Type(format!("Jump on non boolean {:?}", &value)));
748 }
749 }
750 }
751
752 Instr::Goto(target) => frame.pc = *target,
753
754 Instr::Copy(loc, exp) => {
755 let value = eval_exp(exp, &mut frame.local_state, shared_state, solver)?;
756 assign(tid, loc, value, &mut frame.local_state, shared_state, solver)?;
757 frame.pc += 1;
758 }
759
760 Instr::PrimopUnary(loc, f, arg) => {
761 let arg = eval_exp(arg, &mut frame.local_state, shared_state, solver)?;
762 let value = f(arg, solver)?;
763 assign(tid, loc, value, &mut frame.local_state, shared_state, solver)?;
764 frame.pc += 1;
765 }
766
767 Instr::PrimopBinary(loc, f, arg1, arg2) => {
768 let arg1 = eval_exp(arg1, &mut frame.local_state, shared_state, solver)?;
769 let arg2 = eval_exp(arg2, &mut frame.local_state, shared_state, solver)?;
770 let value = f(arg1, arg2, solver)?;
771 assign(tid, loc, value, &mut frame.local_state, shared_state, solver)?;
772 frame.pc += 1;
773 }
774
775 Instr::PrimopVariadic(loc, f, args) => {
776 let args = args
777 .iter()
778 .map(|arg| eval_exp(arg, &mut frame.local_state, shared_state, solver))
779 .collect::<Result<_, _>>()?;
780 let value = f(args, solver, frame)?;
781 assign(tid, loc, value, &mut frame.local_state, shared_state, solver)?;
782 frame.pc += 1;
783 }
784
785 Instr::Call(loc, _, f, args) => {
786 if let Some(s) = stop_functions {
787 if s.contains(f) {
788 let symbol = zencode::decode(shared_state.symtab.to_str(*f));
789 return Err(ExecError::Stopped(symbol));
790 }
791 }
792
793 match shared_state.functions.get(&f) {
794 None => {
795 if *f == INTERNAL_VECTOR_INIT && args.len() == 1 {
796 let arg = eval_exp(&args[0], &mut frame.local_state, shared_state, solver)?;
797 match loc {
798 Loc::Id(v) => match (arg, frame.vars().get(v)) {
799 (Val::I64(len), Some(UVal::Uninit(Ty::Vector(_)))) => assign(
800 tid,
801 loc,
802 Val::Vector(vec![Val::Poison; len as usize]),
803 &mut frame.local_state,
804 shared_state,
805 solver,
806 )?,
807 _ => return Err(ExecError::Type(format!("internal_vector_init {:?}", &loc))),
808 },
809 _ => return Err(ExecError::Type(format!("internal_vector_init {:?}", &loc))),
810 };
811 frame.pc += 1
812 } else if *f == INTERNAL_VECTOR_UPDATE && args.len() == 3 {
813 let args = args
814 .iter()
815 .map(|arg| eval_exp(arg, &mut frame.local_state, shared_state, solver))
816 .collect::<Result<Vec<Val<B>>, _>>()?;
817 let vector = primop::vector_update(args, solver, frame)?;
818 assign(tid, loc, vector, &mut frame.local_state, shared_state, solver)?;
819 frame.pc += 1
820 } else if *f == SAIL_EXIT {
821 return Err(ExecError::Exit);
822 } else if *f == RESET_REGISTERS {
823 for (loc, reset) in &shared_state.reset_registers {
824 if !task_state.reset_registers.contains_key(loc) {
825 let value = reset(&frame.memory, solver)?;
826 assign(tid, loc, value, &mut frame.local_state, shared_state, solver)?
827 }
828 }
829 for (loc, reset) in &task_state.reset_registers {
830 let value = reset(&frame.memory, solver)?;
831 assign(tid, loc, value, &mut frame.local_state, shared_state, solver)?
832 }
833 frame.pc += 1
834 } else if *f == REG_DEREF && args.len() == 1 {
835 if let Val::Ref(reg) = eval_exp(&args[0], &mut frame.local_state, shared_state, solver)? {
836 match get_and_initialize(reg, frame.regs_mut(), shared_state, solver)? {
837 Some(value) => {
838 solver.add_event(Event::ReadReg(reg, Vec::new(), value.clone()));
839 assign(tid, loc, value, &mut frame.local_state, shared_state, solver)?
840 }
841 None => return Err(ExecError::Type(format!("reg_deref {:?}", ®))),
842 }
843 } else {
844 return Err(ExecError::Type(format!("reg_deref (not a register) {:?}", &f)));
845 };
846 frame.pc += 1
847 } else if shared_state.union_ctors.contains(f) {
848 assert!(args.len() == 1);
849 let arg = eval_exp(&args[0], &mut frame.local_state, shared_state, solver)?;
850 assign(
851 tid,
852 loc,
853 Val::Ctor(*f, Box::new(arg)),
854 &mut frame.local_state,
855 shared_state,
856 solver,
857 )?;
858 frame.pc += 1
859 } else {
860 let symbol = zencode::decode(shared_state.symtab.to_str(*f));
861 panic!("Attempted to call non-existent function {} ({:?})", symbol, *f)
862 }
863 }
864
865 Some((params, _, instrs)) => {
866 let mut args = args
867 .iter()
868 .map(|arg| eval_exp(arg, &mut frame.local_state, shared_state, solver))
869 .collect::<Result<Vec<Val<B>>, _>>()?;
870
871 if shared_state.probes.contains(f) {
872 log_from!(tid, log::PROBE, probe::call_info(*f, &args, &shared_state.symtab));
873 probe::args_info(tid, &args, shared_state, solver)
874 }
875
876 let caller_pc = frame.pc;
877 let caller_instrs = frame.instrs;
878 let caller_stack_call = frame.stack_call.clone();
879 push_call_stack(frame);
880 frame.backtrace.push((frame.function_name, caller_pc));
881 frame.function_name = *f;
882
883 frame.stack_call = Some(Arc::new(move |ret, frame, shared_state, solver| {
886 pop_call_stack(frame);
887 if let Some((name, _)) = frame.backtrace.pop() {
889 frame.function_name = name;
890 }
891 frame.pc = caller_pc + 1;
892 frame.instrs = caller_instrs;
893 frame.stack_call = caller_stack_call.clone();
894 assign(tid, &loc.clone(), ret, &mut frame.local_state, shared_state, solver)
895 }));
896
897 for (i, arg) in args.drain(..).enumerate() {
898 frame.vars_mut().insert(params[i].0, UVal::Init(arg));
899 }
900 frame.pc = 0;
901 frame.instrs = instrs;
902 }
903 }
904 }
905
906 Instr::End => match frame.vars().get(&RETURN) {
907 None => panic!("Reached end without assigning to return"),
908 Some(value) => {
909 let value = match value {
910 UVal::Uninit(ty) => symbolic(ty, shared_state, solver)?,
911 UVal::Init(value) => value.clone(),
912 };
913 if shared_state.probes.contains(&frame.function_name) {
914 let symbol = zencode::decode(shared_state.symtab.to_str(frame.function_name));
915 log_from!(
916 tid,
917 log::PROBE,
918 &format!("Returning {} = {}", symbol, value.to_string(&shared_state.symtab))
919 );
920 probe::args_info(tid, std::slice::from_ref(&value), shared_state, solver)
921 }
922 let caller = match &frame.stack_call {
923 None => return Ok(value),
924 Some(caller) => Arc::clone(caller),
925 };
926 (*caller)(value, frame, shared_state, solver)?
927 }
928 },
929
930 Instr::Monomorphize(id) => {
938 let val = get_id_and_initialize(*id, &mut frame.local_state, shared_state, solver, &mut Vec::new())?;
939 if let Val::Symbolic(v) = val {
940 use smtlib::bits64;
941 use smtlib::Def::*;
942 use smtlib::Exp::*;
943 use smtlib::Ty::*;
944
945 let point = checkpoint(solver);
946
947 let len = solver.length(v).ok_or_else(|| ExecError::Type(format!("_monomorphize {:?}", &v)))?;
948
949 let sym = solver.declare_const(BitVec(len));
951 solver.assert_eq(Var(v), Var(sym));
952
953 if solver.check_sat().is_unsat()? {
954 return Err(ExecError::Dead);
955 }
956
957 let (result, size) = {
958 let mut model = Model::new(solver);
959 log_from!(tid, log::FORK, format!("Model: {:?}", model));
960 match model.get_var(v) {
961 Ok(Some(Bits64(bv))) => (bv.lower_u64(), bv.len()),
962 Ok(Some(other)) => return Err(ExecError::Type(format!("__monomorphize {:?}", &other))),
964 Ok(None) => return Err(ExecError::Z3Error(format!("No value for variable v{}", v))),
965 Err(error) => return Err(error),
966 }
967 };
968
969 let loc = format!("Fork @ monomorphizing v{}", v);
970 log_from!(tid, log::FORK, loc);
971 solver.add_event(Event::Fork(frame.forks, v, loc.clone()));
972 frame.forks += 1;
973
974 queue.push(Task {
975 id: task_id,
976 frame: freeze_frame(&frame),
977 checkpoint: point,
978 fork_cond: Some(Assert(Neq(Box::new(Var(v)), Box::new(bits64(result, size))))),
979 state: task_state,
980 stop_functions,
981 });
982
983 solver.assert_eq(Var(v), bits64(result, size));
984
985 assign(
986 tid,
987 &Loc::Id(*id),
988 Val::Bits(B::new(result, size)),
989 &mut frame.local_state,
990 shared_state,
991 solver,
992 )?;
993 }
994 frame.pc += 1
995 }
996
997 Instr::Arbitrary => {
1003 if shared_state.probes.contains(&frame.function_name) {
1004 let symbol = zencode::decode(shared_state.symtab.to_str(frame.function_name));
1005 log_from!(
1006 tid,
1007 log::PROBE,
1008 &format!("Returning via arbitrary {}[{:?}] = poison", symbol, frame.function_name)
1009 );
1010 }
1011 let caller = match &frame.stack_call {
1012 None => return Ok(Val::Poison),
1013 Some(caller) => Arc::clone(caller),
1014 };
1015 (*caller)(Val::Poison, frame, shared_state, solver)?
1016 }
1017
1018 Instr::Failure => return Err(ExecError::MatchFailure),
1019 }
1020 }
1021}
1022
1023pub type Collector<'ir, B, R> = dyn 'ir
1029 + Sync
1030 + Fn(usize, usize, Result<(Val<B>, LocalFrame<'ir, B>), (ExecError, Backtrace)>, &SharedState<'ir, B>, Solver<B>, &R);
1031
1032pub struct TaskState<B> {
1033 reset_registers: HashMap<Loc<Name>, Reset<B>>,
1034}
1035
1036impl<B> TaskState<B> {
1037 pub fn new() -> Self {
1038 TaskState { reset_registers: HashMap::new() }
1039 }
1040
1041 pub fn with_reset_registers(reset_registers: HashMap<Loc<Name>, Reset<B>>) -> Self {
1042 TaskState { reset_registers }
1043 }
1044}
1045
1046impl<B> Default for TaskState<B> {
1047 fn default() -> Self {
1048 Self::new()
1049 }
1050}
1051
1052pub struct Task<'ir, 'task, B> {
1058 id: usize,
1059 frame: Frame<'ir, B>,
1060 checkpoint: Checkpoint<B>,
1061 fork_cond: Option<smtlib::Def>,
1062 state: &'task TaskState<B>,
1063 stop_functions: Option<&'task HashSet<Name>>,
1064}
1065
1066impl<'ir, 'task, B> Task<'ir, 'task, B> {
1067 pub fn set_stop_functions(&mut self, new_fns: &'task HashSet<Name>) {
1068 self.stop_functions = Some(new_fns);
1069 }
1070}
1071
1072pub fn start_single<'ir, 'task, B: BV, R>(
1075 task: Task<'ir, 'task, B>,
1076 shared_state: &SharedState<'ir, B>,
1077 collected: &R,
1078 collector: &Collector<'ir, B, R>,
1079) {
1080 let queue = Worker::new_lifo();
1081 queue.push(task);
1082 while let Some(task) = queue.pop() {
1083 let mut cfg = Config::new();
1084 cfg.set_param_value("model", "true");
1085 let ctx = Context::new(cfg);
1086 let mut solver = Solver::from_checkpoint(&ctx, task.checkpoint);
1087 if let Some(def) = task.fork_cond {
1088 solver.add(def)
1089 };
1090 let result = run(
1091 0,
1092 task.id,
1093 Timeout::unlimited(),
1094 task.stop_functions,
1095 &queue,
1096 &task.frame,
1097 &task.state,
1098 shared_state,
1099 &mut solver,
1100 );
1101 collector(0, task.id, result, shared_state, solver, collected)
1102 }
1103}
1104
1105fn find_task<T>(local: &Worker<T>, global: &Injector<T>, stealers: &RwLock<Vec<Stealer<T>>>) -> Option<T> {
1106 let stealers = stealers.read().unwrap();
1107 local.pop().or_else(|| {
1108 std::iter::repeat_with(|| {
1109 let stolen: Steal<T> = stealers.iter().map(|s| s.steal()).collect();
1110 stolen.or_else(|| global.steal_batch_and_pop(local))
1111 })
1112 .find(|s| !s.is_retry())
1113 .and_then(|s| s.success())
1114 })
1115}
1116
1117fn do_work<'ir, 'task, B: BV, R>(
1118 tid: usize,
1119 timeout: Timeout,
1120 queue: &Worker<Task<'ir, 'task, B>>,
1121 task: Task<'ir, 'task, B>,
1122 shared_state: &SharedState<'ir, B>,
1123 collected: &R,
1124 collector: &Collector<'ir, B, R>,
1125) {
1126 let cfg = Config::new();
1127 let ctx = Context::new(cfg);
1128 let mut solver = Solver::from_checkpoint(&ctx, task.checkpoint);
1129 if let Some(def) = task.fork_cond {
1130 solver.add(def)
1131 };
1132 let result =
1133 run(tid, task.id, timeout, task.stop_functions, queue, &task.frame, &task.state, shared_state, &mut solver);
1134 collector(tid, task.id, result, shared_state, solver, collected)
1135}
1136
1137enum Response {
1138 Poke,
1139 Kill,
1140}
1141
1142#[derive(Clone)]
1143enum Activity {
1144 Idle(usize, Sender<Response>),
1145 Busy(usize),
1146}
1147
1148pub fn start_multi<'ir, 'task, B: BV, R>(
1151 num_threads: usize,
1152 timeout: Option<u64>,
1153 tasks: Vec<Task<'ir, 'task, B>>,
1154 shared_state: &SharedState<'ir, B>,
1155 collected: Arc<R>,
1156 collector: &Collector<'ir, B, R>,
1157) where
1158 R: Send + Sync,
1159{
1160 let timeout = Timeout { start_time: Instant::now(), duration: timeout.map(Duration::from_secs) };
1161
1162 let (tx, rx): (Sender<Activity>, Receiver<Activity>) = mpsc::channel();
1163 let global: Arc<Injector<Task<B>>> = Arc::new(Injector::<Task<B>>::new());
1164 let stealers: Arc<RwLock<Vec<Stealer<Task<B>>>>> = Arc::new(RwLock::new(Vec::new()));
1165
1166 for task in tasks {
1167 global.push(task);
1168 }
1169
1170 thread::scope(|scope| {
1171 for tid in 0..num_threads {
1172 let (poke_tx, poke_rx): (Sender<Response>, Receiver<Response>) = mpsc::channel();
1176 let thread_tx = tx.clone();
1177 let global = global.clone();
1178 let stealers = stealers.clone();
1179 let collected = collected.clone();
1180
1181 scope.spawn(move |_| {
1182 let q = Worker::new_lifo();
1183 {
1184 let mut stealers = stealers.write().unwrap();
1185 stealers.push(q.stealer());
1186 }
1187 loop {
1188 if let Some(task) = find_task(&q, &global, &stealers) {
1189 thread_tx.send(Activity::Busy(tid)).unwrap();
1190 do_work(tid, timeout, &q, task, &shared_state, collected.as_ref(), collector);
1191 while let Some(task) = find_task(&q, &global, &stealers) {
1192 do_work(tid, timeout, &q, task, &shared_state, collected.as_ref(), collector)
1193 }
1194 };
1195 thread_tx.send(Activity::Idle(tid, poke_tx.clone())).unwrap();
1196 match poke_rx.recv().unwrap() {
1197 Response::Poke => (),
1198 Response::Kill => break,
1199 }
1200 }
1201 });
1202 }
1203
1204 let mut current_activity = vec![0; num_threads];
1211 let mut last_messages = vec![Activity::Busy(0); num_threads];
1212 loop {
1213 loop {
1214 match rx.try_recv() {
1215 Ok(Activity::Busy(tid)) => {
1216 last_messages[tid] = Activity::Busy(tid);
1217 current_activity[tid] = 0;
1218 }
1219 Ok(Activity::Idle(tid, poke)) => {
1220 last_messages[tid] = Activity::Idle(tid, poke);
1221 current_activity[tid] += 1;
1222 }
1223 Err(_) => break,
1224 }
1225 }
1226 let mut quiescent = true;
1227 for idleness in ¤t_activity {
1228 if *idleness < 2 {
1229 quiescent = false
1230 }
1231 }
1232 if quiescent {
1233 for message in &last_messages {
1234 match message {
1235 Activity::Idle(_tid, poke) => poke.send(Response::Kill).unwrap(),
1236 Activity::Busy(tid) => panic!("Found busy thread {} when quiescent", tid),
1237 }
1238 }
1239 break;
1240 }
1241 for message in &last_messages {
1242 match message {
1243 Activity::Idle(tid, poke) => {
1244 poke.send(Response::Poke).unwrap();
1245 current_activity[*tid] = 1;
1246 }
1247 Activity::Busy(_) => (),
1248 }
1249 }
1250 sleep(Duration::from_millis(1))
1251 }
1252 })
1253 .unwrap();
1254}
1255
1256pub fn all_unsat_collector<'ir, B: BV>(
1261 tid: usize,
1262 _: usize,
1263 result: Result<(Val<B>, LocalFrame<'ir, B>), (ExecError, Backtrace)>,
1264 shared_state: &SharedState<'ir, B>,
1265 mut solver: Solver<B>,
1266 collected: &AtomicBool,
1267) {
1268 match result {
1269 Ok(value) => match value {
1270 (Val::Symbolic(v), _) => {
1271 use smtlib::Def::*;
1272 use smtlib::Exp::*;
1273 solver.add(Assert(Not(Box::new(Var(v)))));
1274 if solver.check_sat() != SmtResult::Unsat {
1275 log_from!(tid, log::VERBOSE, "Got sat");
1276 collected.store(false, Ordering::Release)
1277 } else {
1278 log_from!(tid, log::VERBOSE, "Got unsat")
1279 }
1280 }
1281 (Val::Bool(true), _) => log_from!(tid, log::VERBOSE, "Got true"),
1282 (Val::Bool(false), _) => {
1283 log_from!(tid, log::VERBOSE, "Got false");
1284 collected.store(false, Ordering::Release)
1285 }
1286 (value, _) => log_from!(tid, log::VERBOSE, &format!("Got value {:?}", value)),
1287 },
1288 Err((err, backtrace)) => match err {
1289 ExecError::Dead => log_from!(tid, log::VERBOSE, "Dead"),
1290 _ => {
1291 if_logging!(log::VERBOSE, {
1292 log_from!(tid, log::VERBOSE, &format!("Got error, {:?}", err));
1293 for (f, pc) in backtrace.iter().rev() {
1294 log_from!(tid, log::VERBOSE, format!(" {} @ {}", shared_state.symtab.to_str(*f), pc));
1295 }
1296 });
1297 collected.store(false, Ordering::Release)
1298 }
1299 },
1300 }
1301}
1302
1303pub type TraceQueue<B> = SegQueue<Result<(usize, Vec<Event<B>>), String>>;
1304
1305pub type TraceResultQueue<B> = SegQueue<Result<(usize, bool, Vec<Event<B>>), String>>;
1306
1307pub type TraceValueQueue<B> = SegQueue<Result<(usize, Val<B>, Vec<Event<B>>), String>>;
1308
1309pub fn trace_collector<'ir, B: BV>(
1310 _: usize,
1311 task_id: usize,
1312 result: Result<(Val<B>, LocalFrame<'ir, B>), (ExecError, Backtrace)>,
1313 _: &SharedState<'ir, B>,
1314 mut solver: Solver<B>,
1315 collected: &TraceQueue<B>,
1316) {
1317 match result {
1318 Ok(_) | Err((ExecError::Exit, _)) => {
1319 let mut events = solver.trace().to_vec();
1320 collected.push(Ok((task_id, events.drain(..).cloned().collect())))
1321 }
1322 Err((ExecError::Dead, _)) => (),
1323 Err((err, _)) => {
1324 if solver.check_sat() == SmtResult::Sat {
1325 let model = Model::new(&solver);
1326 collected.push(Err(format!("Error {:?}\n{:?}", err, model)))
1327 } else {
1328 collected.push(Err(format!("Error {:?}\nno model", err)))
1329 }
1330 }
1331 }
1332}
1333
1334pub fn trace_value_collector<'ir, B: BV>(
1335 _: usize,
1336 task_id: usize,
1337 result: Result<(Val<B>, LocalFrame<'ir, B>), (ExecError, Backtrace)>,
1338 _: &SharedState<'ir, B>,
1339 mut solver: Solver<B>,
1340 collected: &TraceValueQueue<B>,
1341) {
1342 match result {
1343 Ok((val, _)) => {
1344 let mut events = solver.trace().to_vec();
1345 collected.push(Ok((task_id, val, events.drain(..).cloned().collect())))
1346 }
1347 Err((ExecError::Dead, _)) => (),
1348 Err((err, _)) => {
1349 if solver.check_sat() == SmtResult::Sat {
1350 let model = Model::new(&solver);
1351 collected.push(Err(format!("Error {:?}\n{:?}", err, model)))
1352 } else {
1353 collected.push(Err(format!("Error {:?}\nno model", err)))
1354 }
1355 }
1356 }
1357}
1358
1359pub fn trace_result_collector<'ir, B: BV>(
1360 _: usize,
1361 task_id: usize,
1362 result: Result<(Val<B>, LocalFrame<'ir, B>), (ExecError, Backtrace)>,
1363 _: &SharedState<'ir, B>,
1364 solver: Solver<B>,
1365 collected: &TraceResultQueue<B>,
1366) {
1367 match result {
1368 Ok((Val::Bool(result), _)) => {
1369 let mut events = solver.trace().to_vec();
1370 collected.push(Ok((task_id, result, events.drain(..).cloned().collect())))
1371 }
1372 Ok((val, _)) => collected.push(Err(format!("Unexpected footprint return value: {:?}", val))),
1373 Err((ExecError::Dead, _)) => (),
1374 Err((err, _)) => collected.push(Err(format!("Error {:?}", err))),
1375 }
1376}
1377
1378pub fn footprint_collector<'ir, B: BV>(
1379 _: usize,
1380 task_id: usize,
1381 result: Result<(Val<B>, LocalFrame<'ir, B>), (ExecError, Backtrace)>,
1382 _: &SharedState<'ir, B>,
1383 solver: Solver<B>,
1384 collected: &TraceQueue<B>,
1385) {
1386 match result {
1387 Ok((Val::Bool(true), _)) => {
1389 let mut events = solver.trace().to_vec();
1390 collected.push(Ok((task_id, events.drain(..).cloned().collect())))
1391 }
1392 Ok((Val::Bool(false), _)) => (),
1394 Ok((val, _)) => collected.push(Err(format!("Unexpected footprint return value: {:?}", val))),
1396
1397 Err((ExecError::Dead, _)) => (),
1398 Err((err, _)) => collected.push(Err(format!("Error {:?}", err))),
1399 }
1400}