1 (***********************************************************************)
5 (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
7 (* Copyright 1996 Institut National de Recherche en Informatique et *)
8 (* en Automatique. All rights reserved. This file is distributed *)
9 (* under the terms of the Q Public License version 1.0. *)
11 (***********************************************************************)
13 (* $Id: simplif.ml 8850 2008-03-19 10:26:56Z maranget $ *)
15 (* Elimination of useless Llet(Alias) bindings.
16 Also transform let-bound references into variables. *)
21 (* To transform let-bound references into variables *)
23 exception Real_reference
25 let rec eliminate_ref id = function
27 if Ident.same v id then raise Real_reference else lam
28 | Lconst cst as lam -> lam
29 | Lapply(e1, el, loc) ->
30 Lapply(eliminate_ref id e1, List.map (eliminate_ref id) el, loc)
31 | Lfunction(kind, params, body) as lam ->
32 if IdentSet.mem id (free_variables lam)
33 then raise Real_reference
35 | Llet(str, v, e1, e2) ->
36 Llet(str, v, eliminate_ref id e1, eliminate_ref id e2)
37 | Lletrec(idel, e2) ->
38 Lletrec(List.map (fun (v, e) -> (v, eliminate_ref id e)) idel,
40 | Lprim(Pfield 0, [Lvar v]) when Ident.same v id ->
42 | Lprim(Psetfield(0, _), [Lvar v; e]) when Ident.same v id ->
43 Lassign(id, eliminate_ref id e)
44 | Lprim(Poffsetref delta, [Lvar v]) when Ident.same v id ->
45 Lassign(id, Lprim(Poffsetint delta, [Lvar id]))
47 Lprim(p, List.map (eliminate_ref id) el)
49 Lswitch(eliminate_ref id e,
50 {sw_numconsts = sw.sw_numconsts;
52 List.map (fun (n, e) -> (n, eliminate_ref id e)) sw.sw_consts;
53 sw_numblocks = sw.sw_numblocks;
55 List.map (fun (n, e) -> (n, eliminate_ref id e)) sw.sw_blocks;
56 sw_failaction = match sw.sw_failaction with
58 | Some l -> Some (eliminate_ref id l)})
59 | Lstaticraise (i,args) ->
60 Lstaticraise (i,List.map (eliminate_ref id) args)
61 | Lstaticcatch(e1, i, e2) ->
62 Lstaticcatch(eliminate_ref id e1, i, eliminate_ref id e2)
63 | Ltrywith(e1, v, e2) ->
64 Ltrywith(eliminate_ref id e1, v, eliminate_ref id e2)
65 | Lifthenelse(e1, e2, e3) ->
66 Lifthenelse(eliminate_ref id e1,
69 | Lsequence(e1, e2) ->
70 Lsequence(eliminate_ref id e1, eliminate_ref id e2)
72 Lwhile(eliminate_ref id e1, eliminate_ref id e2)
73 | Lfor(v, e1, e2, dir, e3) ->
74 Lfor(v, eliminate_ref id e1, eliminate_ref id e2,
75 dir, eliminate_ref id e3)
77 Lassign(v, eliminate_ref id e)
78 | Lsend(k, m, o, el) ->
79 Lsend(k, eliminate_ref id m, eliminate_ref id o,
80 List.map (eliminate_ref id) el)
82 Levent(eliminate_ref id l, ev)
84 Lifused(v, eliminate_ref id e)
86 (* Simplification of exits *)
88 let simplify_exits lam =
90 (* Count occurrences of (exit n ...) statements *)
91 let exits = Hashtbl.create 17 in
95 !(Hashtbl.find exits i)
101 incr (Hashtbl.find exits i)
103 | Not_found -> Hashtbl.add exits i (ref 1) in
105 let rec count = function
106 | (Lvar _| Lconst _) -> ()
107 | Lapply(l1, ll, _) -> count l1; List.iter count ll
108 | Lfunction(kind, params, l) -> count l
109 | Llet(str, v, l1, l2) ->
111 | Lletrec(bindings, body) ->
112 List.iter (fun (v, l) -> count l) bindings;
114 | Lprim(p, ll) -> List.iter count ll
118 List.iter (fun (_, l) -> count l) sw.sw_consts;
119 List.iter (fun (_, l) -> count l) sw.sw_blocks
120 | Lstaticraise (i,ls) -> incr_exit i ; List.iter count ls
121 | Lstaticcatch (l1,(i,[]),Lstaticraise (j,[])) ->
122 (* i will be replaced by j in l1, so each occurence of i in l1
123 increases j's ref count *)
125 let ic = count_exit i in
127 let r = Hashtbl.find exits j in r := !r + ic
130 Hashtbl.add exits j (ref ic)
132 | Lstaticcatch(l1, (i,_), l2) ->
134 (* If l1 does not contain (exit i),
135 l2 will be removed, so don't count its exits *)
136 if count_exit i > 0 then
138 | Ltrywith(l1, v, l2) -> count l1; count l2
139 | Lifthenelse(l1, l2, l3) -> count l1; count l2; count l3
140 | Lsequence(l1, l2) -> count l1; count l2
141 | Lwhile(l1, l2) -> count l1; count l2
142 | Lfor(_, l1, l2, dir, l3) -> count l1; count l2; count l3
144 (* Lalias-bound variables are never assigned, so don't increase
147 | Lsend(k, m, o, ll) -> List.iter count (m::o::ll)
148 | Levent(l, _) -> count l
149 | Lifused(v, l) -> count l
151 and count_default sw = match sw.sw_failaction with
154 let nconsts = List.length sw.sw_consts
155 and nblocks = List.length sw.sw_blocks in
157 nconsts < sw.sw_numconsts && nblocks < sw.sw_numblocks
158 then begin (* default action will occur twice in native code *)
160 end else begin (* default action will occur once *)
161 assert (nconsts < sw.sw_numconsts || nblocks < sw.sw_numblocks) ;
168 Second pass simplify ``catch body with (i ...) handler''
169 - if (exit i ...) does not occur in body, suppress catch
170 - if (exit i ...) occurs exactly once in body,
171 substitute it with handler
172 - If handler is a single variable, replace (exit i ..) with it
174 In ``catch body with (i x1 .. xn) handler''
175 Substituted expression is
176 let y1 = x1 and ... yn = xn in
177 handler[x1 <- y1 ; ... ; xn <- yn]
178 For the sake of preserving the uniqueness of bound variables.
179 (No alpha conversion of ``handler'' is presently needed, since
180 substitution of several ``(exit i ...)''
181 occurs only when ``handler'' is a variable.)
184 let subst = Hashtbl.create 17 in
186 let rec simplif = function
187 | (Lvar _|Lconst _) as l -> l
188 | Lapply(l1, ll, loc) -> Lapply(simplif l1, List.map simplif ll, loc)
189 | Lfunction(kind, params, l) -> Lfunction(kind, params, simplif l)
190 | Llet(kind, v, l1, l2) -> Llet(kind, v, simplif l1, simplif l2)
191 | Lletrec(bindings, body) ->
192 Lletrec(List.map (fun (v, l) -> (v, simplif l)) bindings, simplif body)
193 | Lprim(p, ll) -> Lprim(p, List.map simplif ll)
195 let new_l = simplif l
196 and new_consts = List.map (fun (n, e) -> (n, simplif e)) sw.sw_consts
197 and new_blocks = List.map (fun (n, e) -> (n, simplif e)) sw.sw_blocks
198 and new_fail = match sw.sw_failaction with
200 | Some l -> Some (simplif l) in
203 {sw with sw_consts = new_consts ; sw_blocks = new_blocks;
204 sw_failaction = new_fail})
205 | Lstaticraise (i,[]) as l ->
207 let _,handler = Hashtbl.find subst i in
212 | Lstaticraise (i,ls) ->
213 let ls = List.map simplif ls in
215 let xs,handler = Hashtbl.find subst i in
216 let ys = List.map Ident.rename xs in
219 (fun x y t -> Ident.add x (Lvar y) t)
222 (fun y l r -> Llet (Alias, y, l, r))
223 ys ls (Lambda.subst_lambda env handler)
225 | Not_found -> Lstaticraise (i,ls)
227 | Lstaticcatch (l1,(i,[]),(Lstaticraise (j,[]) as l2)) ->
228 Hashtbl.add subst i ([],simplif l2) ;
230 | Lstaticcatch (l1,(i,xs), (Lvar _ as l2)) ->
231 begin match count_exit i with
234 Hashtbl.add subst i (xs,l2) ;
237 | Lstaticcatch (l1,(i,xs),l2) ->
238 begin match count_exit i with
241 Hashtbl.add subst i (xs,simplif l2) ;
244 Lstaticcatch (simplif l1, (i,xs), simplif l2)
246 | Ltrywith(l1, v, l2) -> Ltrywith(simplif l1, v, simplif l2)
247 | Lifthenelse(l1, l2, l3) -> Lifthenelse(simplif l1, simplif l2, simplif l3)
248 | Lsequence(l1, l2) -> Lsequence(simplif l1, simplif l2)
249 | Lwhile(l1, l2) -> Lwhile(simplif l1, simplif l2)
250 | Lfor(v, l1, l2, dir, l3) ->
251 Lfor(v, simplif l1, simplif l2, dir, simplif l3)
252 | Lassign(v, l) -> Lassign(v, simplif l)
253 | Lsend(k, m, o, ll) -> Lsend(k, simplif m, simplif o, List.map simplif ll)
254 | Levent(l, ev) -> Levent(simplif l, ev)
255 | Lifused(v, l) -> Lifused (v,simplif l)
259 (* Simplification of lets *)
261 let simplify_lets lam =
263 (* First pass: count the occurrences of all identifiers *)
264 let occ = Hashtbl.create 83 in
267 !(Hashtbl.find occ v)
272 incr(Hashtbl.find occ v)
274 Hashtbl.add occ v (ref 1) in
276 let rec count = function
277 | Lvar v -> incr_var v
279 | Lapply(l1, ll, _) -> count l1; List.iter count ll
280 | Lfunction(kind, params, l) -> count l
281 | Llet(str, v, Lvar w, l2) when not !Clflags.debug ->
282 (* v will be replaced by w in l2, so each occurrence of v in l2
283 increases w's refcount *)
285 let vc = count_var v in
287 let r = Hashtbl.find occ w in r := !r + vc
289 Hashtbl.add occ w (ref vc)
291 | Llet(str, v, l1, l2) ->
293 (* If v is unused, l1 will be removed, so don't count its variables *)
294 if str = Strict || count_var v > 0 then count l1
295 | Lletrec(bindings, body) ->
296 List.iter (fun (v, l) -> count l) bindings;
298 | Lprim(p, ll) -> List.iter count ll
302 List.iter (fun (_, l) -> count l) sw.sw_consts;
303 List.iter (fun (_, l) -> count l) sw.sw_blocks
304 | Lstaticraise (i,ls) -> List.iter count ls
305 | Lstaticcatch(l1, (i,_), l2) ->
307 | Ltrywith(l1, v, l2) -> count l1; count l2
308 | Lifthenelse(l1, l2, l3) -> count l1; count l2; count l3
309 | Lsequence(l1, l2) -> count l1; count l2
310 | Lwhile(l1, l2) -> count l1; count l2
311 | Lfor(_, l1, l2, dir, l3) -> count l1; count l2; count l3
313 (* Lalias-bound variables are never assigned, so don't increase
316 | Lsend(_, m, o, ll) -> List.iter count (m::o::ll)
317 | Levent(l, _) -> count l
319 if count_var v > 0 then count l
321 and count_default sw = match sw.sw_failaction with
324 let nconsts = List.length sw.sw_consts
325 and nblocks = List.length sw.sw_blocks in
327 nconsts < sw.sw_numconsts && nblocks < sw.sw_numblocks
328 then begin (* default action will occur twice in native code *)
330 end else begin (* default action will occur once *)
331 assert (nconsts < sw.sw_numconsts || nblocks < sw.sw_numblocks) ;
336 (* Second pass: remove Lalias bindings of unused variables,
337 and substitute the bindings of variables used exactly once. *)
339 let subst = Hashtbl.create 83 in
341 let rec simplif = function
348 | Lconst cst as l -> l
349 | Lapply(l1, ll, loc) -> Lapply(simplif l1, List.map simplif ll, loc)
350 | Lfunction(kind, params, l) -> Lfunction(kind, params, simplif l)
351 | Llet(str, v, Lvar w, l2) when not !Clflags.debug ->
352 Hashtbl.add subst v (simplif (Lvar w));
354 | Llet(Strict, v, Lprim(Pmakeblock(0, Mutable), [linit]), lbody)
355 when not !Clflags.debug ->
356 let slinit = simplif linit in
357 let slbody = simplif lbody in
359 Llet(Variable, v, slinit, eliminate_ref v slbody)
360 with Real_reference ->
361 Llet(Strict, v, Lprim(Pmakeblock(0, Mutable), [slinit]), slbody)
363 | Llet(Alias, v, l1, l2) ->
364 begin match count_var v with
366 | 1 when not !Clflags.debug ->
367 Hashtbl.add subst v (simplif l1); simplif l2
368 | n -> Llet(Alias, v, simplif l1, simplif l2)
370 | Llet(StrictOpt, v, l1, l2) ->
371 begin match count_var v with
373 | n -> Llet(Alias, v, simplif l1, simplif l2)
375 | Llet(kind, v, l1, l2) -> Llet(kind, v, simplif l1, simplif l2)
376 | Lletrec(bindings, body) ->
377 Lletrec(List.map (fun (v, l) -> (v, simplif l)) bindings, simplif body)
378 | Lprim(p, ll) -> Lprim(p, List.map simplif ll)
380 let new_l = simplif l
381 and new_consts = List.map (fun (n, e) -> (n, simplif e)) sw.sw_consts
382 and new_blocks = List.map (fun (n, e) -> (n, simplif e)) sw.sw_blocks
383 and new_fail = match sw.sw_failaction with
385 | Some l -> Some (simplif l) in
388 {sw with sw_consts = new_consts ; sw_blocks = new_blocks;
389 sw_failaction = new_fail})
390 | Lstaticraise (i,ls) ->
391 Lstaticraise (i, List.map simplif ls)
392 | Lstaticcatch(l1, (i,args), l2) ->
393 Lstaticcatch (simplif l1, (i,args), simplif l2)
394 | Ltrywith(l1, v, l2) -> Ltrywith(simplif l1, v, simplif l2)
395 | Lifthenelse(l1, l2, l3) -> Lifthenelse(simplif l1, simplif l2, simplif l3)
396 | Lsequence(Lifused(v, l1), l2) ->
398 then Lsequence(simplif l1, simplif l2)
400 | Lsequence(l1, l2) -> Lsequence(simplif l1, simplif l2)
401 | Lwhile(l1, l2) -> Lwhile(simplif l1, simplif l2)
402 | Lfor(v, l1, l2, dir, l3) ->
403 Lfor(v, simplif l1, simplif l2, dir, simplif l3)
404 | Lassign(v, l) -> Lassign(v, simplif l)
405 | Lsend(k, m, o, ll) -> Lsend(k, simplif m, simplif o, List.map simplif ll)
406 | Levent(l, ev) -> Levent(simplif l, ev)
408 if count_var v > 0 then simplif l else lambda_unit
412 let simplify_lambda lam = simplify_lets (simplify_exits lam)