isla_lib/
ir.rs

1// BSD 2-Clause License
2//
3// Copyright (c) 2019, 2020 Alasdair Armstrong
4// Copyright (c) 2020 Brian Campbell
5//
6// All rights reserved.
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are
10// met:
11//
12// 1. Redistributions of source code must retain the above copyright
13// notice, this list of conditions and the following disclaimer.
14//
15// 2. Redistributions in binary form must reproduce the above copyright
16// notice, this list of conditions and the following disclaimer in the
17// documentation and/or other materials provided with the distribution.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31//! This module defines and implements functions for working with the
32//! *Jib* IR (intermediate representation) that is produced by
33//! Sail. It is a simple goto/conditional branch language, where each
34//! function can declare and use an arbitrary amount of variables.
35//!
36//! All the IR types are parametric in the identifier type. They are
37//! initially parsed as e.g. `Def<String>` but then the names are
38//! interned into a symbol table ([Symtab]) and they are replaced by
39//! values of type [Name], which is a wrapper around `u32`.
40//!
41//! To conveniently initialize the IR for a Sail architecture
42//! specification see the [crate::init] module.
43
44use serde::{Deserialize, Serialize};
45use std::collections::{HashMap, HashSet};
46use std::fmt;
47use std::hash::Hash;
48use std::sync::Arc;
49
50use crate::bitvector::{b64::B64, BV};
51use crate::error::ExecError;
52use crate::memory::Memory;
53use crate::primop::{Binary, Primops, Unary, Variadic};
54use crate::smt::{Solver, Sym};
55use crate::zencode;
56
57pub mod linearize;
58pub mod serialize;
59pub mod ssa;
60
61#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
62pub struct Name {
63    id: u32,
64}
65
66#[derive(Clone, Debug, Serialize, Deserialize)]
67pub enum Ty<A> {
68    I64,
69    I128,
70    AnyBits,
71    Bits(u32),
72    Unit,
73    Bool,
74    Bit,
75    String,
76    Real,
77    Enum(A),
78    Struct(A),
79    Union(A),
80    Vector(Box<Ty<A>>),
81    FixedVector(u32, Box<Ty<A>>),
82    List(Box<Ty<A>>),
83    Ref(Box<Ty<A>>),
84}
85
86/// A [Loc] is a location that can be assigned to.
87#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
88pub enum Loc<A> {
89    Id(A),
90    Field(Box<Loc<A>>, A),
91    Addr(Box<Loc<A>>),
92}
93
94impl<A: Clone> Loc<A> {
95    pub fn id(&self) -> A {
96        match self {
97            Loc::Id(id) => id.clone(),
98            Loc::Field(loc, _) | Loc::Addr(loc) => loc.id(),
99        }
100    }
101
102    pub fn id_mut(&mut self) -> &mut A {
103        match self {
104            Loc::Id(id) => id,
105            Loc::Field(loc, _) | Loc::Addr(loc) => loc.id_mut(),
106        }
107    }
108}
109
110#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
111pub enum Op {
112    Not,
113    Or,
114    And,
115    Eq,
116    Neq,
117    Lteq,
118    Lt,
119    Gteq,
120    Gt,
121    Add,
122    Sub,
123    Slice(u32),
124    SetSlice,
125    Signed(u32),
126    Unsigned(u32),
127    ZeroExtend(u32),
128    Bvnot,
129    Bvor,
130    Bvxor,
131    Bvand,
132    Bvadd,
133    Bvsub,
134    Bvaccess,
135    Concat,
136    Head,
137    Tail,
138}
139
140#[derive(Copy, Clone, Debug, PartialEq, Eq)]
141pub struct EnumMember {
142    pub enum_id: usize,
143    pub member: usize,
144}
145
146/// A value is either a symbolic value, represented as `Symbolic(n)`
147/// for where n is the identifier of the variable in the SMT solver,
148/// or one of the concrete values in this enum.
149#[derive(Clone, Debug)]
150pub enum Val<B> {
151    Symbolic(Sym),
152    I64(i64),
153    I128(i128),
154    Bool(bool),
155    Bits(B),
156    String(String),
157    Unit,
158    Vector(Vec<Val<B>>),
159    List(Vec<Val<B>>),
160    Enum(EnumMember),
161    Struct(HashMap<Name, Val<B>>),
162    Ctor(Name, Box<Val<B>>),
163    Ref(Name),
164    Poison,
165}
166
167impl<B: BV> Val<B> {
168    fn collect_symbolic_variables(&self, vars: &mut HashSet<Sym>) {
169        use Val::*;
170        match self {
171            Symbolic(v) => {
172                vars.insert(*v);
173            }
174            I64(_) | I128(_) | Bool(_) | Bits(_) | Enum(_) | String(_) | Unit | Ref(_) | Poison => (),
175            Vector(vals) | List(vals) => vals.iter().for_each(|val| val.collect_symbolic_variables(vars)),
176            Struct(vals) => vals.iter().for_each(|(_, val)| val.collect_symbolic_variables(vars)),
177            Ctor(_, val) => val.collect_symbolic_variables(vars),
178        }
179    }
180
181    pub fn symbolic_variables(&self) -> HashSet<Sym> {
182        let mut vars = HashSet::new();
183        self.collect_symbolic_variables(&mut vars);
184        vars
185    }
186
187    pub fn is_symbolic(&self) -> bool {
188        !self.symbolic_variables().is_empty()
189    }
190
191    pub fn as_bits(&self) -> Option<&B> {
192        match self {
193            Val::Bits(bv) => Some(bv),
194            _ => None,
195        }
196    }
197
198    pub fn to_string(&self, symtab: &Symtab) -> String {
199        use Val::*;
200        match self {
201            Symbolic(v) => format!("v{}", v),
202            I64(n) => format!("(_ bv{} 64)", n),
203            I128(n) => format!("(_ bv{} 128)", n),
204            Bool(b) => format!("{}", b),
205            Bits(bv) => format!("{}", bv),
206            String(s) => format!("\"{}\"", s),
207            Enum(EnumMember { enum_id, member }) => format!("e{}_{}", enum_id, member),
208            Unit => "(_ unit)".to_string(),
209            List(vec) => {
210                let vec =
211                    vec.iter()
212                        .map(|elem| elem.to_string(symtab))
213                        .fold(None, |acc, elem| {
214                            if let Some(prefix) = acc {
215                                Some(format!("{} {}", prefix, elem))
216                            } else {
217                                Some(elem)
218                            }
219                        })
220                        .unwrap_or_else(|| "nil".to_string());
221                format!("(_ list {})", vec)
222            }
223            Vector(vec) => {
224                let vec =
225                    vec.iter()
226                        .map(|elem| elem.to_string(symtab))
227                        .fold(None, |acc, elem| {
228                            if let Some(prefix) = acc {
229                                Some(format!("{} {}", prefix, elem))
230                            } else {
231                                Some(elem)
232                            }
233                        })
234                        .unwrap_or_else(|| "nil".to_string());
235                format!("(_ vec {})", vec)
236            }
237            Struct(fields) => {
238                let fields = fields
239                    .iter()
240                    .map(|(k, v)| format!("(|{}| {})", zencode::decode(symtab.to_str(*k)), v.to_string(symtab)))
241                    .fold(
242                        None,
243                        |acc, kv| {
244                            if let Some(prefix) = acc {
245                                Some(format!("{} {}", prefix, kv))
246                            } else {
247                                Some(kv)
248                            }
249                        },
250                    )
251                    .unwrap();
252                format!("(_ struct {})", fields)
253            }
254            Ctor(ctor, v) => format!("(|{}| {})", zencode::decode(symtab.to_str(*ctor)), v.to_string(symtab)),
255            Ref(reg) => format!("(_ reg |{}|)", zencode::decode(symtab.to_str(*reg))),
256            Poison => "(_ poison)".to_string(),
257        }
258    }
259
260    /// Just enough of a type check to pick up bad default registers
261    pub fn plausible<N: std::fmt::Debug>(&self, ty: &Ty<N>, symtab: &Symtab) -> Result<(), String> {
262        match (self, ty) {
263            (Val::Symbolic(_), _) => Ok(()),
264            (Val::I64(_), Ty::I64) => Ok(()),
265            (Val::I128(_), Ty::I128) => Ok(()),
266            (Val::Bool(_), Ty::Bool) => Ok(()),
267            (Val::Bits(_), Ty::AnyBits) => Ok(()),
268            (Val::Bits(bv), Ty::Bits(n)) => {
269                if bv.len() == *n {
270                    Ok(())
271                } else {
272                    Err(format!("value {} doesn't appear to match type {:?}", self.to_string(symtab), ty))
273                }
274            }
275            (Val::String(_), Ty::String) => Ok(()),
276            (Val::Unit, Ty::Unit) => Ok(()),
277            (Val::Vector(_), Ty::Vector(_)) => Ok(()), // TODO: element type
278            (Val::List(_), Ty::List(_)) => Ok(()),     // TODO: element type
279            (Val::Enum(_), Ty::Enum(_)) => Ok(()),     // TODO: element type
280            (Val::Struct(_), Ty::Struct(_)) => Ok(()), // TODO: element type
281            (Val::Ctor(_, _), _) => Ok(()),            // TODO
282            (Val::Ref(_), _) => Ok(()),                // TODO
283            (Val::Poison, _) => Ok(()),
284            (_, _) => Err(format!("value {} doesn't appear to match type {:?}", self.to_string(symtab), ty)),
285        }
286    }
287}
288
289/// A [UVal] is a potentially uninitialized [Val].
290#[derive(Clone, Debug)]
291pub enum UVal<'ir, B> {
292    Uninit(&'ir Ty<Name>),
293    Init(Val<B>),
294}
295
296/// A map from identifers to potentially uninitialized values.
297pub type Bindings<'ir, B> = HashMap<Name, UVal<'ir, B>>;
298
299/// A reference to either the declaration of a variable or a usage
300/// location.
301pub enum Variable<'a, A> {
302    Declaration(&'a mut A),
303    Usage(&'a mut A),
304}
305
306/// An iterator over the [Variable] type for modifying variable usages
307/// and declarations.
308pub struct Variables<'a, A> {
309    vec: Vec<Variable<'a, A>>,
310}
311
312impl<'a, A> Variables<'a, A> {
313    pub fn from_vec(vec: Vec<Variable<'a, A>>) -> Self {
314        Variables { vec }
315    }
316}
317
318impl<'a, A> Iterator for Variables<'a, A> {
319    type Item = Variable<'a, A>;
320
321    fn next(&mut self) -> Option<Self::Item> {
322        self.vec.pop()
323    }
324}
325
326#[derive(Clone, Debug, Serialize, Deserialize)]
327pub enum Exp<A> {
328    Id(A),
329    Ref(A),
330    Bool(bool),
331    Bits(B64),
332    String(String),
333    Unit,
334    I64(i64),
335    I128(i128),
336    Undefined(Ty<A>),
337    Struct(A, Vec<(A, Exp<A>)>),
338    Kind(A, Box<Exp<A>>),
339    Unwrap(A, Box<Exp<A>>),
340    Field(Box<Exp<A>>, A),
341    Call(Op, Vec<Exp<A>>),
342}
343
344impl<A: Hash + Eq + Clone> Exp<A> {
345    fn collect_ids(&self, ids: &mut HashSet<A>) {
346        use Exp::*;
347        match self {
348            Id(id) => {
349                ids.insert(id.clone());
350            }
351            Ref(_) | Bool(_) | Bits(_) | String(_) | Unit | I64(_) | I128(_) | Undefined(_) => (),
352            Kind(_, exp) | Unwrap(_, exp) | Field(exp, _) => exp.collect_ids(ids),
353            Call(_, exps) => exps.iter().for_each(|exp| exp.collect_ids(ids)),
354            Struct(_, fields) => fields.iter().for_each(|(_, exp)| exp.collect_ids(ids)),
355        }
356    }
357
358    pub(crate) fn collect_variables<'a, 'b>(&'a mut self, vars: &'b mut Vec<Variable<'a, A>>) {
359        use Exp::*;
360        match self {
361            Id(id) => vars.push(Variable::Usage(id)),
362            Ref(_) | Bool(_) | Bits(_) | String(_) | Unit | I64(_) | I128(_) | Undefined(_) => (),
363            Kind(_, exp) | Unwrap(_, exp) | Field(exp, _) => exp.collect_variables(vars),
364            Call(_, exps) => exps.iter_mut().for_each(|exp| exp.collect_variables(vars)),
365            Struct(_, fields) => fields.iter_mut().for_each(|(_, exp)| exp.collect_variables(vars)),
366        }
367    }
368
369    pub fn variables(&mut self) -> Variables<'_, A> {
370        let mut vec = Vec::new();
371        self.collect_variables(&mut vec);
372        Variables::from_vec(vec)
373    }
374}
375
376#[derive(Clone)]
377pub enum Instr<A, B> {
378    Decl(A, Ty<A>),
379    Init(A, Ty<A>, Exp<A>),
380    Jump(Exp<A>, usize, String),
381    Goto(usize),
382    Copy(Loc<A>, Exp<A>),
383    Monomorphize(A),
384    Call(Loc<A>, bool, A, Vec<Exp<A>>),
385    PrimopUnary(Loc<A>, Unary<B>, Exp<A>),
386    PrimopBinary(Loc<A>, Binary<B>, Exp<A>, Exp<A>),
387    PrimopVariadic(Loc<A>, Variadic<B>, Vec<Exp<A>>),
388    Failure,
389    Arbitrary,
390    End,
391}
392
393impl<A: fmt::Debug, B: fmt::Debug> fmt::Debug for Instr<A, B> {
394    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395        use Instr::*;
396        match self {
397            Decl(id, ty) => write!(f, "{:?} : {:?}", id, ty),
398            Init(id, ty, exp) => write!(f, "{:?} : {:?} = {:?}", id, ty, exp),
399            Jump(exp, target, info) => write!(f, "jump {:?} to {:?} ` {:?}", exp, target, info),
400            Goto(target) => write!(f, "goto {:?}", target),
401            Copy(loc, exp) => write!(f, "{:?} = {:?}", loc, exp),
402            Monomorphize(id) => write!(f, "mono {:?}", id),
403            Call(loc, ext, id, args) => write!(f, "{:?} = {:?}<{:?}>({:?})", loc, id, ext, args),
404            Failure => write!(f, "failure"),
405            Arbitrary => write!(f, "arbitrary"),
406            End => write!(f, "end"),
407            PrimopUnary(loc, fptr, exp) => write!(f, "{:?} = {:p}({:?})", loc, fptr, exp),
408            PrimopBinary(loc, fptr, lhs, rhs) => write!(f, "{:?} = {:p}({:?}, {:?})", loc, fptr, lhs, rhs),
409            PrimopVariadic(loc, fptr, args) => write!(f, "{:?} = {:p}({:?})", loc, fptr, args),
410        }
411    }
412}
413
414/// Append instructions from rhs into the lhs vector, leaving rhs
415/// empty (the same behavior as `Vec::append`).
416pub fn append_instrs<A, B>(lhs: &mut Vec<Instr<A, B>>, rhs: &mut Vec<Instr<A, B>>) {
417    for instr in rhs.iter_mut() {
418        match instr {
419            Instr::Goto(label) => *label += lhs.len(),
420            Instr::Jump(_, label, _) => *label += lhs.len(),
421            _ => (),
422        }
423    }
424    lhs.append(rhs)
425}
426
427#[derive(Clone)]
428pub enum Def<A, B> {
429    Register(A, Ty<A>),
430    Let(Vec<(A, Ty<A>)>, Vec<Instr<A, B>>),
431    Enum(A, Vec<A>),
432    Struct(A, Vec<(A, Ty<A>)>),
433    Union(A, Vec<(A, Ty<A>)>),
434    Val(A, Vec<Ty<A>>, Ty<A>),
435    Extern(A, String, Vec<Ty<A>>, Ty<A>),
436    Fn(A, Vec<A>, Vec<Instr<A, B>>),
437}
438
439impl Name {
440    pub fn from_u32(id: u32) -> Self {
441        Name { id }
442    }
443}
444
445/// A [Symtab] is a symbol table that maps each `u32` identifier used
446/// in the IR to it's `&str` name and vice-versa.
447#[derive(Clone)]
448pub struct Symtab<'ir> {
449    symbols: Vec<&'ir str>,
450    table: HashMap<&'ir str, u32>,
451    next: u32,
452}
453
454/// When a function returns via the [Instr::End] instruction, the
455/// value returned is contained in the special [RETURN] variable.
456pub const RETURN: Name = Name { id: 0 };
457
458/// Function id for the primop implementing the `assert` construct in
459/// Sail.
460pub const SAIL_ASSERT: Name = Name { id: 1 };
461
462/// Function id for the `assume` primop, which is like a Sail assert
463/// but always corresponds to an raw SMT assert.
464pub const SAIL_ASSUME: Name = Name { id: 2 };
465
466/// Function id for the primop implementing the `exit` construct in
467/// Sail.
468pub const SAIL_EXIT: Name = Name { id: 3 };
469
470/// [CURRENT_EXCEPTION] is a global variable containing an exception
471/// with the sail type `exception`. It is only defined when
472/// [HAVE_EXCEPTION] is true.
473pub const CURRENT_EXCEPTION: Name = Name { id: 4 };
474
475/// [HAVE_EXCEPTION] is a global boolean variable which is true if an
476/// exception is being thrown.
477pub const HAVE_EXCEPTION: Name = Name { id: 5 };
478
479/// [THROW_LOCATION] is a global variable which contains a string
480/// describing the location of the last thrown exeception.
481pub const THROW_LOCATION: Name = Name { id: 6 };
482
483/// Special primitive that initializes a generic vector
484pub const INTERNAL_VECTOR_INIT: Name = Name { id: 7 };
485
486/// Special primitive used while initializing a generic vector
487pub const INTERNAL_VECTOR_UPDATE: Name = Name { id: 8 };
488
489/// Special primitive for `update_fbits`
490pub const BITVECTOR_UPDATE: Name = Name { id: 9 };
491
492/// [NULL] is a global letbinding which contains the empty list
493pub const NULL: Name = Name { id: 10 };
494
495/// The function id for the `elf_entry` function.
496pub const ELF_ENTRY: Name = Name { id: 11 };
497
498/// Is the function id of the `reg_deref` primop, that implements
499/// register dereferencing `*R` in Sail.
500pub const REG_DEREF: Name = Name { id: 12 };
501
502/// [SAIL_EXCEPTION] is the Sail `exception` type
503pub const SAIL_EXCEPTION: Name = Name { id: 13 };
504
505/// [TOP_LEVEL_LET] is a name used in backtraces when evaluating a top-level let definition
506pub const TOP_LEVEL_LET: Name = Name { id: 14 };
507
508/// [BV_BIT_LEFT] is the field name for the left element of a bitvector,bit pair
509pub const BV_BIT_LEFT: Name = Name { id: 15 };
510
511/// [BV_BIT_RIGHT] is the field name for the right element of a bitvector,bit pair
512pub const BV_BIT_RIGHT: Name = Name { id: 16 };
513
514/// [RESET_REGISTERS] is a special function that resets register
515/// values according to the ISA config
516pub const RESET_REGISTERS: Name = Name { id: 17 };
517
518static GENSYM: &str = "|GENSYM|";
519
520impl<'ir> Symtab<'ir> {
521    pub fn intern(&mut self, sym: &'ir str) -> Name {
522        match self.table.get(sym) {
523            None => {
524                let n = self.next;
525                self.symbols.push(sym);
526                self.table.insert(sym, n);
527                self.next += 1;
528                Name::from_u32(n)
529            }
530            Some(n) => Name::from_u32(*n),
531        }
532    }
533
534    pub fn gensym(&mut self) -> Name {
535        let n = self.next;
536        self.symbols.push(GENSYM);
537        self.table.insert(GENSYM, n);
538        self.next += 1;
539        Name::from_u32(n)
540    }
541
542    pub fn to_raw_table(&self) -> Vec<String> {
543        self.symbols.iter().map(|sym| (*sym).to_string()).collect()
544    }
545
546    pub fn from_raw_table(raw: &'ir [String]) -> Self {
547        let mut symtab =
548            Symtab { symbols: Vec::with_capacity(raw.len()), table: HashMap::with_capacity(raw.len()), next: 0 };
549        for sym in raw {
550            symtab.intern(sym);
551        }
552        symtab
553    }
554
555    pub fn to_str(&self, n: Name) -> &'ir str {
556        match self.symbols.get(n.id as usize) {
557            Some(s) => s,
558            None => "zUNKNOWN",
559        }
560    }
561
562    #[allow(clippy::new_without_default)]
563    pub fn new() -> Self {
564        let mut symtab = Symtab { symbols: Vec::new(), table: HashMap::new(), next: 0 };
565        symtab.intern("return");
566        symtab.intern("zsail_assert");
567        symtab.intern("zsail_assume");
568        symtab.intern("zsail_exit");
569        symtab.intern("current_exception");
570        symtab.intern("have_exception");
571        symtab.intern("throw_location");
572        symtab.intern("zinternal_vector_init");
573        symtab.intern("zinternal_vector_update");
574        symtab.intern("zupdate_fbits");
575        symtab.intern("NULL");
576        symtab.intern("elf_entry");
577        symtab.intern("reg_deref");
578        symtab.intern("zexception");
579        symtab.intern("|let|");
580        symtab.intern("ztuplez3z5bv_z5bit0");
581        symtab.intern("ztuplez3z5bv_z5bit1");
582        symtab.intern("reset_registers");
583        symtab
584    }
585
586    pub fn lookup(&self, sym: &str) -> Name {
587        Name::from_u32(*self.table.get(sym).unwrap_or_else(|| {
588            eprintln!("Could not find symbol: {}", sym);
589            &std::u32::MAX
590        }))
591    }
592
593    pub fn get(&self, sym: &str) -> Option<Name> {
594        self.table.get(sym).copied().map(Name::from_u32)
595    }
596
597    pub fn intern_ty(&mut self, ty: &'ir Ty<String>) -> Ty<Name> {
598        use Ty::*;
599        match ty {
600            I64 => I64,
601            I128 => I128,
602            AnyBits => AnyBits,
603            Bits(sz) => Bits(*sz),
604            Unit => Unit,
605            Bool => Bool,
606            Bit => Bit,
607            String => String,
608            Real => Real,
609            Enum(e) => Enum(self.lookup(e)),
610            Struct(s) => Struct(self.lookup(s)),
611            Union(u) => Union(self.lookup(u)),
612            Vector(ty) => Vector(Box::new(self.intern_ty(ty))),
613            FixedVector(sz, ty) => FixedVector(*sz, Box::new(self.intern_ty(ty))),
614            List(ty) => List(Box::new(self.intern_ty(ty))),
615            Ref(ty) => Ref(Box::new(self.intern_ty(ty))),
616        }
617    }
618
619    pub fn get_loc(&self, loc: &Loc<String>) -> Option<Loc<Name>> {
620        use Loc::*;
621        Some(match loc {
622            Id(v) => Id(self.get(v)?),
623            Field(loc, field) => Field(Box::new(self.get_loc(loc)?), self.get(field)?),
624            Addr(loc) => Addr(Box::new(self.get_loc(loc)?)),
625        })
626    }
627
628    pub fn intern_loc(&mut self, loc: &'ir Loc<String>) -> Loc<Name> {
629        use Loc::*;
630        match loc {
631            Id(v) => Id(self.lookup(v)),
632            Field(loc, field) => Field(Box::new(self.intern_loc(loc)), self.lookup(field)),
633            Addr(loc) => Addr(Box::new(self.intern_loc(loc))),
634        }
635    }
636
637    pub fn intern_exp(&mut self, exp: &'ir Exp<String>) -> Exp<Name> {
638        use Exp::*;
639        match exp {
640            Id(v) => Id(self.lookup(v)),
641            Ref(reg) => Ref(self.lookup(reg)),
642            Bool(b) => Bool(*b),
643            Bits(bv) => Bits(*bv),
644            String(s) => String(s.clone()),
645            Unit => Unit,
646            I64(i) => I64(*i),
647            I128(i) => I128(*i),
648            Undefined(ty) => Undefined(self.intern_ty(ty)),
649            Struct(s, fields) => Struct(
650                self.lookup(s),
651                fields.iter().map(|(field, exp)| (self.lookup(field), self.intern_exp(exp))).collect(),
652            ),
653            Kind(ctor, exp) => Kind(self.lookup(ctor), Box::new(self.intern_exp(exp))),
654            Unwrap(ctor, exp) => Unwrap(self.lookup(ctor), Box::new(self.intern_exp(exp))),
655            Field(exp, field) => Field(Box::new(self.intern_exp(exp)), self.lookup(field)),
656            Call(op, args) => Call(*op, args.iter().map(|exp| self.intern_exp(exp)).collect()),
657        }
658    }
659
660    pub fn intern_instr<B: BV>(&mut self, instr: &'ir Instr<String, B>) -> Instr<Name, B> {
661        use Instr::*;
662        match instr {
663            Decl(v, ty) => Decl(self.intern(v), self.intern_ty(ty)),
664            Init(v, ty, exp) => {
665                let exp = self.intern_exp(exp);
666                Init(self.intern(v), self.intern_ty(ty), exp)
667            }
668            Jump(exp, target, loc) => Jump(self.intern_exp(exp), *target, loc.clone()),
669            Goto(target) => Goto(*target),
670            Copy(loc, exp) => Copy(self.intern_loc(loc), self.intern_exp(exp)),
671            Monomorphize(id) => Monomorphize(self.lookup(id)),
672            Call(loc, ext, f, args) => {
673                let loc = self.intern_loc(loc);
674                let args = args.iter().map(|exp| self.intern_exp(exp)).collect();
675                Call(loc, *ext, self.lookup(f), args)
676            }
677            Failure => Failure,
678            Arbitrary => Arbitrary,
679            End => End,
680            // We split calls into primops/regular calls later, so
681            // these shouldn't exist yet.
682            PrimopUnary(_, _, _) => unreachable!("PrimopUnary in intern_instr"),
683            PrimopBinary(_, _, _, _) => unreachable!("PrimopBinary in intern_instr"),
684            PrimopVariadic(_, _, _) => unreachable!("PrimopVariadic in intern_instr"),
685        }
686    }
687
688    pub fn intern_def<B: BV>(&mut self, def: &'ir Def<String, B>) -> Def<Name, B> {
689        use Def::*;
690        match def {
691            Register(reg, ty) => Register(self.intern(reg), self.intern_ty(ty)),
692            Let(bindings, setup) => {
693                let bindings = bindings.iter().map(|(v, ty)| (self.intern(v), self.intern_ty(ty))).collect();
694                let setup = setup.iter().map(|instr| self.intern_instr(instr)).collect();
695                Let(bindings, setup)
696            }
697            Enum(e, ctors) => Enum(self.intern(e), ctors.iter().map(|ctor| self.intern(ctor)).collect()),
698            Struct(s, fields) => {
699                let fields = fields.iter().map(|(field, ty)| (self.intern(field), self.intern_ty(ty))).collect();
700                Struct(self.intern(s), fields)
701            }
702            Union(u, ctors) => {
703                let ctors = ctors.iter().map(|(ctor, ty)| (self.intern(ctor), self.intern_ty(ty))).collect();
704                Union(self.intern(u), ctors)
705            }
706            Val(f, args, ret) => {
707                Val(self.intern(f), args.iter().map(|ty| self.intern_ty(ty)).collect(), self.intern_ty(ret))
708            }
709            Extern(f, ext, args, ret) => Extern(
710                self.intern(f),
711                ext.clone(),
712                args.iter().map(|ty| self.intern_ty(ty)).collect(),
713                self.intern_ty(ret),
714            ),
715            Fn(f, args, body) => {
716                let args = args.iter().map(|arg| self.intern(arg)).collect();
717                let body = body.iter().map(|instr| self.intern_instr(instr)).collect();
718                Fn(self.lookup(f), args, body)
719            }
720        }
721    }
722
723    pub fn intern_defs<B: BV>(&mut self, defs: &'ir [Def<String, B>]) -> Vec<Def<Name, B>> {
724        defs.iter().map(|def| self.intern_def(def)).collect()
725    }
726}
727
728/// A function declaration is a tripe of name * type pairs of
729/// parameters, the return type, and a list of instructions for the
730/// function body.
731type FnDecl<'ir, B> = (Vec<(Name, &'ir Ty<Name>)>, Ty<Name>, &'ir [Instr<Name, B>]);
732
733/// The idea behind the `Reset` type is we dynamically create what is
734/// essentially a Sail function consisting of:
735///
736/// ```text
737/// reg1 = f();
738/// reg2 = g();
739/// ...
740/// ```
741///
742/// where `f` and `g` are Rust closures of type `Reset`. This is used
743/// to define custom architectural reset values of these registers, in
744/// a possibly symbolic way or based on some memory value. As an
745/// example, for ARMv8 system concurrency litmus tests we can set up
746/// something like `X1 = pte(virtual_address)`, where `pte` is the
747/// address of the third level page table entry for a virtual address.
748pub type Reset<B> = Arc<dyn 'static + Send + Sync + Fn(&Memory<B>, &mut Solver<B>) -> Result<Val<B>, ExecError>>;
749
750/// All symbolic evaluation happens over some (immutable) IR. The
751/// [SharedState] provides each worker that is performing symbolic
752/// evaluation with a convenient view into that IR.
753pub struct SharedState<'ir, B> {
754    /// A map from function identifers to function bodies and parameter lists
755    pub functions: HashMap<Name, FnDecl<'ir, B>>,
756    /// The symbol table for the IR
757    pub symtab: Symtab<'ir>,
758    /// A map from struct identifers to a map from field identifiers
759    /// to their types
760    pub structs: HashMap<Name, HashMap<Name, Ty<Name>>>,
761    /// A map from enum identifiers to sets of their member
762    /// identifiers
763    pub enums: HashMap<Name, HashSet<Name>>,
764    /// `enum_members` maps each enum member for every enum to it's
765    /// position (as a (pos, size) pair, i.e. 1 of 3) within its
766    /// respective enum
767    pub enum_members: HashMap<Name, (usize, usize)>,
768    /// `union_ctors` is a set of all union constructor identifiers
769    pub union_ctors: HashSet<Name>,
770    /// `probes` is a set of function/location identifers to trace
771    pub probes: HashSet<Name>,
772    /// `reset_registers` is a are reset values for each register
773    /// derived from the ISA config
774    pub reset_registers: HashMap<Loc<Name>, Reset<B>>,
775}
776
777impl<'ir, B: BV> SharedState<'ir, B> {
778    pub fn new(
779        symtab: Symtab<'ir>,
780        defs: &'ir [Def<Name, B>],
781        probes: HashSet<Name>,
782        reset_registers: HashMap<Loc<Name>, Reset<B>>,
783    ) -> Self {
784        let mut vals = HashMap::new();
785        let mut functions: HashMap<Name, FnDecl<'ir, B>> = HashMap::new();
786        let mut structs: HashMap<Name, HashMap<Name, Ty<Name>>> = HashMap::new();
787        let mut enums: HashMap<Name, HashSet<Name>> = HashMap::new();
788        let mut enum_members: HashMap<Name, (usize, usize)> = HashMap::new();
789        let mut union_ctors: HashSet<Name> = HashSet::new();
790
791        for def in defs {
792            match def {
793                Def::Val(f, arg_tys, ret_ty) => {
794                    vals.insert(f, (arg_tys, ret_ty));
795                }
796
797                Def::Fn(f, args, body) => match vals.get(f) {
798                    None => panic!("Found fn without a val when creating the global state!"),
799                    Some((arg_tys, ret_ty)) => {
800                        assert!(arg_tys.len() == args.len());
801                        let args = args.iter().zip(arg_tys.iter()).map(|(id, ty)| (*id, ty)).collect();
802                        functions.insert(*f, (args, (*ret_ty).clone(), body));
803                    }
804                },
805
806                Def::Struct(name, fields) => {
807                    let fields: HashMap<_, _> = fields.clone().into_iter().collect();
808                    structs.insert(*name, fields);
809                }
810
811                Def::Enum(name, members) => {
812                    for (i, member) in members.iter().enumerate() {
813                        enum_members.insert(*member, (i, members.len()));
814                    }
815                    let members: HashSet<_> = members.clone().into_iter().collect();
816                    enums.insert(*name, members);
817                }
818
819                Def::Union(_, ctors) => {
820                    for (ctor, _) in ctors {
821                        union_ctors.insert(*ctor);
822                    }
823                }
824
825                _ => (),
826            }
827        }
828
829        SharedState { functions, symtab, structs, enums, enum_members, union_ctors, probes, reset_registers }
830    }
831
832    pub fn enum_member_from_str(&self, member: &str) -> Option<usize> {
833        let member = self.symtab.get(&zencode::encode(member))?;
834        self.enum_members.get(&member).map(|(pos, _)| *pos)
835    }
836
837    pub fn enum_member(&self, member: Name) -> Option<usize> {
838        self.enum_members.get(&member).map(|(pos, _)| *pos)
839    }
840}
841
842fn insert_instr_primops<B: BV>(
843    instr: Instr<Name, B>,
844    externs: &HashMap<Name, String>,
845    primops: &Primops<B>,
846) -> Instr<Name, B> {
847    match &instr {
848        Instr::Call(loc, _, f, args) => match externs.get(&f) {
849            Some(name) => {
850                if let Some(unop) = primops.unary.get(name) {
851                    assert!(args.len() == 1);
852                    Instr::PrimopUnary(loc.clone(), *unop, args[0].clone())
853                } else if let Some(binop) = primops.binary.get(name) {
854                    assert!(args.len() == 2);
855                    Instr::PrimopBinary(loc.clone(), *binop, args[0].clone(), args[1].clone())
856                } else if let Some(varop) = primops.variadic.get(name) {
857                    Instr::PrimopVariadic(loc.clone(), *varop, args.clone())
858                } else if name == "reg_deref" {
859                    Instr::Call(loc.clone(), false, REG_DEREF, args.clone())
860                } else if name == "reset_registers" {
861                    Instr::Call(loc.clone(), false, RESET_REGISTERS, args.clone())
862                } else {
863                    // Currently we just warn when we don't have a
864                    // primop. This happens for softfloat based
865                    // floating point in RISC-V right now.
866                    eprintln!("No primop {} ({:?})", name, f);
867                    Instr::Call(loc.clone(), false, *f, args.clone())
868                }
869            }
870            None => instr,
871        },
872        _ => instr,
873    }
874}
875
876/// There are two ways to handle assertions in the Sail code, the
877/// first being to assume that they succeed (essentially treating them
878/// like assumptions in the SMT) - this is the optimistic mode. The
879/// other way is to assume that they might fail, and check each
880/// assertion to ensure that it can never fail - this is the
881/// pessimistic mode.
882pub enum AssertionMode {
883    Pessimistic,
884    Optimistic,
885}
886
887/// Change Calls without implementations into Primops
888pub(crate) fn insert_primops<B: BV>(defs: &mut [Def<Name, B>], mode: AssertionMode) {
889    let mut externs: HashMap<Name, String> = HashMap::new();
890    for def in defs.iter() {
891        if let Def::Extern(f, ext, _, _) = def {
892            externs.insert(*f, ext.to_string());
893        }
894    }
895
896    match mode {
897        AssertionMode::Optimistic => externs.insert(SAIL_ASSERT, "optimistic_assert".to_string()),
898        AssertionMode::Pessimistic => externs.insert(SAIL_ASSERT, "pessimistic_assert".to_string()),
899    };
900    externs.insert(SAIL_ASSUME, "assume".to_string());
901    externs.insert(BITVECTOR_UPDATE, "bitvector_update".to_string());
902
903    let primops = Primops::default();
904
905    for def in defs.iter_mut() {
906        match def {
907            Def::Fn(f, args, body) => {
908                *def = Def::Fn(
909                    *f,
910                    args.to_vec(),
911                    body.to_vec().into_iter().map(|instr| insert_instr_primops(instr, &externs, &primops)).collect(),
912                )
913            }
914            Def::Let(bindings, setup) => {
915                *def = Def::Let(
916                    bindings.clone(),
917                    setup.to_vec().into_iter().map(|instr| insert_instr_primops(instr, &externs, &primops)).collect(),
918                )
919            }
920            _ => (),
921        }
922    }
923}
924
925/// By default each jump or goto just contains a `usize` offset into
926/// the instruction vector. This representation is efficient but hard
927/// to work with, so we support mapping this representation into one
928/// where any instruction can have an explicit label, and jumps point
929/// to those explicit labels, and then going back to the offset based
930/// representation for execution.
931#[derive(Debug)]
932pub enum LabeledInstr<B> {
933    Labeled(usize, Instr<Name, B>),
934    Unlabeled(Instr<Name, B>),
935}
936
937impl<B: BV> LabeledInstr<B> {
938    fn strip(self) -> Instr<Name, B> {
939        use LabeledInstr::*;
940        match self {
941            Labeled(_, instr) => instr,
942            Unlabeled(instr) => instr,
943        }
944    }
945
946    fn strip_ref(&self) -> &Instr<Name, B> {
947        use LabeledInstr::*;
948        match self {
949            Labeled(_, instr) => instr,
950            Unlabeled(instr) => instr,
951        }
952    }
953
954    fn label(&self) -> Option<usize> {
955        match self {
956            LabeledInstr::Labeled(l, _) => Some(*l),
957            LabeledInstr::Unlabeled(_) => None,
958        }
959    }
960
961    fn is_labeled(&self) -> bool {
962        matches!(self, LabeledInstr::Labeled(_, _))
963    }
964
965    fn is_unlabeled(&self) -> bool {
966        !self.is_labeled()
967    }
968}
969
970/// Rewrites an instruction sequence with implicit offset based labels
971/// into a vector where the labels are explicit. Note that this just
972/// adds a label to every instruction which is equal to its
973/// offset. Use [prune_labels] to remove any labels which are not the
974/// target of any jump or goto instruction.
975pub fn label_instrs<B: BV>(mut instrs: Vec<Instr<Name, B>>) -> Vec<LabeledInstr<B>> {
976    use LabeledInstr::*;
977    instrs.drain(..).enumerate().map(|(i, instr)| Labeled(i, instr)).collect()
978}
979
980/// Remove labels which are not the targets of any jump or goto.
981pub fn prune_labels<B: BV>(mut instrs: Vec<LabeledInstr<B>>) -> Vec<LabeledInstr<B>> {
982    use LabeledInstr::*;
983    let mut targets = HashSet::new();
984
985    for instr in &instrs {
986        match instr.strip_ref() {
987            Instr::Goto(target) | Instr::Jump(_, target, _) => {
988                targets.insert(*target);
989            }
990            _ => (),
991        }
992    }
993
994    instrs
995        .drain(..)
996        .map(|instr| match instr {
997            Labeled(l, instr) if targets.contains(&l) => Labeled(l, instr),
998            instr => Unlabeled(instr.strip()),
999        })
1000        .collect()
1001}
1002
1003/// Remove the explicit labels from instructions, and go back to using
1004/// offset based jumps and gotos.
1005pub fn unlabel_instrs<B: BV>(mut instrs: Vec<LabeledInstr<B>>) -> Vec<Instr<Name, B>> {
1006    use LabeledInstr::*;
1007
1008    let mut jump_table: HashMap<usize, usize> = HashMap::new();
1009
1010    for (i, instr) in instrs.iter().enumerate() {
1011        match instr {
1012            Labeled(label, _) => {
1013                jump_table.insert(*label, i);
1014            }
1015            Unlabeled(_) => (),
1016        }
1017    }
1018
1019    instrs
1020        .drain(..)
1021        .map(|instr| match instr.strip() {
1022            Instr::Jump(cond, target, loc) => {
1023                let new_target = jump_table.get(&target).unwrap();
1024                Instr::Jump(cond, *new_target, loc)
1025            }
1026
1027            Instr::Goto(target) => {
1028                let new_target = jump_table.get(&target).unwrap();
1029                Instr::Goto(*new_target)
1030            }
1031
1032            instr => instr,
1033        })
1034        .collect()
1035}
1036
1037fn insert_monomorphize_instrs<B: BV>(instrs: Vec<Instr<Name, B>>, mono_fns: &HashSet<Name>) -> Vec<Instr<Name, B>> {
1038    use LabeledInstr::*;
1039    let mut new_instrs = Vec::new();
1040
1041    for instr in label_instrs(instrs) {
1042        match instr {
1043            Labeled(label, Instr::Call(loc, ext, f, args)) if mono_fns.contains(&f) => {
1044                let mut ids = HashSet::new();
1045                args.iter().for_each(|exp| exp.collect_ids(&mut ids));
1046
1047                if ids.is_empty() {
1048                    new_instrs.push(Labeled(label, Instr::Call(loc, ext, f, args)))
1049                } else {
1050                    for (i, id) in ids.iter().enumerate() {
1051                        if i == 0 {
1052                            new_instrs.push(Labeled(label, Instr::Monomorphize(*id)))
1053                        } else {
1054                            new_instrs.push(Unlabeled(Instr::Monomorphize(*id)))
1055                        }
1056                    }
1057                    new_instrs.push(Unlabeled(Instr::Call(loc, ext, f, args)))
1058                }
1059            }
1060
1061            _ => new_instrs.push(instr),
1062        }
1063    }
1064
1065    unlabel_instrs(new_instrs)
1066}
1067
1068fn has_mono_fn<B: BV>(instrs: &[Instr<Name, B>], mono_fns: &HashSet<Name>) -> bool {
1069    for instr in instrs {
1070        match instr {
1071            Instr::Call(_, _, f, _) if mono_fns.contains(&f) => return true,
1072            _ => (),
1073        }
1074    }
1075    false
1076}
1077
1078pub(crate) fn insert_monomorphize<B: BV>(defs: &mut [Def<Name, B>]) {
1079    let mut mono_fns = HashSet::new();
1080    for def in defs.iter() {
1081        match def {
1082            Def::Extern(f, ext, _, _) if ext == "monomorphize" => {
1083                mono_fns.insert(*f);
1084            }
1085            _ => (),
1086        }
1087    }
1088
1089    for def in defs.iter_mut() {
1090        match def {
1091            Def::Fn(f, args, body) if has_mono_fn(body, &mono_fns) => {
1092                *def = Def::Fn(*f, args.to_vec(), insert_monomorphize_instrs(body.to_vec(), &mono_fns))
1093            }
1094            _ => (),
1095        }
1096    }
1097}