Skip to content

Commit 773b750

Browse files
committed
perf(smt): memories as abstract values, local variables as functions of them
A memory with local variables was translated as a why3 pair of the tuple of all its locals and an abstract memory, and a local `x{m}` as a projection of the first component. why3's `eliminate_algebraic` then expands the tuple type in every task: for a Jasmin extraction with 162 locals (the `param_k`/`result_k` of the extracted procedures), a single `x{hr}` in a hypothesis adds 162 projection definitions and a 27 000-line inversion axiom, 3.4 MB of SMT-LIB per call, 2 s of printing and parsing for goals the provers close in milliseconds. A memory is now the abstract type `memory` whatever its memtype, and a local variable an uninterpreted function `x : memory -> ty`, created once per name and type (`te_lvar`), as global variables already are (`te_xpath`). The argument variable `arg` is the tuple of the functions of the declared parameters (a single parameter is `arg` itself); `glob M` and `Pr[...]` take the whole memory. `tests/smt-memory-locals.ec` covers locals, `arg` and its projections, a single parameter, `={arg}` in an equiv, and the memories of `Pr` statements. On the Keccak safety proofs of jasmin-nist, a prover call goes from 2.2 s to 0.1 s (47 KB task), the AVX2 file from 140 s to 32 s and the scenario of 49 files from 221 s to 135 s elapsed. `unit` and `stdlib` pass. The loop-body step of PIR_s_uniform in examples/PIR.ec closed by a prover call that found its instances of the restriction lemmas with some prover versions only; two step lemmas (restr_addS_eq, restr_S_eq) now rewrite the goal to a propositional one.
1 parent e704d26 commit 773b750

5 files changed

Lines changed: 131 additions & 37 deletions

File tree

‎examples/PIR.ec‎

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,24 @@ proof.
186186
apply fsetP => x;rewrite /restr in_fsetI in_fset0 mem_oflist mem_iota /#.
187187
qed.
188188
189+
lemma restr_addS_eq n s x :
190+
0 <= n => is_restr s n =>
191+
(fset1 n `|` s = restr x (n + 1)) = (n \in x /\ s = restr x n).
192+
proof.
193+
move=> Hn Hs; rewrite restrS //; case: (n \in x) => /= _.
194+
+ by rewrite (is_restr_Ueq n s (restr x n) Hs (is_restr_restr n x)).
195+
by rewrite fset0U; apply/neqF/is_restr_diff/is_restr_restr.
196+
qed.
197+
198+
lemma restr_S_eq n s x :
199+
0 <= n => is_restr s n =>
200+
(s = restr x (n + 1)) = (!n \in x /\ s = restr x n).
201+
proof.
202+
move=> Hn Hs; rewrite restrS //; case: (n \in x) => /= _.
203+
+ by apply/neqF; rewrite eq_sym; apply/(is_restr_diff n (restr x n) s Hs).
204+
by rewrite fset0U.
205+
qed.
206+
189207
axiom N_pos : 0 <= N.
190208
191209
import RField StdOrder.RealOrder.
@@ -355,8 +373,10 @@ proof.
355373
skip => &m1 &m2 /> H0j HjN Hrs1 Hrs2 Hs Hj; split.
356374
+ by move=> b _;ring.
357375
move=> _ b _;split;1: by ring.
358-
move=> _; rewrite !oflist_cons !restrS //.
359-
smt (is_restr_addS is_restrS is_restr_diff is_restr_Ueq is_restr_restr fset0U).
376+
move=> _; rewrite !oflist_cons !(restr_addS_eq, restr_S_eq) //.
377+
have := is_restr_addS _ _ H0j Hrs1; have := is_restr_addS _ _ H0j Hrs2.
378+
have := is_restrS _ _ H0j Hrs1; have := is_restrS _ _ H0j Hrs2.
379+
smt().
360380
auto; move => &m1 &m2 />.
361381
rewrite !restr_0 -set0E /=;smt (is_restr_fset0).
362382
qed.

‎src/ecMemory.ml‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,13 @@ let local_type (Lmt_concrete mt) =
220220

