Skip to content

Commit 6beaca4

Browse files
Gustavo2622strub
andcommitted
Add support for cfold of symbolic assignments
Co-authored-by: Pierre-Yves Strub <pierre-yves.strub@pqshield.com>
1 parent bdb93fb commit 6beaca4

5 files changed

Lines changed: 161 additions & 17 deletions

File tree

doc/tactics/cfold.rst

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
2+
========================================================================
3+
Tactic: ``cfold``
4+
========================================================================
5+
6+
The ``cfold`` tactic is part of the family of tactics that operate by
7+
rewriting the program into a semantically equivalent form.
8+
More concretely, given an assignment of the form::
9+
10+
a <- b;
11+
12+
the tactic propagates this (known) values of a through the program
13+
and inlining this (known) value whenever ``a`` would be used.
14+
15+
For example, the following excerpt::
16+
17+
a <- 3;
18+
c <- a + 1;
19+
a <- a + 2;
20+
b <- a;
21+
22+
would be converted into::
23+
24+
c <- 4; (* 3 + 1 *)
25+
b <- 5; (* a = 3 + 2 = 5 at this point *)
26+
a <- 5; (* we need to make sure a has the
27+
correct value at the end of execution *)
28+
29+
.. contents::
30+
:local:
31+
32+
------------------------------------------------------------------------
33+
Syntax
34+
------------------------------------------------------------------------
35+
36+
The general form of the tactic is:
37+
38+
.. admonition:: Syntax
39+
40+
``cfold {side}? {codepos} {n}?``
41+
42+
Where:
43+
44+
- ``{side}`` is optional and only applicable in relational goals
45+
to specify on which of the two programs the tactic is to be applied.
46+
47+
- ``{codepos}`` specifies the instruction on which the tactic will begin.
48+
The tactic will proceed to propagate the assignment as far as possible,
49+
even through other assignments to the same variable as long as it can or
50+
until it reaches the line limit (if given).
51+
52+
- ``{n}`` is an optional integer argument corresponding to the number of
53+
lines of the program to process in the folding. This serves to limit the
54+
scope of the tactic application and prevent it from acting in the whole
55+
program when this would not be desirable.
56+
57+
.. ecproof::
58+
:title: Cfold example
59+
require import AllCore.
60+
61+
module M = {
62+
proc f(a : int) = {
63+
var x, y : int;
64+
65+
x <- a + 1;
66+
y <- 2 * x;
67+
x <- 3 * y;
68+
69+
return x;
70+
}
71+
}.
72+
73+
lemma L : hoare[M.f : witness a ==> witness res].
74+
proof.
75+
proc.
76+
(*$*) cfold 1.
77+
78+
(* The goal is the same but the program is now rewritten *)
79+
admit.
80+
qed.
81+
82+
83+
84+
In this example the second assignment is not inlined since the tactic only
85+
propagates assignments of x, being the variable in the instruction on
86+
which the tactic was called.
87+
88+
And here is an example of using the parameter ``{n}`` for limiting tactic
89+
application:
90+
91+
.. ecproof::
92+
:title: Cfold line restriction
93+
require import AllCore.
94+
95+
module M = {
96+
proc f() = {
97+
var x, y : int;
98+
var i : int;
99+
100+
i <- 0;
101+
102+
while (i < 10) {
103+
x <- i + 1;
104+
y <- 2 * x;
105+
x <- 3 * y;
106+
i <- i + 1;
107+
}
108+
109+
return x;
110+
}
111+
}.
112+
113+
lemma L : hoare[M.f : witness ==> witness res].
114+
proof.
115+
proc.
116+
117+
unroll for 2.
118+
cfold 1 27.
119+
(* The goal is the same but the program is now rewritten *)
120+
admit.
121+
qed.

src/ecPV.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ module Mpv = struct
111111
check_glob env mp m;
112112
raise Not_found
113113

114+
let pvs { s_pv } = s_pv
115+
114116
type esubst = (expr, unit) t
115117

116118
let rec esubst env (s : esubst) e =

src/ecPV.mli

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@ module Mpv : sig
5353

5454
val find_glob : env -> mpath -> ('a,'b) t -> 'b
5555

