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