221221
let has_locals (Lmt_concrete mt) =
222222
Option.is_some mt
223+
224+
let is_arg (x : symbol) (Lmt_concrete mt) =
225+
match mt with
226+
| None -> false
227+
| Some lmt -> lmt.lmt_name = Some x
228+
229+
let decls (Lmt_concrete mt) =
230+
match mt with
231+
| None -> []
232+
| Some lmt -> lmt.lmt_decl

‎src/ecMemory.mli‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,9 @@ val dump_memtype : memtype -> string
6969
(* -------------------------------------------------------------------- *)
7070
val local_type : memtype -> ty option
7171
val has_locals : memtype -> bool
72+
73+
(* [is_arg x mt]: [x] is the argument variable of [mt] *)
74+
val is_arg : symbol -> memtype -> bool
75+
76+
(* the declared variables of [mt], in order *)
77+
val decls : memtype -> ovariable list

‎src/ecSmt.ml‎

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ type kpattern =
6969
| KApp of EcPath.path * kpattern list
7070
| KProj of kpattern * int
7171

72+
(* -------------------------------------------------------------------- *)
73+
(* Local program variables are translated as uninterpreted functions of
74+
the memory, one per (name, type), like global variables are. *)
75+
module Hlv = Hashtbl.Make(struct
76+
type t = string * WTy.ty
77+
let equal (x1, t1) (x2, t2) = String.equal x1 x2 && WTy.ty_equal t1 t2
78+
let hash (x, t) = Hashtbl.hash (x, WTy.ty_hash t)
79+
end)
80+
7281
(* -------------------------------------------------------------------- *)
7382
type tenv = {
7483
(*---*) te_env : EcEnv.env;
@@ -82,6 +91,7 @@ type tenv = {
8291
mutable te_lam : WTerm.term Mta.t;
8392
(*---*) te_gen : WTerm.term Hf.t;
8493
(*---*) te_xpath : WTerm.lsymbol Hx.t; (* proc and global var *)
94+
(*---*) te_lvar : WTerm.lsymbol Hlv.t; (* local var *)
8595
(*---*) te_absmod : w3absmod Hid.t; (* abstract module *)
8696
}
8797

@@ -97,6 +107,7 @@ let empty_tenv env task (kwty, kw, kwk) =
97107
te_lam = Mta.empty;
98108
te_gen = Hf.create 0;
99109
te_xpath = Hx.create 0;
110+
te_lvar = Hlv.create 0;
100111
te_absmod = Hid.create 0;
101112
}
102113

@@ -258,9 +269,6 @@ let wproj_tuple genv arg i =
258269
let fs = Tuples.proj (n,i) in
259270
WTerm.t_app_infer fs [arg]
260271

261-
let wfst genv arg = wproj_tuple genv arg 0
262-
let wsnd genv arg = wproj_tuple genv arg 1
263-
264272
(* -------------------------------------------------------------------- *)
265273
let trans_tv lenv id = oget (Mid.find_opt id lenv.le_tv)
266274

@@ -462,14 +470,6 @@ and trans_tydecl genv (p, tydecl) =
462470
List.iter (fun (p, wop) -> Hp.add genv.te_op p wop) opts;
463471
ts
464472

465-
(* -------------------------------------------------------------------- *)
466-
let trans_memtype ((genv, _) as env) mt =
467-
match EcMemory.local_type mt with
468-
| None -> ty_mem
469-
| Some ty ->
470-
let ty = trans_ty env ty in
471-
wty_tuple genv [ty; ty_mem]
472-
473473
(* -------------------------------------------------------------------- *)
474474
exception CanNotTranslate
475475
let trans_binding genv lenv (x, xty) =
@@ -478,7 +478,7 @@ let trans_binding genv lenv (x, xty) =
478478
| GTty ty ->
479479
lenv, trans_ty (genv, lenv) ty
480480
| GTmem mt ->
481-
{ lenv with le_mt = Mid.add x mt lenv.le_mt }, trans_memtype (genv, lenv) mt
481+
{ lenv with le_mt = Mid.add x mt lenv.le_mt }, ty_mem
482482

