2 (** A tiny register allocator for tree expressions.
4 Authors: Martin Clochard (École Normale Supérieure)
5 Jean-Christophe Filliâtre (CNRS)
19 type memory = addr -> int
21 function eval (m: memory) (e: expr) : int =
24 | Eneg e -> - (eval m e)
25 | Eadd e1 e2 -> eval m e1 + eval m e2
33 | Iadd register register
37 type registers = register -> int
39 function update (reg: registers) (r: register) (v: int) : registers =
40 fun r' -> if r' = r then v else reg r'
52 function exec (i: instr) (s: state) : state =
54 | Iload x r -> { s with reg = update s.reg r (s.mem x) }
55 | Ineg r -> { s with reg = update s.reg r (- s.reg r) }
56 | Iadd r1 r2 -> { s with reg = update s.reg r2 (s.reg r1 + s.reg r2) }
57 | Ipush r -> { s with st = Cons (s.reg r) s.st }
58 | Ipop r -> match s.st with
59 | Nil -> s (* fails *)
60 | Cons v st -> { s with reg = update s.reg r v; st = st }
63 meta rewrite_def function exec
65 type code = list instr
67 function exec_list (c: code) (s: state) : state =
70 | Cons i l -> exec_list l (exec i s)
75 let rec lemma exec_append (c1 c2: code) (s: state) : unit
76 ensures { exec_list (c1 ++ c2) s = exec_list c2 (exec_list c1 s) }
80 | Cons i1 l1 -> exec_append l1 c2 (exec i1 s)
83 (** specification of the forthcoming compilation:
84 - value of expression e lies in register r in final state
85 - all registers smaller than are preserved
86 - memory and stack are preserved *)
87 function expr_post (e: expr) (r: register) : state -> state -> bool =
88 fun s s' -> s'.mem = s.mem /\ s'.reg r = eval s.mem e /\ s'.st = s.st /\
89 forall r'. r' < r -> s'.reg r' = s.reg r'
90 meta rewrite_def function expr_post
94 (** Double WP technique
96 If you read French, see https://hal.inria.fr/hal-01094488
98 See also this other Why3 proof, from where this technique originates:
99 http://toccata.lri.fr/gallery/double_wp.en.html
108 meta compute_max_steps 0x10000
110 predicate (-->) (x y: 'a) = [@rewrite] x = y
111 meta rewrite_def predicate (-->)
113 type post = state -> state -> bool
118 predicate hcode_ok (hc: hcode) = forall s. hc.post s (exec_list hc.hcode s)
120 type trans = (state -> bool) -> state -> bool
125 predicate wcode_ok (wc: wcode) = forall q s.
126 wc.trans q s -> q (exec_list wc.wcode s)
128 function to_wp (pst: post) : trans = fun q s1 -> forall s2. pst s1 s2 -> q s2
129 meta rewrite_def function to_wp
131 function rcompose : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c = fun f g x -> g (f x)
132 meta rewrite_def function rcompose
134 function exec_closure (i: instr) : state -> state = fun s -> exec i s
135 function id : 'a -> 'a = fun x -> x
137 let ($_) (hc: hcode) : wcode
138 requires { hcode_ok hc }
139 ensures { wcode_ok result }
140 ensures { result.trans --> to_wp hc.post }
141 = { wcode = hc.hcode; trans = to_wp hc.post }
143 let wrap (wc: wcode) (ghost pst: post) : hcode
144 requires { wcode_ok wc }
145 requires { forall x. wc.trans (pst x) x }
146 ensures { hcode_ok result }
147 ensures { result.post --> pst }
148 = { hcode = wc.wcode; post = pst }
150 let (--) (w1 w2: wcode) : wcode
151 requires { wcode_ok w1 /\ wcode_ok w2 }
152 ensures { wcode_ok result }
153 ensures { result.trans --> rcompose w2.trans w1.trans }
154 = { wcode = w1.wcode ++ w2.wcode; trans = rcompose w2.trans w1.trans }
156 let cons (i: instr) (w: wcode) : wcode
157 requires { wcode_ok w }
158 ensures { wcode_ok result }
159 ensures { result.trans --> rcompose w.trans (rcompose (exec i)) }
160 = { wcode = Cons i w.wcode;
161 trans = rcompose w.trans (rcompose (exec_closure i)) }
164 ensures { wcode_ok result }
165 ensures { result.trans --> fun q -> q }
166 = { wcode = Nil; trans = id }
170 module InfinityOfRegisters
178 (** `compile e r` returns a list of instructions that stores the value
179 of `e` in register `r`, without modifying any register `r' < r`. *)
181 let rec compile (e: expr) (r: register) : hcode
183 ensures { hcode_ok result }
184 ensures { result.post --> expr_post e r }
187 | Evar x -> cons (Iload x r) (nil ())
188 | Eneg e -> $ compile e r -- cons (Ineg r) (nil ())
190 $ compile e1 r -- $ compile e2 (r + 1) -- cons (Iadd (r+1) r) (nil ())
193 (* To recover usual specification. *)
194 let ghost recover (e: expr) (r: register) (h: hcode) : unit
195 requires { hcode_ok h /\ h.post --> expr_post e r }
196 ensures { forall s. let s' = exec_list h.hcode s in
198 s'.reg r = eval s.mem e /\
200 forall r'. r' < r -> s'.reg r' = s.reg r' }
205 module FiniteNumberOfRegisters
213 (** we have k registers, namely 0,1,...,k-1,
214 and there are at least two of them, otherwise we can't add *)
216 ensures { 2 <= result }
218 (** `compile e r` returns a list of instructions that stores the value
219 of `e` in register `r`, without modifying any register `r' < r`. *)
221 let rec compile (e: expr) (r: register) : hcode
222 requires { 0 <= r < k }
224 ensures { hcode_ok result }
225 ensures { result.post --> expr_post e r }
228 | Evar x -> cons (Iload x r) (nil ())
229 | Eneg e -> $ compile e r -- cons (Ineg r) (nil ())
232 $ compile e1 r -- $ compile e2 (r + 1) --
233 cons (Iadd (r + 1) r) (nil ())
235 cons (Ipush (k - 2)) (
236 $ compile e1 (k - 2) -- $ compile e2 (k - 1) --
237 cons (Iadd (k - 2) (k - 1)) (
238 cons (Ipop (k - 2)) (nil ())))
243 module OptimalNumberOfRegisters
252 (** we have `k` registers, namely `0,1,...,k-1`,
253 and there are at least two of them, otherwise we can't add *)
255 ensures { 2 <= result }
257 (** the minimal number of registers needed to evaluate e *)
258 let rec function n (e: expr) : int
263 | Eadd e1 e2 -> let n1 = n e1 in let n2 = n e2 in
264 if n1 = n2 then 1 + n1 else max n1 n2
267 (** Note: This is of course inefficient to recompute function `n` many
268 times. A realistic implementation would compute `n e` once for
269 each sub-expression `e`, either with a first pass of tree decoration,
270 or with function `compile` returning the value of `n e` as well,
271 in a bottom-up way *)
273 function measure (e: expr) : int =
276 | Eneg e -> 1 + measure e
277 | Eadd e1 e2 -> 1 + if n e1 >= n e2 then measure e1 + measure e2
278 else 1 + measure e1 + measure e2
281 lemma measure_nonneg: forall e. measure e >= 0
283 (** `compile e r` returns a list of instructions that stores the value
284 of `e` in register `r`, without modifying any register `r' < r`. *)
286 let rec compile (e: expr) (r: register) : hcode
287 requires { 0 <= r < k }
288 variant { measure e }
289 ensures { hcode_ok result }
290 ensures { result.post --> expr_post e r }
293 | Evar x -> cons (Iload x r) (nil ())
294 | Eneg e -> $ compile e r -- cons (Ineg r) (nil ())
296 if n e1 >= n e2 then (* we must compile e1 first *)
298 $ compile e1 r -- $ compile e2 (r + 1) --
299 cons (Iadd (r + 1) r) (nil ())
301 cons (Ipush (k - 2)) (
302 $ compile e1 (k - 2) -- $ compile e2 (k - 1) --
303 cons (Iadd (k - 2) (k - 1)) (
304 cons (Ipop (k - 2)) (nil ())))
306 $ compile (Eadd e2 e1) r (* compile e2 first *)