diff --git a/CHANGES.md b/CHANGES.md index ef6609df48..4ff15d42ae 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,7 @@ requested at compile time (--enable with-js-error) or at startup (OCAMLRUNPARAM=b=1) * Runtime: allow dynlink of precompiled js with separate compilation (#1676) * Lib: Modify Typed_array API for compatibility with WebAssembly +* Compiler: improved global dead code elimination (#2206) ## Bug fixes diff --git a/compiler/lib/code.ml b/compiler/lib/code.ml index 0c48932164..6b4c2d09f3 100644 --- a/compiler/lib/code.ml +++ b/compiler/lib/code.ml @@ -95,6 +95,8 @@ module Var : sig module Map : Map.S with type key = t + module Hashtbl : Hashtbl.S with type key = t + module Tbl : sig type key = t @@ -108,14 +110,6 @@ module Var : sig val fold : ('a -> 'acc -> 'acc) -> 'a t -> 'acc -> 'acc end - module DataMap : sig - type ('a, 'b) t - - val iter : ('a -> 'b -> unit) -> ('a, 'b) t -> unit - - val fold : ('a -> 'b -> 'acc -> 'acc) -> ('a, 'b) t -> 'acc -> 'acc - end - type size = unit val get : 'a t -> key -> 'a @@ -124,12 +118,8 @@ module Var : sig val make : size -> 'a -> 'a t - val make_map : size -> ('a, 'b) DataMap.t t - val make_set : size -> 'a DataSet.t t - val add_map : ('a, 'b) DataMap.t t -> key -> 'a -> 'b -> unit - val add_set : 'a DataSet.t t -> key -> 'a -> unit val iter : (key -> 'a -> unit) -> 'a t -> unit @@ -159,6 +149,8 @@ end = struct let compare : t -> t -> int = compare let equal (a : t) (b : t) = a = b + + let hash x = x end include T @@ -246,24 +238,6 @@ end = struct | Many t -> Hashtbl.fold (fun k () acc -> f k acc) t acc end - module DataMap = struct - type ('a, 'b) t = - | Empty - | One of 'a * 'b - | Many of ('a, 'b) Hashtbl.t - - let iter f = function - | Empty -> () - | One (a, b) -> f a b - | Many t -> Hashtbl.iter f t - - let fold f t acc = - match t with - | Empty -> acc - | One (a, b) -> f a b acc - | Many t -> Hashtbl.fold f t acc - end - type key = T.t type size = unit @@ -276,18 +250,6 @@ end = struct let make_set () = Array.make (count ()) DataSet.Empty - let make_map () = Array.make (count ()) DataMap.Empty - - let add_map t x k v = - match t.(x) with - | DataMap.Empty -> t.(x) <- One (k, v) - | One (k', v') -> - let tbl = Hashtbl.create 0 in - Hashtbl.replace tbl k' v'; - Hashtbl.replace tbl k v; - t.(x) <- Many tbl - | Many tbl -> Hashtbl.replace tbl k v - let add_set t x k = match t.(x) with | DataSet.Empty -> t.(x) <- One k @@ -304,6 +266,8 @@ end = struct done end + module Hashtbl = Hashtbl.Make (T) + module ISet = struct type t = BitSet.t @@ -452,9 +416,7 @@ type prim_arg = | Pv of Var.t | Pc of constant -type special = - | Undefined - | Alias_prim of string +type special = Alias_prim of string type mutability = | Immutable @@ -603,7 +565,6 @@ module Print = struct let special f s = match s with - | Undefined -> Format.fprintf f "undefined" | Alias_prim s -> Format.fprintf f "alias %s" s let expr f e = diff --git a/compiler/lib/code.mli b/compiler/lib/code.mli index 8cb0d6f8db..748856c28f 100644 --- a/compiler/lib/code.mli +++ b/compiler/lib/code.mli @@ -88,6 +88,8 @@ module Var : sig module Map : Map.S with type key = t + module Hashtbl : Hashtbl.S with type key = t + module Tbl : sig type key = t @@ -99,14 +101,6 @@ module Var : sig val fold : ('a -> 'acc -> 'acc) -> 'a t -> 'acc -> 'acc end - module DataMap : sig - type ('a, 'b) t - - val iter : ('a -> 'b -> unit) -> ('a, 'b) t -> unit - - val fold : ('a -> 'b -> 'acc -> 'acc) -> ('a, 'b) t -> 'acc -> 'acc - end - type 'a t type size = unit @@ -117,12 +111,8 @@ module Var : sig val make : size -> 'a -> 'a t - val make_map : size -> ('a, 'b) DataMap.t t - val make_set : size -> 'a DataSet.t t - val add_map : ('a, 'b) DataMap.t t -> key -> 'a -> 'b -> unit - val add_set : 'a DataSet.t t -> key -> 'a -> unit val iter : (key -> 'a -> unit) -> 'a t -> unit @@ -210,9 +200,7 @@ type prim_arg = | Pv of Var.t | Pc of constant -type special = - | Undefined - | Alias_prim of string +type special = Alias_prim of string type mutability = | Immutable diff --git a/compiler/lib/dgraph.ml b/compiler/lib/dgraph.ml index d09ea4f823..5f190a8fe8 100644 --- a/compiler/lib/dgraph.ml +++ b/compiler/lib/dgraph.ml @@ -288,3 +288,90 @@ let t3 = Timer.get t3 in let f size g f = f' size g (fun ~update:_ v x -> f v x) end end + +module type ACTION = sig + type t +end + +module type DOMAIN = sig + type t + + val equal : t -> t -> bool + + val bot : t + + val top : t + + val join : t -> t -> t +end + +module Solver + (N : sig + type t + end) + (NSet : ISet with type elt = N.t) + (NTbl : Tbl with type key = N.t) + (A : ACTION) + (D : DOMAIN) = +struct + type t = + { domain : NSet.t + ; iter_children : (N.t -> A.t -> unit) -> N.t -> unit + } + + type queue = + { queue : N.t Queue.t + ; set : NSet.t + } + + let is_empty st = Queue.is_empty st.queue + + let pop st = + let x = Queue.pop st.queue in + NSet.add st.set x; + x + + let push x st = + if NSet.mem st.set x + then ( + Queue.push x st.queue; + NSet.remove st.set x) + + let rec iterate g f ~state w = + if not (is_empty w) + then ( + let dep = pop w in + if not (D.equal (NTbl.get state dep) D.bot) + then + g.iter_children + (fun target action -> + let a = NTbl.get state target in + if not (D.equal a D.top) + then + let b = D.join a (f ~state ~dep ~target ~action) in + if not (D.equal a b) + then ( + NTbl.set state target b; + push target w)) + dep; + iterate g f ~state w) + + let rec traverse g to_visit lst x = + if NSet.mem to_visit x + then ( + NSet.remove to_visit x; + g.iter_children (fun y _ -> traverse g to_visit lst y) x; + lst := x :: !lst) + + let traverse_all g = + let lst = ref [] in + let to_visit = NSet.copy g.domain in + NSet.iter (fun x -> traverse g to_visit lst x) g.domain; + let queue = Queue.create () in + List.iter ~f:(fun x -> Queue.push x queue) !lst; + { queue; set = to_visit } + + let f ~state g f = + let w = traverse_all g in + iterate g f ~state w +end diff --git a/compiler/lib/dgraph.mli b/compiler/lib/dgraph.mli index 406eca92cb..d611be77ee 100644 --- a/compiler/lib/dgraph.mli +++ b/compiler/lib/dgraph.mli @@ -104,3 +104,39 @@ module Make_Imperative -> D.t NTbl.t end end + +module type ACTION = sig + type t +end + +module type DOMAIN = sig + type t + + val equal : t -> t -> bool + + val bot : t + + val top : t + + val join : t -> t -> t +end + +module Solver + (N : sig + type t + end) + (NSet : ISet with type elt = N.t) + (NTbl : Tbl with type key = N.t) + (A : ACTION) + (D : DOMAIN) : sig + type t = + { domain : NSet.t + ; iter_children : (N.t -> A.t -> unit) -> N.t -> unit + } + + val f : + state:D.t NTbl.t + -> t + -> (state:D.t NTbl.t -> dep:N.t -> target:N.t -> action:A.t -> D.t) + -> unit +end diff --git a/compiler/lib/driver.ml b/compiler/lib/driver.ml index 6c638469ee..9e812c5024 100644 --- a/compiler/lib/driver.ml +++ b/compiler/lib/driver.ml @@ -667,7 +667,7 @@ let full ~standalone ~wrap_with_fun ~profile ~link ~source_map formatter d p = in let deadcode_sentinal = (* If deadcode is disabled, this field is just fresh variable *) - Code.Var.fresh_n "undef" + Code.Var.fresh_n "dummy" in let opt = specialize_js_once diff --git a/compiler/lib/generate.ml b/compiler/lib/generate.ml index 4535394bc1..da6b8947d2 100644 --- a/compiler/lib/generate.ml +++ b/compiler/lib/generate.ml @@ -1031,10 +1031,32 @@ let throw_statement ctx cx k loc = , loc ) ] +let remove_unused_tail_args ctx exact trampolined args = + if exact && not trampolined + then + let has_unused_tail_args = + List.fold_left + ~f:(fun _ x -> Var.equal x ctx.Ctx.deadcode_sentinal) + ~init:false + args + in + if has_unused_tail_args + then + List.fold_right + ~f:(fun x args -> + match args with + | [] when Var.equal x ctx.Ctx.deadcode_sentinal -> [] + | _ -> x :: args) + ~init:[] + args + else args + else args + let rec translate_expr ctx queue loc x e level : _ * J.statement_list = match e with | Apply { f; args; exact } -> let trampolined = Var.Set.mem x ctx.Ctx.trampolined_calls in + let args = remove_unused_tail_args ctx exact trampolined args in let args, prop, queue = List.fold_right ~f:(fun x (args, prop, queue) -> @@ -1090,7 +1112,11 @@ let rec translate_expr ctx queue loc x e level : _ * J.statement_list = (st, loc) :: rem | _ -> clo in - let clo = J.EFun (None, J.fun_ (List.map args ~f:(fun v -> J.V v)) clo loc) in + let clo = + J.EFun + ( None + , J.fun_ (List.map args ~f:(fun v -> J.V v)) (Js_simpl.function_body clo) loc ) + in (clo, (fst const_p, fv), queue), [] | Constant c -> let js, instrs = constant ~ctx c level in @@ -1098,8 +1124,6 @@ let rec translate_expr ctx queue loc x e level : _ * J.statement_list = | Special (Alias_prim name) -> let prim = Share.get_prim (runtime_fun ctx) name ctx.Ctx.share in (prim, const_p, queue), [] - | Special Undefined -> - (J.(EVar (ident (Utf8_string.of_string_exn "undefined"))), const_p, queue), [] | Prim (Extern "debugger", _) -> let ins = if Config.Flag.debugger () then J.Debugger_statement else J.Empty_statement diff --git a/compiler/lib/global_deadcode.ml b/compiler/lib/global_deadcode.ml index b56c0d5cdd..c9d350b8d1 100644 --- a/compiler/lib/global_deadcode.ml +++ b/compiler/lib/global_deadcode.ml @@ -28,35 +28,82 @@ type def = | Expr of expr (** [x] is defined by an expression. *) | Param (** [x] is a block or closure parameter. *) -(** Liveness of a variable [x], forming a lattice structure. *) -type live = - | Top (** [x] is live and not a block. *) - | Live of IntSet.t (** [x] is a live block with a (non-empty) set of live fields. *) - | Dead (** [x] is dead. *) +module Domain : sig + (** Liveness of a variable [x], forming a lattice structure. *) + type t = private + | Top (** [x] is live and not a block. *) + | Live of t IntMap.t + (** [x] is a live block with a (non-empty) set of live fields. *) + | Dead (** [x] is dead. *) -module G = Dgraph.Make_Imperative (Var) (Var.ISet) (Var.Tbl) + val equal : t -> t -> bool -module Domain = struct - type t = live + val bot : t - let equal l1 l2 = + val top : t + + val live_field : int -> t -> t + + val join : t -> t -> t +end = struct + type t = + | Top + | Live of t IntMap.t + | Dead + + let rec equal l1 l2 = match l1, l2 with | Top, Top | Dead, Dead -> true - | Live f1, Live f2 -> IntSet.equal f1 f2 + | Live f1, Live f2 -> IntMap.equal equal f1 f2 | Top, (Dead | Live _) | Live _, (Dead | Top) | Dead, (Live _ | Top) -> false let bot = Dead + let top = Top + + let rec depth l = + match l with + | Top | Dead -> 0 + | Live f -> 1 + IntMap.fold (fun _ l' acc -> max (depth l') acc) f 0 + + let rec truncate depth l = + match l with + | Top | Dead -> l + | Live f -> + if depth = 0 then Top else Live (IntMap.map (fun l' -> truncate (depth - 1) l') f) + + let depth_treshold = 4 + + let live_field i l = + (* We need to limit the depth of the liveness information, + otherwise the information can get more and more precise without + ever converging. Modules are rarely very deeply nested, so this is + not an issue. *) + Live + (IntMap.singleton + i + (if depth l > depth_treshold then truncate depth_treshold l else l)) + (** Join the liveness according to lattice structure. *) - let join l1 l2 = + let rec join l1 l2 = match l1, l2 with | _, Top | Top, _ -> Top - | Live f1, Live f2 -> Live (IntSet.union f1 f2) + | Live f1, Live f2 -> Live (IntMap.union (fun _ l1 l2 -> Some (join l1 l2)) f1 f2) | Dead, Live f | Live f, Dead -> Live f | Dead, Dead -> Dead end -module Solver = G.Solver (Domain) +let iter_with_scope prog f = + Code.fold_closures + prog + (fun scope _ (pc, _) () -> + Code.traverse + { fold = fold_children } + (fun pc () -> f scope (Addr.Map.find pc prog.blocks)) + pc + prog.blocks + ()) + () let definitions prog = let defs = Var.Tbl.make () Param in @@ -82,7 +129,11 @@ let variable_may_escape x (global_info : Global_flow.info) = (** Type of variable usage. *) type usage_kind = | Compute (** variable y is used to compute x *) - | Propagate (** values of y propagate to x *) + | Propagate of + { scope : Var.t list + ; src : Var.t + } (** values of y propagate to x when the scope is live *) + | Scope (** variable x is defined in function y *) (** Compute the adjacency list for the dependency graph of given program. An edge between variables [x] and [y] is marked [Compute] if [x] is used in the definition of [y]. It is marked @@ -90,19 +141,27 @@ type usage_kind = We use information from global flow to try to add edges between function calls and their return values at known call sites. *) -let usages prog (global_info : Global_flow.info) : - (Var.t, usage_kind) Var.Tbl.DataMap.t Var.Tbl.t = - let uses = Var.Tbl.make_map () in - let add_use kind x y = Var.Tbl.add_map uses y x kind in +let usages prog (global_info : Global_flow.info) scoped_live_vars : + (usage_kind * Var.Set.t) list Var.Tbl.t = + let uses = Var.Tbl.make () [] in + let add_uses kind x vars = + let p = kind, vars in + Var.Tbl.set uses x (p :: Var.Tbl.get uses x); + match kind with + | Propagate { scope; _ } -> + List.iter ~f:(fun z -> Var.Tbl.set uses z (p :: Var.Tbl.get uses z)) scope + | _ -> () + in + let add_use kind x y = add_uses kind x (Var.Set.singleton y) in let add_arg_dep params args = - List.iter2 ~f:(fun x y -> add_use Propagate x y) params args + List.iter2 ~f:(fun x y -> add_use (Propagate { scope = []; src = x }) x y) params args in let add_cont_deps (pc, args) = match try Some (Addr.Map.find pc prog.blocks) with Not_found -> None with | Some block -> add_arg_dep block.params args | None -> () (* Dead continuation *) in - let add_expr_uses x e : unit = + let add_expr_uses scope x e : unit = match e with | Apply { f; args; _ } -> (match Var.Tbl.get global_info.info_approximation f with @@ -118,9 +177,16 @@ let usages prog (global_info : Global_flow.info) : So we only need to consider the case when there is an exact application. *) if List.compare_lengths params args = 0 then ( - let return_values = Var.Map.find k global_info.info_return_vals in - Var.Set.iter (add_use Propagate x) return_values; - List.iter2 ~f:(add_use Propagate) params args) + (* Both the function and the call-site must be live *) + let scope = k :: scope in + add_uses + (Propagate { scope; src = x }) + x + (Var.Map.find k global_info.info_return_vals); + List.iter2 + ~f:(fun x y -> add_use (Propagate { scope; src = x }) x y) + params + args) | _ -> ()) known); add_use Compute x f; @@ -140,30 +206,41 @@ let usages prog (global_info : Global_flow.info) : | Pc _ -> ()) args in - Addr.Map.iter - (fun _ block -> - (* Add uses from block body *) - List.iter - ~f:(fun (i, _) -> - match i with - | Let (x, e) -> add_expr_uses x e - (* For assignment, propagate liveness from new to old variable like a block parameter *) - | Assign (x, y) -> add_use Propagate x y - | Set_field (_, _, _, _) | Offset_ref (_, _) | Array_set (_, _, _) -> ()) - block.body; - (* Add uses from block branch *) - match fst block.branch with - | Return _ | Raise _ | Stop -> () - | Branch cont -> add_cont_deps cont - | Cond (_, cont1, cont2) -> - add_cont_deps cont1; - add_cont_deps cont2 - | Switch (_, a) -> Array.iter ~f:add_cont_deps a - | Pushtrap (cont, _, cont_h) -> - add_cont_deps cont; - add_cont_deps cont_h - | Poptrap cont -> add_cont_deps cont) - prog.blocks; + let add_block_uses scope block = + (* Add uses from block body *) + List.iter + ~f:(fun (i, _) -> + match i with + | Let (x, e) -> add_expr_uses scope x e + (* For assignment, propagate liveness from new to old variable like a block parameter *) + | Assign (x, y) -> add_use (Propagate { scope = []; src = x }) x y + | Set_field (_, _, _, _) | Offset_ref (_, _) | Array_set (_, _, _) -> ()) + block.body; + (* Add uses from block branch *) + match fst block.branch with + | Return _ | Raise _ | Stop -> () + | Branch cont -> add_cont_deps cont + | Cond (_, cont1, cont2) -> + add_cont_deps cont1; + add_cont_deps cont2 + | Switch (_, a) -> Array.iter ~f:add_cont_deps a + | Pushtrap (cont, _, cont_h) -> + add_cont_deps cont; + add_cont_deps cont_h + | Poptrap cont -> add_cont_deps cont + in + iter_with_scope prog (fun f block -> + add_block_uses + (match f with + | Some f -> [ f ] + | None -> []) + block); + Var.Tbl.iter + (fun scope h -> + match h with + | None -> () + | Some h -> Var.Hashtbl.iter (fun x _ -> add_use Scope scope x) h) + scoped_live_vars; uses (** Return the set of variables used in a given expression *) @@ -200,17 +277,35 @@ let expr_vars e = A variable [x[i]] is marked as [Live {i}] if it is used in an instruction where field [i] is referenced or set. *) let liveness prog pure_funs (global_info : Global_flow.info) = - let live_vars = Var.Tbl.make () Dead in - let add_top v = Var.Tbl.set live_vars v Top in - let add_live_field v i = - let live_fields = - match Var.Tbl.get live_vars v with - | Live fields -> Live (IntSet.add i fields) - | Top | Dead -> Live (IntSet.singleton i) - in - Var.Tbl.set live_vars v live_fields + let live_vars = Var.Tbl.make () Domain.bot in + let scoped_live_vars = Var.Tbl.make () None in + let get_hashtbl scope = + match Var.Tbl.get scoped_live_vars scope with + | Some h -> h + | None -> + let h = Var.Hashtbl.create 8 in + Var.Tbl.set scoped_live_vars scope (Some h); + h + in + let add_top scope v = + match scope with + | None -> Var.Tbl.set live_vars v Domain.top + | Some scope -> + let h = get_hashtbl scope in + Var.Hashtbl.replace h v Domain.top in - let live_instruction i = + let add_live_field scope v i = + let update_field l i = Domain.join l (Domain.live_field i Domain.top) in + match scope with + | None -> Var.Tbl.set live_vars v (update_field (Var.Tbl.get live_vars v) i) + | Some scope -> + let h = get_hashtbl scope in + Var.Hashtbl.replace + h + v + (update_field (try Var.Hashtbl.find h v with Not_found -> Domain.bot) i) + in + let live_instruction scope i = match i with (* If e is impure, set all variables in e as Top. The only exception is for function applications, where we may be able to do better. Global flow gives us information about which arguments in @@ -220,9 +315,9 @@ let liveness prog pure_funs (global_info : Global_flow.info) = then match e with | Apply { f; args; _ } -> - add_top f; + add_top scope f; List.iter - ~f:(fun x -> if variable_may_escape x global_info then add_top x) + ~f:(fun x -> if variable_may_escape x global_info then add_top scope x) args | Block (_, _, _, _) | Field (_, _, _) @@ -231,29 +326,29 @@ let liveness prog pure_funs (global_info : Global_flow.info) = | Prim (_, _) | Special _ -> let vars = expr_vars e in - Var.Set.iter add_top vars) + Var.Set.iter (fun x -> add_top scope x) vars) | Set_field (x, i, _, y) -> - add_live_field x i; - add_top y + add_live_field scope x i; + add_top scope y | Array_set (x, y, z) -> - add_top x; - add_top y; - add_top z - | Offset_ref (x, _) -> add_live_field x 0 + add_top scope x; + add_top scope y; + add_top scope z + | Offset_ref (x, _) -> add_live_field scope x 0 (* Assignment can be ignored. Liveness of old variable is just propagated to new variable. See [usages]. *) | Assign (_, _) -> () in - let live_block block = - List.iter ~f:(fun (i, _) -> live_instruction i) block.body; + let live_block scope block = + List.iter ~f:(fun (i, _) -> live_instruction scope i) block.body; match fst block.branch with - | Return x -> if variable_may_escape x global_info then add_top x - | Raise (x, _) -> add_top x - | Cond (x, _, _) -> add_top x - | Switch (x, _) -> add_top x + | Return x -> if variable_may_escape x global_info then add_top scope x + | Raise (x, _) -> add_top scope x + | Cond (x, _, _) -> add_top scope x + | Switch (x, _) -> add_top scope x | Stop | Branch _ | Poptrap _ | Pushtrap _ -> () in - Addr.Map.iter (fun _ block -> live_block block) prog.blocks; - live_vars + iter_with_scope prog live_block; + live_vars, scoped_live_vars (* Returns the set of variables given a table of variables. *) let variables deps = @@ -264,50 +359,71 @@ let variables deps = (** Propagate liveness of the usages of a variable [x] to [x]. The liveness of [x] is defined by joining its current liveness and the contribution of each vairable [y] that uses [x]. *) -let propagate uses defs live_vars live_table x = +let propagate defs scoped_live_vars ~state ~dep:y ~target:x ~action:usage_kind = (* Variable [y] uses [x] either in its definition ([Compute]) or as a closure/block parameter ([Propagate]). In the latter case, the contribution is simply the liveness of [y]. In the former, the contribution depends on the liveness of [y] and its definition. *) - let contribution y usage_kind = - match usage_kind with - (* If x is used to compute y, we consider the liveness of y *) - | Compute -> ( - match Var.Tbl.get live_table y with - (* If y is dead, then x is dead. *) - | Dead -> Dead - (* If y is a live block, then x is the join of liveness fields that are x *) - | Live fields -> ( - match Var.Tbl.get defs y with - | Expr (Block (_, vars, _, _)) -> - let found = ref false in - Array.iteri - ~f:(fun i v -> - if Var.equal v x && IntSet.mem i fields then found := true) - vars; - if !found then Top else Dead - | Expr (Field (_, i, _)) -> Live (IntSet.singleton i) - | _ -> Top) - (* If y is top and y is a field access, x depends only on that field *) - | Top -> ( - match Var.Tbl.get defs y with - | Expr (Field (_, i, _)) -> Live (IntSet.singleton i) - | _ -> Top)) - (* If x is used as an argument for parameter y, then contribution is liveness of y *) - | Propagate -> Var.Tbl.get live_table y - in - Var.Tbl.DataMap.fold - (fun y usage_kind live -> Domain.join (contribution y usage_kind) live) - (Var.Tbl.get uses x) - (Domain.join (Var.Tbl.get live_vars x) (Var.Tbl.get live_table x)) + match usage_kind with + (* If x is used to compute y, we consider the liveness of y *) + | Compute -> ( + match Var.Tbl.get state y with + (* If y is dead, then x is dead. *) + | Domain.Dead -> Domain.bot + (* If y is a live block, then x is the join of liveness fields that are x *) + | Live fields as l -> ( + match Var.Tbl.get defs y with + | Expr (Block (_, vars, _, _)) -> + let live = ref Domain.bot in + Array.iteri + ~f:(fun i v -> + if Var.equal v x + then + match IntMap.find_opt i fields with + | Some l -> live := Domain.join !live l + | None -> ()) + vars; + !live + | Expr (Field (_, i, _)) -> Domain.live_field i l + | _ -> Domain.top) + (* If y is top and y is a field access, x depends only on that field *) + | Top -> ( + match Var.Tbl.get defs y with + | Expr (Field (_, i, _)) -> Domain.live_field i Domain.top + | _ -> Domain.top)) + (* If x is used as an argument for parameter y, then contribution is liveness of y *) + | Propagate { scope; src } -> + if List.for_all scope ~f:(fun z -> + match Var.Tbl.get state z with + | Dead -> false + | _ -> true) + then Var.Tbl.get state src + else Domain.bot + | Scope -> ( + match Var.Tbl.get state y with + | Dead -> Domain.bot + | _ -> ( + match Var.Tbl.get scoped_live_vars y with + | Some h -> Var.Hashtbl.find h x + | None -> assert false)) -let solver vars uses defs live_vars = +module Solver = + Dgraph.Solver (Var) (Var.ISet) (Var.Tbl) + (struct + type t = usage_kind + end) + (Domain) + +let solver vars uses defs live_vars scoped_live_vars = let g = - { G.domain = vars - ; G.iter_children = - (fun f x -> Var.Tbl.DataMap.iter (fun y _ -> f y) (Var.Tbl.get uses x)) + { Solver.domain = vars + ; iter_children = + (fun f x -> + List.iter + ~f:(fun (usage_kind, vars) -> Var.Set.iter (fun y -> f y usage_kind) vars) + (Var.Tbl.get uses x)) } in - Solver.f () (G.invert () g) (propagate uses defs live_vars) + Solver.f ~state:live_vars g (propagate defs scoped_live_vars) (** Replace each instance of a dead variable with a sentinal value. Blocks that end in dead variables are compacted to the first live entry. @@ -326,7 +442,7 @@ let zero prog sentinal live_table = in let is_live v = match Var.Tbl.get live_table v with - | Dead -> false + | Domain.Dead -> false | Top | Live _ -> true in let zero_var x = if is_live x then x else sentinal in @@ -339,7 +455,7 @@ let zero prog sentinal live_table = | Live fields -> let vars = Array.mapi - ~f:(fun i v -> if IntSet.mem i fields then v else sentinal) + ~f:(fun i v -> if IntMap.mem i fields then v else sentinal) vars |> compact_vars in @@ -383,9 +499,14 @@ let zero prog sentinal live_table = { prog with blocks } module Print = struct - let live_to_string = function - | Live fields -> - "live { " ^ IntSet.fold (fun i s -> s ^ Format.sprintf "%d " i) fields "" ^ "}" + let rec live_to_string = function + | Domain.Live fields -> + "live { " + ^ IntMap.fold + (fun i l s -> s ^ Format.sprintf "%d: %s; " i (live_to_string l)) + fields + "" + ^ "}" | Top -> "top" | Dead -> "dead" @@ -394,27 +515,30 @@ module Print = struct Var.Tbl.iter (fun v ds -> Format.eprintf "%a: { " Var.print v; - Var.Tbl.DataMap.iter - (fun d k -> - Format.eprintf - "(%a, %s) " - Var.print - d - (match k with - | Compute -> "C" - | Propagate -> "P")) + List.iter + ~f:(fun (k, s) -> + Var.Set.iter + (fun d -> + Format.eprintf + "(%a, %s) " + Var.print + d + (match k with + | Compute -> "C" + | Propagate { scope; src } -> + "P(" + ^ String.concat + ~sep:" " + (List.map ~f:(fun x -> Format.asprintf "%a" Var.print x) scope) + ^ Format.asprintf "/%a)" Var.print src + | Scope -> "S")) + s) ds; Format.eprintf "}\n") uses - let print_liveness live_vars = - Format.eprintf "Liveness:\n"; - Var.Tbl.iter - (fun v l -> Format.eprintf "%a: %s\n" Var.print v (live_to_string l)) - live_vars - let print_live_tbl live_table = - Format.eprintf "Liveness with dependencies:\n"; + Format.eprintf "Liveness:\n"; Var.Tbl.iter (fun v l -> Format.eprintf "%a: %s\n" Var.print v (live_to_string l)) live_table @@ -422,7 +546,7 @@ end (** Add a sentinal variable declaration to the IR. The fresh variable is assigned to `undefined`. *) let add_sentinal p sentinal = - let instr, loc = Let (sentinal, Special Undefined), noloc in + let instr, loc = Let (sentinal, Constant (Int 0l)), noloc in Code.prepend p [ instr, loc ] (** Run the liveness analysis and replace dead variables with the given sentinal. *) @@ -433,27 +557,26 @@ let f p ~deadcode_sentinal global_info = let p = add_sentinal p deadcode_sentinal in (* Compute definitions *) let defs = definitions p in - (* Compute usages *) - let uses = usages p global_info in (* Compute initial liveness *) let pure_funs = Pure_fun.f p in - let live_vars = liveness p pure_funs global_info in + let live_table, scoped_live_vars = liveness p pure_funs global_info in + (* Compute usages *) + let uses = usages p global_info scoped_live_vars in (* Propagate liveness to dependencies *) let vars = variables uses in - let live_table = solver vars uses defs live_vars in + solver vars uses defs live_table scoped_live_vars; (* Print debug info *) if debug () then ( - Format.eprintf "Before Zeroing:\n"; + Format.eprintf "Before Zeroing:@."; Code.Print.program (fun _ _ -> "") p; - Print.print_liveness live_vars; Print.print_uses uses; Print.print_live_tbl live_table); (* Zero out dead fields *) let p = zero p deadcode_sentinal live_table in if debug () then ( - Format.printf "After Zeroing:\n"; + Format.eprintf "After Zeroing:@."; Code.Print.program (fun _ _ -> "") p); - if times () then Format.eprintf " deadcode dgraph.: %a@." Timer.print t; + if times () then Format.eprintf " global dead code elim.: %a@." Timer.print t; p diff --git a/compiler/lib/js_simpl.ml b/compiler/lib/js_simpl.ml index ba61a7df40..efdb3137db 100644 --- a/compiler/lib/js_simpl.ml +++ b/compiler/lib/js_simpl.ml @@ -274,3 +274,26 @@ let if_statement e loc iftrue truestop iffalse falsestop = iftrue' falsestop | _ -> if_statement_2 e loc iftrue truestop iffalse falsestop + +let function_body b = + (* We only check for a return at the end since it is by far the most + common case in practice. *) + let has_useless_return = + let rec check l = + match l with + | [] -> false + | [ (J.Return_statement None, _) ] -> true + | _ :: r -> check r + in + check b + in + if has_useless_return + then + let rec remove acc l = + match l with + | [] -> acc + | [ (J.Return_statement None, _) ] -> acc + | i :: r -> remove (i :: acc) r + in + List.rev (remove [] b) + else b diff --git a/compiler/lib/js_simpl.mli b/compiler/lib/js_simpl.mli index 4c049d1744..4d81f4ef24 100644 --- a/compiler/lib/js_simpl.mli +++ b/compiler/lib/js_simpl.mli @@ -32,3 +32,5 @@ val if_statement : val block : (Javascript.statement * location) list -> Javascript.statement * location val unblock : Javascript.statement * location -> (Javascript.statement * location) list + +val function_body : (statement * location) list -> (statement * location) list diff --git a/compiler/lib/pure_fun.ml b/compiler/lib/pure_fun.ml index 8e566fd135..fbb5bb9fb0 100644 --- a/compiler/lib/pure_fun.ml +++ b/compiler/lib/pure_fun.ml @@ -25,7 +25,7 @@ open Code let pure_expr pure_funs e = match e with | Block _ | Field _ | Closure _ | Constant _ -> true - | Special (Alias_prim _ | Undefined) -> true + | Special (Alias_prim _) -> true | Apply { f; exact; _ } -> exact && Var.Set.mem f pure_funs | Prim (p, _l) -> ( match p with diff --git a/compiler/tests-compiler/direct_calls.ml b/compiler/tests-compiler/direct_calls.ml index 6b5a65c0a9..eac493b749 100644 --- a/compiler/tests-compiler/direct_calls.ml +++ b/compiler/tests-compiler/direct_calls.ml @@ -141,14 +141,14 @@ let%expect_test "direct calls with --enable effects" = {| function test1(param, cont){ function f(g, x){ - try{g(undef); return;} + try{g(); return;} catch(e$0){ var e = caml_wrap_exception(e$0); throw caml_maybe_attach_backtrace(e, 0); } } - f(function(x){return;}, undef); - f(function(x){return;}, undef); + f(function(x){}); + f(function(x){}); return cont(0); } //end @@ -160,11 +160,11 @@ let%expect_test "direct calls with --enable effects" = return raise(e$0); }); return caml_cps_exact_call2 - (g, x, function(_f_){caml_pop_trap(); return cont(undef);}); + (g, x, function(_f_){caml_pop_trap(); return cont();}); } return caml_cps_exact_call3 (f, - function(x, cont){return cont(undef);}, + function(x, cont){return cont();}, 7, function(_d_){ return caml_cps_exact_call3 @@ -179,7 +179,7 @@ let%expect_test "direct calls with --enable effects" = //end function test3(x, cont){ function F(symbol){function f(x){return x + 1 | 0;} return [0, f];} - var M1 = F(undef), M2 = F(undef), _c_ = (0, M2[1])(2); + var M1 = F(), M2 = F(), _c_ = (0, M2[1])(2); return cont([0, (0, M1[1])(1), _c_]); } //end @@ -188,7 +188,7 @@ let%expect_test "direct calls with --enable effects" = function f(x, cont){return caml_cps_call3(Stdlib_Printf[2], _a_, x, cont);} return [0, f]; } - var M1 = F(undef), M2 = F(undef); + var M1 = F(), M2 = F(); return caml_cps_exact_call2 (M1[1], 1, diff --git a/compiler/tests-compiler/effects_toplevel.ml b/compiler/tests-compiler/effects_toplevel.ml index 596b47f1b7..9b3e06aef4 100644 --- a/compiler/tests-compiler/effects_toplevel.ml +++ b/compiler/tests-compiler/effects_toplevel.ml @@ -65,7 +65,7 @@ let%expect_test "test-compiler/lib-effects/test1.ml" = return caml_callback (function(cont){ var - undef = undefined, + dummy = 0, global_data = runtime.caml_get_global_data(), Stdlib_Printf = global_data.Stdlib__Printf, _a_ = @@ -75,18 +75,17 @@ let%expect_test "test-compiler/lib-effects/test1.ml" = function g(param, cont){ return caml_cps_call2(Stdlib_Printf[2], _a_, cont); } - caml_callback(g, [undef]); + caml_callback(g, [dummy]); function _b_(i){ return caml_cps_exact_call2 (g, - undef, + dummy, function(_c_){ var _d_ = i + 1 | 0; if(5 !== i) return caml_cps_exact_call1(_b_, _d_); - caml_callback(g, [undef]); + caml_callback(g, [dummy]); var Test = [0]; runtime.caml_register_global(2, Test, "Test"); - return; }); } return _b_(1); diff --git a/compiler/tests-compiler/global_deadcode.ml b/compiler/tests-compiler/global_deadcode.ml index d262c999eb..4103bea426 100644 --- a/compiler/tests-compiler/global_deadcode.ml +++ b/compiler/tests-compiler/global_deadcode.ml @@ -125,8 +125,7 @@ let%expect_test "Omit unused return expressions" = in (* Expect return value of f to be omitted. *) print_fun_decl program (Some "f"); - [%expect - {| - function f(x){caml_call1(Stdlib[44], x); return;} + [%expect {| + function f(x){caml_call1(Stdlib[44], x);} //end |}] diff --git a/compiler/tests-full/stdlib.cma.expected.js b/compiler/tests-full/stdlib.cma.expected.js index 019f19cab4..254b13f11a 100644 --- a/compiler/tests-full/stdlib.cma.expected.js +++ b/compiler/tests-full/stdlib.cma.expected.js @@ -4880,7 +4880,7 @@ : runtime.caml_call_gen(f, [a0, a1]); } var - undef = undefined, + dummy = 0, global_data = runtime.caml_get_global_data(), Stdlib = global_data.Stdlib, Stdlib_Uchar = global_data.Stdlib__Uchar, @@ -5713,7 +5713,6 @@ /*<>*/ return; } /*<>*/ /*<>*/ caml_bytes_set16(b, i, x); - /*<>*/ return; /*<>*/ } function unsafe_set_uint16_be(b, i, x){ /*<>*/ if(Stdlib_Sys[11]){ @@ -5722,7 +5721,6 @@ } /*<>*/ /*<>*/ caml_bytes_set16 (b, i, caml_bswap16(x)); - /*<>*/ return; /*<>*/ } function set_int16_le(b, i, x){ /*<>*/ return Stdlib_Sys[11] @@ -5974,7 +5972,6 @@ function set_utf_8_uchar(b, i, u){ /*<>*/ function set(_i_, _h_, _g_){ /*<>*/ caml_bytes_unsafe_set(_i_, _h_, _g_); - return; } /*<>*/ var /*<>*/ max = @@ -10024,7 +10021,6 @@ : runtime.caml_call_gen(f, [a0, a1, a2, a3, a4]); } /*<>*/ var - undef = undefined, global_data = runtime.caml_get_global_data(), Stdlib_Obj = global_data.Stdlib__Obj, Stdlib_Array = global_data.Stdlib__Array, @@ -10050,7 +10046,8 @@ 0, 0, 0, - 0]; + 0], + dummy = 0; function grow_stacks(param){ /*<>*/ var oldsize = env[5], @@ -10076,7 +10073,6 @@ (Stdlib_Array[9], env[4], 0, new_end, 0, oldsize); env[4] = new_end; env[5] = newsize; - return; /*<>*/ } function clear_parser(param){ /*<>*/ /*<>*/ caml_call4 @@ -12666,7 +12662,6 @@ : runtime.caml_call_gen(f, [a0, a1, a2, a3, a4]); } var - undef = undefined, global_data = runtime.caml_get_global_data(), Stdlib_Bytes = global_data.Stdlib__Bytes, Stdlib_Sys = global_data.Stdlib__Sys, @@ -12677,7 +12672,8 @@ cst_Buffer_sub = "Buffer.sub", cst_Buffer_blit = "Buffer.blit", cst_Buffer_nth = "Buffer.nth", - cst_Buffer_add_cannot_grow_buf = "Buffer.add: cannot grow buffer"; + cst_Buffer_add_cannot_grow_buf = "Buffer.add: cannot grow buffer", + dummy = 0; function create(n){ /*<>*/ var n$0 = 1 <= n ? n : 1, @@ -12760,7 +12756,6 @@ /*<>*/ /*<>*/ caml_call5 (Stdlib_Bytes[11], b[1][1], 0, new_buffer, 0, b[2]); b[1] = [0, new_buffer, new_len[1]]; - return; /*<>*/ } function add_char(b, c){ /*<>*/ var @@ -13468,7 +13463,7 @@ : runtime.caml_call_gen(f, [a0, a1, a2, a3, a4]); } var - undef = undefined, + dummy = 0, global_data = runtime.caml_get_global_data(), Stdlib_Condition = global_data.Stdlib__Condition, Stdlib_Mutex = global_data.Stdlib__Mutex, @@ -13487,7 +13482,6 @@ st = /*<>*/ caml_make_vect(8, none); /*<>*/ /*<>*/ runtime.caml_domain_dls_set (st); - /*<>*/ return; /*<>*/ } /*<>*/ create_dls(0); /*<>*/ var @@ -13589,10 +13583,7 @@ /*<>*/ first_domain_spawned = /*<>*/ caml_call1(Stdlib_Atomic[1], 0), /*<>*/ first_spawn_function = - [0, - function(param){ - /*<>*/ return; - /*<>*/ }], + [0, function(param){ /*<>*/ }], cst_first_domain_already_spawn = "first domain already spawned"; function before_first_spawn(f){ /*<>*/ if @@ -13800,7 +13791,7 @@ : runtime.caml_call_gen(f, [a0, a1, a2, a3, a4]); } var - undef = undefined, + dummy = 0, global_data = runtime.caml_get_global_data(), cst$9 = "%{", cst$10 = "%}", @@ -14030,14 +14021,12 @@ (Stdlib_Bytes[11], buf[2], 0, new_str, 0, len); buf[2] = new_str; } - return; /*<>*/ } function buffer_add_char(buf, c){ /*<>*/ buffer_check_size(buf, 1); /*<>*/ /*<>*/ caml_bytes_set (buf[2], buf[1], c); buf[1] = buf[1] + 1 | 0; - return; /*<>*/ } function buffer_add_string(buf, s){ /*<>*/ var @@ -14046,7 +14035,6 @@ /*<>*/ /*<>*/ caml_call5 (Stdlib_String[6], s, 0, buf[2], buf[1], str_len); buf[1] = buf[1] + str_len | 0; - return; /*<>*/ } function buffer_contents(buf){ /*<>*/ return caml_call3 @@ -14146,7 +14134,6 @@ } if(prec) /*<>*/ return buffer_add_string(buf, cst); - /*<>*/ return; /*<>*/ } function bprint_iconv_flag(buf, iconv){ /*<>*/ switch(iconv){ @@ -14185,7 +14172,6 @@ } if(8 <= fconv[2]) /*<>*/ return buffer_add_char(buf, 35); - /*<>*/ return; /*<>*/ } function string_of_formatting_lit(formatting_lit){ /*<>*/ if @@ -14242,7 +14228,6 @@ i = _cP_; } } - return; /*<>*/ } function bprint_fmtty(buf, fmtty){ /*<>*/ var fmtty$0 = fmtty; @@ -14848,189 +14833,127 @@ function fmtty_rel_det(param){ /*<>*/ if(typeof param === "number") /*<>*/ return [0, - function(param){ - /*<>*/ return; - /*<>*/ }, - function(param){ - /*<>*/ return; - /*<>*/ }, - function(param){ - /*<>*/ return; - /*<>*/ }, - function(param){ - /*<>*/ return; - /*<>*/ }]; + , + function(param){ /*<>*/ }, + , + function(param){ /*<>*/ }]; switch(param[0]){ case 0: /*<>*/ var rest = param[1], /*<>*/ match = fmtty_rel_det(rest), de = match[4], - ed = match[3], - af = match[2], - fa = match[1]; + af = match[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ af(0); - /*<>*/ return; /*<>*/ }, - ed, + , de]; case 1: /*<>*/ var rest$0 = param[1], /*<>*/ match$0 = fmtty_rel_det(rest$0), de$0 = match$0[4], - ed$0 = match$0[3], - af$0 = match$0[2], - fa$0 = match$0[1]; + af$0 = match$0[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa$0(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ af$0(0); - /*<>*/ return; /*<>*/ }, - ed$0, + , de$0]; case 2: /*<>*/ var rest$1 = param[1], /*<>*/ match$1 = fmtty_rel_det(rest$1), de$1 = match$1[4], - ed$1 = match$1[3], - af$1 = match$1[2], - fa$1 = match$1[1]; + af$1 = match$1[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa$1(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ af$1(0); - /*<>*/ return; /*<>*/ }, - ed$1, + , de$1]; case 3: /*<>*/ var rest$2 = param[1], /*<>*/ match$2 = fmtty_rel_det(rest$2), de$2 = match$2[4], - ed$2 = match$2[3], - af$2 = match$2[2], - fa$2 = match$2[1]; + af$2 = match$2[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa$2(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ af$2(0); - /*<>*/ return; /*<>*/ }, - ed$2, + , de$2]; case 4: /*<>*/ var rest$3 = param[1], /*<>*/ match$3 = fmtty_rel_det(rest$3), de$3 = match$3[4], - ed$3 = match$3[3], - af$3 = match$3[2], - fa$3 = match$3[1]; + af$3 = match$3[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa$3(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ af$3(0); - /*<>*/ return; /*<>*/ }, - ed$3, + , de$3]; case 5: /*<>*/ var rest$4 = param[1], /*<>*/ match$4 = fmtty_rel_det(rest$4), de$4 = match$4[4], - ed$4 = match$4[3], - af$4 = match$4[2], - fa$4 = match$4[1]; + af$4 = match$4[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa$4(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ af$4(0); - /*<>*/ return; /*<>*/ }, - ed$4, + , de$4]; case 6: /*<>*/ var rest$5 = param[1], /*<>*/ match$5 = fmtty_rel_det(rest$5), de$5 = match$5[4], - ed$5 = match$5[3], - af$5 = match$5[2], - fa$5 = match$5[1]; + af$5 = match$5[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa$5(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ af$5(0); - /*<>*/ return; /*<>*/ }, - ed$5, + , de$5]; case 7: /*<>*/ var rest$6 = param[1], /*<>*/ match$6 = fmtty_rel_det(rest$6), de$6 = match$6[4], - ed$6 = match$6[3], - af$6 = match$6[2], - fa$6 = match$6[1]; + af$6 = match$6[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa$6(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ af$6(0); - /*<>*/ return; /*<>*/ }, - ed$6, + , de$6]; case 8: /*<>*/ var rest$7 = param[2], /*<>*/ match$7 = fmtty_rel_det(rest$7), de$7 = match$7[4], - ed$7 = match$7[3], - af$7 = match$7[2], - fa$7 = match$7[1]; + af$7 = match$7[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa$7(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ af$7(0); - /*<>*/ return; /*<>*/ }, - ed$7, + , de$7]; case 9: /*<>*/ var @@ -15039,54 +14962,34 @@ ty1 = param[1], /*<>*/ match$8 = fmtty_rel_det(rest$8), de$8 = match$8[4], - ed$8 = match$8[3], af$8 = match$8[2], - fa$8 = match$8[1], /*<>*/ ty = trans(symm(ty1), ty2), /*<>*/ match$9 = fmtty_rel_det(ty), jd = match$9[4], - dj = match$9[3], - ga = match$9[2], - ag = match$9[1]; + ga = match$9[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa$8(0); - /*<>*/ ag(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ ga(0); /*<>*/ af$8(0); - /*<>*/ return; /*<>*/ }, - function(param){ - /*<>*/ ed$8(0); - /*<>*/ dj(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ jd(0); /*<>*/ de$8(0); - /*<>*/ return; /*<>*/ }]; case 10: /*<>*/ var rest$9 = param[1], /*<>*/ match$10 = fmtty_rel_det(rest$9), de$9 = match$10[4], - ed$9 = match$10[3], - af$9 = match$10[2], - fa$9 = match$10[1]; + af$9 = match$10[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa$9(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ af$9(0); - /*<>*/ return; /*<>*/ }, - ed$9, + , de$9]; case 11: /*<>*/ var @@ -15094,19 +14997,13 @@ /*<>*/ match$11 = fmtty_rel_det(rest$10), de$10 = match$11[4], - ed$10 = match$11[3], - af$10 = match$11[2], - fa$10 = match$11[1]; + af$10 = match$11[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa$10(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ af$10(0); - /*<>*/ return; /*<>*/ }, - ed$10, + , de$10]; case 12: /*<>*/ var @@ -15114,19 +15011,13 @@ /*<>*/ match$12 = fmtty_rel_det(rest$11), de$11 = match$12[4], - ed$11 = match$12[3], - af$11 = match$12[2], - fa$11 = match$12[1]; + af$11 = match$12[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa$11(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ af$11(0); - /*<>*/ return; /*<>*/ }, - ed$11, + , de$11]; case 13: /*<>*/ var @@ -15134,25 +15025,15 @@ /*<>*/ match$13 = fmtty_rel_det(rest$12), de$12 = match$13[4], - ed$12 = match$13[3], - af$12 = match$13[2], - fa$12 = match$13[1]; + af$12 = match$13[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa$12(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ af$12(0); - /*<>*/ return; /*<>*/ }, - function(param){ - /*<>*/ ed$12(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ de$12(0); - /*<>*/ return; /*<>*/ }]; default: /*<>*/ var @@ -15160,25 +15041,15 @@ /*<>*/ match$14 = fmtty_rel_det(rest$13), de$13 = match$14[4], - ed$13 = match$14[3], - af$13 = match$14[2], - fa$13 = match$14[1]; + af$13 = match$14[2]; /*<>*/ return [0, - function(param){ - /*<>*/ fa$13(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ af$13(0); - /*<>*/ return; /*<>*/ }, - function(param){ - /*<>*/ ed$13(0); - /*<>*/ return; - /*<>*/ }, + , function(param){ /*<>*/ de$13(0); - /*<>*/ return; /*<>*/ }]; } } @@ -17057,7 +16928,6 @@ /*<>*/ /*<>*/ caml_bytes_set (buf, pos[1], c); pos[1]++; - return; /*<>*/ } /*<>*/ var /*<>*/ left = @@ -19010,7 +18880,6 @@ (failwith_message(_C_), str, str_ind, _bd_); } flag[1] = 1; - return; /*<>*/ } a: b: @@ -19476,7 +19345,6 @@ i = _a$_; } } - return; /*<>*/ }, fail_single_percent = function(str_ind){ @@ -21564,7 +21432,6 @@ : runtime.caml_call_gen(f, [a0, a1, a2, a3, a4, a5]); } /*<>*/ var - undef = undefined, global_data = runtime.caml_get_global_data(), cst$7 = "\n", cst$4 = cst$8, @@ -21749,7 +21616,8 @@ _n_ = [0, [2, 0, 0], cst_s], _o_ = [0, [2, 0, 0], cst_s], _p_ = [0, [2, 0, 0], cst_s], - _q_ = [0, [2, 0, 0], cst_s]; + _q_ = [0, [2, 0, 0], cst_s], + dummy = 0; function int_of_string_opt(x){ /*<>*/ try{ /*<>*/ /*<>*/ var @@ -23049,7 +22917,6 @@ : runtime.caml_call_gen(f, [a0, a1]); } var - undef = undefined, global_data = runtime.caml_get_global_data(), Stdlib_Printexc = global_data.Stdlib__Printexc, Stdlib = global_data.Stdlib; @@ -23083,6 +22950,7 @@ /*<>*/ caml_call2 (Stdlib[28], cst_Fun_Finally_raised, _a_)]; /*<>*/ }); + var dummy = 0; function protect(finally$0, work){ function finally_no_exn(param){ /*<>*/ try{ @@ -24204,7 +24072,7 @@ : runtime.caml_call_gen(f, [a0, a1, a2]); } var - undef = undefined, + dummy = 0, global_data = runtime.caml_get_global_data(), Stdlib = global_data.Stdlib, Stdlib_Array = global_data.Stdlib__Array, @@ -24270,7 +24138,6 @@ j = _an_; } } - return; /*<>*/ } function floop(arr, idx, f, col, max){ /*<>*/ if(0 > col){ @@ -24292,7 +24159,6 @@ j = _ak_; } } - return; /*<>*/ } function init(kind, layout, dims, f){ /*<>*/ var @@ -27112,7 +26978,7 @@ : runtime.caml_call_gen(f, [a0, a1, a2, a3]); } var - undef = undefined, + dummy = 0, global_data = runtime.caml_get_global_data(), cst$14 = ".", cst$11 = cst$15, @@ -27173,7 +27039,6 @@ /*<>*/ state[9] = state[9] - size | 0; /*<>*/ pp_output_string(state, text); state[11] = 0; - return; /*<>*/ } function format_string(state, s){ /*<>*/ /*<>*/ var @@ -27452,7 +27317,6 @@ } /*<>*/ return; } - /*<>*/ return; /*<>*/ } function scan_push(state, b, token){ /*<>*/ pp_enqueue(state, token); @@ -32760,7 +32624,7 @@ : runtime.caml_call_gen(f, [a0, a1, a2]); } var - undef = undefined, + dummy = 0, global_data = runtime.caml_get_global_data(), _c_ = [0, 0], _b_ = [0, 0], @@ -32911,7 +32775,6 @@ nidx = key_index(h, hkey); /*<>*/ ndata[1 + nidx] = [0, hkey, data, caml_check_bound(ndata, nidx)[1 + nidx]]; - /*<>*/ return; /*<>*/ }, /*<>*/ _af_ = osize - 1 | 0, /*<>*/ _ae_ = 0; @@ -34062,7 +33925,6 @@ : runtime.caml_call_gen(f, [a0, a1, a2, a3]); } var - undef = undefined, global_data = runtime.caml_get_global_data(), cst$18 = cst$19, cst$17 = cst$19, @@ -34238,6 +34100,7 @@ : 0; /*<>*/ } var + dummy = 0, _h_ = [0, 7, 0], _g_ = [0, 1, [0, 3, [0, 5, 0]]], _f_ = [0, [2, 0, [4, 6, [0, 2, 6], 0, [2, 0, 0]]], "%s%06x%s"], @@ -34593,7 +34456,6 @@ j = _L_; } } - return; /*<>*/ } /*<>*/ loop(0); /*<>*/ return /*<>*/ caml_call1