483483
| _ -> raise CanNotTranslate
484484
in
@@ -850,19 +850,40 @@ and trans_op (genv:tenv) p =
850850
try Hp.find genv.te_op p with Not_found -> create_op ~body:true genv p
851851

852852
(* -------------------------------------------------------------------- *)
853+
and trans_lvar ((genv, _) as env) (x : string) (ty : ty) (m : WTerm.term) =
854+
let wty = trans_ty env ty in
855+
let ls =
856+
match Hlv.find_opt genv.te_lvar (x, wty) with
857+
| Some ls -> ls
858+
| None ->
859+
let ls = WTerm.create_lsymbol (WIdent.id_fresh x) [ty_mem] (Some wty) in
860+
genv.te_task <- WTask.add_param_decl genv.te_task ls;
861+
Hlv.add genv.te_lvar (x, wty) ls; ls
862+
in
863+
WTerm.t_app_infer ls [m]
864+
853865
and trans_pvar ((genv, lenv) as env) pv ty mem =
854866
let pv = NormMp.norm_pvar genv.te_env pv in
855867
let mt = get_memtype lenv mem in
856868
match pv with
857869
| PVloc x ->
858-
let m = trans_mem env ~forglobal:false mem in
870+
let m = trans_mem env mem in
859871
begin match EcMemory.lookup x mt with
860-
| Some (_,_,Some i) -> wproj_tuple genv m i
861-
| Some (_,_,None) -> m
862-
| None -> assert false
872+
| Some (v, _, _) when not (EcMemory.is_arg x mt) ->
873+
trans_lvar env x v.v_type m
874+
| Some _ ->
875+
(* the whole argument: the tuple of the declared variables *)
876+
let tr i (ov : ovariable) =
877+
let name = odfl (Printf.sprintf "_arg%d" i) ov.ov_name in
878+
trans_lvar env name ov.ov_type m in
879+
begin match EcMemory.decls mt with
880+
| [ov] -> tr 0 ov
881+
| decls -> wt_tuple genv (List.mapi tr decls)
882+
end
883+
| None -> assert false
863884
end
864885
| PVglob xp ->
865-
let m = trans_mem env ~forglobal:true mem in
886+
let m = trans_mem env mem in
866887
let ls =
867888
match Hx.find_opt genv.te_xpath xp with
868889
| Some ls -> ls
@@ -877,7 +898,7 @@ and trans_pvar ((genv, lenv) as env) pv ty mem =
877898

878899
(* -------------------------------------------------------------------- *)
879900
and trans_glob ((genv, _) as env) m mem =
880-
let wmem = trans_mem env ~forglobal:true mem in
901+
let wmem = trans_mem env mem in
881902
let w3op =
882903
match Hid.find_opt genv.te_lc m with
883904
| Some w3op -> w3op
@@ -895,29 +916,20 @@ and trans_glob ((genv, _) as env) m mem =
895916
in apply_wop genv w3op [] [wmem]
896917

897918
(* -------------------------------------------------------------------- *)
898-
and trans_mem (genv,lenv) ~forglobal mem =
899-
let wmem =
900-
match Hid.find_opt genv.te_lc mem with
901-
| Some w3op -> apply_wop genv w3op [] []
902-
| None -> WTerm.t_var (oget (Mid.find_opt mem lenv.le_lv)) in
903-
let mt = get_memtype lenv mem in
904-
let has_locals = EcMemory.has_locals mt in
905-
if forglobal then
906-
if has_locals then wsnd genv wmem
907-
else wmem
908-
else
909-
(assert has_locals; wfst genv wmem)
919+
and trans_mem (genv,lenv) mem =
920+
match Hid.find_opt genv.te_lc mem with
921+
| Some w3op -> apply_wop genv w3op [] []
922+
| None -> WTerm.t_var (oget (Mid.find_opt mem lenv.le_lv))
910923

