liquid-fixpoint-0.1.0.0: external/fixpoint/fixSimplify.ml
(*
* Copyright © 2009 The Regents of the University of California. All rights reserved.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
* FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION
* TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
*)
module BS = BNstats
module Co = Constants
module C = FixConstraint
module P = Ast.Predicate
module E = Ast.Expression
module Sy = Ast.Symbol
module Kg = Kvgraph
module Su = Ast.Subst
module SM = Ast.Symbol.SMap
module SS = Ast.Symbol.SSet
module Misc = FixMisc
module IM = Misc.IntMap
open Misc.Ops
open Ast
let mydebug = false
(****************************************************************************)
(*********************************** Misc. **********************************)
(****************************************************************************)
let add_cm = List.fold_left (fun cm c -> IM.add (C.id_of_t c) c cm)
let find_cm = fun cm id -> IM.find id cm
let refas_of_k = fun k -> [C.Kvar (Su.empty, k)]
(****************************************************************************)
(************** Generic Simplification/Transformation API *******************)
(****************************************************************************)
module type SIMPLIFIER = sig
val simplify_ts: FixConstraint.t list -> FixConstraint.t list
end
(****************************************************************************)
(******************* Syntactic Simplification *******************************)
(****************************************************************************)
module Syntactic : SIMPLIFIER = struct
let defs_of_pred =
let rec dofp (em, pm) p = match p with
| Atom ((Var v, _), Eq, e), _
when not (P.is_tauto p) ->
Sy.SMap.add v e em, pm
| And [Imp ((Bexp (Var v1, _), _), p1), _;
Imp (p2, (Bexp (Var v2, _), _)), _], _
when v1 = v2 && p1 = p2 && not(P.is_tauto p) ->
em, Sy.SMap.add v1 p1 pm
| And ps, _ ->
List.fold_left dofp (em, pm) ps
| _ -> em, pm
in dofp (Sy.SMap.empty, Sy.SMap.empty)
let rec expr_apply_defs em pm expr =
let ef = expr_apply_defs em pm in
let pf = pred_apply_defs em pm in
match expr with
| Var v, _ when Sy.SMap.mem v em ->
Sy.SMap.find v em, true
| Var _, _ | Con _, _ | Bot, _ ->
expr, false
| App (v, es), _ ->
let _ = asserts (not (Sy.SMap.mem v em)) "binding for UF" in
es |> List.map ef
|> List.split
|> (fun (es', bs') -> (eApp (v, es'), List.fold_left (||) false bs'))
| Bin (e1, op, e2), _ ->
let e1', b1' = ef e1 in
let e2', b2' = ef e2 in
eBin (e1', op, e2'), (b1' || b2')
| Ite (p, e1, e2), _ ->
let p', b' = pf p in
let e1', b1' = ef e1 in
let e2', b2' = ef e2 in
eIte (p', e1', e2'), (b' || b1' || b2')
| Fld (v, e), _ ->
let e', b' = ef e in
eFld (v, e'), b'
| Cst (e, t), _ ->
let e', b' = ef e in
eCst (e', t), b'
| _ -> failwith "Pattern: expr_apply_defs"
and pred_apply_defs em pm pred =
let ef = expr_apply_defs em pm in
let pf = pred_apply_defs em pm in
match pred with
| And ps, _ ->
ps |> List.map pf
|> List.split
|> (fun (ps', bs') -> pAnd ps', List.exists id bs')
| Or ps, _ ->
ps |> List.map pf
|> List.split
|> (fun (ps', bs') -> pOr ps', List.exists id bs')
| Not p, _ ->
p |> pf
|> Misc.app_fst pNot
| Imp (p1, p2), _ ->
let p1', b1' = pf p1 in
let p2', b2' = pf p2 in
pImp (p1', p2'), b1' || b2'
| Bexp (Var v, _), _ when Sy.SMap.mem v pm ->
Sy.SMap.find v pm, true
| Bexp e, _ ->
e |> ef |> Misc.app_fst pBexp
| Atom (e1, brel, e2), _ ->
let e1', b1' = ef e1 in
let e2', b2' = ef e2 in
pAtom (e1', brel, e2'), b1' || b2'
| Forall (qs, p), _ ->
assertf "Forall in Simplify!"
| _ ->
pred, false
(* Why does this fixpointing terminate?
* close em, pm under substitution so that
for all x in dom(em), support(em(x)) \cap dom(em) = empty *)
(* Assume: em is well-formed,
* i.e. exists an ordering on vars of dom(em)
* x1 < x2 < ... < xn s.t. if xj \in em(xi) then xj < xi *)
let expr_apply_defs em fm e =
e |> Misc.fixpoint (expr_apply_defs em fm)
|> fst
let pred_apply_defs em fm p =
p |> Misc.fixpoint (pred_apply_defs em fm)
|> fst
|> simplify_pred
let subs_apply_defs em pm xes = List.map (Misc.app_snd (expr_apply_defs em pm)) xes
let print_em_pm t (em, pm) =
let id = t |> C.id_of_t in
let vv = t |> C.lhs_of_t |> C.vv_of_reft in
let vve = try Sy.SMap.find vv em with Not_found -> bot in
let vve' = expr_apply_defs em pm vve in
Co.bprintf mydebug "\nbodyp em map for %d\n" id ;
Sy.SMap.iter (fun x e -> Co.bprintf mydebug "%a -> %a\n" Sy.print x E.print e) em;
Co.bprintf mydebug "\nbodyp pm map for %d\n" id ;
Sy.SMap.iter (fun x p -> Co.bprintf mydebug "%a -> %a\n" Sy.print x P.print p) pm;
Co.bprintf mydebug "edef for vv %a = %a (simplified %a)\n" Sy.print vv E.print vve E.print vve'
let preds_kvars_of_env env =
Sy.SMap.fold begin fun x r (ps, env) ->
let vv = C.vv_of_reft r in
let xe = Ast.eVar x in
let t = C.sort_of_reft r in
let rps, rks = C.preds_kvars_of_reft r in
let ps' = List.map (fun p -> P.subst p vv xe) rps ++ ps in
let env' = (* match rks with [] -> env | _ -> *) Sy.SMap.add x (vv, t, rks) env in
ps', env'
end env ([], Sy.SMap.empty)
let simplify_kvar em pm (su, sym) =
su |> Su.to_list
|> subs_apply_defs em pm
|> Su.of_list
|> (fun su -> C.Kvar (su, sym))
let simplify_env em pm ks_env =
Sy.SMap.map begin fun (vv, t, ks) ->
ks |> List.map (simplify_kvar em pm) |> C.make_reft vv t
end ks_env
let simplify_grd em pm vv t p =
let _ = Co.bprintf mydebug "simplify_grd [1]: %a \n" P.print p in
let p = pred_apply_defs em pm p in
let _ = Co.bprintf mydebug "simplify_grd [2]: %a \n" P.print p in
begin try
Sy.SMap.find vv em
|> expr_apply_defs em pm
|> (fun vve -> pAnd [p; pAtom (eVar vv, Eq, vve)])
with Not_found -> p end
>> Co.bprintf mydebug "simplify_grd [3]: %a \n" P.print
let simplify_refa em pm = function
| C.Conc p -> C.Conc (pred_apply_defs em pm p)
| C.Kvar (xes, sym) -> simplify_kvar em pm (xes, sym)
(* API *)
let simplify_t c =
let id = c |> C.id_of_t in
let _ = Co.bprintf mydebug "============== Simplifying %d ============== \n"id in
let env_ps, ks_env = c |> C.env_of_t |> preds_kvars_of_env in
let l_ps, l_ks = c |> C.lhs_of_t |> C.preds_kvars_of_reft in
let vv, t = c |> C.lhs_of_t |> Misc.tmap2 (C.vv_of_reft, C.sort_of_reft) in
let bodyp = Ast.pAnd ([C.grd_of_t c] ++ l_ps ++ env_ps)
>> Co.bprintf mydebug "body_pred: %a \n" P.print in
let em, pm = defs_of_pred bodyp
>> print_em_pm c in
let senv = simplify_env em pm ks_env in
let sgrd = simplify_grd em pm vv t bodyp in
let slhs = l_ks |> List.map (simplify_kvar em pm) |> C.make_reft vv t in
let srhs = c |> C.rhs_of_t |> C.ras_of_reft |> List.map (simplify_refa em pm) |> C.make_reft vv t in
C.make_t senv sgrd slhs srhs (C.ido_of_t c) (C.tag_of_t c)
(* API *)
let simplify_ts cs =
cs |> List.map simplify_t
(* |> List.filter (not <.> C.is_tauto) *)
end
(****************************************************************************)
(*** Cone-of-Influence: Remove Constraints that don't reach constant-pred ***)
(****************************************************************************)
module Cone : SIMPLIFIER = struct
let simplify_ts cs =
let cm = add_cm IM.empty cs in
cs |> Kg.add Kg.empty
>> Kg.print_stats
|> Kg.cone_ids
|> List.map (find_cm cm)
end
(**************************************************************************)
(*** Direct-Dependencies: Remove non-data-dependent binders****************)
(*************************************************************************)
module WeakFixpoint : SIMPLIFIER = struct
let weaken_env c e =
C.make_t e
(C.grd_of_t c) (C.lhs_of_t c) (C.rhs_of_t c)
(C.ido_of_t c) (C.tag_of_t c)
let support_of_refa = function
| C.Conc p -> P.support p
| _ -> []
let support_of_reft =
Misc.flap support_of_refa <.> thd3
let rec data_cone env m n xs = match xs with
| _::_ -> let m' = List.fold_left (fun m' x -> Sy.SMap.add x n m') m xs in
xs |> Misc.map_partial (C.lookup_env env)
|> Misc.flap support_of_reft
|> Misc.filter (fun x -> not (Sy.SMap.mem x m))
|> data_cone env m' (n+1)
| [] -> m
let data_cone c =
(P.support (C.grd_of_t c))
|> (++) (support_of_reft (C.lhs_of_t c))
|> data_cone (C.env_of_t c) Sy.SMap.empty 0
let project_cone m x ((v,t,ras) as r) =
if Sy.SMap.mem x m then r else (v, t, List.filter C.is_conc_refa ras)
let simplify_t c =
c |> data_cone |> project_cone |> C.map_env
|> (fun f -> f (C.env_of_t c))
|> weaken_env c
let simplify_ts = Misc.map simplify_t
end
(****************************************************************************)
(***** Merge Write and Read of Kvar: A |- k and B, k |- C iff A,B |- C ****)
(****************************************************************************)
module EliminateK : SIMPLIFIER = struct
type t = { g : Kg.t;
cm : FixConstraint.t IM.t;
id : int; }
let print_k ppf k =
Format.fprintf ppf "%s" (C.refa_to_string k)
let empty =
{ g = Kg.empty;
cm = IM.empty;
id = 0; }
let add me cs =
let n, cs = C.add_ids me.id cs in
{ g = Kg.add me.g cs;
cm = add_cm me.cm cs;
id = n+1 }
let remove me (k, cs) =
{ g = Kg.remove me.g [k];
cm = List.map C.id_of_t cs |> List.fold_left (Misc.flip IM.remove) me.cm;
id = me.id; }
let of_ts = add empty
let to_ts = fun me -> me.cm |> IM.to_list |> List.map snd
let cs_of_k = fun f me k -> f me.g [k] |> List.map (find_cm me.cm)
(* Assume that k is written in (1) and read once in (2)
(1) env1, g1, k_v:l1 |- k[xi := ai]
(2) env2, g2, y:k[xi := bi] |- r2
Now, (1) equiv (1') and (2) equiv (2')
(1') env1, g1, #i:{v=ai}, k_v:l1 |- k[xi := #i]
(2') env2, g2, #i:{v=bi}, y:k2[xi := #i] |- r2
Next, we can merge (1') and (2')
(1'+2') env1 ++ env2, g1 && g2, #i:{v=ai}, #i:{v=bi}, y:l1 |- r2
Which simplifies to:
(1'+2') env1 ++ env2, g1 && g2 && {ai = bi}, y:l1 |- r2
*)
let meet_env env1 env2 xrs =
[env1; env2]
|> Misc.flap C.bindings_of_env
|> (++) xrs
|> C.env_of_bindings
let meet_sub su1 su2 =
[su1; su2]
|> Misc.flap Su.to_list
|> Misc.groupby fst
|> List.map (function [(x,e)] -> pEqual (eVar x, e)| [(_,e1);(_,e2)] -> pEqual (e1,e2))
|> pAnd
let merge_one me k (wc, rc) =
let env1, env2 = Misc.map_pair C.env_of_t (wc, rc) in
let g1, g2 = Misc.map_pair C.grd_of_t (wc, rc) in
let l1 = C.lhs_of_t wc in
let [C.Kvar(su1, k)] = C.rhs_of_t wc |> thd3 in
let su2, yr', l' = match Kg.k_reads me.g (C.id_of_t rc) (C.Kvar (Su.empty, k)) with
| [Kg.Bnd (y, su2)] -> su2, [(y,l1)], (C.lhs_of_t rc)
| [Kg.Lhs su2] -> su2, [], l1
| _ -> assertf "EliminateK.merge_one (k=%s, id=%d)" (Sy.to_string k) (C.id_of_t rc) in
let env' = meet_env env1 env2 yr' in
let g' = pAnd [g1; g2; meet_sub su1 su2] in
let r' = C.rhs_of_t rc in
C.make_t env' g' l' r' None (C.tag_of_t rc)
let eliminate me (k, wcs, rcs) =
me >> (fun _ -> Format.printf "EliminateK.eliminate %s \n" (C.refa_to_string k))
|> Misc.flip add (Misc.cross_product wcs rcs |> List.map (merge_one me k))
|> Misc.flip remove (k, wcs ++ rcs)
let select_ks me =
me.g
|> Kg.filter_kvars (Kg.is_single_wr me.g)
|> List.filter (Kg.is_single_rd me.g)
|> List.map (fun k -> (k, cs_of_k Kg.writes me k, cs_of_k Kg.reads me k))
|> List.filter (fun (_,wcs, rcs) -> Misc.disjoint wcs rcs)
>> (List.map fst3 <+> Format.printf "EliminateK.select_ks [OUT]: %a \n"
(Misc.pprint_many false "," print_k))
let simplify_ts cs =
let me = of_ts cs in
me |> select_ks
|> List.fold_left eliminate me
|> to_ts
end
(****************************************************************************)
(***** Copy Propagation *****************************************************)
(****************************************************************************)
module CopyProp : SIMPLIFIER = struct
let subst_theta =
List.map <.> Misc.app_snd <.> (fun (x, e) e' -> E.subst e' x e)
let subst_bind su x y ((v, t, ras) as r) =
if x = y then (v, t, []) else C.theta su r
let subst_cstr (x, e) c =
let su = Su.of_list [(x, e)] in
let env' = C.env_of_t c |> C.map_env (subst_bind su x) in
let grd' = C.grd_of_t c |> (fun p -> P.subst p x e) in
let lhs' = C.lhs_of_t c |> C.theta su in
let rhs' = C.rhs_of_t c |> C.theta su in
C.make_t env' grd' lhs' rhs' (C.ido_of_t c) (C.tag_of_t c)
let rec eliminate c = function
| (x, e) :: theta' when List.mem x (E.support e)
-> eliminate c theta'
| xe :: theta' (* x not in e *)
-> eliminate (subst_cstr xe c) (subst_theta xe theta')
| [] -> c
let rigid_vars c =
c |> C.kvars_of_t
|> List.map fst
|> Misc.flap Su.to_list
|> List.map fst
|> SS.of_list
let equalities_of_binding = function
| (x, (v, _, [C.Conc ( Atom ((Var v', _), Eq, e), _ )]))
when v = v' -> Some (x, e)
| (x, (v, _, [C.Conc ( Atom (e, Eq, (Var v', _)), _ )]))
when v = v' -> Some (x, e)
| _ -> None
let equalities_of_t c =
c |> C.env_of_t
|> (fun _ -> failwith "CopyProp.equalities_of_t") (* C.bindings_of_env *)
|> Misc.map_partial equalities_of_binding
let simplify_t c =
let ys = rigid_vars c in
c |> equalities_of_t
|> List.filter (fun (x,_) -> not (SS.mem x ys))
|> eliminate c
let simplify_ts = Misc.map simplify_t
end
(* API *)
let simplify_ts cs =
cs
|> Misc.filter (not <.> C.is_tauto)
|> ((not !Co.lfp) <?> BS.time "simplify 0" WeakFixpoint.simplify_ts)
|> BS.time "add ids 1" (C.add_ids 0)
|> snd
(* |> (!Co.copyprop <?> BS.time "simplify CP" CopyProp.simplify_ts) *)
|> (!Co.simplify_t <?> BS.time "simplify 1" Syntactic.simplify_ts) (* termination bug, tickled by C benchmarks *)
|> (!Co.simplify_t <?> BS.time "simplify 2" Cone.simplify_ts)
|> (!Co.simplify_t <?> BS.time "simplify 3" EliminateK.simplify_ts)