|  |  |  | @ -7,6 +7,8 @@ | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | (** Expressions *) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | [@@@warning "+9"] | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | module T = struct | 
			
		
	
		
			
				
					|  |  |  |  |   module T0 = struct | 
			
		
	
		
			
				
					|  |  |  |  |     type op1 = | 
			
		
	
	
		
			
				
					|  |  |  | @ -59,7 +61,9 @@ module T = struct | 
			
		
	
		
			
				
					|  |  |  |  |       | Struct_rec  (** NOTE: may be cyclic *) | 
			
		
	
		
			
				
					|  |  |  |  |     [@@deriving compare, equal, hash, sexp] | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     type t = | 
			
		
	
		
			
				
					|  |  |  |  |     type t = {desc: desc; term: Term.t} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     and desc = | 
			
		
	
		
			
				
					|  |  |  |  |       | Reg of {name: string; typ: Typ.t; global: bool} | 
			
		
	
		
			
				
					|  |  |  |  |       | Nondet of {msg: string; typ: Typ.t} | 
			
		
	
		
			
				
					|  |  |  |  |       | Label of {parent: string; name: string} | 
			
		
	
	
		
			
				
					|  |  |  | @ -78,16 +82,18 @@ end | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | include T | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let term e = e.term | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let fix (f : (t -> 'a as 'f) -> 'f) (bot : 'f) (e : t) : 'a = | 
			
		
	
		
			
				
					|  |  |  |  |   let rec fix_f seen e = | 
			
		
	
		
			
				
					|  |  |  |  |     match e with | 
			
		
	
		
			
				
					|  |  |  |  |     match e.desc with | 
			
		
	
		
			
				
					|  |  |  |  |     | ApN (Struct_rec, _, _) -> | 
			
		
	
		
			
				
					|  |  |  |  |         if List.mem ~equal:( == ) seen e then f bot e | 
			
		
	
		
			
				
					|  |  |  |  |         else f (fix_f (e :: seen)) e | 
			
		
	
		
			
				
					|  |  |  |  |     | _ -> f (fix_f seen) e | 
			
		
	
		
			
				
					|  |  |  |  |   in | 
			
		
	
		
			
				
					|  |  |  |  |   let rec fix_f_seen_nil e = | 
			
		
	
		
			
				
					|  |  |  |  |     match e with | 
			
		
	
		
			
				
					|  |  |  |  |     match e.desc with | 
			
		
	
		
			
				
					|  |  |  |  |     | ApN (Struct_rec, _, _) -> f (fix_f [e]) e | 
			
		
	
		
			
				
					|  |  |  |  |     | _ -> f fix_f_seen_nil e | 
			
		
	
		
			
				
					|  |  |  |  |   in | 
			
		
	
	
		
			
				
					|  |  |  | @ -132,7 +138,7 @@ let rec pp fs exp = | 
			
		
	
		
			
				
					|  |  |  |  |       Format.pp_open_box fs 2 ; | 
			
		
	
		
			
				
					|  |  |  |  |       Format.kfprintf (fun fs -> Format.pp_close_box fs ()) fs fmt | 
			
		
	
		
			
				
					|  |  |  |  |     in | 
			
		
	
		
			
				
					|  |  |  |  |     match exp with | 
			
		
	
		
			
				
					|  |  |  |  |     match exp.desc with | 
			
		
	
		
			
				
					|  |  |  |  |     | Reg {name; global= true} -> pf "%@%s" name | 
			
		
	
		
			
				
					|  |  |  |  |     | Reg {name; global= false} -> pf "%%%s" name | 
			
		
	
		
			
				
					|  |  |  |  |     | Nondet {msg} -> pf "nondet \"%s\"" msg | 
			
		
	
	
		
			
				
					|  |  |  | @ -149,9 +155,11 @@ let rec pp fs exp = | 
			
		
	
		
			
				
					|  |  |  |  |     | Ap1 (Select idx, _, rcd) -> pf "%a[%i]" pp rcd idx | 
			
		
	
		
			
				
					|  |  |  |  |     | Ap2 (Update idx, _, rcd, elt) -> | 
			
		
	
		
			
				
					|  |  |  |  |         pf "[%a@ @[| %i → %a@]]" pp rcd idx pp elt | 
			
		
	
		
			
				
					|  |  |  |  |     | Ap2 (Xor, Integer {bits= 1}, Integer {data}, x) when Z.is_true data -> | 
			
		
	
		
			
				
					|  |  |  |  |     | Ap2 (Xor, Integer {bits= 1}, {desc= Integer {data}}, x) | 
			
		
	
		
			
				
					|  |  |  |  |       when Z.is_true data -> | 
			
		
	
		
			
				
					|  |  |  |  |         pf "¬%a" pp x | 
			
		
	
		
			
				
					|  |  |  |  |     | Ap2 (Xor, Integer {bits= 1}, x, Integer {data}) when Z.is_true data -> | 
			
		
	
		
			
				
					|  |  |  |  |     | Ap2 (Xor, Integer {bits= 1}, x, {desc= Integer {data}}) | 
			
		
	
		
			
				
					|  |  |  |  |       when Z.is_true data -> | 
			
		
	
		
			
				
					|  |  |  |  |         pf "¬%a" pp x | 
			
		
	
		
			
				
					|  |  |  |  |     | Ap2 (op, _, x, y) -> pf "(%a@ %a %a)" pp x pp_op2 op pp y | 
			
		
	
		
			
				
					|  |  |  |  |     | Ap3 (Conditional, _, cnd, thn, els) -> | 
			
		
	
	
		
			
				
					|  |  |  | @ -160,6 +168,7 @@ let rec pp fs exp = | 
			
		
	
		
			
				
					|  |  |  |  |     | ApN (Struct_rec, _, elts) -> pf "{|%a|}" (Vector.pp ",@ " pp) elts | 
			
		
	
		
			
				
					|  |  |  |  |   in | 
			
		
	
		
			
				
					|  |  |  |  |   fix_flip pp_ (fun _ _ -> ()) fs exp | 
			
		
	
		
			
				
					|  |  |  |  |   [@@warning "-9"] | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | and pp_record fs elts = | 
			
		
	
		
			
				
					|  |  |  |  |   [%Trace.fprintf | 
			
		
	
	
		
			
				
					|  |  |  | @ -167,7 +176,7 @@ and pp_record fs elts = | 
			
		
	
		
			
				
					|  |  |  |  |       (fun fs elts -> | 
			
		
	
		
			
				
					|  |  |  |  |         match | 
			
		
	
		
			
				
					|  |  |  |  |           String.init (Vector.length elts) ~f:(fun i -> | 
			
		
	
		
			
				
					|  |  |  |  |               match Vector.get elts i with | 
			
		
	
		
			
				
					|  |  |  |  |               match (Vector.get elts i).desc with | 
			
		
	
		
			
				
					|  |  |  |  |               | Integer {data} -> Char.of_int_exn (Z.to_int data) | 
			
		
	
		
			
				
					|  |  |  |  |               | _ -> raise (Invalid_argument "not a string") ) | 
			
		
	
		
			
				
					|  |  |  |  |         with | 
			
		
	
	
		
			
				
					|  |  |  | @ -175,6 +184,7 @@ and pp_record fs elts = | 
			
		
	
		
			
				
					|  |  |  |  |         | exception _ -> | 
			
		
	
		
			
				
					|  |  |  |  |             Format.fprintf fs "@[<h>%a@]" (Vector.pp ",@ " pp) elts ) | 
			
		
	
		
			
				
					|  |  |  |  |       elts] | 
			
		
	
		
			
				
					|  |  |  |  |   [@@warning "-9"] | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | type exp = t | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -187,7 +197,7 @@ let valid_idx idx elts = 0 <= idx && idx < Vector.length elts | 
			
		
	
		
			
				
					|  |  |  |  | let rec invariant exp = | 
			
		
	
		
			
				
					|  |  |  |  |   Invariant.invariant [%here] exp [%sexp_of: t] | 
			
		
	
		
			
				
					|  |  |  |  |   @@ fun () -> | 
			
		
	
		
			
				
					|  |  |  |  |   match exp with | 
			
		
	
		
			
				
					|  |  |  |  |   match exp.desc with | 
			
		
	
		
			
				
					|  |  |  |  |   | Reg {typ} | Nondet {typ} -> assert (Typ.is_sized typ) | 
			
		
	
		
			
				
					|  |  |  |  |   | Integer {data; typ} -> ( | 
			
		
	
		
			
				
					|  |  |  |  |     match typ with | 
			
		
	
	
		
			
				
					|  |  |  | @ -245,11 +255,12 @@ let rec invariant exp = | 
			
		
	
		
			
				
					|  |  |  |  |           Vector.for_all2_exn elts args ~f:(fun typ arg -> | 
			
		
	
		
			
				
					|  |  |  |  |               Typ.equal typ (typ_of arg) ) ) | 
			
		
	
		
			
				
					|  |  |  |  |     | _ -> assert false ) | 
			
		
	
		
			
				
					|  |  |  |  |   [@@warning "-9"] | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | (** Type query *) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | and typ_of exp = | 
			
		
	
		
			
				
					|  |  |  |  |   match exp with | 
			
		
	
		
			
				
					|  |  |  |  |   match exp.desc with | 
			
		
	
		
			
				
					|  |  |  |  |   | Reg {typ} | Nondet {typ} | Integer {typ} | Float {typ} -> typ | 
			
		
	
		
			
				
					|  |  |  |  |   | Label _ -> Typ.ptr | 
			
		
	
		
			
				
					|  |  |  |  |   | Ap1 (Convert {dst}, _, _) -> dst | 
			
		
	
	
		
			
				
					|  |  |  | @ -273,6 +284,7 @@ and typ_of exp = | 
			
		
	
		
			
				
					|  |  |  |  |    |Ap3 (Conditional, typ, _, _, _) | 
			
		
	
		
			
				
					|  |  |  |  |    |ApN ((Record | Struct_rec), typ, _) -> | 
			
		
	
		
			
				
					|  |  |  |  |       typ | 
			
		
	
		
			
				
					|  |  |  |  |   [@@warning "-9"] | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let typ = typ_of | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -284,6 +296,9 @@ module Reg = struct | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   type reg = t | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   let var r = | 
			
		
	
		
			
				
					|  |  |  |  |     match Var.of_term r.term with Some v -> v | _ -> violates invariant r | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   module Set = struct | 
			
		
	
		
			
				
					|  |  |  |  |     include ( | 
			
		
	
		
			
				
					|  |  |  |  |       Set : | 
			
		
	
	
		
			
				
					|  |  |  | @ -295,7 +310,7 @@ module Reg = struct | 
			
		
	
		
			
				
					|  |  |  |  |     let empty = Set.empty (module T) | 
			
		
	
		
			
				
					|  |  |  |  |     let of_list = Set.of_list (module T) | 
			
		
	
		
			
				
					|  |  |  |  |     let union_list = Set.union_list (module T) | 
			
		
	
		
			
				
					|  |  |  |  |     let of_vector = Set.of_vector (module T) | 
			
		
	
		
			
				
					|  |  |  |  |     let vars = Set.fold ~init:Var.Set.empty ~f:(fun s r -> add s (var r)) | 
			
		
	
		
			
				
					|  |  |  |  |   end | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   module Map = struct | 
			
		
	
	
		
			
				
					|  |  |  | @ -326,27 +341,34 @@ module Reg = struct | 
			
		
	
		
			
				
					|  |  |  |  |       in | 
			
		
	
		
			
				
					|  |  |  |  |       if !@status = 0 then demangled else None | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   let pp_demangled fs = function | 
			
		
	
		
			
				
					|  |  |  |  |   let pp_demangled fs e = | 
			
		
	
		
			
				
					|  |  |  |  |     match e.desc with | 
			
		
	
		
			
				
					|  |  |  |  |     | Reg {name} -> ( | 
			
		
	
		
			
				
					|  |  |  |  |       match demangle name with | 
			
		
	
		
			
				
					|  |  |  |  |       | Some demangled when not (String.equal name demangled) -> | 
			
		
	
		
			
				
					|  |  |  |  |           Format.fprintf fs "“%s”" demangled | 
			
		
	
		
			
				
					|  |  |  |  |       | _ -> () ) | 
			
		
	
		
			
				
					|  |  |  |  |     | _ -> () | 
			
		
	
		
			
				
					|  |  |  |  |     [@@warning "-9"] | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   let invariant x = | 
			
		
	
		
			
				
					|  |  |  |  |     Invariant.invariant [%here] x [%sexp_of: t] | 
			
		
	
		
			
				
					|  |  |  |  |     @@ fun () -> match x with Reg _ -> invariant x | _ -> assert false | 
			
		
	
		
			
				
					|  |  |  |  |     @@ fun () -> | 
			
		
	
		
			
				
					|  |  |  |  |     match x.desc with Reg _ -> invariant x | _ -> assert false | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   let name r = | 
			
		
	
		
			
				
					|  |  |  |  |     match r.desc with Reg x -> x.name | _ -> violates invariant r | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   let name = function Reg {name} -> name | x -> violates invariant x | 
			
		
	
		
			
				
					|  |  |  |  |   let global = function Reg {global} -> global | x -> violates invariant x | 
			
		
	
		
			
				
					|  |  |  |  |   let global r = | 
			
		
	
		
			
				
					|  |  |  |  |     match r.desc with Reg x -> x.global | _ -> violates invariant r | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   let of_exp = function | 
			
		
	
		
			
				
					|  |  |  |  |     | Reg _ as x -> Some (x |> check invariant) | 
			
		
	
		
			
				
					|  |  |  |  |     | _ -> None | 
			
		
	
		
			
				
					|  |  |  |  |   let of_exp e = | 
			
		
	
		
			
				
					|  |  |  |  |     match e.desc with Reg _ -> Some (e |> check invariant) | _ -> None | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   let program ?global typ name = | 
			
		
	
		
			
				
					|  |  |  |  |     Reg {name; typ; global= Option.is_some global} |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  |     { desc= Reg {name; typ; global= Option.is_some global} | 
			
		
	
		
			
				
					|  |  |  |  |     ; term= Term.var (Var.program name) } | 
			
		
	
		
			
				
					|  |  |  |  |     |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | end | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | (** Access *) | 
			
		
	
	
		
			
				
					|  |  |  | @ -354,7 +376,7 @@ end | 
			
		
	
		
			
				
					|  |  |  |  | let fold_exps e ~init ~f = | 
			
		
	
		
			
				
					|  |  |  |  |   let fold_exps_ fold_exps_ e z = | 
			
		
	
		
			
				
					|  |  |  |  |     let z = | 
			
		
	
		
			
				
					|  |  |  |  |       match e with | 
			
		
	
		
			
				
					|  |  |  |  |       match e.desc with | 
			
		
	
		
			
				
					|  |  |  |  |       | Ap1 (_, _, x) -> fold_exps_ x z | 
			
		
	
		
			
				
					|  |  |  |  |       | Ap2 (_, _, x, y) -> fold_exps_ y (fold_exps_ x z) | 
			
		
	
		
			
				
					|  |  |  |  |       | Ap3 (_, _, w, x, y) -> fold_exps_ w (fold_exps_ y (fold_exps_ x z)) | 
			
		
	
	
		
			
				
					|  |  |  | @ -367,87 +389,194 @@ let fold_exps e ~init ~f = | 
			
		
	
		
			
				
					|  |  |  |  |   fix fold_exps_ (fun _ z -> z) e init | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let fold_regs e ~init ~f = | 
			
		
	
		
			
				
					|  |  |  |  |   fold_exps e ~init ~f:(fun z -> function | 
			
		
	
		
			
				
					|  |  |  |  |     | Reg _ as x -> f z (x :> Reg.t) | _ -> z ) | 
			
		
	
		
			
				
					|  |  |  |  |   fold_exps e ~init ~f:(fun z x -> | 
			
		
	
		
			
				
					|  |  |  |  |       match x.desc with Reg _ -> f z (x :> Reg.t) | _ -> z ) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | (** Construct *) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let reg x = x | 
			
		
	
		
			
				
					|  |  |  |  | let nondet typ msg = Nondet {msg; typ} |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let label ~parent ~name = Label {parent; name} |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let integer typ data = Integer {data; typ} |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let nondet typ msg = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Nondet {msg; typ}; term= Term.nondet msg} |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let label ~parent ~name = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Label {parent; name}; term= Term.label ~parent ~name} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let integer typ data = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Integer {data; typ}; term= Term.integer data} |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let null = integer Typ.ptr Z.zero | 
			
		
	
		
			
				
					|  |  |  |  | let bool b = integer Typ.bool (Z.of_bool b) | 
			
		
	
		
			
				
					|  |  |  |  | let float typ data = Float {data; typ} |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let float typ data = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Float {data; typ}; term= Term.float data} |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let convert ?(unsigned = false) ~dst ~src exp = | 
			
		
	
		
			
				
					|  |  |  |  |   ( if (not unsigned) && Typ.equal dst src then exp | 
			
		
	
		
			
				
					|  |  |  |  |   else Ap1 (Convert {unsigned; dst}, src, exp) ) | 
			
		
	
		
			
				
					|  |  |  |  |   else | 
			
		
	
		
			
				
					|  |  |  |  |     { desc= Ap1 (Convert {unsigned; dst}, src, exp) | 
			
		
	
		
			
				
					|  |  |  |  |     ; term= Term.convert ~unsigned ~dst ~src exp.term } ) | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let select typ rcd idx = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap1 (Select idx, typ, rcd); term= Term.select ~rcd:rcd.term ~idx} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let unsigned typ op x y = | 
			
		
	
		
			
				
					|  |  |  |  |   let bits = Option.value_exn (Typ.prim_bit_size_of typ) in | 
			
		
	
		
			
				
					|  |  |  |  |   op | 
			
		
	
		
			
				
					|  |  |  |  |     (Term.extract ~unsigned:true ~bits x) | 
			
		
	
		
			
				
					|  |  |  |  |     (Term.extract ~unsigned:true ~bits y) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let eq typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Eq, typ, x, y); term= Term.eq x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let dq typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Dq, typ, x, y); term= Term.dq x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let select typ rcd idx = Ap1 (Select idx, typ, rcd) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let eq typ x y = Ap2 (Eq, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let dq typ x y = Ap2 (Dq, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let gt typ x y = Ap2 (Gt, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let ge typ x y = Ap2 (Ge, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let lt typ x y = Ap2 (Lt, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let le typ x y = Ap2 (Le, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let ugt typ x y = Ap2 (Ugt, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let uge typ x y = Ap2 (Uge, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let ult typ x y = Ap2 (Ult, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let ule typ x y = Ap2 (Ule, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let ord typ x y = Ap2 (Ord, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let uno typ x y = Ap2 (Uno, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let add typ x y = Ap2 (Add, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let sub typ x y = Ap2 (Sub, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let mul typ x y = Ap2 (Mul, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let div typ x y = Ap2 (Div, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let rem typ x y = Ap2 (Rem, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let udiv typ x y = Ap2 (Udiv, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let urem typ x y = Ap2 (Urem, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let and_ typ x y = Ap2 (And, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let or_ typ x y = Ap2 (Or, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let xor typ x y = Ap2 (Xor, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let shl typ x y = Ap2 (Shl, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let lshr typ x y = Ap2 (Lshr, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let ashr typ x y = Ap2 (Ashr, typ, x, y) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let gt typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Gt, typ, x, y); term= Term.lt y.term x.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let ge typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Ge, typ, x, y); term= Term.le y.term x.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let lt typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Lt, typ, x, y); term= Term.lt x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let le typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Le, typ, x, y); term= Term.le x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let ugt typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Ugt, typ, x, y); term= unsigned typ Term.lt y.term x.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let uge typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Uge, typ, x, y); term= unsigned typ Term.le y.term x.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let ult typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Ult, typ, x, y); term= unsigned typ Term.lt x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let ule typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Ule, typ, x, y); term= unsigned typ Term.le x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let ord typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Ord, typ, x, y); term= Term.ord x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let uno typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Uno, typ, x, y); term= Term.uno x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let add typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Add, typ, x, y); term= Term.add x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let sub typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Sub, typ, x, y); term= Term.sub x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let mul typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Mul, typ, x, y); term= Term.mul x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let div typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Div, typ, x, y); term= Term.div x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let rem typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Rem, typ, x, y); term= Term.rem x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let udiv typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Udiv, typ, x, y); term= unsigned typ Term.div x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let urem typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Urem, typ, x, y); term= unsigned typ Term.rem x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let and_ typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (And, typ, x, y); term= Term.and_ x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let or_ typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Or, typ, x, y); term= Term.or_ x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let xor typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Xor, typ, x, y); term= Term.xor x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let shl typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Shl, typ, x, y); term= Term.shl x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let lshr typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Lshr, typ, x, y); term= Term.lshr x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let ashr typ x y = | 
			
		
	
		
			
				
					|  |  |  |  |   {desc= Ap2 (Ashr, typ, x, y); term= Term.ashr x.term y.term} | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let update typ ~rcd idx ~elt = | 
			
		
	
		
			
				
					|  |  |  |  |   Ap2 (Update idx, typ, rcd, elt) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  |   { desc= Ap2 (Update idx, typ, rcd, elt) | 
			
		
	
		
			
				
					|  |  |  |  |   ; term= Term.update ~rcd:rcd.term ~idx ~elt:elt.term } | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let conditional typ ~cnd ~thn ~els = | 
			
		
	
		
			
				
					|  |  |  |  |   Ap3 (Conditional, typ, cnd, thn, els) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  |   { desc= Ap3 (Conditional, typ, cnd, thn, els) | 
			
		
	
		
			
				
					|  |  |  |  |   ; term= Term.conditional ~cnd:cnd.term ~thn:thn.term ~els:els.term } | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let record typ elts = ApN (Record, typ, elts) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | let record typ elts = | 
			
		
	
		
			
				
					|  |  |  |  |   { desc= ApN (Record, typ, elts) | 
			
		
	
		
			
				
					|  |  |  |  |   ; term= Term.record (Vector.map ~f:(fun elt -> elt.term) elts) } | 
			
		
	
		
			
				
					|  |  |  |  |   |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let struct_rec key = | 
			
		
	
		
			
				
					|  |  |  |  |   let memo_id = Hashtbl.create key in | 
			
		
	
		
			
				
					|  |  |  |  |   let dummy = null in | 
			
		
	
		
			
				
					|  |  |  |  |   let rec_app = (Staged.unstage (Term.rec_app key)) Term.Record in | 
			
		
	
		
			
				
					|  |  |  |  |   Staged.stage | 
			
		
	
		
			
				
					|  |  |  |  |   @@ fun ~id typ elt_thks -> | 
			
		
	
		
			
				
					|  |  |  |  |   match Hashtbl.find memo_id id with | 
			
		
	
		
			
				
					|  |  |  |  |   | None -> | 
			
		
	
		
			
				
					|  |  |  |  |       (* Add placeholder to prevent computing [elts] in calls to | 
			
		
	
		
			
				
					|  |  |  |  |          [struct_rec] from [elt_thks] for recursive occurrences of [id]. *) | 
			
		
	
		
			
				
					|  |  |  |  |       let elta = Array.create ~len:(Vector.length elt_thks) dummy in | 
			
		
	
		
			
				
					|  |  |  |  |       let elta = Array.create ~len:(Vector.length elt_thks) null in | 
			
		
	
		
			
				
					|  |  |  |  |       let elts = Vector.of_array elta in | 
			
		
	
		
			
				
					|  |  |  |  |       Hashtbl.set memo_id ~key:id ~data:elts ; | 
			
		
	
		
			
				
					|  |  |  |  |       let term = | 
			
		
	
		
			
				
					|  |  |  |  |         rec_app ~id (Vector.map ~f:(fun elt -> lazy elt.term) elts) | 
			
		
	
		
			
				
					|  |  |  |  |       in | 
			
		
	
		
			
				
					|  |  |  |  |       Vector.iteri elt_thks ~f:(fun i (lazy elt) -> elta.(i) <- elt) ; | 
			
		
	
		
			
				
					|  |  |  |  |       ApN (Struct_rec, typ, elts) |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  |       {desc= ApN (Struct_rec, typ, elts); term} |> check invariant | 
			
		
	
		
			
				
					|  |  |  |  |   | Some elts -> | 
			
		
	
		
			
				
					|  |  |  |  |       (* Do not check invariant as invariant will be checked above after the | 
			
		
	
		
			
				
					|  |  |  |  |          thunks are forced, before which invariant-checking may spuriously | 
			
		
	
		
			
				
					|  |  |  |  |          fail. Note that it is important that the value constructed here | 
			
		
	
		
			
				
					|  |  |  |  |          shares the array in the memo table, so that the update after | 
			
		
	
		
			
				
					|  |  |  |  |          forcing the recursive thunks also updates this value. *) | 
			
		
	
		
			
				
					|  |  |  |  |       ApN (Struct_rec, typ, elts) | 
			
		
	
		
			
				
					|  |  |  |  |       {desc= ApN (Struct_rec, typ, elts); term= rec_app ~id Vector.empty} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | (** Query *) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let is_true = function | 
			
		
	
		
			
				
					|  |  |  |  | let is_true e = | 
			
		
	
		
			
				
					|  |  |  |  |   match e.desc with | 
			
		
	
		
			
				
					|  |  |  |  |   | Integer {data; typ= Integer {bits= 1}} -> Z.is_true data | 
			
		
	
		
			
				
					|  |  |  |  |   | _ -> false | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | let is_false = function | 
			
		
	
		
			
				
					|  |  |  |  | let is_false e = | 
			
		
	
		
			
				
					|  |  |  |  |   match e.desc with | 
			
		
	
		
			
				
					|  |  |  |  |   | Integer {data; typ= Integer {bits= 1}} -> Z.is_false data | 
			
		
	
		
			
				
					|  |  |  |  |   | _ -> false | 
			
		
	
	
		
			
				
					|  |  |  | 
 |