911924
(* -------------------------------------------------------------------- *)
912925
and trans_pr ((genv,lenv) as env) {pr_mem; pr_fun; pr_args; pr_event} =
913-
let wmem = trans_mem env ~forglobal:true pr_mem in
926+
let wmem = trans_mem env pr_mem in
914927
let warg = trans_form_b env pr_args in
915928

916929
(* Translate the procedure *)
917930
let xp = NormMp.norm_xfun genv.te_env pr_fun in
918931
let _, mt = EcEnv.Fun.prF_memenv (EcIdent.create "&dummy") pr_fun genv.te_env in
919-
let mty = trans_memtype env mt in
920-
let tyr = ty_distr mty in
932+
let tyr = ty_distr ty_mem in
921933
let ls =
922934
let trans () =
923935
let tya = oget warg.WTerm.t_ty in
@@ -1197,9 +1209,8 @@ let trans_hyp ((genv, lenv) as env) (x, ty) =
11971209
env
11981210

11991211
| LD_mem mt ->
1200-
let wty = trans_memtype env mt in
12011212
let lenv = { lenv with le_mt = Mid.add x mt lenv.le_mt } in
1202-
let wcodom = Some wty in
1213+
let wcodom = Some ty_mem in
12031214
let ls = WTerm.create_lsymbol (preid x) [] wcodom in
12041215
let w3op = {
12051216
w3op_fo = `LDecl ls;

‎tests/smt-memory-locals.ec‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
(* smt on goals mentioning program variables: locals, arg, globals, Pr *)
2+
require import AllCore Distr DBool.
3+
4+
module G = { var g : int }.
5+
6+
module M = {
7+
proc f (x y : int) : int = {
8+
var z : int;
9+
z <- x + y;
10+
G.g <- z;
11+
return z + 1;
12+
}
13+
proc one (x : int) : int = { return x + 1; }
14+
proc r () : bool = { var b; b <$ {0,1}; return b; }
15+
}.
16+
17+
(* a local in a hoare post *)
18+
lemma f_hoare : hoare [M.f : 0 <= x /\ 0 <= y ==> 1 <= res /\ 0 <= G.g].
19+
proof. by proc; wp; skip => &hr; smt(). qed.
20+
21+
(* the argument variable *)
22+
lemma f_arg : hoare [M.f : arg = (1, 2) ==> res = 4].
23+
proof. by proc; wp; skip => &hr; smt(). qed.
24+
25+
lemma f_arg2 : hoare [M.f : arg.`1 = 1 /\ y = 2 ==> res = 4].
26+
proof. by proc; wp; skip => &hr; smt(). qed.
27+
28+
(* a single parameter: arg is the parameter *)
29+
lemma one_arg : hoare [M.one : arg = 1 ==> res = 2].
30+
proof. by proc; wp; skip => &hr; smt(). qed.
31+
32+
(* equiv with ={arg} and locals of both sides *)
33+
lemma f_equiv : equiv [M.f ~ M.f : ={arg} ==> ={res, G.g}].
34+
proof. by proc; wp; skip => &1 &2; smt(). qed.
35+
36+
lemma f_equiv2 : equiv [M.f ~ M.f : x{1} = y{2} /\ y{1} = x{2} ==> ={res}].
37+
proof. by proc; wp; skip => &1 &2; smt(). qed.
38+
39+
(* memories in a Pr statement *)
40+
lemma r_pr &m : Pr[M.r() @ &m : res] = 1%r / 2%r.
41+
proof. byphoare => //; proc; rnd; skip => &hr; smt(dbool1E). qed.
42+
43+
lemma r_pr2 &m1 &m2 : Pr[M.r() @ &m1 : res] = Pr[M.r() @ &m2 : res].
44+
proof. by rewrite (r_pr &m1) (r_pr &m2). qed.
45+
46+
lemma r_pr3 &m : G.g{m} = 3 => Pr[M.r() @ &m : res /\ G.g = 3] = 1%r / 2%r.
47+
proof. move=> h; byphoare (_ : G.g = 3 ==> _) => //; proc; rnd; skip => &hr; smt(dbool1E). qed.

0 commit comments

Comments
 (0)