1(**************************************************************************)
2(*                                                                        *)
3(*                                 OCaml                                  *)
4(*                                                                        *)
5(*                       Pierre Chambart, OCamlPro                        *)
6(*           Mark Shinwell and Leo White, Jane Street Europe              *)
7(*                                                                        *)
8(*   Copyright 2013--2016 OCamlPro SAS                                    *)
9(*   Copyright 2014--2016 Jane Street Group LLC                           *)
10(*                                                                        *)
11(*   All rights reserved.  This file is distributed under the terms of    *)
12(*   the GNU Lesser General Public License version 2.1, with the          *)
13(*   special exception on linking described in the file LICENSE.          *)
14(*                                                                        *)
15(**************************************************************************)
16
17[@@@ocaml.warning "+a-4-9-30-40-41-42"]
18
19type for_one_or_more_units = {
20  fun_offset_table : int Closure_id.Map.t;
21  fv_offset_table : int Var_within_closure.Map.t;
22  closures : Flambda.function_declarations Closure_id.Map.t;
23  constant_sets_of_closures : Set_of_closures_id.Set.t;
24}
25
26type t = {
27  current_unit : for_one_or_more_units;
28  imported_units : for_one_or_more_units;
29}
30
31type ('a, 'b) declaration_position =
32  | Current_unit of 'a
33  | Imported_unit of 'b
34  | Not_declared
35
36let get_fun_offset t closure_id =
37  let fun_offset_table =
38    if Closure_id.in_compilation_unit closure_id (Compilenv.current_unit ())
39    then t.current_unit.fun_offset_table
40    else t.imported_units.fun_offset_table
41  in
42  try Closure_id.Map.find closure_id fun_offset_table
43  with Not_found ->
44    Misc.fatal_errorf "Flambda_to_clambda: missing offset for closure %a"
45      Closure_id.print closure_id
46
47let get_fv_offset t var_within_closure =
48  let fv_offset_table =
49    if Var_within_closure.in_compilation_unit var_within_closure
50        (Compilenv.current_unit ())
51    then t.current_unit.fv_offset_table
52    else t.imported_units.fv_offset_table
53  in
54  try Var_within_closure.Map.find var_within_closure fv_offset_table
55  with Not_found ->
56    Misc.fatal_errorf "Flambda_to_clambda: missing offset for variable %a"
57      Var_within_closure.print var_within_closure
58
59let function_declaration_position t closure_id =
60  try
61    Current_unit (Closure_id.Map.find closure_id t.current_unit.closures)
62  with Not_found ->
63    try
64      Imported_unit (Closure_id.Map.find closure_id t.imported_units.closures)
65    with Not_found -> Not_declared
66
67let is_function_constant t closure_id =
68  match function_declaration_position t closure_id with
69  | Current_unit { set_of_closures_id } ->
70    Set_of_closures_id.Set.mem set_of_closures_id
71      t.current_unit.constant_sets_of_closures
72  | Imported_unit { set_of_closures_id } ->
73    Set_of_closures_id.Set.mem set_of_closures_id
74      t.imported_units.constant_sets_of_closures
75  | Not_declared ->
76    Misc.fatal_errorf "Flambda_to_clambda: missing closure %a"
77      Closure_id.print closure_id
78
79(* Instrumentation of closure and field accesses to try to catch compiler
80   bugs. *)
81
82let check_closure ulam named : Clambda.ulambda =
83  if not !Clflags.clambda_checks then ulam
84  else
85    let desc =
86      Primitive.simple ~name:"caml_check_value_is_closure"
87        ~arity:2 ~alloc:false
88    in
89    let str = Format.asprintf "%a" Flambda.print_named named in
90    let str_const =
91      Compilenv.new_structured_constant (Uconst_string str) ~shared:true
92    in
93    Uprim (Pccall desc,
94           [ulam; Clambda.Uconst (Uconst_ref (str_const, None))],
95           Debuginfo.none)
96
97let check_field ulam pos named_opt : Clambda.ulambda =
98  if not !Clflags.clambda_checks then ulam
99  else
100    let desc =
101      Primitive.simple ~name:"caml_check_field_access"
102        ~arity:3 ~alloc:false
103    in
104    let str =
105      match named_opt with
106      | None -> "<none>"
107      | Some named -> Format.asprintf "%a" Flambda.print_named named
108    in
109    let str_const =
110      Compilenv.new_structured_constant (Uconst_string str) ~shared:true
111    in
112    Uprim (Pccall desc, [ulam; Clambda.Uconst (Uconst_int pos);
113        Clambda.Uconst (Uconst_ref (str_const, None))],
114      Debuginfo.none)
115
116module Env : sig
117  type t
118
119  val empty : t
120
121  val add_subst : t -> Variable.t -> Clambda.ulambda -> t
122  val find_subst_exn : t -> Variable.t -> Clambda.ulambda
123
124  val add_fresh_ident : t -> Variable.t -> Ident.t * t
125  val ident_for_var_exn : t -> Variable.t -> Ident.t
126
127  val add_fresh_mutable_ident : t -> Mutable_variable.t -> Ident.t * t
128  val ident_for_mutable_var_exn : t -> Mutable_variable.t -> Ident.t
129
130  val add_allocated_const : t -> Symbol.t -> Allocated_const.t -> t
131  val allocated_const_for_symbol : t -> Symbol.t -> Allocated_const.t option
132
133  val keep_only_symbols : t -> t
134end = struct
135  type t =
136    { subst : Clambda.ulambda Variable.Map.t;
137      var : Ident.t Variable.Map.t;
138      mutable_var : Ident.t Mutable_variable.Map.t;
139      toplevel : bool;
140      allocated_constant_for_symbol : Allocated_const.t Symbol.Map.t;
141    }
142
143  let empty =
144    { subst = Variable.Map.empty;
145      var = Variable.Map.empty;
146      mutable_var = Mutable_variable.Map.empty;
147      toplevel = false;
148      allocated_constant_for_symbol = Symbol.Map.empty;
149    }
150
151  let add_subst t id subst =
152    { t with subst = Variable.Map.add id subst t.subst }
153
154  let find_subst_exn t id = Variable.Map.find id t.subst
155
156  let ident_for_var_exn t id = Variable.Map.find id t.var
157
158  let add_fresh_ident t var =
159    let id = Ident.create (Variable.unique_name var) in
160    id, { t with var = Variable.Map.add var id t.var }
161
162  let ident_for_mutable_var_exn t mut_var =
163    Mutable_variable.Map.find mut_var t.mutable_var
164
165  let add_fresh_mutable_ident t mut_var =
166    let id = Mutable_variable.unique_ident mut_var in
167    let mutable_var = Mutable_variable.Map.add mut_var id t.mutable_var in
168    id, { t with mutable_var; }
169
170  let add_allocated_const t sym cons =
171    { t with
172      allocated_constant_for_symbol =
173        Symbol.Map.add sym cons t.allocated_constant_for_symbol;
174    }
175
176  let allocated_const_for_symbol t sym =
177    try
178      Some (Symbol.Map.find sym t.allocated_constant_for_symbol)
179    with Not_found -> None
180
181  let keep_only_symbols t =
182    { empty with
183      allocated_constant_for_symbol = t.allocated_constant_for_symbol;
184    }
185end
186
187let subst_var env var : Clambda.ulambda =
188  try Env.find_subst_exn env var
189  with Not_found ->
190    try Uvar (Env.ident_for_var_exn env var)
191    with Not_found ->
192      Misc.fatal_errorf "Flambda_to_clambda: unbound variable %a@."
193        Variable.print var
194
195let subst_vars env vars = List.map (subst_var env) vars
196
197let build_uoffset ulam offset : Clambda.ulambda =
198  if offset = 0 then ulam
199  else Uoffset (ulam, offset)
200
201let to_clambda_allocated_constant (const : Allocated_const.t)
202      : Clambda.ustructured_constant =
203  match const with
204  | Float f -> Uconst_float f
205  | Int32 i -> Uconst_int32 i
206  | Int64 i -> Uconst_int64 i
207  | Nativeint i -> Uconst_nativeint i
208  | Immutable_string s | String s -> Uconst_string s
209  | Immutable_float_array a | Float_array a -> Uconst_float_array a
210
211let to_uconst_symbol env symbol : Clambda.ustructured_constant option =
212  match Env.allocated_const_for_symbol env symbol with
213  | Some ((Float _ | Int32 _ | Int64 _ | Nativeint _) as const) ->
214    Some (to_clambda_allocated_constant const)
215  | None  (* CR-soon mshinwell: Try to make this an error. *)
216  | Some _ -> None
217
218let to_clambda_symbol' env sym : Clambda.uconstant =
219  let lbl = Linkage_name.to_string (Symbol.label sym) in
220  Uconst_ref (lbl, to_uconst_symbol env sym)
221
222let to_clambda_symbol env sym : Clambda.ulambda =
223  Uconst (to_clambda_symbol' env sym)
224
225let to_clambda_const env (const : Flambda.constant_defining_value_block_field)
226      : Clambda.uconstant =
227  match const with
228  | Symbol symbol -> to_clambda_symbol' env symbol
229  | Const (Int i) -> Uconst_int i
230  | Const (Char c) -> Uconst_int (Char.code c)
231  | Const (Const_pointer i) -> Uconst_ptr i
232
233let rec to_clambda t env (flam : Flambda.t) : Clambda.ulambda =
234  match flam with
235  | Var var -> subst_var env var
236  | Let { var; defining_expr; body; _ } ->
237    (* TODO: synthesize proper value_kind *)
238    let id, env_body = Env.add_fresh_ident env var in
239    Ulet (Immutable, Pgenval, id, to_clambda_named t env var defining_expr,
240      to_clambda t env_body body)
241  | Let_mutable { var = mut_var; initial_value = var; body; contents_kind } ->
242    let id, env_body = Env.add_fresh_mutable_ident env mut_var in
243    let def = subst_var env var in
244    Ulet (Mutable, contents_kind, id, def, to_clambda t env_body body)
245  | Let_rec (defs, body) ->
246    let env, defs =
247      List.fold_right (fun (var, def) (env, defs) ->
248          let id, env = Env.add_fresh_ident env var in
249          env, (id, var, def) :: defs)
250        defs (env, [])
251    in
252    let defs =
253      List.map (fun (id, var, def) -> id, to_clambda_named t env var def) defs
254    in
255    Uletrec (defs, to_clambda t env body)
256  | Apply { func; args; kind = Direct direct_func; dbg = dbg } ->
257    (* The closure _parameter_ of the function is added by cmmgen.
258       At the call site, for a direct call, the closure argument must be
259       explicitly added (by [to_clambda_direct_apply]); there is no special
260       handling of such in the direct call primitive.
261       For an indirect call, we do not need to do anything here; Cmmgen will
262       do the equivalent of the previous paragraph when it generates a direct
263       call to [caml_apply]. *)
264    to_clambda_direct_apply t func args direct_func dbg env
265  | Apply { func; args; kind = Indirect; dbg = dbg } ->
266    let callee = subst_var env func in
267    Ugeneric_apply (check_closure callee (Flambda.Expr (Var func)),
268      subst_vars env args, dbg)
269  | Switch (arg, sw) ->
270    let aux () : Clambda.ulambda =
271      let const_index, const_actions =
272        to_clambda_switch t env sw.consts sw.numconsts sw.failaction
273      in
274      let block_index, block_actions =
275        to_clambda_switch t env sw.blocks sw.numblocks sw.failaction
276      in
277      Uswitch (subst_var env arg,
278        { us_index_consts = const_index;
279          us_actions_consts = const_actions;
280          us_index_blocks = block_index;
281          us_actions_blocks = block_actions;
282        })
283    in
284    (* Check that the [failaction] may be duplicated.  If this is not the
285       case, share it through a static raise / static catch. *)
286    (* CR-someday pchambart for pchambart: This is overly simplified.
287       We should verify that this does not generates too bad code.
288       If it the case, handle some let cases.
289    *)
290    begin match sw.failaction with
291    | None -> aux ()
292    | Some (Static_raise _) -> aux ()
293    | Some failaction ->
294      let exn = Static_exception.create () in
295      let sw =
296        { sw with
297          failaction = Some (Flambda.Static_raise (exn, []));
298        }
299      in
300      let expr : Flambda.t =
301        Static_catch (exn, [], Switch (arg, sw), failaction)
302      in
303      to_clambda t env expr
304    end
305  | String_switch (arg, sw, def) ->
306    let arg = subst_var env arg in
307    let sw = List.map (fun (s, e) -> s, to_clambda t env e) sw in
308    let def = Misc.may_map (to_clambda t env) def in
309    Ustringswitch (arg, sw, def)
310  | Static_raise (static_exn, args) ->
311    Ustaticfail (Static_exception.to_int static_exn,
312      List.map (subst_var env) args)
313  | Static_catch (static_exn, vars, body, handler) ->
314    let env_handler, ids =
315      List.fold_right (fun var (env, ids) ->
316          let id, env = Env.add_fresh_ident env var in
317          env, id :: ids)
318        vars (env, [])
319    in
320    Ucatch (Static_exception.to_int static_exn, ids,
321      to_clambda t env body, to_clambda t env_handler handler)
322  | Try_with (body, var, handler) ->
323    let id, env_handler = Env.add_fresh_ident env var in
324    Utrywith (to_clambda t env body, id, to_clambda t env_handler handler)
325  | If_then_else (arg, ifso, ifnot) ->
326    Uifthenelse (subst_var env arg, to_clambda t env ifso,
327      to_clambda t env ifnot)
328  | While (cond, body) ->
329    Uwhile (to_clambda t env cond, to_clambda t env body)
330  | For { bound_var; from_value; to_value; direction; body } ->
331    let id, env_body = Env.add_fresh_ident env bound_var in
332    Ufor (id, subst_var env from_value, subst_var env to_value,
333      direction, to_clambda t env_body body)
334  | Assign { being_assigned; new_value } ->
335    let id =
336      try Env.ident_for_mutable_var_exn env being_assigned
337      with Not_found ->
338        Misc.fatal_errorf "Unbound mutable variable %a in [Assign]: %a"
339          Mutable_variable.print being_assigned
340          Flambda.print flam
341    in
342    Uassign (id, subst_var env new_value)
343  | Send { kind; meth; obj; args; dbg } ->
344    Usend (kind, subst_var env meth, subst_var env obj,
345      subst_vars env args, dbg)
346  | Proved_unreachable -> Uunreachable
347
348and to_clambda_named t env var (named : Flambda.named) : Clambda.ulambda =
349  match named with
350  | Symbol sym -> to_clambda_symbol env sym
351  | Const (Const_pointer n) -> Uconst (Uconst_ptr n)
352  | Const (Int n) -> Uconst (Uconst_int n)
353  | Const (Char c) -> Uconst (Uconst_int (Char.code c))
354  | Allocated_const _ ->
355    Misc.fatal_errorf "[Allocated_const] should have been lifted to a \
356        [Let_symbol] construction before [Flambda_to_clambda]: %a = %a"
357      Variable.print var
358      Flambda.print_named named
359  | Read_mutable mut_var ->
360    begin try Uvar (Env.ident_for_mutable_var_exn env mut_var)
361    with Not_found ->
362      Misc.fatal_errorf "Unbound mutable variable %a in [Read_mutable]: %a"
363        Mutable_variable.print mut_var
364        Flambda.print_named named
365    end
366  | Read_symbol_field (symbol, field) ->
367    Uprim (Pfield field, [to_clambda_symbol env symbol], Debuginfo.none)
368  | Set_of_closures set_of_closures ->
369    to_clambda_set_of_closures t env set_of_closures
370  | Project_closure { set_of_closures; closure_id } ->
371    (* Note that we must use [build_uoffset] to ensure that we do not generate
372       a [Uoffset] construction in the event that the offset is zero, otherwise
373       we might break pattern matches in Cmmgen (in particular for the
374       compilation of "let rec"). *)
375    check_closure (
376      build_uoffset
377        (check_closure (subst_var env set_of_closures)
378           (Flambda.Expr (Var set_of_closures)))
379        (get_fun_offset t closure_id))
380      named
381  | Move_within_set_of_closures { closure; start_from; move_to } ->
382    check_closure (build_uoffset
383      (check_closure (subst_var env closure)
384         (Flambda.Expr (Var closure)))
385      ((get_fun_offset t move_to) - (get_fun_offset t start_from)))
386      named
387  | Project_var { closure; var; closure_id } ->
388    let ulam = subst_var env closure in
389    let fun_offset = get_fun_offset t closure_id in
390    let var_offset = get_fv_offset t var in
391    let pos = var_offset - fun_offset in
392    Uprim (Pfield pos,
393      [check_field (check_closure ulam (Expr (Var closure))) pos (Some named)],
394      Debuginfo.none)
395  | Prim (Pfield index, [block], dbg) ->
396    Uprim (Pfield index, [check_field (subst_var env block) index None], dbg)
397  | Prim (Psetfield (index, maybe_ptr, init), [block; new_value], dbg) ->
398    Uprim (Psetfield (index, maybe_ptr, init), [
399        check_field (subst_var env block) index None;
400        subst_var env new_value;
401      ], dbg)
402  | Prim (Popaque, args, dbg) ->
403    Uprim (Pidentity, subst_vars env args, dbg)
404  | Prim (p, args, dbg) ->
405    Uprim (p, subst_vars env args, dbg)
406  | Expr expr -> to_clambda t env expr
407
408and to_clambda_switch t env cases num_keys default =
409  let num_keys =
410    if Numbers.Int.Set.cardinal num_keys = 0 then 0
411    else Numbers.Int.Set.max_elt num_keys + 1
412  in
413  let index = Array.make num_keys 0 in
414  let store = Flambda_utils.Switch_storer.mk_store () in
415  begin match default with
416  | Some def when List.length cases < num_keys -> ignore (store.act_store def)
417  | _ -> ()
418  end;
419  List.iter (fun (key, lam) -> index.(key) <- store.act_store lam) cases;
420  let actions = Array.map (to_clambda t env) (store.act_get ()) in
421  match actions with
422  | [| |] -> [| |], [| |]  (* May happen when [default] is [None]. *)
423  | _ -> index, actions
424
425and to_clambda_direct_apply t func args direct_func dbg env : Clambda.ulambda =
426  let closed = is_function_constant t direct_func in
427  let label = Compilenv.function_label direct_func in
428  let uargs =
429    let uargs = subst_vars env args in
430    (* Remove the closure argument if the closure is closed.  (Note that the
431       closure argument is always a variable, so we can be sure we are not
432       dropping any side effects.) *)
433    if closed then uargs else uargs @ [subst_var env func]
434  in
435  Udirect_apply (label, uargs, dbg)
436
437(* Describe how to build a runtime closure block that corresponds to the
438   given Flambda set of closures.
439
440   For instance the closure for the following set of closures:
441
442     let rec fun_a x =
443       if x <= 0 then 0 else fun_b (x-1) v1
444     and fun_b x y =
445       if x <= 0 then 0 else v1 + v2 + y + fun_a (x-1)
446
447   will be represented in memory as:
448
449     [ closure header; fun_a;
450       1; infix header; fun caml_curry_2;
451       2; fun_b; v1; v2 ]
452
453   fun_a and fun_b will take an additional parameter 'env' to
454   access their closure.  It will be arranged such that in the body
455   of each function the env parameter points to its own code
456   pointer.  For example, in fun_b it will be shifted by 3 words.
457
458   Hence accessing v1 in the body of fun_a is accessing the
459   6th field of 'env' and in the body of fun_b the 1st field.
460*)
461and to_clambda_set_of_closures t env
462      (({ function_decls; free_vars } : Flambda.set_of_closures)
463        as set_of_closures) : Clambda.ulambda =
464  let all_functions = Variable.Map.bindings function_decls.funs in
465  let env_var = Ident.create "env" in
466  let to_clambda_function
467        (closure_id, (function_decl : Flambda.function_declaration))
468        : Clambda.ufunction =
469    let closure_id = Closure_id.wrap closure_id in
470    let fun_offset =
471      Closure_id.Map.find closure_id t.current_unit.fun_offset_table
472    in
473    let env =
474      (* Inside the body of the function, we cannot access variables
475         declared outside, so start with a suitably clean environment.
476         Note that we must not forget the information about which allocated
477         constants contain which unboxed values. *)
478      let env = Env.keep_only_symbols env in
479      (* Add the Clambda expressions for the free variables of the function
480         to the environment. *)
481      let add_env_free_variable id _ env =
482        let var_offset =
483          try
484            Var_within_closure.Map.find
485              (Var_within_closure.wrap id) t.current_unit.fv_offset_table
486          with Not_found ->
487            Misc.fatal_errorf "Clambda.to_clambda_set_of_closures: offset for \
488                free variable %a is unknown.  Set of closures: %a"
489              Variable.print id
490              Flambda.print_set_of_closures set_of_closures
491        in
492        let pos = var_offset - fun_offset in
493        Env.add_subst env id
494          (Uprim (Pfield pos, [Clambda.Uvar env_var], Debuginfo.none))
495      in
496      let env = Variable.Map.fold add_env_free_variable free_vars env in
497      (* Add the Clambda expressions for all functions defined in the current
498         set of closures to the environment.  The various functions may be
499         retrieved by moving within the runtime closure, starting from the
500         current function's closure. *)
501      let add_env_function pos env (id, _) =
502        let offset =
503          Closure_id.Map.find (Closure_id.wrap id)
504            t.current_unit.fun_offset_table
505        in
506        let exp : Clambda.ulambda = Uoffset (Uvar env_var, offset - pos) in
507        Env.add_subst env id exp
508      in
509      List.fold_left (add_env_function fun_offset) env all_functions
510    in
511    let env_body, params =
512      List.fold_right (fun var (env, params) ->
513          let id, env = Env.add_fresh_ident env var in
514          env, id :: params)
515        function_decl.params (env, [])
516    in
517    { label = Compilenv.function_label closure_id;
518      arity = Flambda_utils.function_arity function_decl;
519      params = params @ [env_var];
520      body = to_clambda t env_body function_decl.body;
521      dbg = function_decl.dbg;
522      env = Some env_var;
523    }
524  in
525  let funs = List.map to_clambda_function all_functions in
526  let free_vars =
527    Variable.Map.bindings (Variable.Map.map (
528      fun (free_var : Flambda.specialised_to) ->
529        subst_var env free_var.var) free_vars)
530  in
531  Uclosure (funs, List.map snd free_vars)
532
533and to_clambda_closed_set_of_closures t env symbol
534      ({ function_decls; } : Flambda.set_of_closures)
535      : Clambda.ustructured_constant =
536  let functions = Variable.Map.bindings function_decls.funs in
537  let to_clambda_function (id, (function_decl : Flambda.function_declaration))
538        : Clambda.ufunction =
539    (* All that we need in the environment, for translating one closure from
540       a closed set of closures, is the substitutions for variables bound to
541       the various closures in the set.  Such closures will always be
542       referenced via symbols. *)
543    let env =
544      List.fold_left (fun env (var, _) ->
545          let closure_id = Closure_id.wrap var in
546          let symbol = Compilenv.closure_symbol closure_id in
547          Env.add_subst env var (to_clambda_symbol env symbol))
548        (Env.keep_only_symbols env)
549        functions
550    in
551    let env_body, params =
552      List.fold_right (fun var (env, params) ->
553          let id, env = Env.add_fresh_ident env var in
554          env, id :: params)
555        function_decl.params (env, [])
556    in
557    { label = Compilenv.function_label (Closure_id.wrap id);
558      arity = Flambda_utils.function_arity function_decl;
559      params;
560      body = to_clambda t env_body function_decl.body;
561      dbg = function_decl.dbg;
562      env = None;
563    }
564  in
565  let ufunct = List.map to_clambda_function functions in
566  let closure_lbl = Linkage_name.to_string (Symbol.label symbol) in
567  Uconst_closure (ufunct, closure_lbl, [])
568
569let to_clambda_initialize_symbol t env symbol fields : Clambda.ulambda =
570  let fields =
571    List.mapi (fun index expr -> index, to_clambda t env expr) fields
572  in
573  let build_setfield (index, field) : Clambda.ulambda =
574    (* Note that this will never cause a write barrier hit, owing to
575       the [Initialization]. *)
576    Uprim (Psetfield (index, Pointer, Root_initialization),
577      [to_clambda_symbol env symbol; field],
578      Debuginfo.none)
579  in
580  match fields with
581  | [] -> Uconst (Uconst_ptr 0)
582  | h :: t ->
583    List.fold_left (fun acc (p, field) ->
584        Clambda.Usequence (build_setfield (p, field), acc))
585      (build_setfield h) t
586
587let accumulate_structured_constants t env symbol
588      (c : Flambda.constant_defining_value) acc =
589  match c with
590  | Allocated_const c ->
591    Symbol.Map.add symbol (to_clambda_allocated_constant c) acc
592  | Block (tag, fields) ->
593    let fields = List.map (to_clambda_const env) fields in
594    Symbol.Map.add symbol (Clambda.Uconst_block (Tag.to_int tag, fields)) acc
595  | Set_of_closures set_of_closures ->
596    let to_clambda_set_of_closures =
597      to_clambda_closed_set_of_closures t env symbol set_of_closures
598    in
599    Symbol.Map.add symbol to_clambda_set_of_closures acc
600  | Project_closure _ -> acc
601
602let to_clambda_program t env constants (program : Flambda.program) =
603  let rec loop env constants (program : Flambda.program_body)
604        : Clambda.ulambda * Clambda.ustructured_constant Symbol.Map.t =
605    match program with
606    | Let_symbol (symbol, alloc, program) ->
607      (* Useful only for unboxing. Since floats and boxed integers will
608         never be part of a Let_rec_symbol, handling only the Let_symbol
609         is sufficient. *)
610      let env =
611        match alloc with
612        | Allocated_const const -> Env.add_allocated_const env symbol const
613        | _ -> env
614      in
615      let constants =
616        accumulate_structured_constants t env symbol alloc constants
617      in
618      loop env constants program
619    | Let_rec_symbol (defs, program) ->
620      let constants =
621        List.fold_left (fun constants (symbol, alloc) ->
622            accumulate_structured_constants t env symbol alloc constants)
623          constants defs
624      in
625      loop env constants program
626    | Initialize_symbol (symbol, _tag, fields, program) ->
627      (* The tag is ignored here: It is used separately to generate the
628         preallocated block. Only the initialisation code is generated
629         here. *)
630      let e1 = to_clambda_initialize_symbol t env symbol fields in
631      let e2, constants = loop env constants program in
632      Usequence (e1, e2), constants
633    | Effect (expr, program) ->
634      let e1 = to_clambda t env expr in
635      let e2, constants = loop env constants program in
636      Usequence (e1, e2), constants
637    | End _ ->
638      Uconst (Uconst_ptr 0), constants
639  in
640  loop env constants program.program_body
641
642type result = {
643  expr : Clambda.ulambda;
644  preallocated_blocks : Clambda.preallocated_block list;
645  structured_constants : Clambda.ustructured_constant Symbol.Map.t;
646  exported : Export_info.t;
647}
648
649let convert (program, exported) : result =
650  let current_unit =
651    let offsets = Closure_offsets.compute program in
652    { fun_offset_table = offsets.function_offsets;
653      fv_offset_table = offsets.free_variable_offsets;
654      closures = Flambda_utils.make_closure_map program;
655      constant_sets_of_closures =
656        Flambda_utils.all_lifted_constant_sets_of_closures program;
657    }
658  in
659  let imported_units =
660    let imported = Compilenv.approx_env () in
661    { fun_offset_table = imported.offset_fun;
662      fv_offset_table = imported.offset_fv;
663      closures = imported.closures;
664      constant_sets_of_closures = imported.constant_sets_of_closures;
665    }
666  in
667  let t = { current_unit; imported_units; } in
668  let preallocated_blocks =
669    List.map (fun (symbol, tag, fields) ->
670        { Clambda.
671          symbol = Linkage_name.to_string (Symbol.label symbol);
672          exported = true;
673          tag = Tag.to_int tag;
674          size = List.length fields;
675        })
676      (Flambda_utils.initialize_symbols program)
677  in
678  let expr, structured_constants =
679    to_clambda_program t Env.empty Symbol.Map.empty program
680  in
681  let offset_fun, offset_fv =
682    Closure_offsets.compute_reexported_offsets program
683      ~current_unit_offset_fun:current_unit.fun_offset_table
684      ~current_unit_offset_fv:current_unit.fv_offset_table
685      ~imported_units_offset_fun:imported_units.fun_offset_table
686      ~imported_units_offset_fv:imported_units.fv_offset_table
687  in
688  let exported =
689    Export_info.add_clambda_info exported
690      ~offset_fun
691      ~offset_fv
692      ~constant_sets_of_closures:current_unit.constant_sets_of_closures
693  in
694  { expr; preallocated_blocks; structured_constants; exported; }
695