summaryrefslogtreecommitdiff
path: root/middle_end/flambda/ref_to_variables.ml
diff options
context:
space:
mode:
Diffstat (limited to 'middle_end/flambda/ref_to_variables.ml')
-rw-r--r--middle_end/flambda/ref_to_variables.ml199
1 files changed, 199 insertions, 0 deletions
diff --git a/middle_end/flambda/ref_to_variables.ml b/middle_end/flambda/ref_to_variables.ml
new file mode 100644
index 0000000000..f93948f912
--- /dev/null
+++ b/middle_end/flambda/ref_to_variables.ml
@@ -0,0 +1,199 @@
+(**************************************************************************)
+(* *)
+(* OCaml *)
+(* *)
+(* Pierre Chambart, OCamlPro *)
+(* Mark Shinwell and Leo White, Jane Street Europe *)
+(* *)
+(* Copyright 2013--2016 OCamlPro SAS *)
+(* Copyright 2014--2016 Jane Street Group LLC *)
+(* *)
+(* All rights reserved. This file is distributed under the terms of *)
+(* the GNU Lesser General Public License version 2.1, with the *)
+(* special exception on linking described in the file LICENSE. *)
+(* *)
+(**************************************************************************)
+
+[@@@ocaml.warning "+a-4-9-30-40-41-42-66"]
+open! Int_replace_polymorphic_compare
+
+let variables_not_used_as_local_reference (tree:Flambda.t) =
+ let set = ref Variable.Set.empty in
+ let rec loop_named (flam : Flambda.named) =
+ match flam with
+ (* Directly used block: does not prevent use as a variable *)
+ | Prim(Pfield _, [_], _)
+ | Prim(Poffsetref _, [_], _) -> ()
+ | Prim(Psetfield _, [_block; v], _) ->
+ (* block is not prevented to be used as a local reference, but v is *)
+ set := Variable.Set.add v !set
+ | Prim(_, _, _)
+ | Symbol _ |Const _ | Allocated_const _ | Read_mutable _
+ | Read_symbol_field _ | Project_closure _
+ | Move_within_set_of_closures _ | Project_var _ ->
+ set := Variable.Set.union !set (Flambda.free_variables_named flam)
+ | Set_of_closures set_of_closures ->
+ set := Variable.Set.union !set (Flambda.free_variables_named flam);
+ Variable.Map.iter (fun _ (function_decl : Flambda.function_declaration) ->
+ loop function_decl.body)
+ set_of_closures.function_decls.funs
+ | Expr e ->
+ loop e
+ and loop (flam : Flambda.t) =
+ match flam with
+ | Let { defining_expr; body; _ } ->
+ loop_named defining_expr;
+ loop body
+ | Let_rec (defs, body) ->
+ List.iter (fun (_var, named) -> loop_named named) defs;
+ loop body
+ | Var v ->
+ set := Variable.Set.add v !set
+ | Let_mutable { initial_value = v; body } ->
+ set := Variable.Set.add v !set;
+ loop body
+ | If_then_else (cond, ifso, ifnot) ->
+ set := Variable.Set.add cond !set;
+ loop ifso;
+ loop ifnot
+ | Switch (cond, { consts; blocks; failaction }) ->
+ set := Variable.Set.add cond !set;
+ List.iter (fun (_, branch) -> loop branch) consts;
+ List.iter (fun (_, branch) -> loop branch) blocks;
+ Misc.may loop failaction
+ | String_switch (cond, branches, default) ->
+ set := Variable.Set.add cond !set;
+ List.iter (fun (_, branch) -> loop branch) branches;
+ Misc.may loop default
+ | Static_catch (_, _, body, handler) ->
+ loop body;
+ loop handler
+ | Try_with (body, _, handler) ->
+ loop body;
+ loop handler
+ | While (cond, body) ->
+ loop cond;
+ loop body
+ | For { bound_var = _; from_value; to_value; direction = _; body; } ->
+ set := Variable.Set.add from_value !set;
+ set := Variable.Set.add to_value !set;
+ loop body
+ | Static_raise (_, args) ->
+ set := Variable.Set.union (Variable.Set.of_list args) !set
+ | Proved_unreachable | Apply _ | Send _ | Assign _ ->
+ set := Variable.Set.union !set (Flambda.free_variables flam)
+ in
+ loop tree;
+ !set
+
+let variables_containing_ref (flam:Flambda.t) =
+ let map = ref Variable.Map.empty in
+ let aux (flam : Flambda.t) =
+ match flam with
+ | Let { var;
+ defining_expr = Prim(Pmakeblock(0, Asttypes.Mutable, _), l, _);
+ } ->
+ map := Variable.Map.add var (List.length l) !map
+ | _ -> ()
+ in
+ Flambda_iterators.iter aux (fun _ -> ()) flam;
+ !map
+
+let eliminate_ref_of_expr flam =
+ let variables_not_used_as_local_reference =
+ variables_not_used_as_local_reference flam
+ in
+ let convertible_variables =
+ Variable.Map.filter
+ (fun v _ ->
+ not (Variable.Set.mem v variables_not_used_as_local_reference))
+ (variables_containing_ref flam)
+ in
+ if Variable.Map.cardinal convertible_variables = 0 then flam
+ else
+ let convertible_variables =
+ Variable.Map.mapi (fun v size ->
+ Array.init size (fun _ -> Mutable_variable.create_from_variable v))
+ convertible_variables
+ in
+ let convertible_variable v = Variable.Map.mem v convertible_variables in
+ let get_variable v field =
+ let arr = try Variable.Map.find v convertible_variables
+ with Not_found -> assert false in
+ if Array.length arr <= field
+ then None (* This case could apply when inlining code containing GADTS *)
+ else Some (arr.(field), Array.length arr)
+ in
+ let aux (flam : Flambda.t) : Flambda.t =
+ match flam with
+ | Let { var;
+ defining_expr = Prim(Pmakeblock(0, Asttypes.Mutable, shape), l,_);
+ body }
+ when convertible_variable var ->
+ let shape = match shape with
+ | None -> List.map (fun _ -> Lambda.Pgenval) l
+ | Some shape -> shape
+ in
+ let _, expr =
+ List.fold_left2 (fun (field,body) init kind ->
+ match get_variable var field with
+ | None -> assert false
+ | Some (field_var, _) ->
+ field+1,
+ (Let_mutable { var = field_var;
+ initial_value = init;
+ body;
+ contents_kind = kind } : Flambda.t))
+ (0,body) l shape in
+ expr
+ | Let _ | Let_mutable _
+ | Assign _ | Var _ | Apply _
+ | Let_rec _ | Switch _ | String_switch _
+ | Static_raise _ | Static_catch _
+ | Try_with _ | If_then_else _
+ | While _ | For _ | Send _ | Proved_unreachable ->
+ flam
+ and aux_named (named : Flambda.named) : Flambda.named =
+ match named with
+ | Prim(Pfield field, [v], _)
+ when convertible_variable v ->
+ (match get_variable v field with
+ | None -> Expr Proved_unreachable
+ | Some (var,_) -> Read_mutable var)
+ | Prim(Poffsetref delta, [v], dbg)
+ when convertible_variable v ->
+ (match get_variable v 0 with
+ | None -> Expr Proved_unreachable
+ | Some (var,size) ->
+ if size = 1
+ then begin
+ let mut_name = Internal_variable_names.read_mutable in
+ let mut = Variable.create mut_name in
+ let new_value_name = Internal_variable_names.offsetted in
+ let new_value = Variable.create new_value_name in
+ let expr =
+ Flambda.create_let mut (Read_mutable var)
+ (Flambda.create_let new_value
+ (Prim(Poffsetint delta, [mut], dbg))
+ (Assign { being_assigned = var; new_value }))
+ in
+ Expr expr
+ end
+ else
+ Expr Proved_unreachable)
+ | Prim(Psetfield (field, _, _), [v; new_value], _)
+ when convertible_variable v ->
+ (match get_variable v field with
+ | None -> Expr Proved_unreachable
+ | Some (being_assigned,_) ->
+ Expr (Assign { being_assigned; new_value }))
+ | Prim _ | Symbol _ | Const _ | Allocated_const _ | Read_mutable _
+ | Read_symbol_field _ | Set_of_closures _ | Project_closure _
+ | Move_within_set_of_closures _ | Project_var _ | Expr _ ->
+ named
+ in
+ Flambda_iterators.map aux aux_named flam
+
+let eliminate_ref (program:Flambda.program) =
+ Flambda_iterators.map_exprs_at_toplevel_of_program program
+ ~f:eliminate_ref_of_expr