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(*  bytegen.ml : translation of lambda terms to lists of instructions. *)
17
18open Misc
19open Asttypes
20open Primitive
21open Types
22open Lambda
23open Switch
24open Instruct
25
26(**** Label generation ****)
27
28let label_counter = ref 0
29
30let new_label () =
31  incr label_counter; !label_counter
32
33(**** Operations on compilation environments. ****)
34
35let empty_env =
36  { ce_stack = Ident.empty; ce_heap = Ident.empty; ce_rec = Ident.empty }
37
38(* Add a stack-allocated variable *)
39
40let add_var id pos env =
41  { ce_stack = Ident.add id pos env.ce_stack;
42    ce_heap = env.ce_heap;
43    ce_rec = env.ce_rec }
44
45let rec add_vars idlist pos env =
46  match idlist with
47    [] -> env
48  | id :: rem -> add_vars rem (pos + 1) (add_var id pos env)
49
50(**** Examination of the continuation ****)
51
52(* Return a label to the beginning of the given continuation.
53   If the sequence starts with a branch, use the target of that branch
54   as the label, thus avoiding a jump to a jump. *)
55
56let label_code = function
57    Kbranch lbl :: _ as cont -> (lbl, cont)
58  | Klabel lbl :: _ as cont -> (lbl, cont)
59  | cont -> let lbl = new_label() in (lbl, Klabel lbl :: cont)
60
61(* Return a branch to the continuation. That is, an instruction that,
62   when executed, branches to the continuation or performs what the
63   continuation performs. We avoid generating branches to branches and
64   branches to returns. *)
65
66let rec make_branch_2 lbl n cont =
67  function
68    Kreturn m :: _ -> (Kreturn (n + m), cont)
69  | Klabel _ :: c  -> make_branch_2 lbl n cont c
70  | Kpop m :: c    -> make_branch_2 lbl (n + m) cont c
71  | _              ->
72      match lbl with
73        Some lbl -> (Kbranch lbl, cont)
74      | None     -> let lbl = new_label() in (Kbranch lbl, Klabel lbl :: cont)
75
76let make_branch cont =
77  match cont with
78    (Kbranch _ as branch) :: _ -> (branch, cont)
79  | (Kreturn _ as return) :: _ -> (return, cont)
80  | Kraise k :: _ -> (Kraise k, cont)
81  | Klabel lbl :: _ -> make_branch_2 (Some lbl) 0 cont cont
82  | _ ->  make_branch_2 (None) 0 cont cont
83
84(* Avoid a branch to a label that follows immediately *)
85
86let branch_to label cont = match cont with
87| Klabel label0::_ when label = label0 -> cont
88| _ -> Kbranch label::cont
89
90(* Discard all instructions up to the next label.
91   This function is to be applied to the continuation before adding a
92   non-terminating instruction (branch, raise, return) in front of it. *)
93
94let rec discard_dead_code = function
95    [] -> []
96  | (Klabel _ | Krestart | Ksetglobal _) :: _ as cont -> cont
97  | _ :: cont -> discard_dead_code cont
98
99(* Check if we're in tailcall position *)
100
101let rec is_tailcall = function
102    Kreturn _ :: _ -> true
103  | Klabel _ :: c -> is_tailcall c
104  | Kpop _ :: c -> is_tailcall c
105  | _ -> false
106
107(* Add a Kpop N instruction in front of a continuation *)
108
109let rec add_pop n cont =
110  if n = 0 then cont else
111    match cont with
112      Kpop m :: cont -> add_pop (n + m) cont
113    | Kreturn m :: cont -> Kreturn(n + m) :: cont
114    | Kraise _ :: _ -> cont
115    | _ -> Kpop n :: cont
116
117(* Add the constant "unit" in front of a continuation *)
118
119let add_const_unit = function
120    (Kacc _ | Kconst _ | Kgetglobal _ | Kpush_retaddr _) :: _ as cont -> cont
121  | cont -> Kconst const_unit :: cont
122
123let rec push_dummies n k = match n with
124| 0 -> k
125| _ -> Kconst const_unit::Kpush::push_dummies (n-1) k
126
127
128(**** Auxiliary for compiling "let rec" ****)
129
130type rhs_kind =
131  | RHS_block of int
132  | RHS_floatblock of int
133  | RHS_nonrec
134  | RHS_function of int * int
135;;
136
137let rec check_recordwith_updates id e =
138  match e with
139  | Lsequence (Lprim ((Psetfield _ | Psetfloatfield _), [Lvar id2; _], _), cont)
140      -> id2 = id && check_recordwith_updates id cont
141  | Lvar id2 -> id2 = id
142  | _ -> false
143;;
144
145let rec size_of_lambda = function
146  | Lfunction{params} as funct ->
147      RHS_function (1 + IdentSet.cardinal(free_variables funct),
148                    List.length params)
149  | Llet (Strict, _k, id, Lprim (Pduprecord (kind, size), _, _), body)
150    when check_recordwith_updates id body ->
151      begin match kind with
152      | Record_regular | Record_inlined _ -> RHS_block size
153      | Record_unboxed _ -> assert false
154      | Record_float -> RHS_floatblock size
155      | Record_extension -> RHS_block (size + 1)
156      end
157  | Llet(_str, _k, _id, _arg, body) -> size_of_lambda body
158  | Lletrec(_bindings, body) -> size_of_lambda body
159  | Lprim(Pmakeblock _, args, _) -> RHS_block (List.length args)
160  | Lprim (Pmakearray ((Paddrarray|Pintarray), _), args, _) ->
161      RHS_block (List.length args)
162  | Lprim (Pmakearray (Pfloatarray, _), args, _) ->
163      RHS_floatblock (List.length args)
164  | Lprim (Pmakearray (Pgenarray, _), _, _) -> assert false
165  | Lprim (Pduprecord ((Record_regular | Record_inlined _), size), _, _) ->
166      RHS_block size
167  | Lprim (Pduprecord (Record_unboxed _, _), _, _) ->
168      assert false
169  | Lprim (Pduprecord (Record_extension, size), _, _) ->
170      RHS_block (size + 1)
171  | Lprim (Pduprecord (Record_float, size), _, _) -> RHS_floatblock size
172  | Levent (lam, _) -> size_of_lambda lam
173  | Lsequence (_lam, lam') -> size_of_lambda lam'
174  | _ -> RHS_nonrec
175
176(**** Merging consecutive events ****)
177
178let copy_event ev kind info repr =
179  { ev_pos = 0;                   (* patched in emitcode *)
180    ev_module = ev.ev_module;
181    ev_loc = ev.ev_loc;
182    ev_kind = kind;
183    ev_info = info;
184    ev_typenv = ev.ev_typenv;
185    ev_typsubst = ev.ev_typsubst;
186    ev_compenv = ev.ev_compenv;
187    ev_stacksize = ev.ev_stacksize;
188    ev_repr = repr }
189
190let merge_infos ev ev' =
191  match ev.ev_info, ev'.ev_info with
192    Event_other, info -> info
193  | info, Event_other -> info
194  | _                 -> fatal_error "Bytegen.merge_infos"
195
196let merge_repr ev ev' =
197  match ev.ev_repr, ev'.ev_repr with
198    Event_none, x -> x
199  | x, Event_none -> x
200  | Event_parent r, Event_child r' when r == r' && !r = 1 -> Event_none
201  | Event_child r, Event_parent r' when r == r' -> Event_parent r
202  | _, _          -> fatal_error "Bytegen.merge_repr"
203
204let merge_events ev ev' =
205  let (maj, min) =
206    match ev.ev_kind, ev'.ev_kind with
207    (* Discard pseudo-events *)
208      Event_pseudo,  _                              -> ev', ev
209    | _,             Event_pseudo                   -> ev,  ev'
210    (* Keep following event, supposedly more informative *)
211    | Event_before,  (Event_after _ | Event_before) -> ev',  ev
212    (* Discard following events, supposedly less informative *)
213    | Event_after _, (Event_after _ | Event_before) -> ev, ev'
214  in
215  copy_event maj maj.ev_kind (merge_infos maj min) (merge_repr maj min)
216
217let weaken_event ev cont =
218  match ev.ev_kind with
219    Event_after _ ->
220      begin match cont with
221        Kpush :: Kevent ({ev_repr = Event_none} as ev') :: c ->
222          begin match ev.ev_info with
223            Event_return _ ->
224              (* Weaken event *)
225              let repr = ref 1 in
226              let ev =
227                copy_event ev Event_pseudo ev.ev_info (Event_parent repr)
228              and ev' =
229                copy_event ev' ev'.ev_kind ev'.ev_info (Event_child repr)
230              in
231              Kevent ev :: Kpush :: Kevent ev' :: c
232          | _ ->
233              (* Only keep following event, equivalent *)
234              cont
235          end
236      | _ ->
237          Kevent ev :: cont
238      end
239  | _ ->
240      Kevent ev :: cont
241
242let add_event ev =
243  function
244    Kevent ev' :: cont -> weaken_event (merge_events ev ev') cont
245  | cont               -> weaken_event ev cont
246
247(**** Compilation of a lambda expression ****)
248
249let try_blocks = ref []  (* list of stack size for each nested try block *)
250
251(* association staticraise numbers -> (lbl,size of stack, try_blocks *)
252
253let sz_static_raises = ref []
254
255let push_static_raise i lbl_handler sz =
256  sz_static_raises := (i, (lbl_handler, sz, !try_blocks)) :: !sz_static_raises
257
258let find_raise_label i =
259  try
260    List.assoc i !sz_static_raises
261  with
262  | Not_found ->
263      Misc.fatal_error
264        ("exit("^string_of_int i^") outside appropriated catch")
265
266(* Will the translation of l lead to a jump to label ? *)
267let code_as_jump l sz = match l with
268| Lstaticraise (i,[]) ->
269    let label,size,tb = find_raise_label i in
270    if sz = size && tb == !try_blocks then
271      Some label
272    else
273      None
274| _ -> None
275
276(* Function bodies that remain to be compiled *)
277
278type function_to_compile =
279  { params: Ident.t list;               (* function parameters *)
280    body: lambda;                       (* the function body *)
281    label: label;                       (* the label of the function entry *)
282    free_vars: Ident.t list;            (* free variables of the function *)
283    num_defs: int;            (* number of mutually recursive definitions *)
284    rec_vars: Ident.t list;             (* mutually recursive fn names *)
285    rec_pos: int }                      (* rank in recursive definition *)
286
287let functions_to_compile  = (Stack.create () : function_to_compile Stack.t)
288
289(* Name of current compilation unit (for debugging events) *)
290
291let compunit_name = ref ""
292
293(* Maximal stack size reached during the current function body *)
294
295let max_stack_used = ref 0
296
297
298(* Sequence of string tests *)
299
300
301(* Translate a primitive to a bytecode instruction (possibly a call to a C
302   function) *)
303
304let comp_bint_primitive bi suff args =
305  let pref =
306    match bi with Pnativeint -> "caml_nativeint_"
307                | Pint32 -> "caml_int32_"
308                | Pint64 -> "caml_int64_" in
309  Kccall(pref ^ suff, List.length args)
310
311let comp_primitive p args =
312  match p with
313    Pgetglobal id -> Kgetglobal id
314  | Psetglobal id -> Ksetglobal id
315  | Pintcomp cmp -> Kintcomp cmp
316  | Pmakeblock(tag, _mut, _) -> Kmakeblock(List.length args, tag)
317  | Pfield n -> Kgetfield n
318  | Pfield_computed -> Kgetvectitem
319  | Psetfield(n, _ptr, _init) -> Ksetfield n
320  | Psetfield_computed(_ptr, _init) -> Ksetvectitem
321  | Pfloatfield n -> Kgetfloatfield n
322  | Psetfloatfield (n, _init) -> Ksetfloatfield n
323  | Pduprecord _ -> Kccall("caml_obj_dup", 1)
324  | Pccall p -> Kccall(p.prim_name, p.prim_arity)
325  | Pnegint -> Knegint
326  | Paddint -> Kaddint
327  | Psubint -> Ksubint
328  | Pmulint -> Kmulint
329  | Pdivint _ -> Kdivint
330  | Pmodint _ -> Kmodint
331  | Pandint -> Kandint
332  | Porint -> Korint
333  | Pxorint -> Kxorint
334  | Plslint -> Klslint
335  | Plsrint -> Klsrint
336  | Pasrint -> Kasrint
337  | Poffsetint n -> Koffsetint n
338  | Poffsetref n -> Koffsetref n
339  | Pintoffloat -> Kccall("caml_int_of_float", 1)
340  | Pfloatofint -> Kccall("caml_float_of_int", 1)
341  | Pnegfloat -> Kccall("caml_neg_float", 1)
342  | Pabsfloat -> Kccall("caml_abs_float", 1)
343  | Paddfloat -> Kccall("caml_add_float", 2)
344  | Psubfloat -> Kccall("caml_sub_float", 2)
345  | Pmulfloat -> Kccall("caml_mul_float", 2)
346  | Pdivfloat -> Kccall("caml_div_float", 2)
347  | Pfloatcomp Ceq -> Kccall("caml_eq_float", 2)
348  | Pfloatcomp Cneq -> Kccall("caml_neq_float", 2)
349  | Pfloatcomp Clt -> Kccall("caml_lt_float", 2)
350  | Pfloatcomp Cgt -> Kccall("caml_gt_float", 2)
351  | Pfloatcomp Cle -> Kccall("caml_le_float", 2)
352  | Pfloatcomp Cge -> Kccall("caml_ge_float", 2)
353  | Pstringlength -> Kccall("caml_ml_string_length", 1)
354  | Pbyteslength -> Kccall("caml_ml_bytes_length", 1)
355  | Pstringrefs -> Kccall("caml_string_get", 2)
356  | Pbytesrefs -> Kccall("caml_bytes_get", 2)
357  | Pbytessets -> Kccall("caml_bytes_set", 3)
358  | Pstringrefu | Pbytesrefu -> Kgetstringchar
359  | Pbytessetu -> Ksetstringchar
360  | Pstring_load_16(_) -> Kccall("caml_string_get16", 2)
361  | Pstring_load_32(_) -> Kccall("caml_string_get32", 2)
362  | Pstring_load_64(_) -> Kccall("caml_string_get64", 2)
363  | Pstring_set_16(_) -> Kccall("caml_string_set16", 3)
364  | Pstring_set_32(_) -> Kccall("caml_string_set32", 3)
365  | Pstring_set_64(_) -> Kccall("caml_string_set64", 3)
366  | Parraylength _ -> Kvectlength
367  | Parrayrefs Pgenarray -> Kccall("caml_array_get", 2)
368  | Parrayrefs Pfloatarray -> Kccall("caml_array_get_float", 2)
369  | Parrayrefs _ -> Kccall("caml_array_get_addr", 2)
370  | Parraysets Pgenarray -> Kccall("caml_array_set", 3)
371  | Parraysets Pfloatarray -> Kccall("caml_array_set_float", 3)
372  | Parraysets _ -> Kccall("caml_array_set_addr", 3)
373  | Parrayrefu Pgenarray -> Kccall("caml_array_unsafe_get", 2)
374  | Parrayrefu Pfloatarray -> Kccall("caml_array_unsafe_get_float", 2)
375  | Parrayrefu _ -> Kgetvectitem
376  | Parraysetu Pgenarray -> Kccall("caml_array_unsafe_set", 3)
377  | Parraysetu Pfloatarray -> Kccall("caml_array_unsafe_set_float", 3)
378  | Parraysetu _ -> Ksetvectitem
379  | Pctconst c ->
380     let const_name = match c with
381       | Big_endian -> "big_endian"
382       | Word_size -> "word_size"
383       | Int_size -> "int_size"
384       | Max_wosize -> "max_wosize"
385       | Ostype_unix -> "ostype_unix"
386       | Ostype_win32 -> "ostype_win32"
387       | Ostype_cygwin -> "ostype_cygwin"
388       | Backend_type -> "backend_type" in
389     Kccall(Printf.sprintf "caml_sys_const_%s" const_name, 1)
390  | Pisint -> Kisint
391  | Pisout -> Kisout
392  | Pbittest -> Kccall("caml_bitvect_test", 2)
393  | Pbintofint bi -> comp_bint_primitive bi "of_int" args
394  | Pintofbint bi -> comp_bint_primitive bi "to_int" args
395  | Pcvtbint(Pint32, Pnativeint) -> Kccall("caml_nativeint_of_int32", 1)
396  | Pcvtbint(Pnativeint, Pint32) -> Kccall("caml_nativeint_to_int32", 1)
397  | Pcvtbint(Pint32, Pint64) -> Kccall("caml_int64_of_int32", 1)
398  | Pcvtbint(Pint64, Pint32) -> Kccall("caml_int64_to_int32", 1)
399  | Pcvtbint(Pnativeint, Pint64) -> Kccall("caml_int64_of_nativeint", 1)
400  | Pcvtbint(Pint64, Pnativeint) -> Kccall("caml_int64_to_nativeint", 1)
401  | Pnegbint bi -> comp_bint_primitive bi "neg" args
402  | Paddbint bi -> comp_bint_primitive bi "add" args
403  | Psubbint bi -> comp_bint_primitive bi "sub" args
404  | Pmulbint bi -> comp_bint_primitive bi "mul" args
405  | Pdivbint { size = bi } -> comp_bint_primitive bi "div" args
406  | Pmodbint { size = bi } -> comp_bint_primitive bi "mod" args
407  | Pandbint bi -> comp_bint_primitive bi "and" args
408  | Porbint bi -> comp_bint_primitive bi "or" args
409  | Pxorbint bi -> comp_bint_primitive bi "xor" args
410  | Plslbint bi -> comp_bint_primitive bi "shift_left" args
411  | Plsrbint bi -> comp_bint_primitive bi "shift_right_unsigned" args
412  | Pasrbint bi -> comp_bint_primitive bi "shift_right" args
413  | Pbintcomp(_, Ceq) -> Kccall("caml_equal", 2)
414  | Pbintcomp(_, Cneq) -> Kccall("caml_notequal", 2)
415  | Pbintcomp(_, Clt) -> Kccall("caml_lessthan", 2)
416  | Pbintcomp(_, Cgt) -> Kccall("caml_greaterthan", 2)
417  | Pbintcomp(_, Cle) -> Kccall("caml_lessequal", 2)
418  | Pbintcomp(_, Cge) -> Kccall("caml_greaterequal", 2)
419  | Pbigarrayref(_, n, _, _) -> Kccall("caml_ba_get_" ^ string_of_int n, n + 1)
420  | Pbigarrayset(_, n, _, _) -> Kccall("caml_ba_set_" ^ string_of_int n, n + 2)
421  | Pbigarraydim(n) -> Kccall("caml_ba_dim_" ^ string_of_int n, 1)
422  | Pbigstring_load_16(_) -> Kccall("caml_ba_uint8_get16", 2)
423  | Pbigstring_load_32(_) -> Kccall("caml_ba_uint8_get32", 2)
424  | Pbigstring_load_64(_) -> Kccall("caml_ba_uint8_get64", 2)
425  | Pbigstring_set_16(_) -> Kccall("caml_ba_uint8_set16", 3)
426  | Pbigstring_set_32(_) -> Kccall("caml_ba_uint8_set32", 3)
427  | Pbigstring_set_64(_) -> Kccall("caml_ba_uint8_set64", 3)
428  | Pbswap16 -> Kccall("caml_bswap16", 1)
429  | Pbbswap(bi) -> comp_bint_primitive bi "bswap" args
430  | Pint_as_pointer -> Kccall("caml_int_as_pointer", 1)
431  | _ -> fatal_error "Bytegen.comp_primitive"
432
433let is_immed n = immed_min <= n && n <= immed_max
434
435module Storer =
436  Switch.Store
437    (struct type t = lambda type key = lambda
438      let make_key = Lambda.make_key end)
439
440(* Compile an expression.
441   The value of the expression is left in the accumulator.
442   env = compilation environment
443   exp = the lambda expression to compile
444   sz = current size of the stack frame
445   cont = list of instructions to execute afterwards
446   Result = list of instructions that evaluate exp, then perform cont. *)
447
448let rec comp_expr env exp sz cont =
449  if sz > !max_stack_used then max_stack_used := sz;
450  match exp with
451    Lvar id ->
452      begin try
453        let pos = Ident.find_same id env.ce_stack in
454        Kacc(sz - pos) :: cont
455      with Not_found ->
456      try
457        let pos = Ident.find_same id env.ce_heap in
458        Kenvacc(pos) :: cont
459      with Not_found ->
460      try
461        let ofs = Ident.find_same id env.ce_rec in
462        Koffsetclosure(ofs) :: cont
463      with Not_found ->
464        fatal_error ("Bytegen.comp_expr: var " ^ Ident.unique_name id)
465      end
466  | Lconst cst ->
467      Kconst cst :: cont
468  | Lapply{ap_func = func; ap_args = args} ->
469      let nargs = List.length args in
470      if is_tailcall cont then begin
471        comp_args env args sz
472          (Kpush :: comp_expr env func (sz + nargs)
473            (Kappterm(nargs, sz + nargs) :: discard_dead_code cont))
474      end else begin
475        if nargs < 4 then
476          comp_args env args sz
477            (Kpush :: comp_expr env func (sz + nargs) (Kapply nargs :: cont))
478        else begin
479          let (lbl, cont1) = label_code cont in
480          Kpush_retaddr lbl ::
481          comp_args env args (sz + 3)
482            (Kpush :: comp_expr env func (sz + 3 + nargs)
483                      (Kapply nargs :: cont1))
484        end
485      end
486  | Lsend(kind, met, obj, args, _) ->
487      let args = if kind = Cached then List.tl args else args in
488      let nargs = List.length args + 1 in
489      let getmethod, args' =
490        if kind = Self then (Kgetmethod, met::obj::args) else
491        match met with
492          Lconst(Const_base(Const_int n)) -> (Kgetpubmet n, obj::args)
493        | _ -> (Kgetdynmet, met::obj::args)
494      in
495      if is_tailcall cont then
496        comp_args env args' sz
497          (getmethod :: Kappterm(nargs, sz + nargs) :: discard_dead_code cont)
498      else
499        if nargs < 4 then
500          comp_args env args' sz
501            (getmethod :: Kapply nargs :: cont)
502        else begin
503          let (lbl, cont1) = label_code cont in
504          Kpush_retaddr lbl ::
505          comp_args env args' (sz + 3)
506            (getmethod :: Kapply nargs :: cont1)
507        end
508  | Lfunction{params; body} -> (* assume kind = Curried *)
509      let lbl = new_label() in
510      let fv = IdentSet.elements(free_variables exp) in
511      let to_compile =
512        { params = params; body = body; label = lbl;
513          free_vars = fv; num_defs = 1; rec_vars = []; rec_pos = 0 } in
514      Stack.push to_compile functions_to_compile;
515      comp_args env (List.map (fun n -> Lvar n) fv) sz
516        (Kclosure(lbl, List.length fv) :: cont)
517  | Llet(_str, _k, id, arg, body) ->
518      comp_expr env arg sz
519        (Kpush :: comp_expr (add_var id (sz+1) env) body (sz+1)
520          (add_pop 1 cont))
521  | Lletrec(decl, body) ->
522      let ndecl = List.length decl in
523      if List.for_all (function (_, Lfunction _) -> true | _ -> false)
524                      decl then begin
525        (* let rec of functions *)
526        let fv =
527          IdentSet.elements (free_variables (Lletrec(decl, lambda_unit))) in
528        let rec_idents = List.map (fun (id, _lam) -> id) decl in
529        let rec comp_fun pos = function
530            [] -> []
531          | (_id, Lfunction{params; body}) :: rem ->
532              let lbl = new_label() in
533              let to_compile =
534                { params = params; body = body; label = lbl; free_vars = fv;
535                  num_defs = ndecl; rec_vars = rec_idents; rec_pos = pos} in
536              Stack.push to_compile functions_to_compile;
537              lbl :: comp_fun (pos + 1) rem
538          | _ -> assert false in
539        let lbls = comp_fun 0 decl in
540        comp_args env (List.map (fun n -> Lvar n) fv) sz
541          (Kclosurerec(lbls, List.length fv) ::
542            (comp_expr (add_vars rec_idents (sz+1) env) body (sz + ndecl)
543                       (add_pop ndecl cont)))
544      end else begin
545        let decl_size =
546          List.map (fun (id, exp) -> (id, exp, size_of_lambda exp)) decl in
547        let rec comp_init new_env sz = function
548          | [] -> comp_nonrec new_env sz ndecl decl_size
549          | (id, _exp, RHS_floatblock blocksize) :: rem ->
550              Kconst(Const_base(Const_int blocksize)) ::
551              Kccall("caml_alloc_dummy_float", 1) :: Kpush ::
552              comp_init (add_var id (sz+1) new_env) (sz+1) rem
553          | (id, _exp, RHS_block blocksize) :: rem ->
554              Kconst(Const_base(Const_int blocksize)) ::
555              Kccall("caml_alloc_dummy", 1) :: Kpush ::
556              comp_init (add_var id (sz+1) new_env) (sz+1) rem
557          | (id, _exp, RHS_function (blocksize,arity)) :: rem ->
558              Kconst(Const_base(Const_int arity)) ::
559              Kpush ::
560              Kconst(Const_base(Const_int blocksize)) ::
561              Kccall("caml_alloc_dummy_function", 2) :: Kpush ::
562              comp_init (add_var id (sz+1) new_env) (sz+1) rem
563          | (id, _exp, RHS_nonrec) :: rem ->
564              Kconst(Const_base(Const_int 0)) :: Kpush ::
565              comp_init (add_var id (sz+1) new_env) (sz+1) rem
566        and comp_nonrec new_env sz i = function
567          | [] -> comp_rec new_env sz ndecl decl_size
568          | (_id, _exp, (RHS_block _ | RHS_floatblock _ | RHS_function _))
569            :: rem ->
570              comp_nonrec new_env sz (i-1) rem
571          | (_id, exp, RHS_nonrec) :: rem ->
572              comp_expr new_env exp sz
573                (Kassign (i-1) :: comp_nonrec new_env sz (i-1) rem)
574        and comp_rec new_env sz i = function
575          | [] -> comp_expr new_env body sz (add_pop ndecl cont)
576          | (_id, exp, (RHS_block _ | RHS_floatblock _ | RHS_function _))
577            :: rem ->
578              comp_expr new_env exp sz
579                (Kpush :: Kacc i :: Kccall("caml_update_dummy", 2) ::
580                 comp_rec new_env sz (i-1) rem)
581          | (_id, _exp, RHS_nonrec) :: rem ->
582              comp_rec new_env sz (i-1) rem
583        in
584        comp_init env sz decl_size
585      end
586  | Lprim((Pidentity | Popaque | Pbytes_to_string | Pbytes_of_string), [arg], _)
587    ->
588      comp_expr env arg sz cont
589  | Lprim(Pignore, [arg], _) ->
590      comp_expr env arg sz (add_const_unit cont)
591  | Lprim(Pdirapply, [func;arg], loc)
592  | Lprim(Prevapply, [arg;func], loc) ->
593      let exp = Lapply{ap_should_be_tailcall=false;
594                       ap_loc=loc;
595                       ap_func=func;
596                       ap_args=[arg];
597                       ap_inlined=Default_inline;
598                       ap_specialised=Default_specialise} in
599      comp_expr env exp sz cont
600  | Lprim(Pnot, [arg], _) ->
601      let newcont =
602        match cont with
603          Kbranchif lbl :: cont1 -> Kbranchifnot lbl :: cont1
604        | Kbranchifnot lbl :: cont1 -> Kbranchif lbl :: cont1
605        | _ -> Kboolnot :: cont in
606      comp_expr env arg sz newcont
607  | Lprim(Psequand, [exp1; exp2], _) ->
608      begin match cont with
609        Kbranchifnot lbl :: _ ->
610          comp_expr env exp1 sz (Kbranchifnot lbl ::
611            comp_expr env exp2 sz cont)
612      | Kbranchif lbl :: cont1 ->
613          let (lbl2, cont2) = label_code cont1 in
614          comp_expr env exp1 sz (Kbranchifnot lbl2 ::
615            comp_expr env exp2 sz (Kbranchif lbl :: cont2))
616      | _ ->
617          let (lbl, cont1) = label_code cont in
618          comp_expr env exp1 sz (Kstrictbranchifnot lbl ::
619            comp_expr env exp2 sz cont1)
620      end
621  | Lprim(Psequor, [exp1; exp2], _) ->
622      begin match cont with
623        Kbranchif lbl :: _ ->
624          comp_expr env exp1 sz (Kbranchif lbl ::
625            comp_expr env exp2 sz cont)
626      | Kbranchifnot lbl :: cont1 ->
627          let (lbl2, cont2) = label_code cont1 in
628          comp_expr env exp1 sz (Kbranchif lbl2 ::
629            comp_expr env exp2 sz (Kbranchifnot lbl :: cont2))
630      | _ ->
631          let (lbl, cont1) = label_code cont in
632          comp_expr env exp1 sz (Kstrictbranchif lbl ::
633            comp_expr env exp2 sz cont1)
634      end
635  | Lprim(Praise k, [arg], _) ->
636      comp_expr env arg sz (Kraise k :: discard_dead_code cont)
637  | Lprim(Paddint, [arg; Lconst(Const_base(Const_int n))], _)
638    when is_immed n ->
639      comp_expr env arg sz (Koffsetint n :: cont)
640  | Lprim(Psubint, [arg; Lconst(Const_base(Const_int n))], _)
641    when is_immed (-n) ->
642      comp_expr env arg sz (Koffsetint (-n) :: cont)
643  | Lprim (Poffsetint n, [arg], _)
644    when not (is_immed n) ->
645      comp_expr env arg sz
646        (Kpush::
647         Kconst (Const_base (Const_int n))::
648         Kaddint::cont)
649  | Lprim(Pmakearray (kind, _), args, _) ->
650      begin match kind with
651        Pintarray | Paddrarray ->
652          comp_args env args sz (Kmakeblock(List.length args, 0) :: cont)
653      | Pfloatarray ->
654          comp_args env args sz (Kmakefloatblock(List.length args) :: cont)
655      | Pgenarray ->
656          if args = []
657          then Kmakeblock(0, 0) :: cont
658          else comp_args env args sz
659                 (Kmakeblock(List.length args, 0) ::
660                  Kccall("caml_make_array", 1) :: cont)
661      end
662  | Lprim (Pduparray (kind, mutability),
663           [Lprim (Pmakearray (kind',_),args,_)], loc) ->
664      assert (kind = kind');
665      comp_expr env (Lprim (Pmakearray (kind, mutability), args, loc)) sz cont
666  | Lprim (Pduparray _, [arg], loc) ->
667      let prim_obj_dup =
668        Primitive.simple ~name:"caml_obj_dup" ~arity:1 ~alloc:true
669      in
670      comp_expr env (Lprim (Pccall prim_obj_dup, [arg], loc)) sz cont
671  | Lprim (Pduparray _, _, _) ->
672      Misc.fatal_error "Bytegen.comp_expr: Pduparray takes exactly one arg"
673(* Integer first for enabling futher optimization (cf. emitcode.ml)  *)
674  | Lprim (Pintcomp c, [arg ; (Lconst _ as k)], _) ->
675      let p = Pintcomp (commute_comparison c)
676      and args = [k ; arg] in
677      comp_args env args sz (comp_primitive p args :: cont)
678  | Lprim(p, args, _) ->
679      comp_args env args sz (comp_primitive p args :: cont)
680  | Lstaticcatch (body, (i, vars) , handler) ->
681      let nvars = List.length vars in
682      let branch1, cont1 = make_branch cont in
683      let r =
684        if nvars <> 1 then begin (* general case *)
685          let lbl_handler, cont2 =
686            label_code
687              (comp_expr
688                (add_vars vars (sz+1) env)
689                handler (sz+nvars) (add_pop nvars cont1)) in
690          push_static_raise i lbl_handler (sz+nvars);
691          push_dummies nvars
692            (comp_expr env body (sz+nvars)
693            (add_pop nvars (branch1 :: cont2)))
694        end else begin (* small optimization for nvars = 1 *)
695          let var = match vars with [var] -> var | _ -> assert false in
696          let lbl_handler, cont2 =
697            label_code
698              (Kpush::comp_expr
699                (add_var var (sz+1) env)
700                handler (sz+1) (add_pop 1 cont1)) in
701          push_static_raise i lbl_handler sz;
702          comp_expr env body sz (branch1 :: cont2)
703        end in
704      sz_static_raises := List.tl !sz_static_raises ;
705      r
706  | Lstaticraise (i, args) ->
707      let cont = discard_dead_code cont in
708      let label,size,tb = find_raise_label i in
709      let cont = branch_to label cont in
710      let rec loop sz tbb =
711        if tb == tbb then add_pop (sz-size) cont
712        else match tbb with
713        | [] -> assert false
714        | try_sz :: tbb -> add_pop (sz-try_sz-4) (Kpoptrap :: loop try_sz tbb)
715      in
716      let cont = loop sz !try_blocks in
717      begin match args with
718      | [arg] -> (* optim, argument passed in accumulator *)
719          comp_expr env arg sz cont
720      | _ -> comp_exit_args env args sz size cont
721      end
722  | Ltrywith(body, id, handler) ->
723      let (branch1, cont1) = make_branch cont in
724      let lbl_handler = new_label() in
725      let body_cont =
726        Kpoptrap :: branch1 ::
727        Klabel lbl_handler :: Kpush ::
728        comp_expr (add_var id (sz+1) env) handler (sz+1) (add_pop 1 cont1)
729      in
730      try_blocks := sz :: !try_blocks;
731      let l = comp_expr env body (sz+4) body_cont in
732      try_blocks := List.tl !try_blocks;
733      Kpushtrap lbl_handler :: l
734  | Lifthenelse(cond, ifso, ifnot) ->
735      comp_binary_test env cond ifso ifnot sz cont
736  | Lsequence(exp1, exp2) ->
737      comp_expr env exp1 sz (comp_expr env exp2 sz cont)
738  | Lwhile(cond, body) ->
739      let lbl_loop = new_label() in
740      let lbl_test = new_label() in
741      Kbranch lbl_test :: Klabel lbl_loop :: Kcheck_signals ::
742        comp_expr env body sz
743          (Klabel lbl_test ::
744            comp_expr env cond sz (Kbranchif lbl_loop :: add_const_unit cont))
745  | Lfor(param, start, stop, dir, body) ->
746      let lbl_loop = new_label() in
747      let lbl_exit = new_label() in
748      let offset = match dir with Upto -> 1 | Downto -> -1 in
749      let comp = match dir with Upto -> Cgt | Downto -> Clt in
750      comp_expr env start sz
751        (Kpush :: comp_expr env stop (sz+1)
752          (Kpush :: Kpush :: Kacc 2 :: Kintcomp comp :: Kbranchif lbl_exit ::
753           Klabel lbl_loop :: Kcheck_signals ::
754           comp_expr (add_var param (sz+1) env) body (sz+2)
755             (Kacc 1 :: Kpush :: Koffsetint offset :: Kassign 2 ::
756              Kacc 1 :: Kintcomp Cneq :: Kbranchif lbl_loop ::
757              Klabel lbl_exit :: add_const_unit (add_pop 2 cont))))
758  | Lswitch(arg, sw) ->
759      let (branch, cont1) = make_branch cont in
760      let c = ref (discard_dead_code cont1) in
761
762(* Build indirection vectors *)
763      let store = Storer.mk_store () in
764      let act_consts = Array.make sw.sw_numconsts 0
765      and act_blocks = Array.make sw.sw_numblocks 0 in
766      begin match sw.sw_failaction with (* default is index 0 *)
767      | Some fail -> ignore (store.act_store fail)
768      | None      -> ()
769      end ;
770      List.iter
771        (fun (n, act) -> act_consts.(n) <- store.act_store act) sw.sw_consts;
772      List.iter
773        (fun (n, act) -> act_blocks.(n) <- store.act_store act) sw.sw_blocks;
774(* Compile and label actions *)
775      let acts = store.act_get () in
776(*
777      let a = store.act_get_shared () in
778      Array.iter
779        (function
780          | Switch.Shared (Lstaticraise _) -> ()
781          | Switch.Shared act ->
782              Printlambda.lambda Format.str_formatter act ;
783              Printf.eprintf "SHARE BYTE:\n%s\n" (Format.flush_str_formatter ())
784          | _ -> ())
785        a ;
786*)
787      let lbls = Array.make (Array.length acts) 0 in
788      for i = Array.length acts-1 downto 0 do
789        let lbl,c1 = label_code (comp_expr env acts.(i) sz (branch :: !c)) in
790        lbls.(i) <- lbl ;
791        c := discard_dead_code c1
792      done ;
793
794(* Build label vectors *)
795      let lbl_blocks = Array.make sw.sw_numblocks 0 in
796      for i = sw.sw_numblocks - 1 downto 0 do
797        lbl_blocks.(i) <- lbls.(act_blocks.(i))
798      done;
799      let lbl_consts = Array.make sw.sw_numconsts 0 in
800      for i = sw.sw_numconsts - 1 downto 0 do
801        lbl_consts.(i) <- lbls.(act_consts.(i))
802      done;
803      comp_expr env arg sz (Kswitch(lbl_consts, lbl_blocks) :: !c)
804  | Lstringswitch (arg,sw,d,loc) ->
805      comp_expr env (Matching.expand_stringswitch loc arg sw d) sz cont
806  | Lassign(id, expr) ->
807      begin try
808        let pos = Ident.find_same id env.ce_stack in
809        comp_expr env expr sz (Kassign(sz - pos) :: cont)
810      with Not_found ->
811        fatal_error "Bytegen.comp_expr: assign"
812      end
813  | Levent(lam, lev) ->
814      let event kind info =
815        { ev_pos = 0;                   (* patched in emitcode *)
816          ev_module = !compunit_name;
817          ev_loc = lev.lev_loc;
818          ev_kind = kind;
819          ev_info = info;
820          ev_typenv = lev.lev_env;
821          ev_typsubst = Subst.identity;
822          ev_compenv = env;
823          ev_stacksize = sz;
824          ev_repr =
825            begin match lev.lev_repr with
826              None ->
827                Event_none
828            | Some ({contents = 1} as repr) when lev.lev_kind = Lev_function ->
829                Event_child repr
830            | Some ({contents = 1} as repr) ->
831                Event_parent repr
832            | Some repr when lev.lev_kind = Lev_function ->
833                Event_parent repr
834            | Some repr ->
835                Event_child repr
836            end }
837      in
838      begin match lev.lev_kind with
839        Lev_before ->
840          let c = comp_expr env lam sz cont in
841          let ev = event Event_before Event_other in
842          add_event ev c
843      | Lev_function ->
844          let c = comp_expr env lam sz cont in
845          let ev = event Event_pseudo Event_function in
846          add_event ev c
847      | Lev_pseudo ->
848          let c = comp_expr env lam sz cont in
849          let ev = event Event_pseudo Event_other in
850          add_event ev c
851      | Lev_after _ when is_tailcall cont -> (* don't destroy tail call opt *)
852          comp_expr env lam sz cont
853      | Lev_after ty ->
854          let info =
855            match lam with
856              Lapply{ap_args = args}  -> Event_return (List.length args)
857            | Lsend(_, _, _, args, _) -> Event_return (List.length args + 1)
858            | _                       -> Event_other
859          in
860          let ev = event (Event_after ty) info in
861          let cont1 = add_event ev cont in
862          comp_expr env lam sz cont1
863      end
864  | Lifused (_, exp) ->
865      comp_expr env exp sz cont
866
867(* Compile a list of arguments [e1; ...; eN] to a primitive operation.
868   The values of eN ... e2 are pushed on the stack, e2 at top of stack,
869   then e3, then ... The value of e1 is left in the accumulator. *)
870
871and comp_args env argl sz cont =
872  comp_expr_list env (List.rev argl) sz cont
873
874and comp_expr_list env exprl sz cont = match exprl with
875    [] -> cont
876  | [exp] -> comp_expr env exp sz cont
877  | exp :: rem ->
878      comp_expr env exp sz (Kpush :: comp_expr_list env rem (sz+1) cont)
879
880and comp_exit_args  env argl sz pos cont =
881   comp_expr_list_assign env (List.rev argl) sz pos cont
882
883and comp_expr_list_assign env exprl sz pos cont = match exprl with
884  | [] -> cont
885  | exp :: rem ->
886      comp_expr env exp sz
887        (Kassign (sz-pos)::comp_expr_list_assign env rem sz (pos-1) cont)
888
889(* Compile an if-then-else test. *)
890
891and comp_binary_test env cond ifso ifnot sz cont =
892  let cont_cond =
893    if ifnot = Lconst const_unit then begin
894      let (lbl_end, cont1) = label_code cont in
895      Kstrictbranchifnot lbl_end :: comp_expr env ifso sz cont1
896    end else
897    match code_as_jump ifso sz with
898    | Some label ->
899      let cont = comp_expr env ifnot sz cont in
900      Kbranchif label :: cont
901    | _ ->
902        match code_as_jump ifnot sz with
903        | Some label ->
904            let cont = comp_expr env ifso sz cont in
905            Kbranchifnot label :: cont
906        | _ ->
907            let (branch_end, cont1) = make_branch cont in
908            let (lbl_not, cont2) = label_code(comp_expr env ifnot sz cont1) in
909            Kbranchifnot lbl_not ::
910            comp_expr env ifso sz (branch_end :: cont2) in
911
912  comp_expr env cond sz cont_cond
913
914(**** Compilation of a code block (with tracking of stack usage) ****)
915
916let comp_block env exp sz cont =
917  max_stack_used := 0;
918  let code = comp_expr env exp sz cont in
919  let used_safe = !max_stack_used + Config.stack_safety_margin in
920  if used_safe > Config.stack_threshold then
921    Kconst(Const_base(Const_int used_safe)) ::
922    Kccall("caml_ensure_stack_capacity", 1) ::
923    code
924  else
925    code
926
927(**** Compilation of functions ****)
928
929let comp_function tc cont =
930  let arity = List.length tc.params in
931  let rec positions pos delta = function
932      [] -> Ident.empty
933    | id :: rem -> Ident.add id pos (positions (pos + delta) delta rem) in
934  let env =
935    { ce_stack = positions arity (-1) tc.params;
936      ce_heap = positions (2 * (tc.num_defs - tc.rec_pos) - 1) 1 tc.free_vars;
937      ce_rec = positions (-2 * tc.rec_pos) 2 tc.rec_vars } in
938  let cont =
939    comp_block env tc.body arity (Kreturn arity :: cont) in
940  if arity > 1 then
941    Krestart :: Klabel tc.label :: Kgrab(arity - 1) :: cont
942  else
943    Klabel tc.label :: cont
944
945let comp_remainder cont =
946  let c = ref cont in
947  begin try
948    while true do
949      c := comp_function (Stack.pop functions_to_compile) !c
950    done
951  with Stack.Empty ->
952    ()
953  end;
954  !c
955
956(**** Compilation of a lambda phrase ****)
957
958let compile_implementation modulename expr =
959  Stack.clear functions_to_compile;
960  label_counter := 0;
961  sz_static_raises := [] ;
962  compunit_name := modulename;
963  let init_code = comp_block empty_env expr 0 [] in
964  if Stack.length functions_to_compile > 0 then begin
965    let lbl_init = new_label() in
966    Kbranch lbl_init :: comp_remainder (Klabel lbl_init :: init_code)
967  end else
968    init_code
969
970let compile_phrase expr =
971  Stack.clear functions_to_compile;
972  label_counter := 0;
973  sz_static_raises := [] ;
974  let init_code = comp_block empty_env expr 1 [Kreturn 1] in
975  let fun_code = comp_remainder [] in
976  (init_code, fun_code)
977
978let reset () =
979  label_counter := 0;
980  sz_static_raises := [];
981  compunit_name := "";
982  Stack.clear functions_to_compile;
983  max_stack_used := 0
984