My working unpac repository
1(**************************************************************************)
2(* *)
3(* OCaml *)
4(* *)
5(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
6(* *)
7(* Copyright 1996 Institut National de Recherche en Informatique et *)
8(* en Automatique. *)
9(* *)
10(* All rights reserved. This file is distributed under the terms of *)
11(* the GNU Lesser General Public License version 2.1, with the *)
12(* special exception on linking described in the file LICENSE. *)
13(* *)
14(**************************************************************************)
15
16(* Elimination of useless Llet(Alias) bindings.
17 Also transform let-bound references into variables. *)
18
19open Asttypes
20open Lambda
21open Debuginfo.Scoped_location
22
23(* To transform let-bound references into variables *)
24
25exception Real_reference
26
27let check_function_escape id lfun =
28 (* Check that the identifier is not one of the parameters *)
29 let param_is_id (param, _) = Ident.same id param in
30 assert (not (List.exists param_is_id lfun.params));
31 if Ident.Set.mem id (Lambda.free_variables lfun.body) then
32 raise Real_reference
33
34let rec eliminate_ref id = function
35 Lvar v as lam ->
36 if Ident.same v id then raise Real_reference else lam
37 | Lmutvar _ | Lconst _ as lam -> lam
38 | Lapply ap ->
39 Lapply{ap with ap_func = eliminate_ref id ap.ap_func;
40 ap_args = List.map (eliminate_ref id) ap.ap_args}
41 | Lfunction lfun as lam ->
42 check_function_escape id lfun;
43 lam
44 | Llet(str, kind, v, e1, e2) ->
45 Llet(str, kind, v, eliminate_ref id e1, eliminate_ref id e2)
46 | Lmutlet(kind, v, e1, e2) ->
47 Lmutlet(kind, v, eliminate_ref id e1, eliminate_ref id e2)
48 | Lletrec(idel, e2) ->
49 List.iter (fun rb -> check_function_escape id rb.def) idel;
50 Lletrec(idel, eliminate_ref id e2)
51 | Lprim(Pfield (0, _, _), [Lvar v], _) when Ident.same v id ->
52 Lmutvar id
53 | Lprim(Psetfield(0, _, _), [Lvar v; e], _) when Ident.same v id ->
54 Lassign(id, eliminate_ref id e)
55 | Lprim(Poffsetref delta, [Lvar v], loc) when Ident.same v id ->
56 Lassign(id, Lprim(Poffsetint delta, [Lmutvar id], loc))
57 | Lprim(p, el, loc) ->
58 Lprim(p, List.map (eliminate_ref id) el, loc)
59 | Lswitch(e, sw, loc) ->
60 Lswitch(eliminate_ref id e,
61 {sw_numconsts = sw.sw_numconsts;
62 sw_consts =
63 List.map (fun (n, e) -> (n, eliminate_ref id e)) sw.sw_consts;
64 sw_numblocks = sw.sw_numblocks;
65 sw_blocks =
66 List.map (fun (n, e) -> (n, eliminate_ref id e)) sw.sw_blocks;
67 sw_failaction =
68 Option.map (eliminate_ref id) sw.sw_failaction; },
69 loc)
70 | Lstringswitch(e, sw, default, loc) ->
71 Lstringswitch
72 (eliminate_ref id e,
73 List.map (fun (s, e) -> (s, eliminate_ref id e)) sw,
74 Option.map (eliminate_ref id) default, loc)
75 | Lstaticraise (i,args) ->
76 Lstaticraise (i,List.map (eliminate_ref id) args)
77 | Lstaticcatch(e1, i, e2) ->
78 Lstaticcatch(eliminate_ref id e1, i, eliminate_ref id e2)
79 | Ltrywith(e1, v, e2) ->
80 Ltrywith(eliminate_ref id e1, v, eliminate_ref id e2)
81 | Lifthenelse(e1, e2, e3) ->
82 Lifthenelse(eliminate_ref id e1,
83 eliminate_ref id e2,
84 eliminate_ref id e3)
85 | Lsequence(e1, e2) ->
86 Lsequence(eliminate_ref id e1, eliminate_ref id e2)
87 | Lwhile(e1, e2) ->
88 Lwhile(eliminate_ref id e1, eliminate_ref id e2)
89 | Lfor(v, e1, e2, dir, e3) ->
90 Lfor(v, eliminate_ref id e1, eliminate_ref id e2,
91 dir, eliminate_ref id e3)
92 | Lassign(v, e) ->
93 Lassign(v, eliminate_ref id e)
94 | Lsend(k, m, o, el, loc) ->
95 Lsend(k, eliminate_ref id m, eliminate_ref id o,
96 List.map (eliminate_ref id) el, loc)
97 | Levent(l, ev) ->
98 Levent(eliminate_ref id l, ev)
99 | Lifused(v, e) ->
100 Lifused(v, eliminate_ref id e)
101
102(* Simplification of exits *)
103
104type exit = {
105 mutable count: int;
106 mutable max_depth: int;
107}
108
109let simplify_exits lam =
110
111 (* Count occurrences of (exit n ...) statements *)
112 let exits = Hashtbl.create 17 in
113
114 let get_exit i =
115 try Hashtbl.find exits i
116 with Not_found -> {count = 0; max_depth = 0}
117
118 and incr_exit i nb d =
119 match Hashtbl.find_opt exits i with
120 | Some r ->
121 r.count <- r.count + nb;
122 r.max_depth <- Int.max r.max_depth d
123 | None ->
124 let r = {count = nb; max_depth = d} in
125 Hashtbl.add exits i r
126 in
127
128 let rec count ~try_depth = function
129 | (Lvar _| Lmutvar _ | Lconst _) -> ()
130 | Lapply ap ->
131 count ~try_depth ap.ap_func;
132 List.iter (count ~try_depth) ap.ap_args
133 | Lfunction {body} -> count ~try_depth body
134 | Llet(_, _kind, _v, l1, l2)
135 | Lmutlet(_kind, _v, l1, l2) ->
136 count ~try_depth l2; count ~try_depth l1
137 | Lletrec(bindings, body) ->
138 List.iter (fun { def = { body } } -> count ~try_depth body) bindings;
139 count ~try_depth body
140 | Lprim(_p, ll, _) -> List.iter (count ~try_depth) ll
141 | Lswitch(l, sw, _loc) ->
142 count_default ~try_depth sw ;
143 count ~try_depth l;
144 List.iter (fun (_, l) -> count ~try_depth l) sw.sw_consts;
145 List.iter (fun (_, l) -> count ~try_depth l) sw.sw_blocks
146 | Lstringswitch(l, sw, d, _) ->
147 count ~try_depth l;
148 List.iter (fun (_, l) -> count ~try_depth l) sw;
149 begin match d with
150 | None -> ()
151 | Some d -> match sw with
152 | []|[_] -> count ~try_depth d
153 | _ -> (* default will get replicated *)
154 count ~try_depth d; count ~try_depth d
155 end
156 | Lstaticraise (i,ls) ->
157 incr_exit i 1 try_depth;
158 List.iter (count ~try_depth) ls
159 | Lstaticcatch (l1,(i,[]),Lstaticraise (j,[])) ->
160 (* i will be replaced by j in l1, so each occurrence of i in l1
161 increases j's ref count *)
162 count ~try_depth l1 ;
163 let ic = get_exit i in
164 incr_exit j ic.count (Int.max try_depth ic.max_depth)
165 | Lstaticcatch(l1, (i,_), l2) ->
166 count ~try_depth l1;
167 (* If l1 does not contain (exit i),
168 l2 will be removed, so don't count its exits *)
169 if (get_exit i).count > 0 then
170 count ~try_depth l2
171 | Ltrywith(l1, _v, l2) ->
172 count ~try_depth:(try_depth+1) l1;
173 count ~try_depth l2;
174 | Lifthenelse(l1, l2, l3) ->
175 count ~try_depth l1;
176 count ~try_depth l2;
177 count ~try_depth l3
178 | Lsequence(l1, l2) -> count ~try_depth l1; count ~try_depth l2
179 | Lwhile(l1, l2) -> count ~try_depth l1; count ~try_depth l2
180 | Lfor(_, l1, l2, _dir, l3) ->
181 count ~try_depth l1;
182 count ~try_depth l2;
183 count ~try_depth l3
184 | Lassign(_v, l) -> count ~try_depth l
185 | Lsend(_k, m, o, ll, _) -> List.iter (count ~try_depth) (m::o::ll)
186 | Levent(l, _) -> count ~try_depth l
187 | Lifused(_v, l) -> count ~try_depth l
188
189 and count_default ~try_depth sw = match sw.sw_failaction with
190 | None -> ()
191 | Some al ->
192 let nconsts = List.length sw.sw_consts
193 and nblocks = List.length sw.sw_blocks in
194 if
195 nconsts < sw.sw_numconsts && nblocks < sw.sw_numblocks
196 then begin (* default action will occur twice in native code *)
197 count ~try_depth al ; count ~try_depth al
198 end else begin (* default action will occur once *)
199 assert (nconsts < sw.sw_numconsts || nblocks < sw.sw_numblocks) ;
200 count ~try_depth al
201 end
202 in
203 count ~try_depth:0 lam;
204
205 (*
206 Second pass simplify ``catch body with (i ...) handler''
207 - if (exit i ...) does not occur in body, suppress catch
208 - if (exit i ...) occurs exactly once in body,
209 substitute it with handler
210 - If handler is a single variable, replace (exit i ..) with it
211 Note:
212 In ``catch body with (i x1 .. xn) handler''
213 Substituted expression is
214 let y1 = x1 and ... yn = xn in
215 handler[x1 <- y1 ; ... ; xn <- yn]
216 For the sake of preserving the uniqueness of bound variables.
217 (No alpha conversion of ``handler'' is presently needed, since
218 substitution of several ``(exit i ...)''
219 occurs only when ``handler'' is a variable.)
220 *)
221
222 let subst = Hashtbl.create 17 in
223 let rec simplif ~try_depth = function
224 | (Lvar _| Lmutvar _ | Lconst _) as l -> l
225 | Lapply ap ->
226 Lapply{ap with ap_func = simplif ~try_depth ap.ap_func;
227 ap_args = List.map (simplif ~try_depth) ap.ap_args}
228 | Lfunction lfun ->
229 Lfunction (map_lfunction (simplif ~try_depth) lfun)
230 | Llet(str, kind, v, l1, l2) ->
231 Llet(str, kind, v, simplif ~try_depth l1, simplif ~try_depth l2)
232 | Lmutlet(kind, v, l1, l2) ->
233 Lmutlet(kind, v, simplif ~try_depth l1, simplif ~try_depth l2)
234 | Lletrec(bindings, body) ->
235 let bindings =
236 List.map (fun ({ def = {kind; params; return; body = l; attr; loc} }
237 as rb) ->
238 let def =
239 lfunction' ~kind ~params ~return
240 ~body:(simplif ~try_depth l) ~attr ~loc
241 in
242 { rb with def })
243 bindings
244 in
245 Lletrec(bindings, simplif ~try_depth body)
246 | Lprim(p, ll, loc) -> begin
247 let ll = List.map (simplif ~try_depth) ll in
248 match p, ll with
249 (* Simplify Obj.with_tag *)
250 | Pccall { Primitive.prim_name = "caml_obj_with_tag"; _ },
251 [Lconst (Const_int tag);
252 Lprim (Pmakeblock (_, mut, shape), fields, loc)] ->
253 Lprim (Pmakeblock(tag, mut, shape), fields, loc)
254 | Pccall { Primitive.prim_name = "caml_obj_with_tag"; _ },
255 [Lconst (Const_int tag);
256 Lconst (Const_block (_, fields))] ->
257 Lconst (Const_block (tag, fields))
258
259 | _ -> Lprim(p, ll, loc)
260 end
261 | Lswitch(l, sw, loc) ->
262 let new_l = simplif ~try_depth l
263 and new_consts =
264 List.map (fun (n, e) -> (n, simplif ~try_depth e)) sw.sw_consts
265 and new_blocks =
266 List.map (fun (n, e) -> (n, simplif ~try_depth e)) sw.sw_blocks
267 and new_fail = Option.map (simplif ~try_depth) sw.sw_failaction in
268 Lswitch
269 (new_l,
270 {sw with sw_consts = new_consts ; sw_blocks = new_blocks;
271 sw_failaction = new_fail},
272 loc)
273 | Lstringswitch(l,sw,d,loc) ->
274 Lstringswitch
275 (simplif ~try_depth l,List.map (fun (s,l) -> s,simplif ~try_depth l) sw,
276 Option.map (simplif ~try_depth) d,loc)
277 | Lstaticraise (i,[]) as l ->
278 begin try
279 let _,handler = Hashtbl.find subst i in
280 handler
281 with
282 | Not_found -> l
283 end
284 | Lstaticraise (i,ls) ->
285 let ls = List.map (simplif ~try_depth) ls in
286 begin try
287 let xs,handler = Hashtbl.find subst i in
288 let ys = List.map (fun (x, k) -> Ident.rename x, k) xs in
289 let env =
290 List.fold_right2
291 (fun (x, _) (y, _) env -> Ident.Map.add x y env)
292 xs ys Ident.Map.empty
293 in
294 (* The evaluation order for Lstaticraise arguments is currently
295 right-to-left in all backends.
296 To preserve this, we use fold_left2 instead of fold_right2
297 (the first argument is inserted deepest in the expression,
298 so will be evaluated last).
299 *)
300 List.fold_left2
301 (fun r (y, kind) l -> Llet (Strict, kind, y, l, r))
302 (Lambda.rename env handler) ys ls
303 with
304 | Not_found -> Lstaticraise (i,ls)
305 end
306 | Lstaticcatch (l1,(i,[]),(Lstaticraise (_j,[]) as l2)) ->
307 Hashtbl.add subst i ([],simplif ~try_depth l2) ;
308 simplif ~try_depth l1
309 | Lstaticcatch (l1,(i,xs),l2) ->
310 let {count; max_depth} = get_exit i in
311 if count = 0 then
312 (* Discard staticcatch: not matching exit *)
313 simplif ~try_depth l1
314 else if
315 count = 1 && max_depth <= try_depth then begin
316 (* Inline handler if there is a single occurrence and it is not
317 nested within an inner try..with *)
318 assert(max_depth = try_depth);
319 Hashtbl.add subst i (xs,simplif ~try_depth l2);
320 simplif ~try_depth l1
321 end else
322 Lstaticcatch (simplif ~try_depth l1, (i,xs), simplif ~try_depth l2)
323 | Ltrywith(l1, v, l2) ->
324 let l1 = simplif ~try_depth:(try_depth + 1) l1 in
325 Ltrywith(l1, v, simplif ~try_depth l2)
326 | Lifthenelse(l1, l2, l3) -> Lifthenelse(simplif ~try_depth l1,
327 simplif ~try_depth l2, simplif ~try_depth l3)
328 | Lsequence(l1, l2) -> Lsequence(simplif ~try_depth l1, simplif ~try_depth l2)
329 | Lwhile(l1, l2) -> Lwhile(simplif ~try_depth l1, simplif ~try_depth l2)
330 | Lfor(v, l1, l2, dir, l3) ->
331 Lfor(v, simplif ~try_depth l1, simplif ~try_depth l2, dir,
332 simplif ~try_depth l3)
333 | Lassign(v, l) -> Lassign(v, simplif ~try_depth l)
334 | Lsend(k, m, o, ll, loc) ->
335 Lsend(k, simplif ~try_depth m, simplif ~try_depth o,
336 List.map (simplif ~try_depth) ll, loc)
337 | Levent(l, ev) -> Levent(simplif ~try_depth l, ev)
338 | Lifused(v, l) -> Lifused (v,simplif ~try_depth l)
339 in
340 simplif ~try_depth:0 lam
341
342(* Compile-time beta-reduction of functions immediately applied:
343 Lapply(Lfunction(Curried, params, body), args, loc) ->
344 let paramN = argN in ... let param1 = arg1 in body
345 Lapply(Lfunction(Tupled, params, body), [Lprim(Pmakeblock(args))], loc) ->
346 let paramN = argN in ... let param1 = arg1 in body
347 Assumes |args| = |params|.
348*)
349
350let exact_application {kind; params; _} args =
351 let arity = List.length params in
352 Lambda.find_exact_application kind ~arity args
353
354let beta_reduce params body args =
355 List.fold_left2 (fun l (param, kind) arg -> Llet(Strict, kind, param, arg, l))
356 body params args
357
358(* Simplification of lets *)
359
360let simplify_lets lam =
361
362 (* Disable optimisations for bytecode compilation with -g flag *)
363 let optimize = !Clflags.native_code || not !Clflags.debug in
364
365 (* First pass: count the occurrences of all let-bound identifiers *)
366
367 let occ = (Hashtbl.create 83: (Ident.t, int ref) Hashtbl.t) in
368 (* The global table [occ] associates to each let-bound identifier
369 the number of its uses (as a reference):
370 - 0 if never used
371 - 1 if used exactly once in and not under a lambda or within a loop
372 - > 1 if used several times or under a lambda or within a loop.
373 The local table [bv] associates to each locally-let-bound variable
374 its reference count, as above. [bv] is enriched at let bindings
375 but emptied when crossing lambdas and loops. *)
376
377 (* Current use count of a variable. *)
378 let count_var v =
379 try
380 !(Hashtbl.find occ v)
381 with Not_found ->
382 0
383
384 (* Entering a [let]. Returns updated [bv]. *)
385 and bind_var bv v =
386 let r = ref 0 in
387 Hashtbl.add occ v r;
388 Ident.Map.add v r bv
389
390 (* Record a use of a variable *)
391 and use_var bv v n =
392 try
393 let r = Ident.Map.find v bv in r := !r + n
394 with Not_found ->
395 (* v is not locally bound, therefore this is a use under a lambda
396 or within a loop. Increase use count by 2 -- enough so
397 that single-use optimizations will not apply. *)
398 try
399 let r = Hashtbl.find occ v in r := !r + 2
400 with Not_found ->
401 (* Not a let-bound variable, ignore *)
402 () in
403
404 let rec count bv = function
405 | Lconst _ -> ()
406 | Lvar v ->
407 use_var bv v 1
408 | Lmutvar _ -> ()
409 | Lapply{ap_func = ll; ap_args = args} ->
410 let no_opt () = count bv ll; List.iter (count bv) args in
411 begin match ll with
412 | Lfunction lf when optimize ->
413 begin match exact_application lf args with
414 | None -> no_opt ()
415 | Some exact_args ->
416 count bv (beta_reduce lf.params lf.body exact_args)
417 end
418 | _ -> no_opt ()
419 end
420 | Lfunction fn ->
421 count_lfunction fn
422 | Llet(_str, _k, v, Lvar w, l2) when optimize ->
423 (* v will be replaced by w in l2, so each occurrence of v in l2
424 increases w's refcount *)
425 count (bind_var bv v) l2;
426 use_var bv w (count_var v)
427 | Llet(str, _kind, v, l1, l2) ->
428 count (bind_var bv v) l2;
429 (* If v is unused, l1 will be removed, so don't count its variables *)
430 if str = Strict || count_var v > 0 then count bv l1
431 | Lmutlet(_kind, _v, l1, l2) ->
432 count bv l1;
433 count bv l2
434 | Lletrec(bindings, body) ->
435 List.iter (fun { def } -> count_lfunction def) bindings;
436 count bv body
437 | Lprim(_p, ll, _) -> List.iter (count bv) ll
438 | Lswitch(l, sw, _loc) ->
439 count_default bv sw ;
440 count bv l;
441 List.iter (fun (_, l) -> count bv l) sw.sw_consts;
442 List.iter (fun (_, l) -> count bv l) sw.sw_blocks
443 | Lstringswitch(l, sw, d, _) ->
444 count bv l ;
445 List.iter (fun (_, l) -> count bv l) sw ;
446 begin match d with
447 | Some d ->
448 begin match sw with
449 | []|[_] -> count bv d
450 | _ -> count bv d ; count bv d
451 end
452 | None -> ()
453 end
454 | Lstaticraise (_i,ls) -> List.iter (count bv) ls
455 | Lstaticcatch(l1, _, l2) -> count bv l1; count bv l2
456 | Ltrywith(l1, _v, l2) -> count bv l1; count bv l2
457 | Lifthenelse(l1, l2, l3) -> count bv l1; count bv l2; count bv l3
458 | Lsequence(l1, l2) -> count bv l1; count bv l2
459 | Lwhile(l1, l2) -> count Ident.Map.empty l1; count Ident.Map.empty l2
460 | Lfor(_, l1, l2, _dir, l3) ->
461 count bv l1; count bv l2; count Ident.Map.empty l3
462 | Lassign(_v, l) ->
463 (* Lalias-bound variables are never assigned, so don't increase
464 v's refcount *)
465 count bv l
466 | Lsend(_, m, o, ll, _) -> List.iter (count bv) (m::o::ll)
467 | Levent(l, _) -> count bv l
468 | Lifused(v, l) ->
469 if count_var v > 0 then count bv l
470
471 and count_lfunction fn =
472 count Ident.Map.empty fn.body
473
474 and count_default bv sw = match sw.sw_failaction with
475 | None -> ()
476 | Some al ->
477 let nconsts = List.length sw.sw_consts
478 and nblocks = List.length sw.sw_blocks in
479 if
480 nconsts < sw.sw_numconsts && nblocks < sw.sw_numblocks
481 then begin (* default action will occur twice in native code *)
482 count bv al ; count bv al
483 end else begin (* default action will occur once *)
484 assert (nconsts < sw.sw_numconsts || nblocks < sw.sw_numblocks) ;
485 count bv al
486 end
487 in
488 count Ident.Map.empty lam;
489
490 (* Second pass: remove Lalias bindings of unused variables,
491 and substitute the bindings of variables used exactly once. *)
492
493 let subst = Hashtbl.create 83 in
494
495(* This (small) optimisation is always legal, it may uncover some
496 tail call later on. *)
497
498 let mklet str kind v e1 e2 =
499 match e2 with
500 | Lvar w when optimize && Ident.same v w -> e1
501 | _ -> Llet (str, kind,v,e1,e2)
502 in
503
504 let mkmutlet kind v e1 e2 =
505 match e2 with
506 | Lmutvar w when optimize && Ident.same v w -> e1
507 | _ -> Lmutlet (kind,v,e1,e2)
508 in
509
510 let rec simplif = function
511 Lvar v as l ->
512 begin try
513 Hashtbl.find subst v
514 with Not_found ->
515 l
516 end
517 | Lmutvar _ | Lconst _ as l -> l
518 | Lapply ({ap_func = ll; ap_args = args} as ap) ->
519 let no_opt () =
520 Lapply {ap with ap_func = simplif ap.ap_func;
521 ap_args = List.map simplif ap.ap_args} in
522 begin match ll with
523 | Lfunction lf when optimize ->
524 begin match exact_application lf args with
525 | None -> no_opt ()
526 | Some exact_args ->
527 simplif (beta_reduce lf.params lf.body exact_args)
528 end
529 | _ -> no_opt ()
530 end
531 | Lfunction{kind; params; return=return1; body = l; attr=attr1; loc}
532 ->
533 begin match simplif l with
534 Lfunction{kind=Curried; params=params'; return=return2; body;
535 attr=attr2; loc}
536 when kind = Curried && optimize &&
537 attr1.may_fuse_arity && attr2.may_fuse_arity &&
538 List.length params + List.length params' <= Lambda.max_arity() ->
539 (* The return type is the type of the value returned after
540 applying all the parameters to the function. The return
541 type of the merged function taking [params @ params'] as
542 parameters is the type returned after applying [params']. *)
543 let return = return2 in
544 lfunction ~kind ~params:(params @ params') ~return ~body ~attr:attr2
545 ~loc
546 | body ->
547 lfunction ~kind ~params ~return:return1 ~body ~attr:attr1 ~loc
548 end
549 | Llet(_str, _k, v, Lvar w, l2) when optimize ->
550 Hashtbl.add subst v (simplif (Lvar w));
551 simplif l2
552 | Llet(Strict, kind, v,
553 Lprim(Pmakeblock(0, Mutable, kind_ref) as prim, [linit], loc), lbody)
554 when optimize ->
555 let slinit = simplif linit in
556 let slbody = simplif lbody in
557 begin try
558 let kind = match kind_ref with
559 | None -> Pgenval
560 | Some [field_kind] -> field_kind
561 | Some _ -> assert false
562 in
563 mkmutlet kind v slinit (eliminate_ref v slbody)
564 with Real_reference ->
565 mklet Strict kind v (Lprim(prim, [slinit], loc)) slbody
566 end
567 | Llet(Alias, kind, v, l1, l2) ->
568 begin match count_var v with
569 0 -> simplif l2
570 | 1 when optimize -> Hashtbl.add subst v (simplif l1); simplif l2
571 | _ -> Llet(Alias, kind, v, simplif l1, simplif l2)
572 end
573 | Llet(StrictOpt, kind, v, l1, l2) ->
574 begin match count_var v with
575 0 -> simplif l2
576 | _ -> mklet StrictOpt kind v (simplif l1) (simplif l2)
577 end
578 | Llet(str, kind, v, l1, l2) -> mklet str kind v (simplif l1) (simplif l2)
579 | Lmutlet(kind, v, l1, l2) -> mkmutlet kind v (simplif l1) (simplif l2)
580 | Lletrec(bindings, body) ->
581 let bindings =
582 List.map (fun rb ->
583 { rb with def = map_lfunction simplif rb.def }
584 ) bindings
585 in
586 Lletrec(bindings, simplif body)
587 | Lprim(p, ll, loc) -> Lprim(p, List.map simplif ll, loc)
588 | Lswitch(l, sw, loc) ->
589 let new_l = simplif l
590 and new_consts = List.map (fun (n, e) -> (n, simplif e)) sw.sw_consts
591 and new_blocks = List.map (fun (n, e) -> (n, simplif e)) sw.sw_blocks
592 and new_fail = Option.map simplif sw.sw_failaction in
593 Lswitch
594 (new_l,
595 {sw with sw_consts = new_consts ; sw_blocks = new_blocks;
596 sw_failaction = new_fail},
597 loc)
598 | Lstringswitch (l,sw,d,loc) ->
599 Lstringswitch
600 (simplif l,List.map (fun (s,l) -> s,simplif l) sw,
601 Option.map simplif d,loc)
602 | Lstaticraise (i,ls) ->
603 Lstaticraise (i, List.map simplif ls)
604 | Lstaticcatch(l1, (i,args), l2) ->
605 Lstaticcatch (simplif l1, (i,args), simplif l2)
606 | Ltrywith(l1, v, l2) -> Ltrywith(simplif l1, v, simplif l2)
607 | Lifthenelse(l1, l2, l3) -> Lifthenelse(simplif l1, simplif l2, simplif l3)
608 | Lsequence(Lifused(v, l1), l2) ->
609 if count_var v > 0
610 then Lsequence(simplif l1, simplif l2)
611 else simplif l2
612 | Lsequence(l1, l2) -> Lsequence(simplif l1, simplif l2)
613 | Lwhile(l1, l2) -> Lwhile(simplif l1, simplif l2)
614 | Lfor(v, l1, l2, dir, l3) ->
615 Lfor(v, simplif l1, simplif l2, dir, simplif l3)
616 | Lassign(v, l) -> Lassign(v, simplif l)
617 | Lsend(k, m, o, ll, loc) ->
618 Lsend(k, simplif m, simplif o, List.map simplif ll, loc)
619 | Levent(l, ev) -> Levent(simplif l, ev)
620 | Lifused(v, l) ->
621 if count_var v > 0 then simplif l else lambda_unit
622 in
623 simplif lam
624
625(* Tail call info in annotation files *)
626
627let rec emit_tail_infos is_tail lambda =
628 match lambda with
629 | Lvar _ -> ()
630 | Lmutvar _ -> ()
631 | Lconst _ -> ()
632 | Lapply ap ->
633 begin
634 (* Note: is_tail does not take backend-specific logic into
635 account (maximum number of parameters, etc.) so it may
636 over-approximate tail-callness.
637
638 Trying to do something more fine-grained would result in
639 different warnings depending on whether the native or
640 bytecode compiler is used. *)
641 let maybe_warn ~is_tail ~expect_tail =
642 if is_tail <> expect_tail then
643 Location.prerr_warning (to_location ap.ap_loc)
644 (Warnings.Wrong_tailcall_expectation expect_tail) in
645 match ap.ap_tailcall with
646 | Default_tailcall -> ()
647 | Tailcall_expectation expect_tail ->
648 maybe_warn ~is_tail ~expect_tail
649 end;
650 emit_tail_infos false ap.ap_func;
651 list_emit_tail_infos false ap.ap_args
652 | Lfunction lfun ->
653 emit_tail_infos_lfunction is_tail lfun
654 | Llet (_, _k, _, lam, body)
655 | Lmutlet (_k, _, lam, body) ->
656 emit_tail_infos false lam;
657 emit_tail_infos is_tail body
658 | Lletrec (bindings, body) ->
659 List.iter (fun { def } -> emit_tail_infos_lfunction is_tail def) bindings;
660 emit_tail_infos is_tail body
661 | Lprim ((Pbytes_to_string | Pbytes_of_string), [arg], _) ->
662 emit_tail_infos is_tail arg
663 | Lprim (Psequand, [arg1; arg2], _)
664 | Lprim (Psequor, [arg1; arg2], _) ->
665 emit_tail_infos false arg1;
666 emit_tail_infos is_tail arg2
667 | Lprim (_, l, _) ->
668 list_emit_tail_infos false l
669 | Lswitch (lam, sw, _loc) ->
670 emit_tail_infos false lam;
671 list_emit_tail_infos_fun snd is_tail sw.sw_consts;
672 list_emit_tail_infos_fun snd is_tail sw.sw_blocks;
673 Option.iter (emit_tail_infos is_tail) sw.sw_failaction
674 | Lstringswitch (lam, sw, d, _) ->
675 emit_tail_infos false lam;
676 List.iter
677 (fun (_,lam) -> emit_tail_infos is_tail lam)
678 sw ;
679 Option.iter (emit_tail_infos is_tail) d
680 | Lstaticraise (_, l) ->
681 list_emit_tail_infos false l
682 | Lstaticcatch (body, _, handler) ->
683 emit_tail_infos is_tail body;
684 emit_tail_infos is_tail handler
685 | Ltrywith (body, _, handler) ->
686 emit_tail_infos false body;
687 emit_tail_infos is_tail handler
688 | Lifthenelse (cond, ifso, ifno) ->
689 emit_tail_infos false cond;
690 emit_tail_infos is_tail ifso;
691 emit_tail_infos is_tail ifno
692 | Lsequence (lam1, lam2) ->
693 emit_tail_infos false lam1;
694 emit_tail_infos is_tail lam2
695 | Lwhile (cond, body) ->
696 emit_tail_infos false cond;
697 emit_tail_infos false body
698 | Lfor (_, low, high, _, body) ->
699 emit_tail_infos false low;
700 emit_tail_infos false high;
701 emit_tail_infos false body
702 | Lassign (_, lam) ->
703 emit_tail_infos false lam
704 | Lsend (_, meth, obj, args, _loc) ->
705 emit_tail_infos false meth;
706 emit_tail_infos false obj;
707 list_emit_tail_infos false args
708 | Levent (lam, _) ->
709 emit_tail_infos is_tail lam
710 | Lifused (_, lam) ->
711 emit_tail_infos is_tail lam
712and list_emit_tail_infos_fun f is_tail =
713 List.iter (fun x -> emit_tail_infos is_tail (f x))
714and list_emit_tail_infos is_tail =
715 List.iter (emit_tail_infos is_tail)
716and emit_tail_infos_lfunction _is_tail lfun =
717 (* Tail call annotations are only meaningful with respect to the
718 current function; so entering a function resets the [is_tail] flag *)
719 emit_tail_infos true lfun.body
720
721(* Split a function with default parameters into a wrapper and an
722 inner function. The wrapper fills in missing optional parameters
723 with their default value and tail-calls the inner function. The
724 wrapper can then hopefully be inlined on most call sites to avoid
725 the overhead associated with boxing an optional argument with a
726 'Some' constructor, only to deconstruct it immediately in the
727 function's body. *)
728
729let split_default_wrapper ~id:fun_id ~kind ~params ~return ~body ~attr ~loc =
730 let rec aux map = function
731 (* When compiling [fun ?(x=expr) -> body], this is first translated
732 to:
733 [fun *opt* ->
734 let x =
735 match *opt* with
736 | None -> expr
737 | Some *sth* -> *sth*
738 in
739 body]
740 We want to detect the let binding to put it into the wrapper instead of
741 the inner function.
742 We need to find which optional parameter the binding corresponds to,
743 which is why we need a deep pattern matching on the expected result of
744 the pattern-matching compiler for options.
745 *)
746 | Llet(Strict, k, id,
747 (Lifthenelse(Lprim (Pisint, [Lvar optparam], _), _, _) as def),
748 rest) when
749 Ident.name optparam = "*opt*" && List.mem_assoc optparam params
750 && not (List.mem_assoc optparam map)
751 ->
752 let wrapper_body, inner = aux ((optparam, id) :: map) rest in
753 Llet(Strict, k, id, def, wrapper_body), inner
754 | _ when map = [] -> raise Exit
755 | body ->
756 (* Check that those *opt* identifiers don't appear in the remaining
757 body. This should not appear, but let's be on the safe side. *)
758 let fv = Lambda.free_variables body in
759 List.iter (fun (id, _) -> if Ident.Set.mem id fv then raise Exit) map;
760
761 let inner_id = Ident.create_local (Ident.name fun_id ^ "_inner") in
762 let map_param p = try List.assoc p map with Not_found -> p in
763 let args = List.map (fun (p, _) -> Lvar (map_param p)) params in
764 let wrapper_body =
765 Lapply {
766 ap_func = Lvar inner_id;
767 ap_args = args;
768 ap_loc = Loc_unknown;
769 ap_tailcall = Default_tailcall;
770 ap_inlined = Default_inline;
771 ap_specialised = Default_specialise;
772 }
773 in
774 let inner_params = List.map map_param (List.map fst params) in
775 let new_ids = List.map Ident.rename inner_params in
776 let subst =
777 List.fold_left2 (fun s id new_id ->
778 Ident.Map.add id new_id s
779 ) Ident.Map.empty inner_params new_ids
780 in
781 let body = Lambda.rename subst body in
782 let inner_fun =
783 lfunction' ~kind:Curried
784 ~params:(List.map (fun id -> id, Pgenval) new_ids)
785 ~return ~body ~attr ~loc
786 in
787 (wrapper_body, { id = inner_id;
788 def = inner_fun })
789 in
790 try
791 let body, inner = aux [] body in
792 let attr = default_stub_attribute in
793 [{ id = fun_id;
794 def = lfunction' ~kind ~params ~return ~body ~attr ~loc };
795 inner]
796 with Exit ->
797 [{ id = fun_id;
798 def = lfunction' ~kind ~params ~return ~body ~attr ~loc }]
799
800(* Simplify local let-bound functions: if all occurrences are
801 fully-applied function calls in the same "tail scope", replace the
802 function by a staticcatch handler (on that scope).
803
804 This handles as a special case functions used exactly once (in any
805 scope) for a full application.
806*)
807
808type slot =
809 {
810 func: lfunction;
811 function_scope: lambda;
812 mutable scope: lambda option;
813 }
814
815module LamTbl = Hashtbl.Make(struct
816 type t = lambda
817 let equal = (==)
818 let hash = Hashtbl.hash
819 end)
820
821let simplify_local_functions lam =
822 let slots = Hashtbl.create 16 in
823 let static_id = Hashtbl.create 16 in (* function id -> static id *)
824 let static = LamTbl.create 16 in (* scope -> static function on that scope *)
825 (* We keep track of the current "tail scope", identified
826 by the outermost lambda for which the current lambda
827 is in tail position. *)
828 let current_scope = ref lam in
829 (* PR11383: We will only apply the transformation if we don't have to move
830 code across function boundaries *)
831 let current_function_scope = ref lam in
832 let check_static lf =
833 if lf.attr.local = Always_local then
834 Location.prerr_warning (to_location lf.loc)
835 (Warnings.Inlining_impossible
836 "This function cannot be compiled into a static continuation")
837 in
838 let enabled = function
839 | {local = Always_local; _}
840 | {local = Default_local; inline = (Never_inline | Default_inline); _}
841 -> true
842 | {local = Default_local;
843 inline = (Always_inline | Unroll _ | Hint_inline); _}
844 | {local = Never_local; _}
845 -> false
846 in
847 let rec tail = function
848 | Llet (_str, _kind, id, Lfunction lf, cont) when enabled lf.attr ->
849 let r =
850 { func = lf;
851 function_scope = !current_function_scope;
852 scope = None }
853 in
854 Hashtbl.add slots id r;
855 tail cont;
856 begin match Hashtbl.find_opt slots id with
857 | Some {scope = Some scope; _} ->
858 let st = next_raise_count () in
859 let sc =
860 (* Do not move higher than current lambda *)
861 if scope == !current_scope then cont
862 else scope
863 in
864 Hashtbl.add static_id id st;
865 LamTbl.add static sc (st, lf);
866 (* The body of the function will become an handler
867 in that "scope". *)
868 with_scope ~scope lf.body
869 | _ ->
870 check_static lf;
871 (* note: if scope = None, the function is unused *)
872 function_definition lf
873 end
874 | Lapply {ap_func = Lvar id; ap_args; _} ->
875 begin match Hashtbl.find_opt slots id with
876 | Some {func; _}
877 when exact_application func ap_args = None ->
878 (* Wrong arity *)
879 Hashtbl.remove slots id
880 | Some {scope = Some scope; _} when scope != !current_scope ->
881 (* Different "tail scope" *)
882 Hashtbl.remove slots id
883 | Some {function_scope = fscope; _}
884 when fscope != !current_function_scope ->
885 (* Non local function *)
886 Hashtbl.remove slots id
887 | Some ({scope = None; _} as slot) ->
888 (* First use of the function: remember the current tail scope *)
889 slot.scope <- Some !current_scope
890 | _ ->
891 ()
892 end;
893 List.iter non_tail ap_args
894 | Lvar id ->
895 Hashtbl.remove slots id
896 | Lfunction lf ->
897 check_static lf;
898 function_definition lf
899 | lam ->
900 Lambda.shallow_iter ~tail ~non_tail lam
901 and non_tail lam =
902 with_scope ~scope:lam lam
903 and function_definition lf =
904 let old_function_scope = !current_function_scope in
905 current_function_scope := lf.body;
906 non_tail lf.body;
907 current_function_scope := old_function_scope
908 and with_scope ~scope lam =
909 let old_scope = !current_scope in
910 current_scope := scope;
911 tail lam;
912 current_scope := old_scope
913 in
914 tail lam;
915 let rec rewrite lam0 =
916 let lam =
917 match lam0 with
918 | Llet (_, _, id, _, cont) when Hashtbl.mem static_id id ->
919 rewrite cont
920 | Lapply {ap_func = Lvar id; ap_args; _} when Hashtbl.mem static_id id ->
921 let st = Hashtbl.find static_id id in
922 let slot = Hashtbl.find slots id in
923 begin match exact_application slot.func ap_args with
924 | None -> assert false
925 | Some exact_args ->
926 Lstaticraise (st, List.map rewrite exact_args)
927 end
928 | lam ->
929 Lambda.shallow_map rewrite lam
930 in
931 List.fold_right
932 (fun (st, lf) lam ->
933 Lstaticcatch (lam, (st, lf.params), rewrite lf.body)
934 )
935 (LamTbl.find_all static lam0)
936 lam
937 in
938 if LamTbl.length static = 0 then
939 lam
940 else
941 rewrite lam
942
943(* The entry point:
944 simplification
945 + rewriting of tail-modulo-cons calls
946 + emission of tailcall annotations, if needed
947*)
948
949let simplify_lambda lam =
950 let lam =
951 lam
952 |> (if !Clflags.native_code || not !Clflags.debug
953 then simplify_local_functions else Fun.id
954 )
955 |> simplify_exits
956 |> simplify_lets
957 |> Tmc.rewrite
958 in
959 if !Clflags.annotations
960 || Warnings.is_active (Warnings.Wrong_tailcall_expectation true)
961 then emit_tail_infos true lam;
962 lam