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
16open Misc
17open Asttypes
18
19type compile_time_constant =
20 | Big_endian
21 | Word_size
22 | Int_size
23 | Max_wosize
24 | Ostype_unix
25 | Ostype_win32
26 | Ostype_cygwin
27 | Backend_type
28 | Standard_library_default
29
30type immediate_or_pointer =
31 | Immediate
32 | Pointer
33
34type initialization_or_assignment =
35 | Assignment
36 | Heap_initialization
37 | Root_initialization
38
39type is_safe =
40 | Safe
41 | Unsafe
42
43type lazy_block_tag =
44 | Lazy_tag
45 | Forward_tag
46
47let tag_of_lazy_tag = function
48 | Lazy_tag -> Config.lazy_tag
49 | Forward_tag -> Obj.forward_tag
50
51type primitive =
52 | Pbytes_to_string
53 | Pbytes_of_string
54 | Pignore
55 (* Globals *)
56 | Pgetglobal of Ident.t
57 | Psetglobal of Ident.t
58 (* Operations on heap blocks *)
59 | Pmakeblock of int * mutable_flag * block_shape
60 | Pmakelazyblock of lazy_block_tag
61 | Pfield of int * immediate_or_pointer * mutable_flag
62 | Pfield_computed
63 | Psetfield of int * immediate_or_pointer * initialization_or_assignment
64 | Psetfield_computed of immediate_or_pointer * initialization_or_assignment
65 | Pfloatfield of int
66 | Psetfloatfield of int * initialization_or_assignment
67 | Pduprecord of Types.record_representation * int
68 (* Context switches *)
69 | Prunstack
70 | Pperform
71 | Presume
72 | Preperform
73 (* External call *)
74 | Pccall of Primitive.description
75 (* Exceptions *)
76 | Praise of raise_kind
77 (* Boolean operations *)
78 | Psequand | Psequor | Pnot
79 (* Integer operations *)
80 | Pnegint | Paddint | Psubint | Pmulint
81 | Pdivint of is_safe | Pmodint of is_safe
82 | Pandint | Porint | Pxorint
83 | Plslint | Plsrint | Pasrint
84 | Pintcomp of integer_comparison
85 | Pcompare_ints | Pcompare_floats | Pcompare_bints of boxed_integer
86 | Poffsetint of int
87 | Poffsetref of int
88 (* Float operations *)
89 | Pintoffloat | Pfloatofint
90 | Pnegfloat | Pabsfloat
91 | Paddfloat | Psubfloat | Pmulfloat | Pdivfloat
92 | Pfloatcomp of float_comparison
93 (* String operations *)
94 | Pstringlength | Pstringrefu | Pstringrefs
95 | Pbyteslength | Pbytesrefu | Pbytessetu | Pbytesrefs | Pbytessets
96 (* Array operations *)
97 | Pmakearray of array_kind * mutable_flag
98 | Pduparray of array_kind * mutable_flag
99 | Parraylength of array_kind
100 | Parrayrefu of array_kind
101 | Parraysetu of array_kind
102 | Parrayrefs of array_kind
103 | Parraysets of array_kind
104 (* Test if the argument is a block or an immediate integer *)
105 | Pisint
106 (* Test if the (integer) argument is outside an interval *)
107 | Pisout
108 (* Operations on boxed integers (Nativeint.t, Int32.t, Int64.t) *)
109 | Pbintofint of boxed_integer
110 | Pintofbint of boxed_integer
111 | Pcvtbint of boxed_integer (*source*) * boxed_integer (*destination*)
112 | Pnegbint of boxed_integer
113 | Paddbint of boxed_integer
114 | Psubbint of boxed_integer
115 | Pmulbint of boxed_integer
116 | Pdivbint of { size : boxed_integer; is_safe : is_safe }
117 | Pmodbint of { size : boxed_integer; is_safe : is_safe }
118 | Pandbint of boxed_integer
119 | Porbint of boxed_integer
120 | Pxorbint of boxed_integer
121 | Plslbint of boxed_integer
122 | Plsrbint of boxed_integer
123 | Pasrbint of boxed_integer
124 | Pbintcomp of boxed_integer * integer_comparison
125 (* Operations on Bigarrays: (unsafe, #dimensions, kind, layout) *)
126 | Pbigarrayref of bool * int * bigarray_kind * bigarray_layout
127 | Pbigarrayset of bool * int * bigarray_kind * bigarray_layout
128 (* size of the nth dimension of a Bigarray *)
129 | Pbigarraydim of int
130 (* load/set 16,32,64 bits from a string: (unsafe)*)
131 | Pstring_load_16 of bool
132 | Pstring_load_32 of bool
133 | Pstring_load_64 of bool
134 | Pbytes_load_16 of bool
135 | Pbytes_load_32 of bool
136 | Pbytes_load_64 of bool
137 | Pbytes_set_16 of bool
138 | Pbytes_set_32 of bool
139 | Pbytes_set_64 of bool
140 (* load/set 16,32,64 bits from a
141 (char, int8_unsigned_elt, c_layout) Bigarray.Array1.t : (unsafe) *)
142 | Pbigstring_load_16 of bool
143 | Pbigstring_load_32 of bool
144 | Pbigstring_load_64 of bool
145 | Pbigstring_set_16 of bool
146 | Pbigstring_set_32 of bool
147 | Pbigstring_set_64 of bool
148 (* Compile time constants *)
149 | Pctconst of compile_time_constant
150 (* byte swap *)
151 | Pbswap16
152 | Pbbswap of boxed_integer
153 (* Integer to external pointer *)
154 | Pint_as_pointer
155 (* Atomic operations *)
156 | Patomic_load
157 (* Inhibition of optimisation *)
158 | Popaque
159 (* Fetching domain-local state *)
160 | Pdls_get
161 (* Poll for runtime actions *)
162 | Ppoll
163
164and integer_comparison =
165 Ceq | Cne | Clt | Cgt | Cle | Cge
166
167and float_comparison =
168 CFeq | CFneq | CFlt | CFnlt | CFgt | CFngt | CFle | CFnle | CFge | CFnge
169
170and value_kind =
171 Pgenval | Pfloatval | Pboxedintval of boxed_integer | Pintval
172
173and block_shape =
174 value_kind list option
175
176and array_kind =
177 Pgenarray | Paddrarray | Pintarray | Pfloatarray
178
179and boxed_integer = Primitive.boxed_integer =
180 Pnativeint | Pint32 | Pint64
181
182and bigarray_kind =
183 Pbigarray_unknown
184 | Pbigarray_float16 | Pbigarray_float32 | Pbigarray_float64
185 | Pbigarray_sint8 | Pbigarray_uint8
186 | Pbigarray_sint16 | Pbigarray_uint16
187 | Pbigarray_int32 | Pbigarray_int64
188 | Pbigarray_caml_int | Pbigarray_native_int
189 | Pbigarray_complex32 | Pbigarray_complex64
190
191and bigarray_layout =
192 Pbigarray_unknown_layout
193 | Pbigarray_c_layout
194 | Pbigarray_fortran_layout
195
196and raise_kind =
197 | Raise_regular
198 | Raise_reraise
199 | Raise_notrace
200
201let equal_boxed_integer = Primitive.equal_boxed_integer
202
203let equal_primitive =
204 (* Should be implemented like [equal_value_kind] of [equal_boxed_integer],
205 i.e. by matching over the various constructors but the type has more
206 than 100 constructors... *)
207 (=)
208
209let equal_value_kind x y =
210 match x, y with
211 | Pgenval, Pgenval -> true
212 | Pfloatval, Pfloatval -> true
213 | Pboxedintval bi1, Pboxedintval bi2 -> equal_boxed_integer bi1 bi2
214 | Pintval, Pintval -> true
215 | (Pgenval | Pfloatval | Pboxedintval _ | Pintval), _ -> false
216
217
218type structured_constant =
219 Const_int of int
220 | Const_char of char
221 | Const_float of string
222 | Const_int32 of int32
223 | Const_int64 of int64
224 | Const_nativeint of nativeint
225 | Const_block of int * structured_constant list
226 | Const_float_array of string list
227 | Const_immstring of string
228
229type tailcall_attribute =
230 | Tailcall_expectation of bool
231 (* [@tailcall] and [@tailcall true] have [true],
232 [@tailcall false] has [false] *)
233 | Default_tailcall (* no [@tailcall] attribute *)
234
235type inline_attribute =
236 | Always_inline (* [@inline] or [@inline always] *)
237 | Never_inline (* [@inline never] *)
238 | Hint_inline (* [@inlined hint] attribute *)
239 | Unroll of int (* [@unroll x] *)
240 | Default_inline (* no [@inline] attribute *)
241
242let equal_inline_attribute x y =
243 match x, y with
244 | Always_inline, Always_inline
245 | Never_inline, Never_inline
246 | Hint_inline, Hint_inline
247 | Default_inline, Default_inline
248 ->
249 true
250 | Unroll u, Unroll v ->
251 u = v
252 | (Always_inline | Never_inline
253 | Hint_inline | Unroll _ | Default_inline), _ ->
254 false
255
256type specialise_attribute =
257 | Always_specialise (* [@specialise] or [@specialise always] *)
258 | Never_specialise (* [@specialise never] *)
259 | Default_specialise (* no [@specialise] attribute *)
260
261let equal_specialise_attribute x y =
262 match x, y with
263 | Always_specialise, Always_specialise
264 | Never_specialise, Never_specialise
265 | Default_specialise, Default_specialise ->
266 true
267 | (Always_specialise | Never_specialise | Default_specialise), _ ->
268 false
269
270type local_attribute =
271 | Always_local (* [@local] or [@local always] *)
272 | Never_local (* [@local never] *)
273 | Default_local (* [@local maybe] or no [@local] attribute *)
274
275type poll_attribute =
276 | Error_poll (* [@poll error] *)
277 | Default_poll (* no [@poll] attribute *)
278
279type function_kind = Curried | Tupled
280
281type let_kind = Strict | Alias | StrictOpt
282
283type meth_kind = Self | Public | Cached
284
285let equal_meth_kind x y =
286 match x, y with
287 | Self, Self -> true
288 | Public, Public -> true
289 | Cached, Cached -> true
290 | (Self | Public | Cached), _ -> false
291
292type shared_code = (int * int) list
293
294type function_attribute = {
295 inline : inline_attribute;
296 specialise : specialise_attribute;
297 local: local_attribute;
298 poll: poll_attribute;
299 is_a_functor: bool;
300 stub: bool;
301 tmc_candidate: bool;
302 may_fuse_arity: bool;
303}
304
305type scoped_location = Debuginfo.Scoped_location.t
306
307type lambda =
308 Lvar of Ident.t
309 | Lmutvar of Ident.t
310 | Lconst of structured_constant
311 | Lapply of lambda_apply
312 | Lfunction of lfunction
313 | Llet of let_kind * value_kind * Ident.t * lambda * lambda
314 | Lmutlet of value_kind * Ident.t * lambda * lambda
315 | Lletrec of rec_binding list * lambda
316 | Lprim of primitive * lambda list * scoped_location
317 | Lswitch of lambda * lambda_switch * scoped_location
318 | Lstringswitch of
319 lambda * (string * lambda) list * lambda option * scoped_location
320 | Lstaticraise of int * lambda list
321 | Lstaticcatch of lambda * (int * (Ident.t * value_kind) list) * lambda
322 | Ltrywith of lambda * Ident.t * lambda
323 | Lifthenelse of lambda * lambda * lambda
324 | Lsequence of lambda * lambda
325 | Lwhile of lambda * lambda
326 | Lfor of Ident.t * lambda * lambda * direction_flag * lambda
327 | Lassign of Ident.t * lambda
328 | Lsend of meth_kind * lambda * lambda * lambda list * scoped_location
329 | Levent of lambda * lambda_event
330 | Lifused of Ident.t * lambda
331
332and rec_binding = {
333 id : Ident.t;
334 def : lfunction;
335}
336
337and lfunction =
338 { kind: function_kind;
339 params: (Ident.t * value_kind) list;
340 return: value_kind;
341 body: lambda;
342 attr: function_attribute; (* specified with [@inline] attribute *)
343 loc: scoped_location; }
344
345and lambda_apply =
346 { ap_func : lambda;
347 ap_args : lambda list;
348 ap_loc : scoped_location;
349 ap_tailcall : tailcall_attribute;
350 ap_inlined : inline_attribute;
351 ap_specialised : specialise_attribute; }
352
353and lambda_switch =
354 { sw_numconsts: int;
355 sw_consts: (int * lambda) list;
356 sw_numblocks: int;
357 sw_blocks: (int * lambda) list;
358 sw_failaction : lambda option}
359
360and lambda_event =
361 { lev_loc: scoped_location;
362 lev_kind: lambda_event_kind;
363 lev_repr: int ref option;
364 lev_env: Env.t }
365
366and lambda_event_kind =
367 Lev_before
368 | Lev_after of Types.type_expr
369 | Lev_function
370 | Lev_pseudo
371
372type program =
373 { module_ident : Ident.t;
374 main_module_block_size : int;
375 required_globals : Ident.Set.t;
376 code : lambda }
377
378let const_int n = Const_int n
379
380let const_unit = const_int 0
381
382let dummy_constant = Lconst (const_int (0xBBBB / 2))
383
384let lambda_of_const (c : Asttypes.constant) =
385 match c with
386 | Const_int n -> Lconst (Const_int n)
387 | Const_char c -> Lconst (Const_char c)
388 | Const_float f -> Lconst (Const_float f)
389 | Const_int32 n -> Lconst (Const_int32 n)
390 | Const_int64 n -> Lconst (Const_int64 n)
391 | Const_nativeint n -> Lconst (Const_nativeint n)
392 | Const_string (s, _, _) -> Lconst (Const_immstring s)
393
394let max_arity () =
395 if !Clflags.native_code then 126 else max_int
396 (* 126 = 127 (the maximal number of parameters supported in C--)
397 - 1 (the hidden parameter containing the environment) *)
398
399let lfunction' ~kind ~params ~return ~body ~attr ~loc =
400 assert (List.length params <= max_arity ());
401 { kind; params; return; body; attr; loc }
402
403let lfunction ~kind ~params ~return ~body ~attr ~loc =
404 Lfunction (lfunction' ~kind ~params ~return ~body ~attr ~loc)
405
406let lambda_unit = Lconst const_unit
407
408let default_function_attribute = {
409 inline = Default_inline;
410 specialise = Default_specialise;
411 local = Default_local;
412 poll = Default_poll;
413 is_a_functor = false;
414 stub = false;
415 tmc_candidate = false;
416 (* Plain functions ([fun] and [function]) set [may_fuse_arity] to [false] so
417 that runtime arity matches syntactic arity in more situations.
418
419 Many things compile to functions without having a notion of syntactic arity
420 that survives typechecking, e.g. functors. Multi-arg functors are compiled
421 as nested unary functions, and rely on the arity fusion in simplif to make
422 them multi-argument. So, we keep arity fusion turned on by default for now.
423 *)
424 may_fuse_arity = true;
425}
426
427let default_stub_attribute =
428 { default_function_attribute with stub = true }
429
430(* Build sharing keys *)
431(*
432 Those keys are later compared with Stdlib.compare.
433 For that reason, they should not include cycles.
434*)
435
436let max_raw = 32
437
438let make_key e =
439 let exception Not_simple in
440 let count = ref 0 (* Used for controlling size *)
441 and make_key = Ident.make_key_generator () in
442 (* make_key is used for normalizing let-bound variables *)
443 let rec tr_rec env e =
444 incr count ;
445 if !count > max_raw then raise Not_simple ; (* Too big ! *)
446 match e with
447 | Lvar id
448 | Lmutvar id ->
449 begin
450 try Ident.find_same id env
451 with Not_found -> e
452 end
453 | Lconst _ -> e
454 | Lapply ap ->
455 Lapply {ap with ap_func = tr_rec env ap.ap_func;
456 ap_args = tr_recs env ap.ap_args;
457 ap_loc = Loc_unknown}
458 | Llet (Alias,_k,x,ex,e) -> (* Ignore aliases -> substitute *)
459 let ex = tr_rec env ex in
460 tr_rec (Ident.add x ex env) e
461 | Llet ((Strict | StrictOpt),_k,x,ex,Lvar v) when Ident.same v x ->
462 tr_rec env ex
463 | Llet (str,k,x,ex,e) ->
464 (* Because of side effects, keep other lets with normalized names *)
465 let ex = tr_rec env ex in
466 let y = make_key x in
467 Llet (str,k,y,ex,tr_rec (Ident.add x (Lvar y) env) e)
468 | Lmutlet (k,x,ex,e) ->
469 let ex = tr_rec env ex in
470 let y = make_key x in
471 Lmutlet (k,y,ex,tr_rec (Ident.add x (Lmutvar y) env) e)
472 | Lprim (p,es,_) ->
473 Lprim (p,tr_recs env es, Loc_unknown)
474 | Lswitch (e,sw,loc) ->
475 Lswitch (tr_rec env e,tr_sw env sw,loc)
476 | Lstringswitch (e,sw,d,_) ->
477 Lstringswitch
478 (tr_rec env e,
479 List.map (fun (s,e) -> s,tr_rec env e) sw,
480 tr_opt env d,
481 Loc_unknown)
482 | Lstaticraise (i,es) ->
483 Lstaticraise (i,tr_recs env es)
484 | Lstaticcatch (e1,xs,e2) ->
485 Lstaticcatch (tr_rec env e1,xs,tr_rec env e2)
486 | Ltrywith (e1,x,e2) ->
487 Ltrywith (tr_rec env e1,x,tr_rec env e2)
488 | Lifthenelse (cond,ifso,ifnot) ->
489 Lifthenelse (tr_rec env cond,tr_rec env ifso,tr_rec env ifnot)
490 | Lsequence (e1,e2) ->
491 Lsequence (tr_rec env e1,tr_rec env e2)
492 | Lassign (x,e) ->
493 Lassign (x,tr_rec env e)
494 | Lsend (m,e1,e2,es,_loc) ->
495 Lsend (m,tr_rec env e1,tr_rec env e2,tr_recs env es,Loc_unknown)
496 | Lifused (id,e) -> Lifused (id,tr_rec env e)
497 | Lletrec _|Lfunction _
498 | Lfor _ | Lwhile _
499(* Beware: (PR#6412) the event argument to Levent
500 may include cyclic structure of type Type.typexpr *)
501 | Levent _ ->
502 raise Not_simple
503
504 and tr_recs env es = List.map (tr_rec env) es
505
506 and tr_sw env sw =
507 { sw with
508 sw_consts = List.map (fun (i,e) -> i,tr_rec env e) sw.sw_consts ;
509 sw_blocks = List.map (fun (i,e) -> i,tr_rec env e) sw.sw_blocks ;
510 sw_failaction = tr_opt env sw.sw_failaction ; }
511
512 and tr_opt env = function
513 | None -> None
514 | Some e -> Some (tr_rec env e) in
515
516 try
517 Some (tr_rec Ident.empty e)
518 with Not_simple -> None
519
520(***************)
521
522let name_lambda strict arg fn =
523 match arg with
524 Lvar id -> fn id
525 | _ ->
526 let id = Ident.create_local "let" in
527 Llet(strict, Pgenval, id, arg, fn id)
528
529let name_lambda_list args fn =
530 let rec name_list names = function
531 [] -> fn (List.rev names)
532 | (Lvar _ as arg) :: rem ->
533 name_list (arg :: names) rem
534 | arg :: rem ->
535 let id = Ident.create_local "let" in
536 Llet(Strict, Pgenval, id, arg, name_list (Lvar id :: names) rem) in
537 name_list [] args
538
539
540let iter_opt f = function
541 | None -> ()
542 | Some e -> f e
543
544let shallow_iter ~tail ~non_tail:f = function
545 Lvar _
546 | Lmutvar _
547 | Lconst _ -> ()
548 | Lapply{ap_func = fn; ap_args = args} ->
549 f fn; List.iter f args
550 | Lfunction{body} ->
551 f body
552 | Llet(_, _k, _id, arg, body)
553 | Lmutlet(_k, _id, arg, body) ->
554 f arg; tail body
555 | Lletrec(decl, body) ->
556 tail body;
557 List.iter (fun { def } -> f (Lfunction def)) decl
558 | Lprim (Psequand, [l1; l2], _)
559 | Lprim (Psequor, [l1; l2], _) ->
560 f l1;
561 tail l2
562 | Lprim(_p, args, _loc) ->
563 List.iter f args
564 | Lswitch(arg, sw,_) ->
565 f arg;
566 List.iter (fun (_key, case) -> tail case) sw.sw_consts;
567 List.iter (fun (_key, case) -> tail case) sw.sw_blocks;
568 iter_opt tail sw.sw_failaction
569 | Lstringswitch (arg,cases,default,_) ->
570 f arg ;
571 List.iter (fun (_,act) -> tail act) cases ;
572 iter_opt tail default
573 | Lstaticraise (_,args) ->
574 List.iter f args
575 | Lstaticcatch(e1, _, e2) ->
576 tail e1; tail e2
577 | Ltrywith(e1, _, e2) ->
578 f e1; tail e2
579 | Lifthenelse(e1, e2, e3) ->
580 f e1; tail e2; tail e3
581 | Lsequence(e1, e2) ->
582 f e1; tail e2
583 | Lwhile(e1, e2) ->
584 f e1; f e2
585 | Lfor(_v, e1, e2, _dir, e3) ->
586 f e1; f e2; f e3
587 | Lassign(_, e) ->
588 f e
589 | Lsend (_k, met, obj, args, _) ->
590 List.iter f (met::obj::args)
591 | Levent (e, _evt) ->
592 tail e
593 | Lifused (_v, e) ->
594 tail e
595
596let iter_head_constructor f l =
597 shallow_iter ~tail:f ~non_tail:f l
598
599let is_evaluated = function
600 | Lconst _ | Lvar _ | Lfunction _ -> true
601 | _ -> false
602
603let rec free_variables = function
604 | Lvar id
605 | Lmutvar id -> Ident.Set.singleton id
606 | Lconst _ -> Ident.Set.empty
607 | Lapply{ap_func = fn; ap_args = args} ->
608 free_variables_list (free_variables fn) args
609 | Lfunction{body; params} ->
610 Ident.Set.diff (free_variables body)
611 (Ident.Set.of_list (List.map fst params))
612 | Llet(_, _k, id, arg, body)
613 | Lmutlet(_k, id, arg, body) ->
614 Ident.Set.union
615 (free_variables arg)
616 (Ident.Set.remove id (free_variables body))
617 | Lletrec(decl, body) ->
618 let set =
619 free_variables_list (free_variables body)
620 (List.map (fun { def } -> Lfunction def) decl)
621 in
622 Ident.Set.diff set
623 (Ident.Set.of_list (List.map (fun { id } -> id) decl))
624 | Lprim(_p, args, _loc) ->
625 free_variables_list Ident.Set.empty args
626 | Lswitch(arg, sw,_) ->
627 let set =
628 free_variables_list
629 (free_variables_list (free_variables arg)
630 (List.map snd sw.sw_consts))
631 (List.map snd sw.sw_blocks)
632 in
633 begin match sw.sw_failaction with
634 | None -> set
635 | Some failaction -> Ident.Set.union set (free_variables failaction)
636 end
637 | Lstringswitch (arg,cases,default,_) ->
638 let set =
639 free_variables_list (free_variables arg)
640 (List.map snd cases)
641 in
642 begin match default with
643 | None -> set
644 | Some default -> Ident.Set.union set (free_variables default)
645 end
646 | Lstaticraise (_,args) ->
647 free_variables_list Ident.Set.empty args
648 | Lstaticcatch(body, (_, params), handler) ->
649 Ident.Set.union
650 (Ident.Set.diff
651 (free_variables handler)
652 (Ident.Set.of_list (List.map fst params)))
653 (free_variables body)
654 | Ltrywith(body, param, handler) ->
655 Ident.Set.union
656 (Ident.Set.remove
657 param
658 (free_variables handler))
659 (free_variables body)
660 | Lifthenelse(e1, e2, e3) ->
661 Ident.Set.union
662 (Ident.Set.union (free_variables e1) (free_variables e2))
663 (free_variables e3)
664 | Lsequence(e1, e2) ->
665 Ident.Set.union (free_variables e1) (free_variables e2)
666 | Lwhile(e1, e2) ->
667 Ident.Set.union (free_variables e1) (free_variables e2)
668 | Lfor(v, lo, hi, _dir, body) ->
669 let set = Ident.Set.union (free_variables lo) (free_variables hi) in
670 Ident.Set.union set (Ident.Set.remove v (free_variables body))
671 | Lassign(id, e) ->
672 Ident.Set.add id (free_variables e)
673 | Lsend (_k, met, obj, args, _) ->
674 free_variables_list
675 (Ident.Set.union (free_variables met) (free_variables obj))
676 args
677 | Levent (lam, _evt) ->
678 free_variables lam
679 | Lifused (_v, e) ->
680 (* Shouldn't v be considered a free variable ? *)
681 free_variables e
682
683and free_variables_list set exprs =
684 List.fold_left (fun set expr -> Ident.Set.union (free_variables expr) set)
685 set exprs
686
687(* Check if an action has a "when" guard *)
688let raise_count = ref 0
689
690let next_raise_count () =
691 incr raise_count ;
692 !raise_count
693
694(* Anticipated staticraise, for guards *)
695let staticfail = Lstaticraise (0,[])
696
697let rec is_guarded = function
698 | Lifthenelse(_cond, _body, Lstaticraise (0,[])) -> true
699 | Llet(_str, _k, _id, _lam, body) -> is_guarded body
700 | Levent(lam, _ev) -> is_guarded lam
701 | _ -> false
702
703let rec patch_guarded patch = function
704 | Lifthenelse (cond, body, Lstaticraise (0,[])) ->
705 Lifthenelse (cond, body, patch)
706 | Llet(str, k, id, lam, body) ->
707 Llet (str, k, id, lam, patch_guarded patch body)
708 | Levent(lam, ev) ->
709 Levent (patch_guarded patch lam, ev)
710 | _ -> fatal_error "Lambda.patch_guarded"
711
712(* Translate an access path *)
713
714let rec transl_address loc = function
715 | Env.Aident id ->
716 if Ident.global id
717 then Lprim(Pgetglobal id, [], loc)
718 else Lvar id
719 | Env.Adot(addr, pos) ->
720 Lprim(Pfield(pos, Pointer, Immutable),
721 [transl_address loc addr], loc)
722
723let transl_path find loc env path =
724 match find path env with
725 | exception Not_found ->
726 fatal_error ("Cannot find address for: " ^ (Path.name path))
727 | addr -> transl_address loc addr
728
729(* Translation of identifiers *)
730
731let transl_module_path loc env path =
732 transl_path Env.find_module_address loc env path
733
734let transl_value_path loc env path =
735 transl_path Env.find_value_address loc env path
736
737let transl_extension_path loc env path =
738 transl_path Env.find_constructor_address loc env path
739
740let transl_class_path loc env path =
741 transl_path Env.find_class_address loc env path
742
743let transl_prim modname field =
744 let mod_ident = Ident.create_persistent modname in
745 let env = Env.add_persistent_structure mod_ident Env.initial in
746 match Env.open_pers_signature modname env with
747 | Error `Not_found ->
748 fatal_errorf "Module %s unavailable." modname
749 | Ok env -> (
750 match Env.find_value_by_name (Longident.Lident field) env with
751 | exception Not_found ->
752 fatal_errorf "Primitive %s.%s not found." modname field
753 | path, _ -> transl_value_path Loc_unknown env path
754 )
755
756(* Compile a sequence of expressions *)
757
758let rec make_sequence fn = function
759 [] -> lambda_unit
760 | [x] -> fn x
761 | x::rem ->
762 let lam = fn x in Lsequence(lam, make_sequence fn rem)
763
764(* Apply a substitution to a lambda-term.
765 Assumes that the image of the substitution is out of reach
766 of the bound variables of the lambda-term (no capture). *)
767
768type substitution_functions = {
769 subst_lambda : lambda -> lambda;
770 subst_lfunction : lfunction -> lfunction;
771}
772
773let build_substs update_env ?(freshen_bound_variables = false) s =
774 (* [s] contains a partial substitution for the free variables of the
775 input term.
776
777 During our traversal of the term we maintain a second environment
778 [l] with all the bound variables of the input term in the current
779 scope, mapped to either themselves or freshened versions of
780 themselves when [freshen_bound_variables] is set. *)
781 let bind id l =
782 let id' = if not freshen_bound_variables then id else Ident.rename id in
783 id', Ident.Map.add id id' l
784 in
785 let bind_many ids l =
786 List.fold_right (fun (id, rhs) (ids', l) ->
787 let id', l = bind id l in
788 ((id', rhs) :: ids' , l)
789 ) ids ([], l)
790 in
791 let bind_rec ids l =
792 List.fold_right (fun rb (ids', l) ->
793 let id', l = bind rb.id l in
794 ({ rb with id = id' } :: ids' , l)
795 ) ids ([], l)
796 in
797 let rec subst s l lam =
798 match lam with
799 | Lvar id as lam ->
800 begin match Ident.Map.find id l with
801 | id' -> Lvar id'
802 | exception Not_found ->
803 (* note: as this point we know [id] is not a bound
804 variable of the input term, otherwise it would belong
805 to [l]; it is a free variable of the input term. *)
806 begin try Ident.Map.find id s with Not_found -> lam end
807 end
808 | Lmutvar id as lam ->
809 begin match Ident.Map.find id l with
810 | id' -> Lmutvar id'
811 | exception Not_found ->
812 (* Note: a mutable [id] should not appear in [s].
813 Keeping the behavior of Lvar case for now. *)
814 begin try Ident.Map.find id s with Not_found -> lam end
815 end
816 | Lconst _ as l -> l
817 | Lapply ap ->
818 Lapply{ap with ap_func = subst s l ap.ap_func;
819 ap_args = subst_list s l ap.ap_args}
820 | Lfunction lf ->
821 Lfunction (subst_lfun s l lf)
822 | Llet(str, k, id, arg, body) ->
823 let id, l' = bind id l in
824 Llet(str, k, id, subst s l arg, subst s l' body)
825 | Lmutlet(k, id, arg, body) ->
826 let id, l' = bind id l in
827 Lmutlet(k, id, subst s l arg, subst s l' body)
828 | Lletrec(decl, body) ->
829 let decl, l' = bind_rec decl l in
830 Lletrec(List.map (subst_decl s l') decl, subst s l' body)
831 | Lprim(p, args, loc) -> Lprim(p, subst_list s l args, loc)
832 | Lswitch(arg, sw, loc) ->
833 Lswitch(subst s l arg,
834 {sw with sw_consts = List.map (subst_case s l) sw.sw_consts;
835 sw_blocks = List.map (subst_case s l) sw.sw_blocks;
836 sw_failaction = subst_opt s l sw.sw_failaction; },
837 loc)
838 | Lstringswitch (arg,cases,default,loc) ->
839 Lstringswitch
840 (subst s l arg,
841 List.map (subst_strcase s l) cases,
842 subst_opt s l default,
843 loc)
844 | Lstaticraise (i,args) -> Lstaticraise (i, subst_list s l args)
845 | Lstaticcatch(body, (id, params), handler) ->
846 let params, l' = bind_many params l in
847 Lstaticcatch(subst s l body, (id, params),
848 subst s l' handler)
849 | Ltrywith(body, exn, handler) ->
850 let exn, l' = bind exn l in
851 Ltrywith(subst s l body, exn, subst s l' handler)
852 | Lifthenelse(e1, e2, e3) ->
853 Lifthenelse(subst s l e1, subst s l e2, subst s l e3)
854 | Lsequence(e1, e2) -> Lsequence(subst s l e1, subst s l e2)
855 | Lwhile(e1, e2) -> Lwhile(subst s l e1, subst s l e2)
856 | Lfor(v, lo, hi, dir, body) ->
857 let v, l' = bind v l in
858 Lfor(v, subst s l lo, subst s l hi, dir, subst s l' body)
859 | Lassign(id, e) ->
860 assert (not (Ident.Map.mem id s));
861 let id = try Ident.Map.find id l with Not_found -> id in
862 Lassign(id, subst s l e)
863 | Lsend (k, met, obj, args, loc) ->
864 Lsend (k, subst s l met, subst s l obj, subst_list s l args, loc)
865 | Levent (lam, evt) ->
866 let old_env = evt.lev_env in
867 let env_updates =
868 let find_in_old id = Env.find_value (Path.Pident id) old_env in
869 let rebind id id' new_env =
870 match find_in_old id with
871 | exception Not_found -> new_env
872 | vd -> Env.add_value id' vd new_env
873 in
874 let update_free id new_env =
875 match find_in_old id with
876 | exception Not_found -> new_env
877 | vd -> update_env id vd new_env
878 in
879 Ident.Map.merge (fun id bound free ->
880 match bound, free with
881 | Some id', _ ->
882 if Ident.equal id id' then None else Some (rebind id id')
883 | None, Some _ -> Some (update_free id)
884 | None, None -> None
885 ) l s
886 in
887 let new_env =
888 Ident.Map.fold (fun _id update env -> update env) env_updates old_env
889 in
890 Levent (subst s l lam, { evt with lev_env = new_env })
891 | Lifused (id, e) ->
892 let id = try Ident.Map.find id l with Not_found -> id in
893 Lifused (id, subst s l e)
894 and subst_list s l li = List.map (subst s l) li
895 and subst_decl s l decl = { decl with def = subst_lfun s l decl.def }
896 and subst_lfun s l lf =
897 let params, l' = bind_many lf.params l in
898 { lf with params; body = subst s l' lf.body }
899 and subst_case s l (key, case) = (key, subst s l case)
900 and subst_strcase s l (key, case) = (key, subst s l case)
901 and subst_opt s l = function
902 | None -> None
903 | Some e -> Some (subst s l e)
904 in
905 { subst_lambda = (fun lam -> subst s Ident.Map.empty lam);
906 subst_lfunction = (fun lfun -> subst_lfun s Ident.Map.empty lfun);
907 }
908
909let subst update_env ?freshen_bound_variables s =
910 (build_substs update_env ?freshen_bound_variables s).subst_lambda
911
912let rename idmap lam =
913 let update_env oldid vd env =
914 let newid = Ident.Map.find oldid idmap in
915 Env.add_value newid vd env
916 in
917 let s = Ident.Map.map (fun new_id -> Lvar new_id) idmap in
918 subst update_env s lam
919
920let duplicate_function =
921 (build_substs
922 (fun _ _ env -> env)
923 ~freshen_bound_variables:true
924 Ident.Map.empty).subst_lfunction
925
926let map_lfunction f { kind; params; return; body; attr; loc } =
927 let body = f body in
928 { kind; params; return; body; attr; loc }
929
930let shallow_map f = function
931 | Lvar _
932 | Lmutvar _
933 | Lconst _ as lam -> lam
934 | Lapply { ap_func; ap_args; ap_loc; ap_tailcall;
935 ap_inlined; ap_specialised } ->
936 Lapply {
937 ap_func = f ap_func;
938 ap_args = List.map f ap_args;
939 ap_loc;
940 ap_tailcall;
941 ap_inlined;
942 ap_specialised;
943 }
944 | Lfunction lfun ->
945 Lfunction (map_lfunction f lfun)
946 | Llet (str, k, v, e1, e2) ->
947 Llet (str, k, v, f e1, f e2)
948 | Lmutlet (k, v, e1, e2) ->
949 Lmutlet (k, v, f e1, f e2)
950 | Lletrec (idel, e2) ->
951 Lletrec
952 (List.map (fun rb ->
953 { rb with def = map_lfunction f rb.def })
954 idel,
955 f e2)
956 | Lprim (p, el, loc) ->
957 Lprim (p, List.map f el, loc)
958 | Lswitch (e, sw, loc) ->
959 Lswitch (f e,
960 { sw_numconsts = sw.sw_numconsts;
961 sw_consts = List.map (fun (n, e) -> (n, f e)) sw.sw_consts;
962 sw_numblocks = sw.sw_numblocks;
963 sw_blocks = List.map (fun (n, e) -> (n, f e)) sw.sw_blocks;
964 sw_failaction = Option.map f sw.sw_failaction;
965 },
966 loc)
967 | Lstringswitch (e, sw, default, loc) ->
968 Lstringswitch (
969 f e,
970 List.map (fun (s, e) -> (s, f e)) sw,
971 Option.map f default,
972 loc)
973 | Lstaticraise (i, args) ->
974 Lstaticraise (i, List.map f args)
975 | Lstaticcatch (body, id, handler) ->
976 Lstaticcatch (f body, id, f handler)
977 | Ltrywith (e1, v, e2) ->
978 Ltrywith (f e1, v, f e2)
979 | Lifthenelse (e1, e2, e3) ->
980 Lifthenelse (f e1, f e2, f e3)
981 | Lsequence (e1, e2) ->
982 Lsequence (f e1, f e2)
983 | Lwhile (e1, e2) ->
984 Lwhile (f e1, f e2)
985 | Lfor (v, e1, e2, dir, e3) ->
986 Lfor (v, f e1, f e2, dir, f e3)
987 | Lassign (v, e) ->
988 Lassign (v, f e)
989 | Lsend (k, m, o, el, loc) ->
990 Lsend (k, f m, f o, List.map f el, loc)
991 | Levent (l, ev) ->
992 Levent (f l, ev)
993 | Lifused (v, e) ->
994 Lifused (v, f e)
995
996let map f =
997 let rec g lam = f (shallow_map g lam) in
998 g
999
1000(* To let-bind expressions to variables *)
1001
1002let bind_with_value_kind str (var, kind) exp body =
1003 match exp with
1004 Lvar var' when Ident.same var var' -> body
1005 | _ -> Llet(str, kind, var, exp, body)
1006
1007let bind str var exp body =
1008 bind_with_value_kind str (var, Pgenval) exp body
1009
1010let negate_integer_comparison = function
1011 | Ceq -> Cne
1012 | Cne -> Ceq
1013 | Clt -> Cge
1014 | Cle -> Cgt
1015 | Cgt -> Cle
1016 | Cge -> Clt
1017
1018let swap_integer_comparison = function
1019 | Ceq -> Ceq
1020 | Cne -> Cne
1021 | Clt -> Cgt
1022 | Cle -> Cge
1023 | Cgt -> Clt
1024 | Cge -> Cle
1025
1026let negate_float_comparison = function
1027 | CFeq -> CFneq
1028 | CFneq -> CFeq
1029 | CFlt -> CFnlt
1030 | CFnlt -> CFlt
1031 | CFgt -> CFngt
1032 | CFngt -> CFgt
1033 | CFle -> CFnle
1034 | CFnle -> CFle
1035 | CFge -> CFnge
1036 | CFnge -> CFge
1037
1038let swap_float_comparison = function
1039 | CFeq -> CFeq
1040 | CFneq -> CFneq
1041 | CFlt -> CFgt
1042 | CFnlt -> CFngt
1043 | CFle -> CFge
1044 | CFnle -> CFnge
1045 | CFgt -> CFlt
1046 | CFngt -> CFnlt
1047 | CFge -> CFle
1048 | CFnge -> CFnle
1049
1050let raise_kind = function
1051 | Raise_regular -> "raise"
1052 | Raise_reraise -> "reraise"
1053 | Raise_notrace -> "raise_notrace"
1054
1055let merge_inline_attributes attr1 attr2 =
1056 match attr1, attr2 with
1057 | Default_inline, _ -> Some attr2
1058 | _, Default_inline -> Some attr1
1059 | _, _ ->
1060 if attr1 = attr2 then Some attr1
1061 else None
1062
1063let function_is_curried func =
1064 match func.kind with
1065 | Curried -> true
1066 | Tupled -> false
1067
1068let find_exact_application kind ~arity args =
1069 match kind with
1070 | Curried ->
1071 if arity <> List.length args
1072 then None
1073 else Some args
1074 | Tupled ->
1075 begin match args with
1076 | [Lprim(Pmakeblock _, tupled_args, _)] ->
1077 if arity <> List.length tupled_args
1078 then None
1079 else Some tupled_args
1080 | [Lconst(Const_block (_, const_args))] ->
1081 if arity <> List.length const_args
1082 then None
1083 else Some (List.map (fun cst -> Lconst cst) const_args)
1084 | _ -> None
1085 end
1086
1087let reset () =
1088 raise_count := 0