diff --git a/compiler/lib/driver.ml b/compiler/lib/driver.ml index e6a6eb615e..469492fc40 100644 --- a/compiler/lib/driver.ml +++ b/compiler/lib/driver.ml @@ -218,6 +218,7 @@ let round profile : 'a -> 'a = +> tailcall +> Ref_unboxing.f +> (flow +> specialize +> eval +> fst) + +> Unboxing.f +> inline profile +> phi +> deadcode diff --git a/compiler/lib/stdlib.ml b/compiler/lib/stdlib.ml index 1cf5ea7465..bb48792a87 100644 --- a/compiler/lib/stdlib.ml +++ b/compiler/lib/stdlib.ml @@ -201,6 +201,14 @@ module List = struct let append l1 l2 = count_append l1 l2 0 [@@if ocaml_version < (5, 1, 0)] + let find_index ~f = + let rec aux i = function + | [] -> None + | a :: l -> if f a then Some i else aux (i + 1) l + in + aux 0 + [@@if ocaml_version < (5, 1, 0)] + let group l ~f = let rec loop (l : 'a list) (this_group : 'a list) (acc : 'a list list) : 'a list list = diff --git a/compiler/lib/unboxing.ml b/compiler/lib/unboxing.ml new file mode 100644 index 0000000000..f32ecc2658 --- /dev/null +++ b/compiler/lib/unboxing.ml @@ -0,0 +1,668 @@ +(* Js_of_ocaml compiler + * http://www.ocsigen.org/js_of_ocaml/ + * Copyright (C) 2025 Jérome Vouillon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, with linking exception; + * either version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *) + +(* +Unboxing wrappers should be inlined + +Also look at function calls to see whether we know more about a parameter. +*) + +open! Stdlib + +let debug = Debug.find "unboxing" + +let times = Debug.find "times" + +let show_stats = Debug.find "stats" + +open Code + +type stats = + { mutable continuations : int + ; mutable call_sites : int + } + +type loc = + | Block of Addr.t + | Closure of Var.t * Var.t list + +type tuple = + { size : int + ; kind : field_type + ; loc : loc + ; start_pc : Addr.t + ; closure_pc : Addr.t option + ; mutable needed : IntSet.t + } + +let is_unboxing_wrapper p pc = + let block = Addr.Map.find pc p.blocks in + let rec check instrs = + match instrs with + | Let (_, Field _) :: rem -> check rem + | [ Let (x, Apply _) ] -> ( + match block.branch with + | Return y -> Var.equal x y + | _ -> false) + | _ -> false + in + check block.body + +let find_candidates p = + let unboxed_floats = + match Config.target () with + | `JavaScript -> false + | `Wasm -> false + in + let tbl = Var.Hashtbl.create 16 in + let visited = BitSet.create' p.free_pc in + let allowed_access kind = + match kind with + | Non_float -> true + | Float -> unboxed_floats + in + let rec traverse closure_pc start_pc pc params seen_apply = + if not (BitSet.mem visited pc) + then ( + BitSet.set visited pc; + let block = Addr.Map.find pc p.blocks in + let params = + List.fold_left + ~f:(fun s x -> Var.Map.add x (Block pc) s) + ~init:params + block.params + in + let seen_apply = ref seen_apply in + List.iter + ~f:(fun i -> + match i with + | Let (_, Field (x, n, kind)) when Var.Map.mem x params && allowed_access kind + -> + if !seen_apply + then (* A function call could have mutated the block through + an alias, so the field value may be stale after + unboxing. Discard this candidate. *) + Var.Hashtbl.remove tbl x + else + let size = n + 1 in + let tuple = + try Var.Hashtbl.find tbl x + with Not_found -> + { size = 0 + ; loc = Var.Map.find x params + ; start_pc + ; closure_pc + ; kind + ; needed = IntSet.empty + } + in + if tuple.size < size then Var.Hashtbl.replace tbl x { tuple with size } + | Let (_, Apply _) -> seen_apply := true + | Let (y, Closure (params, (pc', args), _)) when not (is_unboxing_wrapper p pc') + -> + traverse + (Some pc) + pc' + pc' + (List.fold_left + ~f:(fun s x -> Var.Map.remove x s) + ~init: + (List.fold_left + ~f:(fun s x -> Var.Map.add x (Closure (y, params)) s) + ~init:Var.Map.empty + params) + args) + false + | _ -> ()) + block.body; + match block.branch with + | Branch (pc', _) -> traverse closure_pc start_pc pc' params !seen_apply + | _ -> + Code.fold_children + p.blocks + pc + (fun pc' () -> traverse closure_pc start_pc pc' Var.Map.empty false) + ()) + in + traverse None p.start p.start Var.Map.empty false; + tbl + +let check_tuple_accesses p tbl = + let discard x = Var.Hashtbl.remove tbl x in + let visited = BitSet.create' p.free_pc in + let rec traverse pc = + if not (BitSet.mem visited pc) + then ( + BitSet.set visited pc; + let block = Addr.Map.find pc p.blocks in + List.iter + ~f:(fun i -> + match i with + | Let (_, Field (x, n, _)) -> + if Var.Hashtbl.mem tbl x + then + let tuple = Var.Hashtbl.find tbl x in + if n < tuple.size + then tuple.needed <- IntSet.add n tuple.needed + else discard x + | Let (_, Closure (_, (pc', _), _)) -> traverse pc' + | Assign (x, y) -> + discard x; + discard y + | _ -> Freevars.iter_instr_free_vars discard i) + block.body; + Freevars.iter_last_free_var discard block.branch; + Code.fold_children p.blocks pc (fun pc' () -> traverse pc') ()) + in + Var.Hashtbl.iter + (fun _ tuple -> + traverse + (match tuple.loc with + | Block pc -> pc + | Closure _ -> tuple.start_pc)) + tbl + +(* Check that there is no function call between the start of the + scope and the last field access. A function call could update a + mutable block through an alias, making the unboxed field values + stale. *) +let check_no_apply_before_field_access p tbl = + Var.Hashtbl.filter_map_inplace + (fun x tuple -> + let start_pc = + match tuple.loc with + | Block pc -> pc + | Closure _ -> tuple.start_pc + in + let visited = BitSet.create' p.free_pc in + (* Walk the scope of this candidate. Return true if we find + an Apply that can reach a Field access on [x]. We do this + by tracking [seen_apply] through the traversal. *) + let dominated_by_apply = ref false in + let rec traverse pc seen_apply = + if not (BitSet.mem visited pc) + then ( + BitSet.set visited pc; + let block = Addr.Map.find pc p.blocks in + let seen_apply = ref seen_apply in + List.iter + ~f:(fun i -> + match i with + | Let (_, Field (y, _, _)) when Var.equal x y -> + if !seen_apply then dominated_by_apply := true + | Let (_, Apply _) -> seen_apply := true + | Let (_, Closure (_, (pc', _), _)) -> traverse pc' false + | _ -> ()) + block.body; + Code.fold_children p.blocks pc (fun pc' () -> traverse pc' !seen_apply) ()) + in + traverse start_pc false; + if !dominated_by_apply then None else Some tuple) + tbl + +let check_call_sites p tbl = + let relevant_closures = + Var.Hashtbl.fold + (fun _ tuple s -> + match tuple.loc with + | Block _ -> s + | Closure (x, _) -> Var.Set.add x s) + tbl + Var.Set.empty + in + let visited = BitSet.create' p.free_pc in + let rec traverse pc state = + if BitSet.mem visited pc + then state + else ( + BitSet.set visited pc; + let block = Addr.Map.find pc p.blocks in + let state = + List.fold_left + ~f:(fun state i -> + match i with + | Let (_, Apply { f; exact = true; _ }) when Var.Set.mem f relevant_closures + -> + let closures, locations = state in + Var.Set.add f closures, Addr.Set.add pc locations + | Let (_, Closure (_, (pc', _), _)) -> traverse pc' state + | _ -> state) + ~init:state + block.body + in + Code.fold_children p.blocks pc traverse state) + in + let closures, locations = + Var.Hashtbl.fold + (fun _ tuple state -> + match tuple.loc, tuple.closure_pc with + | Block _, _ -> state + | Closure _, Some pc -> traverse pc state + | Closure _, None -> assert false) + tbl + (Var.Set.empty, Addr.Set.empty) + in + Var.Hashtbl.filter_map_inplace + (fun _ tuple -> + match tuple.loc with + | Closure (x, _) when not (Var.Set.mem x closures) -> None + | _ -> Some tuple) + tbl; + locations + +let check_eliminates_tuple p tbl = + (* 1. Collect all variables defined as Block literals *) + let is_block = ref Var.Set.empty in + let visited = BitSet.create' p.free_pc in + let rec collect pc = + if not (BitSet.mem visited pc) + then ( + BitSet.set visited pc; + let block = Addr.Map.find pc p.blocks in + List.iter + ~f:(fun i -> + match i with + | Let (x, Block _) | Let (x, Constant (Tuple _)) -> + is_block := Var.Set.add x !is_block + | Let (_, Closure (_, (pc', _), _)) -> collect pc' + | _ -> ()) + block.body; + Code.fold_children p.blocks pc (fun pc' () -> collect pc') ()) + in + collect p.start; + let is_block = !is_block in + (* 2. Build reverse maps for efficient lookup *) + (* block_cands: target_pc -> (candidate var * position) list *) + let block_cands = ref Addr.Map.empty in + (* closure_cands: closure var -> (candidate var * position) list *) + let closure_cands = Var.Hashtbl.create 16 in + Var.Hashtbl.iter + (fun x tuple -> + match tuple.loc with + | Block pc -> + let block = Addr.Map.find pc p.blocks in + let i = Option.get (List.find_index ~f:(fun y -> Var.equal x y) block.params) in + block_cands := + Addr.Map.update + pc + (function + | None -> Some [ x, i ] + | Some l -> Some ((x, i) :: l)) + !block_cands + | Closure (f, params) -> + let i = Option.get (List.find_index ~f:(fun y -> Var.equal x y) params) in + let existing = + try Var.Hashtbl.find closure_cands f with Not_found -> [] + in + Var.Hashtbl.replace closure_cands f ((x, i) :: existing)) + tbl; + let block_cands = !block_cands in + (* 3. Traverse, check branches and call sites *) + let useful = ref Var.Set.empty in + let mark_useful_cont (pc', args) = + match Addr.Map.find pc' block_cands with + | lst -> + List.iter + ~f:(fun (x, pos) -> + if pos < List.length args && Var.Set.mem (List.nth args pos) is_block + then useful := Var.Set.add x !useful) + lst + | exception Not_found -> () + in + let visited2 = BitSet.create' p.free_pc in + let rec check pc = + if not (BitSet.mem visited2 pc) + then ( + BitSet.set visited2 pc; + let block = Addr.Map.find pc p.blocks in + List.iter + ~f:(fun i -> + match i with + | Let (_, Apply { f; args; exact = true; _ }) -> ( + match Var.Hashtbl.find closure_cands f with + | lst -> + List.iter + ~f:(fun (x, pos) -> + if pos < List.length args + && Var.Set.mem (List.nth args pos) is_block + then useful := Var.Set.add x !useful) + lst + | exception Not_found -> ()) + | Let (_, Closure (_, (pc', _), _)) -> check pc' + | _ -> ()) + block.body; + (match block.branch with + | Branch cont -> mark_useful_cont cont + | Cond (_, c1, c2) -> + mark_useful_cont c1; + mark_useful_cont c2 + | Switch (_, conts) -> Array.iter ~f:mark_useful_cont conts + | Pushtrap (c1, _, c2) -> + mark_useful_cont c1; + mark_useful_cont c2 + | Poptrap cont -> mark_useful_cont cont + | Return _ | Raise _ | Stop -> ()); + Code.fold_children p.blocks pc (fun pc' () -> check pc') ()) + in + check p.start; + let useful = !useful in + (* 4. Filter *) + Var.Hashtbl.filter_map_inplace + (fun x tuple -> if Var.Set.mem x useful then Some tuple else None) + tbl + +let rewrite_blocks p tbl = + let ops = Int.Hashtbl.create 16 in + let blocks = + Var.Hashtbl.fold + (fun x tuple blocks -> + match tuple.loc with + | Closure _ -> blocks + | Block pc -> + Addr.Map.update + pc + (fun block -> + match block with + | None -> assert false + | Some block -> + let vars = Array.init tuple.size ~f:(fun _ -> Var.fresh ()) in + let i = List.find_index ~f:(fun y -> Var.equal x y) block.params in + Int.Hashtbl.add ops pc (Option.get i, tuple.size, tuple.kind); + Some + { block with + params = + Array.to_list vars + @ List.filter ~f:(fun y -> not (Var.equal x y)) block.params + ; body = + Let + ( x + , Block + ( (match tuple.kind with + | Float -> 254 + | Non_float -> 0) + , vars + , NotArray + , Immutable ) ) + :: block.body + }) + blocks) + tbl + p.blocks + in + { p with blocks }, ops + +let inserted_block ops (pc, args) = + let l = Int.Hashtbl.find_all ops pc in + let body, args' = + List.fold_right + ~f:(fun (i, n, kind) (body, args) -> + let x = List.nth args i in + let args = List.filteri ~f:(fun i' _ -> i <> i') args in + let vars = List.init ~len:n ~f:(fun _ -> Var.fresh ()) in + let body = List.mapi ~f:(fun i y -> Let (y, Field (x, i, kind))) vars @ body in + body, vars @ args) + l + ~init:([], args) + in + { params = List.map ~f:(fun _ -> Var.fresh ()) args; body; branch = Branch (pc, args') } + +let rewrite_args lst args = + List.fold_right + ~f:(fun (i, needed, kind) (code, args) -> + let x = List.nth args i in + let args = List.filteri ~f:(fun i' _ -> i <> i') args in + let vars = IntSet.fold (fun i vars -> (i, Var.fresh ()) :: vars) needed [] in + let code = List.map ~f:(fun (i, y) -> Let (y, Field (x, i, kind))) vars @ code in + code, List.rev_map ~f:snd vars @ args) + lst + ~init:([], args) + +let rewrite_continuations stats p tbl ops closure_ops locations = + let conts = Poly.Hashtbl.create 16 in + let free_pc = ref p.free_pc in + let new_blocks = ref [] in + let add_block block = + let pc = !free_pc in + incr free_pc; + new_blocks := (pc, block) :: !new_blocks; + pc + in + let rewrite ((pc, args) as cont) = + if Int.Hashtbl.mem ops pc + then ( + try Hashtbl.find conts cont + with Not_found -> + stats.continuations <- stats.continuations + 1; + let pc' = add_block (inserted_block ops cont) in + let cont' = pc', args in + Hashtbl.add conts cont cont'; + cont') + else cont + in + let rewritten = BitSet.create' p.free_pc in + let rewrite_body body = + List.fold_right + ~f:(fun i rem -> + match i with + | Let (x, Apply { f; args; exact = true }) when Var.Hashtbl.mem closure_ops f -> + stats.call_sites <- stats.call_sites + 1; + let f', _, _, lst = Var.Hashtbl.find closure_ops f in + let code, args = rewrite_args lst args in + code @ (Let (x, Apply { f = f'; args; exact = true }) :: rem) + | Let (f, Closure (params, cont, loc)) -> ( + match Var.Hashtbl.find closure_ops f with + | f', params', body, lst -> + let pc' = + add_block { params = []; body; branch = Branch (rewrite cont) } + in + Let (f', Closure (params', (pc', []), loc)) + :: + (*ZZZ events *) + (let params'' = List.map ~f:(fun _ -> Var.fresh ()) params in + let pc'' = + let code, args'' = rewrite_args lst params'' in + let call = Apply { f = f'; args = args''; exact = true } in + let res = Var.fresh () in + add_block + { params = [] + ; body = code @ [ Let (res, call) ] + ; branch = Return res + } + in + Let (f, Closure (params'', (pc'', []), loc)) :: rem) + | exception Not_found -> Let (f, Closure (params, rewrite cont, loc)) :: rem) + | _ -> i :: rem) + body + ~init:[] + in + let rewrite_block pc blocks = + if BitSet.mem rewritten pc + then blocks + else ( + BitSet.set rewritten pc; + Addr.Map.update + pc + (fun block -> + match block with + | None -> assert false + | Some block -> + let body = + if + List.exists + ~f:(fun i -> + match i with + | Let (_, Closure _) -> true + | Let (_, Apply { f; exact = true; _ }) -> + Var.Hashtbl.mem closure_ops f + | _ -> false) + block.body + then rewrite_body block.body + else block.body + in + let branch = + match block.branch with + | Return _ | Raise _ | Stop -> block.branch + | Branch cont -> Branch (rewrite cont) + | Cond (x, cont, cont') -> Cond (x, rewrite cont, rewrite cont') + | Switch (x, l) -> Switch (x, Array.map ~f:rewrite l) + | Pushtrap (cont, x, cont') -> Pushtrap (rewrite cont, x, rewrite cont') + | Poptrap cont -> Poptrap (rewrite cont) + in + Some { block with body; branch }) + blocks) + in + let visited = BitSet.create' p.free_pc in + let rec traverse pc blocks = + if BitSet.mem visited pc + then blocks + else ( + BitSet.set visited pc; + let blocks = rewrite_block pc blocks in + Code.fold_children p.blocks pc (fun pc' blocks -> traverse pc' blocks) blocks) + in + let blocks = + Var.Hashtbl.fold + (fun _ tuple blocks -> + traverse + tuple.start_pc + (match tuple.closure_pc with + | None -> blocks + | Some pc -> rewrite_block pc blocks)) + tbl + p.blocks + in + let blocks = Addr.Set.fold rewrite_block locations blocks in + { p with + free_pc = !free_pc + ; blocks = + List.fold_left + ~f:(fun blocks (pc, block) -> Addr.Map.add pc block blocks) + ~init:blocks + !new_blocks + } + +let closure_operations tbl = + let closure_ops = Var.Hashtbl.create 16 in + Var.Hashtbl.iter + (fun x tuple -> + match tuple.loc with + | Block _ -> () + | Closure (f, params) -> + let f', params, body, lst = + try Var.Hashtbl.find closure_ops f + with Not_found -> Var.fork f, params, [], [] + in + let vars = + IntSet.fold + (fun i m -> IntMap.add i (Var.fresh ()) m) + tuple.needed + IntMap.empty + in + let i = List.find_index ~f:(fun y -> Var.equal x y) params in + let params = + List.map ~f:snd (IntMap.bindings vars) + @ List.filter ~f:(fun y -> not (Var.equal x y)) params + in + let c = Var.fresh () in + let body = + Let + ( c + , Constant + (match tuple.kind with + | Float -> Float (Int64.bits_of_float 0.) + | Non_float -> Int (Targetint.of_int_exn 0)) ) + :: Let + ( x + , Block + ( (match tuple.kind with + | Float -> 254 + | Non_float -> 0) + , Array.init + ~f:(fun i -> try IntMap.find i vars with Not_found -> c) + tuple.size + , NotArray + , Immutable ) ) + :: body + in + let lst = (Option.get i, tuple.needed, tuple.kind) :: lst in + Var.Hashtbl.replace closure_ops f (f', params, body, lst)) + tbl; + closure_ops + +let f p = + let stats = { continuations = 0; call_sites = 0 } in + let t = Timer.make () in + (* Find parameters that could be unboxed *) + let tbl = find_candidates p in + if debug () + then ( + Format.eprintf "Unboxing candidates:@."; + Var.Hashtbl.iter + (fun x { size; _ } -> Format.eprintf " %a: %d@." Var.print x size) + tbl); + (* Check that the tuples don't escape and that we don't access more + fields than expected *) + check_tuple_accesses p tbl; + (* Discard candidates where a function call could mutate the block + through an alias before a field access *) + check_no_apply_before_field_access p tbl; + (* Do not unbox function parameters when we are using too many of + their fields *) + Var.Hashtbl.filter_map_inplace + (fun _ tuple -> + match tuple.loc with + | Closure _ when IntSet.cardinal tuple.needed > 6 -> None + | _ -> Some tuple) + tbl; + (* Only unbox closure parameters when they have at least one known + call site *) + let locations = check_call_sites p tbl in + (* Only unbox when at least one call site / branch passes a Block + literal, so that we actually eliminate a tuple allocation *) + check_eliminates_tuple p tbl; + if debug () + then ( + Format.eprintf "Unboxed tuples:@."; + Var.Hashtbl.iter + (fun x { size; loc; needed; _ } -> + Format.eprintf + " %a: %d (%d) @@ %a@." + Var.print + x + size + (IntSet.cardinal needed) + (fun f loc -> + match loc with + | Block pc -> Format.fprintf f "%d" pc + | Closure (y, _) -> Format.fprintf f "%a" Var.print y) + loc) + tbl); + let p, ops = rewrite_blocks p tbl in + let closure_ops = closure_operations tbl in + let p = rewrite_continuations stats p tbl ops closure_ops locations in + if times () then Format.eprintf " tuple unboxing: %a@." Timer.print t; + if show_stats () + then + Format.eprintf + "Stats - tuple unboxing: %d blocks, %d continuations, %d functions, %d call sites@." + (Int.Hashtbl.length ops) + stats.continuations + (Var.Hashtbl.length closure_ops) + stats.call_sites; + p diff --git a/compiler/lib/unboxing.mli b/compiler/lib/unboxing.mli new file mode 100644 index 0000000000..60dd5bc570 --- /dev/null +++ b/compiler/lib/unboxing.mli @@ -0,0 +1,20 @@ +(* Js_of_ocaml compiler + * http://www.ocsigen.org/js_of_ocaml/ + * Copyright (C) 2025 Jérome Vouillon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, with linking exception; + * either version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *) + +val f : Code.program -> Code.program diff --git a/compiler/tests-compiler/dune.inc b/compiler/tests-compiler/dune.inc index d5eab92d0a..b3c6b37ec0 100644 --- a/compiler/tests-compiler/dune.inc +++ b/compiler/tests-compiler/dune.inc @@ -944,6 +944,21 @@ (preprocess (pps ppx_expect))) +(library + ;; compiler/tests-compiler/tuple_unboxing.ml + (name tuple_unboxing_15) + (enabled_if true) + (modules tuple_unboxing) + (libraries js_of_ocaml_compiler unix str jsoo_compiler_expect_tests_helper) + (inline_tests + (enabled_if true) + (deps + (file %{project_root}/compiler/bin-js_of_ocaml/js_of_ocaml.exe) + (file %{project_root}/compiler/bin-jsoo_minify/jsoo_minify.exe))) + (flags (:standard -open Jsoo_compiler_expect_tests_helper)) + (preprocess + (pps ppx_expect))) + (library ;; compiler/tests-compiler/unix_fs.ml (name unix_fs_15) diff --git a/compiler/tests-compiler/global_deadcode.ml b/compiler/tests-compiler/global_deadcode.ml index 1a57f58de8..f9cf9626d0 100644 --- a/compiler/tests-compiler/global_deadcode.ml +++ b/compiler/tests-compiler/global_deadcode.ml @@ -109,7 +109,7 @@ let%expect_test "Omit unused fields" = {| function f(b, x){ l[1] = [0, function(y){return x + y | 0;}, l[1]]; - var t = b ? [0, 1, , x] : [0, 3, , 4], v = t[3], u = t[1]; + if(b) var v = x, u = 1; else var v = 4, u = 3; return [0, u, v]; } //end diff --git a/compiler/tests-compiler/tuple_unboxing.ml b/compiler/tests-compiler/tuple_unboxing.ml new file mode 100644 index 0000000000..748ad75ae4 --- /dev/null +++ b/compiler/tests-compiler/tuple_unboxing.ml @@ -0,0 +1,98 @@ +open Util + +let%expect_test _ = + let program = + compile_and_parse + ~flags:[ "--no-inline" ] + {| + let f (x, y) = x + y + let x = f(1, 2) + |} + in + print_program program; + [%expect + {| + (function(globalThis){ + "use strict"; + var runtime = globalThis.jsoo_runtime; + function f$0(x, y){return x + y | 0;} + function f(_a_){return f$0(_a_[1], _a_[2]);} + var _a_ = [0, 1, 2], x = f$0(_a_[1], _a_[2]); + runtime.caml_register_global(1, [0, f, x], "Test"); + return; + } + (globalThis)); + //end + |}] + +let%expect_test _ = + let program = + compile_and_parse + ~flags:[ "--no-inline" ] + {| + type t = {x : int; y : int} + let f b y t = let {x; _} = if b then {x=1; y} else t in x + let g b t = let {x; _} = if b then {x=1; y=1} else t in x + |} + in + print_program program; + [%expect + {| + (function(globalThis){ + "use strict"; + var runtime = globalThis.jsoo_runtime; + function f(b, y, t){var x = b ? 1 : t[1]; return x;} + var _a_ = [0, 1, 1]; + function g(b, t){var x = b ? _a_[1] : t[1]; return x;} + runtime.caml_register_global(1, [0, f, g], "Test"); + return; + } + (globalThis)); + //end + |}] + +let%expect_test _ = + let program = + compile_and_parse + ~flags:[ "--no-inline" ] + ~debug:false + {| + type t = C | D | E + type s = A of int | B of int + let foo c a b = + let m = + match c with + | C -> A a + | D -> B b + | E -> B (b + 1) + in + match m with + | A x -> x + | B y -> y + |} + in + print_program program; + [%expect + {| + (function(globalThis){ + "use strict"; + var + runtime = globalThis.jsoo_runtime, + Test = + [0, + function(_d_, _c_, _b_){ + switch(_d_){ + case 2: + var _a_ = _b_ + 1 | 0; break; + case 1: + var _a_ = _b_; break; + default: var _a_ = _c_; + } + return _a_; + }]; + runtime.caml_register_global(0, Test, "Test"); + return; + } + (globalThis)); + //end + |}] diff --git a/compiler/tests-full/stdlib.cma.expected.js b/compiler/tests-full/stdlib.cma.expected.js index 4ab84065ea..a5a37a6a79 100644 --- a/compiler/tests-full/stdlib.cma.expected.js +++ b/compiler/tests-full/stdlib.cma.expected.js @@ -1683,14 +1683,20 @@ /*<>*/ caml_atomic_exchange_field(t[1], t[2], v); return 0; /*<>*/ } - function incr(t){ - /*<>*/ caml_atomic_fetch_add_field(t[1], t[2], 1); + function incr$1(_b_, _c_){ + /*<>*/ caml_atomic_fetch_add_field(_b_, _c_, 1); return 0; /*<>*/ } - function decr(t){ - /*<>*/ caml_atomic_fetch_add_field(t[1], t[2], -1); + function incr(_b_){ + /*<>*/ return incr$1(_b_[1], _b_[2]) /*<>*/ ; + } + function decr$1(_a_, _b_){ + /*<>*/ caml_atomic_fetch_add_field(_a_, _b_, -1); return 0; /*<>*/ } + function decr(_a_){ + /*<>*/ return decr$1(_a_[1], _a_[2]) /*<>*/ ; + } function make(v){ /*<>*/ return [0, v]; /*<>*/ } @@ -1712,10 +1718,10 @@ /*<>*/ return caml_atomic_fetch_add_field(t, 0, incr) /*<>*/ ; } function incr$0(t){ - /*<>*/ return incr([0, t, 0]) /*<>*/ ; + /*<>*/ return incr$1(t, 0) /*<>*/ ; } function decr$0(t){ - /*<>*/ return decr([0, t, 0]) /*<>*/ ; + /*<>*/ return decr$1(t, 0) /*<>*/ ; } var Stdlib_Atomic = @@ -19198,9 +19204,8 @@ } } } - function failwith_message(param){ + function failwith_message(fmt){ var - fmt = /*<>*/ param[1], buf = /*<>*/ Stdlib_Buffer[1].call(null, 256); function k(acc){ @@ -19227,7 +19232,7 @@ len = /*<>*/ caml_ml_string_length(str); function invalid_box(param){ /*<>*/ return caml_call1 - (failwith_message(_y_), str) /*<>*/ ; + (failwith_message(_y_[1]), str) /*<>*/ ; } function parse_spaces(i$1){ var i = /*<>*/ i$1; @@ -19334,12 +19339,9 @@ /*<>*/ } function make_padprec_fmt_ebb(pad, prec, fmt){ /*<>*/ if(typeof prec === "number") - var match = prec ? [0, 1] : [0, 0]; + var prec$0 = prec ? 1 : 0; else - var - p = prec[1], - match = /*<>*/ [0, [0, p]]; - var prec$0 = /*<>*/ match[1]; + var p = prec[1], prec$0 = /*<>*/ [0, p]; /*<>*/ if(typeof pad === "number") /*<>*/ return [0, 0, prec$0, fmt]; /*<>*/ if(0 === pad[0]){ @@ -19509,15 +19511,15 @@ var legacy_behavior$0 = /*<>*/ 1; function invalid_format_message(str_ind, msg){ /*<>*/ return caml_call3 - (failwith_message(_z_), str, str_ind, msg) /*<>*/ ; + (failwith_message(_z_[1]), str, str_ind, msg) /*<>*/ ; } function invalid_format_without(str_ind, c, s){ /*<>*/ return caml_call4 - (failwith_message(_A_), str, str_ind, c, s) /*<>*/ ; + (failwith_message(_A_[1]), str, str_ind, c, s) /*<>*/ ; } function expected_character(str_ind, expected, read){ /*<>*/ return caml_call4 - (failwith_message(_B_), str, str_ind, expected, read) /*<>*/ ; + (failwith_message(_B_[1]), str, str_ind, expected, read) /*<>*/ ; } var cst_unexpected_end_of_format = /*<>*/ "unexpected end of format"; function parse(lit_start, end_ind){ @@ -19569,7 +19571,7 @@ _aJ_ = /*<>*/ caml_string_get(str, str_ind); /*<>*/ caml_call3 - (failwith_message(_C_), str, str_ind, _aJ_); + (failwith_message(_C_[1]), str, str_ind, _aJ_); } /*<>*/ flag[1] = 1; /*<>*/ } @@ -20336,7 +20338,7 @@ var fmt_result = /*<>*/ /*<>*/ caml_call3 - (failwith_message(_M_), str, pct_ind, symb); + (failwith_message(_M_[1]), str, pct_ind, symb); break a; case 88: case 100: @@ -20621,7 +20623,7 @@ var fmt_result = /*<>*/ /*<>*/ caml_call3 - (failwith_message(_J_), str, str_ind - 1 | 0, symb); + (failwith_message(_J_[1]), str, str_ind - 1 | 0, symb); } /*<>*/ if(1 - legacy_behavior$0){ var @@ -21049,7 +21051,7 @@ /*<>*/ } function fail_single_percent(str_ind){ /*<>*/ return caml_call2 - (failwith_message(_R_), str, str_ind) /*<>*/ ; + (failwith_message(_R_[1]), str, str_ind) /*<>*/ ; } function parse_char_set_content(counter, str_ind$1, end_ind){ var str_ind = /*<>*/ str_ind$1; @@ -21219,7 +21221,7 @@ | 0; /*<>*/ if(Stdlib_Sys[13] < new_acc){ var _aa_ = /*<>*/ Stdlib_Sys[13]; - return caml_call3(failwith_message(_S_), str, new_acc, _aa_) /*<>*/ ; + return caml_call3(failwith_message(_S_[1]), str, new_acc, _aa_) /*<>*/ ; } var str_ind$0 = /*<>*/ str_ind + 1 | 0; str_ind = str_ind$0; @@ -21283,7 +21285,7 @@ for(;;){ if(str_ind === end_ind) /*<>*/ caml_call3 - (failwith_message(_U_), str, c, end_ind); + (failwith_message(_U_[1]), str, c, end_ind); /*<>*/ if (37 === caml_string_get(str, str_ind)){ /*<>*/ if((str_ind + 1 | 0) === end_ind) @@ -21489,7 +21491,7 @@ /*<>*/ Stdlib_String[16].call (null, str, pct_ind, str_ind - pct_ind | 0); /*<>*/ return caml_call5 - (failwith_message(_Y_), str, pct_ind, option, symb, subfmt) /*<>*/ ; + (failwith_message(_Y_[1]), str, pct_ind, option, symb, subfmt) /*<>*/ ; } /*<>*/ return parse (0, caml_ml_string_length(str)); @@ -21520,7 +21522,7 @@ if(exn !== Type_mismatch) throw caml_maybe_attach_backtrace(exn, 0); var _$_ = /*<>*/ string_of_fmtty(fmtty); /*<>*/ return caml_call2 - (failwith_message(_Z_), str, _$_); + (failwith_message(_Z_[1]), str, _$_); } } var @@ -21547,7 +21549,7 @@ var exn = /*<>*/ caml_wrap_exception(exn$0); if(exn === Type_mismatch) /*<>*/ return caml_call2 - (failwith_message(___), str, str$0) /*<>*/ ; + (failwith_message(___[1]), str, str$0) /*<>*/ ; /*<>*/ throw caml_maybe_attach_backtrace(exn, 0); } /*<>*/ } @@ -27053,17 +27055,13 @@ /*<>*/ } var cst = /*<>*/ ""; function format_string(state, s){ - var _ak_ = /*<>*/ s !== cst ? 1 : 0; - /*<>*/ return _ak_ + var _al_ = /*<>*/ s !== cst ? 1 : 0; + /*<>*/ return _al_ ? /*<>*/ format_pp_text (state, /*<>*/ pp_string_width(state, s), s) - : _ak_ /*<>*/ ; + : _al_ /*<>*/ ; } - function break_new_line(state, param, width){ - var - after = /*<>*/ param[3], - offset = param[2], - before = param[1]; + function break_new_line(before, offset, after, state, width){ /*<>*/ format_string(state, before); /*<>*/ pp_output_newline(state); /*<>*/ state[11] = 1; @@ -27077,17 +27075,17 @@ /*<>*/ caml_call1(state[22], n); /*<>*/ return format_string(state, after) /*<>*/ ; } - function break_same_line(state, param){ - var - after = /*<>*/ param[3], - width = param[2], - before = param[1]; + var _a_ = /*<>*/ [0, cst, 0, cst]; + function break_line(state, width){ + /*<>*/ return break_new_line + (_a_[1], _a_[2], _a_[3], state, width) /*<>*/ ; + } + function break_same_line(before, width, after, state){ /*<>*/ format_string(state, before); /*<>*/ state[9] = state[9] - width | 0; /*<>*/ caml_call1(state[21], width); /*<>*/ return format_string(state, after) /*<>*/ ; } - var _a_ = /*<>*/ [0, cst, 0, cst]; function format_pp_token(state, size$0, param){ /*<>*/ if(typeof param === "number") switch(param){ @@ -27124,14 +27122,14 @@ /*<>*/ if(! match$4) /*<>*/ return pp_output_newline(state) /*<>*/ ; var width$0 = /*<>*/ match$4[1][2]; - /*<>*/ return break_new_line(state, _a_, width$0) /*<>*/ ; + /*<>*/ return break_line(state, width$0) /*<>*/ ; case 4: var - _ai_ = + _aj_ = /*<>*/ state[10] !== (state[6] - state[9] | 0) ? 1 : 0; - if(! _ai_) return _ai_; + if(! _aj_) return _aj_; var match$1 = /*<>*/ Stdlib_Queue[6].call(null, state[29]); @@ -27184,18 +27182,20 @@ box_type$0 = match$7[1]; /*<>*/ switch(box_type$0){ case 3: - var _aj_ = /*<>*/ state[9]; - return _aj_ < (size$0 + pp_string_width(state, before) | 0) + var _ak_ = /*<>*/ state[9]; + return _ak_ < (size$0 + pp_string_width(state, before) | 0) ? /*<>*/ break_new_line - (state, breaks, width$1) - : /*<>*/ break_same_line(state, fits) /*<>*/ ; + (breaks[1], breaks[2], breaks[3], state, width$1) + : /*<>*/ break_same_line + (fits[1], fits[2], fits[3], state) /*<>*/ ; case 4: /*<>*/ if(state[11]) - /*<>*/ return break_same_line(state, fits) /*<>*/ ; - var _ak_ = /*<>*/ state[9]; - return _ak_ < (size$0 + pp_string_width(state, before) | 0) + /*<>*/ return break_same_line + (fits[1], fits[2], fits[3], state) /*<>*/ ; + var _al_ = /*<>*/ state[9]; + return _al_ < (size$0 + pp_string_width(state, before) | 0) ? /*<>*/ break_new_line - (state, breaks, width$1) + (breaks[1], breaks[2], breaks[3], state, width$1) : ((state [6] - width$1 @@ -27204,14 +27204,16 @@ | 0) < state[10] ? /*<>*/ break_new_line - (state, breaks, width$1) - : /*<>*/ break_same_line(state, fits) /*<>*/ ; + (breaks[1], breaks[2], breaks[3], state, width$1) + : /*<>*/ break_same_line + (fits[1], fits[2], fits[3], state) /*<>*/ ; case 0: case 5: - /*<>*/ return break_same_line(state, fits) /*<>*/ ; + /*<>*/ return break_same_line + (fits[1], fits[2], fits[3], state) /*<>*/ ; default: /*<>*/ return break_new_line - (state, breaks, width$1) /*<>*/ ; + (breaks[1], breaks[2], breaks[3], state, width$1) /*<>*/ ; } case 3: var @@ -27239,9 +27241,9 @@ var offset = /*<>*/ tab - insertion_point | 0; /*<>*/ return 0 <= offset ? /*<>*/ break_same_line - (state, [0, cst, offset + n | 0, cst]) + (cst, offset + n | 0, cst, state) : /*<>*/ break_new_line - (state, [0, cst, tab + off$0 | 0, cst], state[6]) /*<>*/ ; + (cst, tab + off$0 | 0, cst, state, state[6]) /*<>*/ ; case 4: var ty = /*<>*/ param[2], @@ -27254,7 +27256,7 @@ var match$0 = match[1], width = match$0[2], box_type = match$0[1]; /*<>*/ if (state[9] < width && 3 >= box_type - 1 >>> 0) - /*<>*/ break_new_line(state, _a_, width); + /*<>*/ break_line(state, width); } else /*<>*/ pp_output_newline(state); @@ -27290,10 +27292,10 @@ length = match$0[3], token = match$0[2], pending_count = /*<>*/ state[13] - state[12] | 0, - _ai_ = /*<>*/ 0 <= size ? 1 : 0, - _ah_ = - /*<>*/ _ai_ || (state[9] <= pending_count ? 1 : 0); - if(! _ah_) return _ah_; + _aj_ = /*<>*/ 0 <= size ? 1 : 0, + _ai_ = + /*<>*/ _aj_ || (state[9] <= pending_count ? 1 : 0); + if(! _ai_) return _ai_; /*<>*/ if(! Stdlib_Queue[6].call(null, state[29])) /*<>*/ return Stdlib[1].call (null, "Format: Unsynchronized access to formatter") /*<>*/ ; @@ -27361,8 +27363,8 @@ elem = /*<>*/ [0, size$0, [4, indent, br_ty], 0]; /*<>*/ return scan_push(state, 0, elem) /*<>*/ ; } - var _ah_ = /*<>*/ state[14] === state[15] ? 1 : 0; - if(! _ah_) return _ah_; + var _ai_ = /*<>*/ state[14] === state[15] ? 1 : 0; + if(! _ai_) return _ai_; var s = /*<>*/ state[16], size = /*<>*/ pp_string_width(state, s); @@ -27370,46 +27372,46 @@ } var zero = /*<>*/ 0; function pp_close_box(state, param){ - var _ag_ = /*<>*/ 1 < state[14] ? 1 : 0; - if(_ag_){ + var _ah_ = /*<>*/ 1 < state[14] ? 1 : 0; + if(_ah_){ /*<>*/ if(state[14] < state[15]){ /*<>*/ pp_enqueue(state, [0, zero, 1, 0]); /*<>*/ set_size(state, 1); /*<>*/ set_size(state, 0); } /*<>*/ state[14] = state[14] - 1 | 0; - var _ah_ = 0; + var _ai_ = 0; } else - var _ah_ = /*<>*/ _ag_; - return _ah_; + var _ai_ = /*<>*/ _ah_; + return _ai_; /*<>*/ } function pp_open_stag(state, tag_name){ /*<>*/ if(state[23]){ /*<>*/ Stdlib_Stack[3].call(null, tag_name, state[4]); /*<>*/ caml_call1(state[27], tag_name); } - var _ag_ = /*<>*/ state[24]; - if(! _ag_) return _ag_; + var _ah_ = /*<>*/ state[24]; + if(! _ah_) return _ah_; var token = /*<>*/ [6, tag_name]; /*<>*/ return pp_enqueue(state, [0, zero, token, 0]) /*<>*/ ; } function pp_close_stag(state, param){ /*<>*/ if(state[24]) /*<>*/ pp_enqueue(state, [0, zero, 5, 0]); - var _af_ = /*<>*/ state[23]; - if(_af_){ + var _ag_ = /*<>*/ state[23]; + if(_ag_){ var match = /*<>*/ Stdlib_Stack[5].call(null, state[4]); /*<>*/ if(match){ var tag_name = match[1]; /*<>*/ return caml_call1(state[28], tag_name) /*<>*/ ; } - var _ag_ = /*<>*/ 0; + var _ah_ = /*<>*/ 0; } else - var _ag_ = /*<>*/ _af_; - return _ag_; + var _ah_ = /*<>*/ _ag_; + return _ah_; /*<>*/ } function pp_set_print_tags(state, b){ /*<>*/ state[23] = b; @@ -27481,10 +27483,10 @@ } /*<>*/ } function pp_print_as_size(state, size, s){ - var _af_ = /*<>*/ state[14] < state[15] ? 1 : 0; - return _af_ + var _ag_ = /*<>*/ state[14] < state[15] ? 1 : 0; + return _ag_ ? /*<>*/ enqueue_string_as(state, size, s) - : _af_ /*<>*/ ; + : _ag_ /*<>*/ ; } function pp_print_as(state, isize, s){ /*<>*/ return pp_print_as_size(state, isize, s) /*<>*/ ; @@ -27494,8 +27496,8 @@ /*<>*/ return pp_print_as_size(state, isize, s) /*<>*/ ; } function pp_print_substring_as(pos, len, state, size, source){ - var _af_ = /*<>*/ state[14] < state[15] ? 1 : 0; - if(! _af_) return _af_; + var _ag_ = /*<>*/ state[14] < state[15] ? 1 : 0; + if(! _ag_) return _ag_; var token = /*<>*/ [1, source, pos, len]; /*<>*/ return enqueue_advance (state, [0, size, token, size]) /*<>*/ ; @@ -27553,31 +27555,31 @@ /*<>*/ return caml_call1(state[19], 0) /*<>*/ ; } function pp_force_newline(state, param){ - var _af_ = /*<>*/ state[14] < state[15] ? 1 : 0; - return _af_ + var _ag_ = /*<>*/ state[14] < state[15] ? 1 : 0; + return _ag_ ? /*<>*/ enqueue_advance(state, [0, zero, 3, 0]) - : _af_ /*<>*/ ; + : _ag_ /*<>*/ ; } function pp_print_if_newline(state, param){ - var _af_ = /*<>*/ state[14] < state[15] ? 1 : 0; - return _af_ + var _ag_ = /*<>*/ state[14] < state[15] ? 1 : 0; + return _ag_ ? /*<>*/ enqueue_advance(state, [0, zero, 4, 0]) - : _af_ /*<>*/ ; + : _ag_ /*<>*/ ; } function pp_print_custom_break(state, fits, breaks){ var after = /*<>*/ fits[3], width = fits[2], before = fits[1], - _ae_ = /*<>*/ state[14] < state[15] ? 1 : 0; - if(! _ae_) return _ae_; + _af_ = /*<>*/ state[14] < state[15] ? 1 : 0; + if(! _af_) return _af_; var size = /*<>*/ - state[13] | 0, token = /*<>*/ [2, fits, breaks], - _af_ = /*<>*/ pp_string_width(state, after), + _ag_ = /*<>*/ pp_string_width(state, after), length = /*<>*/ (pp_string_width(state, before) + width | 0) - + _af_ + + _ag_ | 0; /*<>*/ return scan_push (state, 1, [0, size, token, length]) /*<>*/ ; @@ -27594,30 +27596,30 @@ } function pp_open_tbox(state, param){ /*<>*/ state[14] = state[14] + 1 | 0; - var _ae_ = /*<>*/ state[14] < state[15] ? 1 : 0; - if(! _ae_) return _ae_; + var _af_ = /*<>*/ state[14] < state[15] ? 1 : 0; + if(! _af_) return _af_; /*<>*/ return enqueue_advance (state, [0, zero, [5, [0, [0, 0]]], 0]) /*<>*/ ; } function pp_close_tbox(state, param){ - var _ad_ = /*<>*/ 1 < state[14] ? 1 : 0; - if(_ad_){ - var _ae_ = /*<>*/ state[14] < state[15] ? 1 : 0; - if(_ae_){ + var _ae_ = /*<>*/ 1 < state[14] ? 1 : 0; + if(_ae_){ + var _af_ = /*<>*/ state[14] < state[15] ? 1 : 0; + if(_af_){ /*<>*/ enqueue_advance(state, [0, zero, 2, 0]); /*<>*/ state[14] = state[14] - 1 | 0; - var _ac_ = 0; + var _ad_ = 0; } else - var _ac_ = /*<>*/ _ae_; + var _ad_ = /*<>*/ _af_; } else - var _ac_ = /*<>*/ _ad_; - return _ac_; + var _ad_ = /*<>*/ _ae_; + return _ad_; /*<>*/ } function pp_print_tbreak(state, width, offset){ - var _ac_ = /*<>*/ state[14] < state[15] ? 1 : 0; - if(! _ac_) return _ac_; + var _ad_ = /*<>*/ state[14] < state[15] ? 1 : 0; + if(! _ad_) return _ad_; var size = /*<>*/ - state[13] | 0, elem = /*<>*/ [0, size, [3, width, offset], width]; @@ -27627,15 +27629,15 @@ /*<>*/ return pp_print_tbreak(state, 0, 0) /*<>*/ ; } function pp_set_tab(state, param){ - var _ac_ = /*<>*/ state[14] < state[15] ? 1 : 0; - if(! _ac_) return _ac_; + var _ad_ = /*<>*/ state[14] < state[15] ? 1 : 0; + if(! _ad_) return _ad_; /*<>*/ return enqueue_advance(state, [0, zero, 0, 0]) /*<>*/ ; } function pp_set_max_boxes(state, n){ var - _ab_ = /*<>*/ 1 < n ? 1 : 0, - _ac_ = _ab_ ? (state[15] = n, 0) : _ab_; - return _ac_; + _ac_ = /*<>*/ 1 < n ? 1 : 0, + _ad_ = _ac_ ? (state[15] = n, 0) : _ac_; + return _ad_; /*<>*/ } function pp_get_max_boxes(state, param){ /*<>*/ return state[15]; @@ -27654,12 +27656,12 @@ /*<>*/ return n < 1000000010 ? n : 1000000009 /*<>*/ ; } function pp_set_max_indent(state, n$0){ - var _ab_ = /*<>*/ 1 < n$0 ? 1 : 0; - if(! _ab_) return _ab_; + var _ac_ = /*<>*/ 1 < n$0 ? 1 : 0; + if(! _ac_) return _ac_; var n$1 = /*<>*/ state[6] - n$0 | 0, - _aa_ = /*<>*/ 1 <= n$1 ? 1 : 0; - if(! _aa_) return _aa_; + _ab_ = /*<>*/ 1 <= n$1 ? 1 : 0; + if(! _ab_) return _ab_; var n = /*<>*/ pp_limit(n$1); /*<>*/ state[7] = n; /*<>*/ state[8] = state[6] - state[7] | 0; @@ -27669,20 +27671,20 @@ /*<>*/ return state[8]; /*<>*/ } function pp_set_margin(state, n){ - var _$_ = /*<>*/ 1 <= n ? 1 : 0; - if(! _$_) return _$_; + var _aa_ = /*<>*/ 1 <= n ? 1 : 0; + if(! _aa_) return _aa_; var n$0 = /*<>*/ pp_limit(n); /*<>*/ state[6] = n$0; /*<>*/ if(state[8] <= state[6]) var new_max_indent = /*<>*/ state[8]; else var - _aa_ = + _ab_ = /*<>*/ Stdlib_Int[11].call (null, state[6] - state[7] | 0, state[6] / 2 | 0), new_max_indent = /*<>*/ /*<>*/ Stdlib_Int[11].call - (null, _aa_, 1); + (null, _ab_, 1); /*<>*/ return pp_set_max_indent(state, new_max_indent) /*<>*/ ; } var @@ -27690,62 +27692,57 @@ _d_ = [1, "margin >= pp_infinity"], _e_ = [0, 0], _f_ = [1, "max_indent < 2"]; - function validate_geometry(param){ - var margin = /*<>*/ param[2], max_indent = param[1]; + function validate_geometry(max_indent, margin){ /*<>*/ return 2 <= max_indent ? margin <= max_indent ? _c_ : 1000000010 <= margin ? _d_ : _e_ : _f_ /*<>*/ ; } function check_geometry(geometry){ - /*<>*/ return 0 === validate_geometry(geometry)[0] + /*<>*/ return 0 + === validate_geometry(geometry[1], geometry[2])[0] ? 1 : 0 /*<>*/ ; } function pp_get_margin(state, param){ /*<>*/ return state[6]; /*<>*/ } - function pp_set_full_geometry(state, param){ - var margin = /*<>*/ param[2], max_indent = param[1]; + function pp_set_full_geometry(max_indent, margin, state){ /*<>*/ pp_set_margin(state, margin); /*<>*/ pp_set_max_indent(state, max_indent); /*<>*/ return 0; /*<>*/ } function pp_set_geometry(state, max_indent, margin){ var - geometry = /*<>*/ [0, max_indent, margin], - match = /*<>*/ validate_geometry(geometry); + match = /*<>*/ validate_geometry(max_indent, margin); /*<>*/ if(0 === match[0]) - /*<>*/ return pp_set_full_geometry(state, geometry) /*<>*/ ; + /*<>*/ return pp_set_full_geometry + (max_indent, margin, state) /*<>*/ ; var msg = /*<>*/ match[1], - _$_ = + _aa_ = /*<>*/ Stdlib[28].call (null, "Format.pp_set_geometry: ", msg); /*<>*/ throw caml_maybe_attach_backtrace - ([0, Stdlib[6], _$_], 1); + ([0, Stdlib[6], _aa_], 1); /*<>*/ } function pp_safe_set_geometry(state, max_indent, margin){ - var geometry = /*<>*/ [0, max_indent, margin]; - /*<>*/ return 0 === validate_geometry(geometry)[0] - ? /*<>*/ pp_set_full_geometry(state, geometry) + /*<>*/ return 0 + === validate_geometry(max_indent, margin)[0] + ? /*<>*/ pp_set_full_geometry + (max_indent, margin, state) : 0 /*<>*/ ; } function pp_get_geometry(state, param){ /*<>*/ return [0, state[8], state[6]]; /*<>*/ } function pp_update_geometry(state, update){ - var geometry = /*<>*/ pp_get_geometry(state, 0); - /*<>*/ return /*<>*/ pp_set_full_geometry - (state, /*<>*/ caml_call1(update, geometry)) /*<>*/ ; - } - function pp_set_formatter_out_functions(state, param){ - var - j = /*<>*/ param[6], - i = param[5], - h = param[4], - g = param[3], - f2 = param[2], - f = param[1]; + var + geometry = /*<>*/ pp_get_geometry(state, 0), + _aa_ = /*<>*/ caml_call1(update, geometry); + /*<>*/ return pp_set_full_geometry + (_aa_[1], _aa_[2], state); + } + function pp_set_formatter_out_functions$0(f, f2, g, h, i, j, state){ /*<>*/ state[17] = f; /*<>*/ state[18] = f2; /*<>*/ state[19] = g; @@ -27754,6 +27751,10 @@ /*<>*/ state[22] = j; return 0; /*<>*/ } + function pp_set_formatter_out_functions(_aa_, _$_){ + /*<>*/ return pp_set_formatter_out_functions$0 + (_$_[1], _$_[2], _$_[3], _$_[4], _$_[5], _$_[6], _aa_) /*<>*/ ; + } function pp_get_formatter_out_functions(state, param){ /*<>*/ return [0, state[17], @@ -28472,10 +28473,12 @@ v) /*<>*/ ; } function set_formatter_out_functions(v){ - /*<>*/ return /*<>*/ pp_set_formatter_out_functions - ( /*<>*/ caml_call1 - (Stdlib_Domain[11][2], std_formatter_key), - v) /*<>*/ ; + var + _D_ = + /*<>*/ caml_call1 + (Stdlib_Domain[11][2], std_formatter_key); + /*<>*/ return pp_set_formatter_out_functions$0 + (v[1], v[2], v[3], v[4], v[5], v[6], _D_) /*<>*/ ; } function get_formatter_out_functions(v){ /*<>*/ return /*<>*/ pp_get_formatter_out_functions @@ -29194,41 +29197,39 @@ /*<>*/ pp_get_formatter_out_functions (std_formatter, 0), _g_ = /*<>*/ Stdlib[39]; - /*<>*/ pp_set_formatter_out_functions - (std_formatter, - [0, - function(_k_, _l_, _m_){ - /*<>*/ return buffered_out_string - (std_buf_key, _k_, _l_, _m_); - }, - fs[2], - function(_k_){ - /*<>*/ return buffered_out_flush - (_g_, std_buf_key, _k_); - }, - fs[4], - fs[5], - fs[6]]); + /*<>*/ pp_set_formatter_out_functions$0 + (function(_k_, _l_, _m_){ + /*<>*/ return buffered_out_string + (std_buf_key, _k_, _l_, _m_); + }, + fs[2], + function(_k_){ + /*<>*/ return buffered_out_flush + (_g_, std_buf_key, _k_); + }, + fs[4], + fs[5], + fs[6], + std_formatter); var fs$0 = /*<>*/ pp_get_formatter_out_functions (err_formatter, 0), _h_ = /*<>*/ Stdlib[40]; - /*<>*/ return pp_set_formatter_out_functions - (err_formatter, - [0, - function(_i_, _j_, _k_){ - /*<>*/ return buffered_out_string - (err_buf_key, _i_, _j_, _k_); - }, - fs$0[2], - function(_i_){ - /*<>*/ return buffered_out_flush - (_h_, err_buf_key, _i_); - }, - fs$0[4], - fs$0[5], - fs$0[6]]) /*<>*/ ; + /*<>*/ return pp_set_formatter_out_functions$0 + (function(_i_, _j_, _k_){ + /*<>*/ return buffered_out_string + (err_buf_key, _i_, _j_, _k_); + }, + fs$0[2], + function(_i_){ + /*<>*/ return buffered_out_flush + (_h_, err_buf_key, _i_); + }, + fs$0[4], + fs$0[5], + fs$0[6], + err_formatter) /*<>*/ ; }); /*<>*/ runtime.caml_register_global (44, @@ -36021,26 +36022,6 @@ /*<>*/ return generic_dirname (is_dir_sep, current_dir_name, _M_); } - var - cst$5 = /*<>*/ "..", - cst$6 = "/", - cst_dev_null = "/dev/null", - Unix = - /*<>*/ [0, - cst_dev_null, - current_dir_name, - cst$5, - cst$6, - is_dir_sep, - is_relative, - is_implicit, - check_suffix, - chop_suffix_opt, - temp_dir_name, - quote, - quote_command, - basename, - dirname]; function is_dir_sep$0(s, i){ var c = /*<>*/ caml_string_get(s, i), @@ -36432,58 +36413,59 @@ (is_dir_sep$0, current_dir_name$1, _h_); } var - Cygwin = - /*<>*/ [0, - cst_dev_null, - current_dir_name$1, - cst$5, - cst$6, - is_dir_sep$0, - is_relative$0, - is_implicit$0, - check_suffix$0, - chop_suffix_opt$0, - temp_dir_name, - quote, - quote_command, - basename$1, - dirname$1], - match = Stdlib_Sys[5], - Sysdeps = - match !== "Cygwin" - ? match - !== "Win32" - ? Unix - : [0, - "NUL", - current_dir_name$0, - cst$5, - "\\", - is_dir_sep$0, - is_relative$0, - is_implicit$0, - check_suffix$0, - chop_suffix_opt$0, - temp_dir_name$0, - quote$0, - quote_command$0, - basename$0, - dirname$0] - : Cygwin, - null$ = Sysdeps[1], - current_dir_name$2 = Sysdeps[2], - parent_dir_name = Sysdeps[3], - dir_sep = Sysdeps[4], - is_dir_sep$1 = Sysdeps[5], - is_relative$1 = Sysdeps[6], - is_implicit$1 = Sysdeps[7], - check_suffix$1 = Sysdeps[8], - chop_suffix_opt$1 = Sysdeps[9], - temp_dir_name$1 = Sysdeps[10], - quote$1 = Sysdeps[11], - quote_command$1 = Sysdeps[12], - basename$2 = Sysdeps[13], - dirname$2 = Sysdeps[14]; + match = /*<>*/ Stdlib_Sys[5], + cst$5 = /*<>*/ "..", + cst$6 = "/", + cst_dev_null = "/dev/null"; + /*<>*/ if(match !== "Cygwin") + if(match !== "Win32") + var + dirname$2 = dirname, + basename$2 = basename, + quote_command$1 = quote_command, + quote$1 = quote, + temp_dir_name$1 = temp_dir_name, + chop_suffix_opt$1 = chop_suffix_opt, + check_suffix$1 = check_suffix, + is_implicit$1 = is_implicit, + is_relative$1 = is_relative, + is_dir_sep$1 = is_dir_sep, + dir_sep = cst$6, + parent_dir_name = cst$5, + current_dir_name$2 = current_dir_name, + null$ = cst_dev_null; + else + var + dirname$2 = dirname$0, + basename$2 = basename$0, + quote_command$1 = quote_command$0, + quote$1 = quote$0, + temp_dir_name$1 = temp_dir_name$0, + chop_suffix_opt$1 = chop_suffix_opt$0, + check_suffix$1 = check_suffix$0, + is_implicit$1 = is_implicit$0, + is_relative$1 = is_relative$0, + is_dir_sep$1 = is_dir_sep$0, + dir_sep = "\\", + parent_dir_name = cst$5, + current_dir_name$2 = current_dir_name$0, + null$ = "NUL"; + else + var + dirname$2 = dirname$1, + basename$2 = basename$1, + quote_command$1 = quote_command, + quote$1 = quote, + temp_dir_name$1 = temp_dir_name, + chop_suffix_opt$1 = chop_suffix_opt$0, + check_suffix$1 = check_suffix$0, + is_implicit$1 = is_implicit$0, + is_relative$1 = is_relative$0, + is_dir_sep$1 = is_dir_sep$0, + dir_sep = cst$6, + parent_dir_name = cst$5, + current_dir_name$2 = current_dir_name$1, + null$ = cst_dev_null; function concat(dirname, filename){ var l = /*<>*/ caml_ml_string_length(dirname); /*<>*/ if @@ -36683,32 +36665,32 @@ } } /*<>*/ } - /*<>*/ runtime.caml_register_global - (64, - [0, - current_dir_name$2, - parent_dir_name, - dir_sep, - concat, - is_relative$1, - is_implicit$1, - check_suffix$1, - chop_suffix, - chop_suffix_opt$1, - extension, - remove_extension, - chop_extension, - basename$2, - dirname$2, - null$, - temp_file, - open_temp_file, - temp_dir, - get_temp_dir_name, - set_temp_dir_name, - quote$1, - quote_command$1], - "Stdlib__Filename"); + var + Stdlib_Filename = + /*<>*/ [0, + current_dir_name$2, + parent_dir_name, + dir_sep, + concat, + is_relative$1, + is_implicit$1, + check_suffix$1, + chop_suffix, + chop_suffix_opt$1, + extension, + remove_extension, + chop_extension, + basename$2, + dirname$2, + null$, + temp_file, + open_temp_file, + temp_dir, + get_temp_dir_name, + set_temp_dir_name, + quote$1, + quote_command$1]; + runtime.caml_register_global(64, Stdlib_Filename, "Stdlib__Filename"); return; /*<>*/ } (globalThis));