56-
val esubst : env -> (expr, unit) t -> expr -> expr
56+
val pvs : ('a,'b) t -> 'a Mnpv.t
57+
58+
val esubst : env -> (expr, unit) t -> expr -> expr
5759
val issubst : env -> (expr, unit) t -> instr list -> instr list
58-
val isubst : env -> (expr, unit) t -> instr -> instr
59-
val ssubst : env -> (expr, unit) t -> stmt -> stmt
60+
val isubst : env -> (expr, unit) t -> instr -> instr
61+
val ssubst : env -> (expr, unit) t -> stmt -> stmt
6062
end
6163

6264
(* -------------------------------------------------------------------- *)

src/phl/ecPhlCodeTx.ml

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -196,19 +196,23 @@ let cfold_stmt ?(simplify = true) (pf, hyps) (me : memenv) (olen : int option) (
196196
e
197197
) else identity in
198198

199-
let is_const_expression (e : expr) =
200-
PV.is_empty (e_read env e) in
201-
202199
let for_instruction ((subst as subst0) : (expr, unit) Mpv.t) (i : instr) =
203200
let wr = EcPV.i_write env i in
204201
let i = Mpv.isubst env subst i in
205202

206203
let (subst, asgn) =
207-
List.fold_left_map (fun subst ((pv, _) as pvty) ->
208-
match Mpv.find env pv subst with
209-
| e -> Mpv.remove env pv subst, Some (pvty, e)
210-
| exception Not_found -> subst, None
211-
) subst (fst (PV.elements wr)) in
204+
List.fold_left_map (fun subst (pv, e) ->
205+
let exception Remove in
206+
207+
try
208+
if PV.mem_pv env pv wr then raise Remove;
209+
let rd = EcPV.e_read env e in
210+
if PV.mem_pv env pv rd then raise Remove;
211+
subst, None
212+
213+
with Remove ->
214+
Mpv.remove env pv subst, Some ((pv, e.e_ty), e)
215+
) subst (EcPV.Mnpv.bindings (Mpv.pvs subst)) in
212216

213217
let asgn = List.filter_map identity asgn in
214218

@@ -227,7 +231,7 @@ let cfold_stmt ?(simplify = true) (pf, hyps) (me : memenv) (olen : int option) (
227231
try
228232
match i.i_node with
229233
| Sasgn (lv, e) ->
230-
(* We already removed the variables of `lv` from the substitution *)
234+
(* We already removed the variables of `lv` & the rhs from the substitution *)
231235
(* We are only interested in the variables of `lv` that are in `wr` *)
232236
let es =
233237
match simplify e, lv with
@@ -237,8 +241,8 @@ let cfold_stmt ?(simplify = true) (pf, hyps) (me : memenv) (olen : int option) (
237241

238242
let lv = lv_to_ty_list lv in
239243

240-
let tosubst, asgn2 = List.partition (fun ((pv, _), e) ->
241-
Mpv.mem env pv subst0 && is_const_expression e
244+
let tosubst, asgn2 = List.partition (fun ((pv, _), _) ->
245+
Mpv.mem env pv subst0
242246
) (List.combine lv es) in
243247

244248
let subst =
@@ -292,9 +296,6 @@ let cfold_stmt ?(simplify = true) (pf, hyps) (me : memenv) (olen : int option) (
292296
| e, _ -> [e] in
293297
let lv = lv_to_ty_list lv in
294298

295-
if not (List.for_all is_const_expression es) then
296-
tc_error pf "right-values are not closed expressions";
297-
298299
if not (List.for_all (is_loc -| fst) lv) then
299300
tc_error pf "left-values must be made of local variables only";
300301

tests/cfold.ec

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,21 @@ theory CfoldWhileUnroll.
111111
by auto => />.
112112
qed.
113113
end CfoldWhileUnroll.
114+
115+
module CfoldSymbolic = {
116+
proc f(a : int) = {
117+
var x, y : int;
118+
119+
x <- a + 1;
120+
y <- 2 * x;
121+
x <- 3 * y;
122+
123+
return x;
124+
}
125+
}.
126+
127+
lemma L : hoare[CfoldSymbolic.f : witness a ==> witness res].
128+
proof.
129+
proc.
130+
cfold 1.
131+
abort.

0 commit comments

Comments
 (0)