|
|
|
@ -102,9 +102,8 @@ module rec T : sig
|
|
|
|
|
| Float of {data: string}
|
|
|
|
|
[@@deriving compare, equal, hash, sexp]
|
|
|
|
|
|
|
|
|
|
(* Note: solve (and invariant) requires Qset.min_elt to return a
|
|
|
|
|
non-coefficient, so Integer exps must compare higher than any valid
|
|
|
|
|
monomial *)
|
|
|
|
|
(* Note: invariant requires Qset.min_elt to return a non-coefficient, so
|
|
|
|
|
Integer exps must compare higher than any valid monomial *)
|
|
|
|
|
|
|
|
|
|
type comparator_witness
|
|
|
|
|
|
|
|
|
@ -209,7 +208,6 @@ type _t = T0.t
|
|
|
|
|
|
|
|
|
|
include T
|
|
|
|
|
|
|
|
|
|
let empty_map = Map.empty (module T)
|
|
|
|
|
let empty_qset = Qset.empty (module T)
|
|
|
|
|
|
|
|
|
|
let fix (f : (t -> 'a as 'f) -> 'f) (bot : 'f) (e : t) : 'a =
|
|
|
|
@ -376,8 +374,6 @@ let rec typ_of = function
|
|
|
|
|
match typ_of thn with Some _ as t -> t | None -> typ_of els )
|
|
|
|
|
| _ -> None
|
|
|
|
|
|
|
|
|
|
let typ = typ_of
|
|
|
|
|
|
|
|
|
|
let type_check e typ =
|
|
|
|
|
assert (
|
|
|
|
|
Option.for_all ~f:(Typ.castable typ) (typ_of e)
|
|
|
|
@ -518,7 +514,6 @@ module Reg = struct
|
|
|
|
|
let pp_full ?is_x vs = Set.pp (pp_full ?is_x) vs
|
|
|
|
|
let pp = pp_full ?is_x:None
|
|
|
|
|
let empty = Set.empty (module T)
|
|
|
|
|
let of_option = Option.fold ~f:Set.add ~init:empty
|
|
|
|
|
let of_list = Set.of_list (module T)
|
|
|
|
|
let union_list = Set.union_list (module T)
|
|
|
|
|
let of_vector = Set.of_vector (module T)
|
|
|
|
@ -564,7 +559,6 @@ module Reg = struct
|
|
|
|
|
Invariant.invariant [%here] x [%sexp_of: t]
|
|
|
|
|
@@ fun () -> match x with Reg _ -> invariant x | _ -> assert false
|
|
|
|
|
|
|
|
|
|
let id = function Reg {id} -> id | x -> violates invariant x
|
|
|
|
|
let name = function Reg {name} -> name | x -> violates invariant x
|
|
|
|
|
let global = function Reg {global} -> global | x -> violates invariant x
|
|
|
|
|
|
|
|
|
@ -574,85 +568,6 @@ module Reg = struct
|
|
|
|
|
|
|
|
|
|
let program ?global name =
|
|
|
|
|
Reg {id= 0; name; global= Option.is_some global} |> check invariant
|
|
|
|
|
|
|
|
|
|
let fresh name ~(wrt : Set.t) =
|
|
|
|
|
let max = match Set.max_elt wrt with None -> 0 | Some max -> id max in
|
|
|
|
|
let x' = Reg {name; id= max + 1; global= false} in
|
|
|
|
|
(x', Set.add wrt x')
|
|
|
|
|
|
|
|
|
|
(** Register renaming substitutions *)
|
|
|
|
|
module Subst = struct
|
|
|
|
|
type t = T.t Map.M(T).t [@@deriving compare, equal, sexp]
|
|
|
|
|
|
|
|
|
|
let invariant s =
|
|
|
|
|
Invariant.invariant [%here] s [%sexp_of: t]
|
|
|
|
|
@@ fun () ->
|
|
|
|
|
let domain, range =
|
|
|
|
|
Map.fold s ~init:(Set.empty, Set.empty)
|
|
|
|
|
~f:(fun ~key ~data (domain, range) ->
|
|
|
|
|
assert (not (Set.mem range data)) ;
|
|
|
|
|
(Set.add domain key, Set.add range data) )
|
|
|
|
|
in
|
|
|
|
|
assert (Set.disjoint domain range)
|
|
|
|
|
|
|
|
|
|
let pp fs s =
|
|
|
|
|
Format.fprintf fs "@[<1>[%a]@]"
|
|
|
|
|
(List.pp ",@ " (fun fs (k, v) ->
|
|
|
|
|
Format.fprintf fs "@[[%a ↦ %a]@]" pp_t k pp_t v ))
|
|
|
|
|
(Map.to_alist s)
|
|
|
|
|
|
|
|
|
|
let empty = empty_map
|
|
|
|
|
let is_empty = Map.is_empty
|
|
|
|
|
|
|
|
|
|
let freshen vs ~wrt =
|
|
|
|
|
let xs = Set.inter wrt vs in
|
|
|
|
|
let wrt = Set.union wrt vs in
|
|
|
|
|
Set.fold xs ~init:(empty, wrt) ~f:(fun (sub, wrt) x ->
|
|
|
|
|
let x', wrt = fresh (name x) ~wrt in
|
|
|
|
|
let sub = Map.add_exn sub ~key:x ~data:x' in
|
|
|
|
|
(sub, wrt) )
|
|
|
|
|
|> fst |> check invariant
|
|
|
|
|
|
|
|
|
|
let extend sub ~replace ~with_ =
|
|
|
|
|
( match Map.add sub ~key:replace ~data:with_ with
|
|
|
|
|
| `Duplicate -> sub
|
|
|
|
|
| `Ok sub ->
|
|
|
|
|
Map.map_preserving_phys_equal sub ~f:(fun v ->
|
|
|
|
|
if T.equal v replace then with_ else v ) )
|
|
|
|
|
|> check invariant
|
|
|
|
|
|
|
|
|
|
let invert sub =
|
|
|
|
|
Map.fold sub ~init:empty ~f:(fun ~key ~data sub' ->
|
|
|
|
|
Map.add_exn sub' ~key:data ~data:key )
|
|
|
|
|
|> check invariant
|
|
|
|
|
|
|
|
|
|
let exclude sub vs =
|
|
|
|
|
Set.fold vs ~init:sub ~f:Map.remove |> check invariant
|
|
|
|
|
|
|
|
|
|
let restrict sub vs =
|
|
|
|
|
Map.filter_keys ~f:(Set.mem vs) sub |> check invariant
|
|
|
|
|
|
|
|
|
|
let domain sub =
|
|
|
|
|
Map.fold sub ~init:Set.empty ~f:(fun ~key ~data:_ domain ->
|
|
|
|
|
Set.add domain key )
|
|
|
|
|
|
|
|
|
|
let range sub =
|
|
|
|
|
Map.fold sub ~init:Set.empty ~f:(fun ~key:_ ~data range ->
|
|
|
|
|
Set.add range data )
|
|
|
|
|
|
|
|
|
|
let apply sub v = try Map.find_exn sub v with Caml.Not_found -> v
|
|
|
|
|
|
|
|
|
|
let apply_set sub vs =
|
|
|
|
|
Map.fold sub ~init:vs ~f:(fun ~key ~data vs ->
|
|
|
|
|
let vs' = Set.remove vs key in
|
|
|
|
|
if Set.to_tree vs' == Set.to_tree vs then vs
|
|
|
|
|
else (
|
|
|
|
|
assert (not (Set.equal vs' vs)) ;
|
|
|
|
|
Set.add vs' data ) )
|
|
|
|
|
|
|
|
|
|
let close_set sub vs =
|
|
|
|
|
Map.fold sub ~init:vs ~f:(fun ~key:_ ~data vs -> Set.add vs data)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let fold_exps e ~init ~f =
|
|
|
|
@ -679,8 +594,6 @@ let fold_regs e ~init ~f =
|
|
|
|
|
fold_exps e ~init ~f:(fun z -> function
|
|
|
|
|
| Reg _ as x -> f z (x :> Reg.t) | _ -> z )
|
|
|
|
|
|
|
|
|
|
let fv e = fold_regs e ~f:Set.add ~init:Reg.Set.empty
|
|
|
|
|
|
|
|
|
|
(** Construct *)
|
|
|
|
|
|
|
|
|
|
let reg x = x
|
|
|
|
@ -694,9 +607,6 @@ let float data = Float {data} |> check invariant
|
|
|
|
|
let zero (typ : Typ.t) =
|
|
|
|
|
match typ with Float _ -> float "0" | _ -> integer Z.zero typ
|
|
|
|
|
|
|
|
|
|
let one (typ : Typ.t) =
|
|
|
|
|
match typ with Float _ -> float "1" | _ -> integer Z.one typ
|
|
|
|
|
|
|
|
|
|
let minus_one (typ : Typ.t) =
|
|
|
|
|
match typ with Float _ -> float "-1" | _ -> integer Z.minus_one typ
|
|
|
|
|
|
|
|
|
@ -917,17 +827,6 @@ let rec simp_mul2 typ e f =
|
|
|
|
|
(* x₁ × x₂ ==> ∏ᵢ₌₁² xᵢ *)
|
|
|
|
|
| _ -> Mul {args= Prod.add e (Prod.singleton f); typ}
|
|
|
|
|
|
|
|
|
|
let simp_mul typ es =
|
|
|
|
|
(* (bas ^ pwr) × exp *)
|
|
|
|
|
let rec mul_pwr bas pwr exp =
|
|
|
|
|
if Q.equal Q.zero pwr then exp
|
|
|
|
|
else mul_pwr bas Q.(pwr - one) (simp_mul2 typ bas exp)
|
|
|
|
|
in
|
|
|
|
|
let one = one typ in
|
|
|
|
|
Qset.fold es ~init:one ~f:(fun bas pwr exp ->
|
|
|
|
|
if Q.sign pwr >= 0 then mul_pwr bas pwr exp
|
|
|
|
|
else simp_div exp (mul_pwr bas (Q.neg pwr) one) )
|
|
|
|
|
|
|
|
|
|
let simp_negate typ x = simp_mul2 typ (minus_one typ) x
|
|
|
|
|
|
|
|
|
|
let simp_sub typ x y =
|
|
|
|
@ -1126,17 +1025,6 @@ let iter e ~f =
|
|
|
|
|
| Concat {args} | Struct_rec {elts= args} -> Vector.iter ~f args
|
|
|
|
|
| _ -> ()
|
|
|
|
|
|
|
|
|
|
let fold e ~init:s ~f =
|
|
|
|
|
match e with
|
|
|
|
|
| App {op= x; arg= y} | Splat {byt= x; siz= y} | Memory {siz= x; arr= y}
|
|
|
|
|
->
|
|
|
|
|
f y (f x s)
|
|
|
|
|
| Add {args} | Mul {args} ->
|
|
|
|
|
Qset.fold ~f:(fun e _ s -> f e s) args ~init:s
|
|
|
|
|
| Concat {args} | Struct_rec {elts= args} ->
|
|
|
|
|
Vector.fold ~f:(fun s e -> f e s) args ~init:s
|
|
|
|
|
| _ -> s
|
|
|
|
|
|
|
|
|
|
let is_subexp ~sub ~sup =
|
|
|
|
|
With_return.with_return
|
|
|
|
|
@@ fun {return} ->
|
|
|
|
@ -1192,8 +1080,6 @@ let app1 ?(partial = false) op arg =
|
|
|
|
|
|
|
|
|
|
let app2 op x y = app1 (app1 ~partial:true op x) y
|
|
|
|
|
let app3 op x y z = app1 (app1 ~partial:true (app1 ~partial:true op x) y) z
|
|
|
|
|
let addN typ args = simp_add typ args |> check invariant
|
|
|
|
|
let mulN typ args = simp_mul typ args |> check invariant
|
|
|
|
|
|
|
|
|
|
let check1 op typ x =
|
|
|
|
|
type_check x typ ;
|
|
|
|
@ -1293,49 +1179,6 @@ let struct_rec key =
|
|
|
|
|
let convert ?(signed = false) ~dst ~src exp =
|
|
|
|
|
app1 (Convert {signed; dst; src}) exp
|
|
|
|
|
|
|
|
|
|
let size_of t =
|
|
|
|
|
Option.bind (Typ.prim_bit_size_of t) ~f:(fun n ->
|
|
|
|
|
if n % 8 = 0 then Some (integer (Z.of_int (n / 8)) Typ.siz) else None
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
(** Transform *)
|
|
|
|
|
|
|
|
|
|
let map e ~f =
|
|
|
|
|
let map_ : (t -> t) -> t -> t =
|
|
|
|
|
fun map_ e ->
|
|
|
|
|
let map_bin mk ~f x y =
|
|
|
|
|
let x' = f x in
|
|
|
|
|
let y' = f y in
|
|
|
|
|
if x' == x && y' == y then e else mk x' y'
|
|
|
|
|
in
|
|
|
|
|
let map_vector mk ~f args =
|
|
|
|
|
let args' = Vector.map_preserving_phys_equal ~f args in
|
|
|
|
|
if args' == args then e else mk args'
|
|
|
|
|
in
|
|
|
|
|
let map_qset mk typ ~f args =
|
|
|
|
|
let args' = Qset.map ~f:(fun arg q -> (f arg, q)) args in
|
|
|
|
|
if args' == args then e else mk typ args'
|
|
|
|
|
in
|
|
|
|
|
match e with
|
|
|
|
|
| App {op; arg} -> map_bin (app1 ~partial:true) ~f op arg
|
|
|
|
|
| Add {args; typ} -> map_qset addN typ ~f args
|
|
|
|
|
| Mul {args; typ} -> map_qset mulN typ ~f args
|
|
|
|
|
| Splat {byt; siz} -> map_bin simp_splat ~f byt siz
|
|
|
|
|
| Memory {siz; arr} -> map_bin simp_memory ~f siz arr
|
|
|
|
|
| Concat {args} -> map_vector simp_concat ~f args
|
|
|
|
|
| Struct_rec {elts= args} -> Struct_rec {elts= Vector.map args ~f:map_}
|
|
|
|
|
| _ -> e
|
|
|
|
|
in
|
|
|
|
|
fix map_ (fun e -> e) e
|
|
|
|
|
|
|
|
|
|
let rename sub e =
|
|
|
|
|
let rec rename_ sub e =
|
|
|
|
|
match e with
|
|
|
|
|
| Reg _ -> Reg.Subst.apply sub e
|
|
|
|
|
| _ -> map ~f:(rename_ sub) e
|
|
|
|
|
in
|
|
|
|
|
rename_ sub e |> check (invariant ~partial:true)
|
|
|
|
|
|
|
|
|
|
(** Query *)
|
|
|
|
|
|
|
|
|
|
let is_true = function
|
|
|
|
@ -1345,72 +1188,3 @@ let is_true = function
|
|
|
|
|
let is_false = function
|
|
|
|
|
| Integer {data; typ= Integer {bits= 1}} -> Z.is_false data
|
|
|
|
|
| _ -> false
|
|
|
|
|
|
|
|
|
|
let rec is_constant e =
|
|
|
|
|
let is_constant_bin x y = is_constant x && is_constant y in
|
|
|
|
|
match e with
|
|
|
|
|
| Reg _ | Nondet _ -> false
|
|
|
|
|
| App {op= x; arg= y} | Splat {byt= x; siz= y} | Memory {siz= x; arr= y}
|
|
|
|
|
->
|
|
|
|
|
is_constant_bin x y
|
|
|
|
|
| Add {args} | Mul {args} ->
|
|
|
|
|
Qset.for_all ~f:(fun arg _ -> is_constant arg) args
|
|
|
|
|
| Concat {args} | Struct_rec {elts= args} ->
|
|
|
|
|
Vector.for_all ~f:is_constant args
|
|
|
|
|
| _ -> true
|
|
|
|
|
|
|
|
|
|
let classify = function
|
|
|
|
|
| Add _ | Mul _ -> `Interpreted
|
|
|
|
|
| App {op= Eq | Dq | App {op= Eq | Dq}} -> `Simplified
|
|
|
|
|
| App _ -> `Uninterpreted
|
|
|
|
|
| _ -> `Atomic
|
|
|
|
|
|
|
|
|
|
let solve e f =
|
|
|
|
|
[%Trace.call fun {pf} -> pf "%a@ %a" pp e pp f]
|
|
|
|
|
;
|
|
|
|
|
let rec solve_ e f s =
|
|
|
|
|
let solve_uninterp e f =
|
|
|
|
|
let ord = compare e f in
|
|
|
|
|
match (is_constant e, is_constant f) with
|
|
|
|
|
| true, true when ord <> 0 -> None
|
|
|
|
|
(* orient equation to discretionarily prefer exp that is constant or
|
|
|
|
|
compares smaller as class representative *)
|
|
|
|
|
| true, false -> Some (Map.add_exn s ~key:f ~data:e)
|
|
|
|
|
| false, true -> Some (Map.add_exn s ~key:e ~data:f)
|
|
|
|
|
| _ ->
|
|
|
|
|
let key, data = if ord > 0 then (e, f) else (f, e) in
|
|
|
|
|
Some (Map.add_exn s ~key ~data)
|
|
|
|
|
in
|
|
|
|
|
let concat_size args =
|
|
|
|
|
Vector.fold_until args ~init:(integer Z.zero Typ.siz)
|
|
|
|
|
~f:(fun sum -> function
|
|
|
|
|
| Memory {siz} -> Continue (add Typ.siz siz sum) | _ -> Stop None
|
|
|
|
|
)
|
|
|
|
|
~finish:(fun _ -> None)
|
|
|
|
|
in
|
|
|
|
|
match (e, f) with
|
|
|
|
|
| (Add {typ} | Mul {typ} | Integer {typ}), _
|
|
|
|
|
|_, (Add {typ} | Mul {typ} | Integer {typ}) -> (
|
|
|
|
|
match sub typ e f with
|
|
|
|
|
| Add {args} ->
|
|
|
|
|
let c, q = Qset.min_elt_exn args in
|
|
|
|
|
let n = Sum.to_exp typ (Qset.remove args c) in
|
|
|
|
|
let d = rational (Q.neg q) typ in
|
|
|
|
|
let r = div n d in
|
|
|
|
|
Some (Map.add_exn s ~key:c ~data:r)
|
|
|
|
|
| e_f -> solve_uninterp e_f (zero typ) )
|
|
|
|
|
| Concat {args= ms}, Concat {args= ns} -> (
|
|
|
|
|
match (concat_size ms, concat_size ns) with
|
|
|
|
|
| Some p, Some q -> solve_uninterp e f >>= solve_ p q
|
|
|
|
|
| _ -> solve_uninterp e f )
|
|
|
|
|
| Memory {siz= m}, Concat {args= ns} | Concat {args= ns}, Memory {siz= m}
|
|
|
|
|
-> (
|
|
|
|
|
match concat_size ns with
|
|
|
|
|
| Some p -> solve_uninterp e f >>= solve_ p m
|
|
|
|
|
| _ -> solve_uninterp e f )
|
|
|
|
|
| _ -> solve_uninterp e f
|
|
|
|
|
in
|
|
|
|
|
solve_ e f empty_map
|
|
|
|
|
|>
|
|
|
|
|
[%Trace.retn fun {pf} ->
|
|
|
|
|
function Some s -> pf "%a" Reg.Subst.pp s | None -> pf "false"]
|
|
|
|
|