diff --git a/compiler/bin-js_of_ocaml/build_fs.ml b/compiler/bin-js_of_ocaml/build_fs.ml index 94fb9916cc..84ed7fb55d 100644 --- a/compiler/bin-js_of_ocaml/build_fs.ml +++ b/compiler/bin-js_of_ocaml/build_fs.ml @@ -75,10 +75,10 @@ function jsoo_create_file_extern(name,content){ let pfs_fmt = Pretty_print.to_out_channel chan in let (_ : Source_map.t option) = Driver.f + ~target:(JavaScript pfs_fmt) ~standalone:true ~wrap_with_fun:`Iife ~link:`Needed - pfs_fmt (Parse_bytecode.Debug.create ~include_cmis:false false) code in diff --git a/compiler/bin-js_of_ocaml/compile.ml b/compiler/bin-js_of_ocaml/compile.ml index 3e01a30974..8e2f1b812a 100644 --- a/compiler/bin-js_of_ocaml/compile.ml +++ b/compiler/bin-js_of_ocaml/compile.ml @@ -196,12 +196,12 @@ let run in let code = Code.prepend one.code instr in Driver.f + ~target:(JavaScript fmt) ~standalone ?profile ~link ~wrap_with_fun ?source_map - fmt one.debug code | `File, fmt -> @@ -220,12 +220,12 @@ let run let code = Code.prepend one.code instr in let res = Driver.f + ~target:(JavaScript fmt) ~standalone ?profile ~link ~wrap_with_fun ?source_map - fmt one.debug code in @@ -285,7 +285,7 @@ let run | `None -> let prims = Linker.list_all () |> StringSet.elements in assert (List.length prims > 0); - let code, uinfo = Parse_bytecode.predefined_exceptions () in + let code, uinfo = Parse_bytecode.predefined_exceptions ~target:`JavaScript in let uinfo = { uinfo with primitives = uinfo.primitives @ prims } in let code : Parse_bytecode.one = { code @@ -331,6 +331,7 @@ let run let linkall = linkall || toplevel || dynlink in let code = Parse_bytecode.from_exe + ~target:`JavaScript ~includes:include_dirs ~include_cmis ~link_info:(toplevel || dynlink) @@ -363,6 +364,7 @@ let run let t1 = Timer.make () in let code = Parse_bytecode.from_cmo + ~target:`JavaScript ~includes:include_dirs ~include_cmis ~debug:need_debug @@ -419,6 +421,7 @@ let run let t1 = Timer.make () in let code = Parse_bytecode.from_cmo + ~target:`JavaScript ~includes:include_dirs ~include_cmis ~debug:need_debug @@ -450,6 +453,7 @@ let run let t1 = Timer.make () in let code = Parse_bytecode.from_cmo + ~target:`JavaScript ~includes:include_dirs ~include_cmis ~debug:need_debug diff --git a/compiler/bin-js_of_ocaml/js_of_ocaml.ml b/compiler/bin-js_of_ocaml/js_of_ocaml.ml index 144543663c..b6db162a15 100644 --- a/compiler/bin-js_of_ocaml/js_of_ocaml.ml +++ b/compiler/bin-js_of_ocaml/js_of_ocaml.ml @@ -22,6 +22,7 @@ open! Js_of_ocaml_compiler.Stdlib open Js_of_ocaml_compiler let () = + Config.set_target `JavaScript; Sys.catch_break true; let argv = Jsoo_cmdline.normalize_argv ~warn:(warn "%s") Sys.argv in let argv = diff --git a/compiler/lib/config.ml b/compiler/lib/config.ml index 9385a063ba..7865b6fd33 100644 --- a/compiler/lib/config.ml +++ b/compiler/lib/config.ml @@ -164,7 +164,7 @@ module Param = struct p ~name:"tc" ~desc:"Set tailcall optimisation" - (enum [ "trampoline", TcTrampoline; (* default *) "none", TcNone ]) + (enum [ "trampoline", TcTrampoline (* default *); "none", TcNone ]) let lambda_lifting_threshold = (* When we reach this depth, we start looking for functions to be lifted *) @@ -180,3 +180,11 @@ module Param = struct ~desc:"Set baseline for lifting deeply nested functions" (int 1) end + +(****) + +let target_ : [ `JavaScript | `Wasm ] ref = ref `JavaScript + +let target () = !target_ + +let set_target t = target_ := t diff --git a/compiler/lib/config.mli b/compiler/lib/config.mli index e4c86d37b0..4954602b1b 100644 --- a/compiler/lib/config.mli +++ b/compiler/lib/config.mli @@ -80,6 +80,7 @@ module Flag : sig val disable : string -> unit end +(** This module contains parameters that may be modified through command-line flags. *) module Param : sig val set : string -> string -> unit @@ -104,3 +105,13 @@ module Param : sig val lambda_lifting_baseline : unit -> int end + +(****) + +(** {2 Parameters that are constant across a program run} *) + +(** These parameters should be set at most once at the beginning of the program. *) + +val target : unit -> [ `JavaScript | `Wasm ] + +val set_target : [ `JavaScript | `Wasm ] -> unit diff --git a/compiler/lib/driver.ml b/compiler/lib/driver.ml index 6c638469ee..5dfbfcab51 100644 --- a/compiler/lib/driver.ml +++ b/compiler/lib/driver.ml @@ -658,13 +658,34 @@ let configure formatter = Code.Var.set_pretty (pretty && not (Config.Flag.shortvar ())); Code.Var.set_stable (Config.Flag.stable_var ()) -let full ~standalone ~wrap_with_fun ~profile ~link ~source_map formatter d p = - let exported_runtime = not standalone in +type 'a target = + | JavaScript : Pretty_print.t -> Source_map.t option target + | Wasm + : (Deadcode.variable_uses * Effects.in_cps * Code.program * Parse_bytecode.Debug.t) + target + +let link_and_pack ?(standalone = true) ?(wrap_with_fun = `Iife) ?(link = `No) p = let export_runtime = match link with | `All | `All_from _ -> true | `Needed | `No -> false in + p + |> link' ~export_runtime ~standalone ~link + |> pack ~wrap_with_fun ~standalone + |> coloring + |> check_js + +let full + (type result) + ~(target : result target) + ~standalone + ~wrap_with_fun + ~profile + ~link + ~source_map + d + p : result = let deadcode_sentinal = (* If deadcode is disabled, this field is just fresh variable *) Code.Var.fresh_n "undef" @@ -677,56 +698,71 @@ let full ~standalone ~wrap_with_fun ~profile ~link ~source_map formatter d p = | O3 -> o3) +> exact_calls ~deadcode_sentinal profile +> effects ~deadcode_sentinal - +> map_fst (if Config.Flag.effects () then fun x -> x else Generate_closure.f) + +> map_fst + (match target with + | JavaScript _ -> if Config.Flag.effects () then Fun.id else Generate_closure.f + | Wasm -> Fun.id) +> map_fst deadcode' in - let emit = - generate - d - ~exported_runtime - ~wrap_with_fun - ~warn_on_unhandled_effect:standalone - ~deadcode_sentinal - +> link' ~export_runtime ~standalone ~link - +> pack ~wrap_with_fun ~standalone - +> coloring - +> check_js - +> output formatter ~source_map () - in if times () then Format.eprintf "Start Optimizing...@."; let t = Timer.make () in let r = opt p in let () = if times () then Format.eprintf " optimizations : %a@." Timer.print t in - emit r + match target with + | JavaScript formatter -> + let exported_runtime = not standalone in + let emit formatter = + generate + d + ~exported_runtime + ~wrap_with_fun + ~warn_on_unhandled_effect:standalone + ~deadcode_sentinal + +> link_and_pack ~standalone ~wrap_with_fun ~link + +> output formatter ~source_map () + in + let source_map = emit formatter r in + source_map + | Wasm -> + let (p, live_vars), _, in_cps = r in + live_vars, in_cps, p, d -let full_no_source_map ~standalone ~wrap_with_fun ~profile ~link formatter d p = +let full_no_source_map ~formatter ~standalone ~wrap_with_fun ~profile ~link d p = let (_ : Source_map.t option) = - full ~standalone ~wrap_with_fun ~profile ~link ~source_map:None formatter d p + full + ~target:(JavaScript formatter) + ~standalone + ~wrap_with_fun + ~profile + ~link + ~source_map:None + d + p in () let f + ~target ?(standalone = true) ?(wrap_with_fun = `Iife) ?(profile = O1) ~link ?source_map - formatter d p = - full ~standalone ~wrap_with_fun ~profile ~link ~source_map formatter d p + full ~target ~standalone ~wrap_with_fun ~profile ~link ~source_map d p let f' ?(standalone = true) ?(wrap_with_fun = `Iife) ?(profile = O1) ~link formatter d p = - full_no_source_map ~standalone ~wrap_with_fun ~profile ~link formatter d p + full_no_source_map ~formatter ~standalone ~wrap_with_fun ~profile ~link d p let from_string ~prims ~debug s formatter = let p, d = Parse_bytecode.from_string ~prims ~debug s in full_no_source_map + ~formatter ~standalone:false ~wrap_with_fun:`Anonymous ~profile:O1 ~link:`No - formatter d p diff --git a/compiler/lib/driver.mli b/compiler/lib/driver.mli index f4562f59e7..1b9eaa616a 100644 --- a/compiler/lib/driver.mli +++ b/compiler/lib/driver.mli @@ -20,16 +20,22 @@ type profile +type 'a target = + | JavaScript : Pretty_print.t -> Source_map.t option target + | Wasm + : (Deadcode.variable_uses * Effects.in_cps * Code.program * Parse_bytecode.Debug.t) + target + val f : - ?standalone:bool + target:'result target + -> ?standalone:bool -> ?wrap_with_fun:[ `Iife | `Anonymous | `Named of string ] -> ?profile:profile -> link:[ `All | `All_from of string list | `Needed | `No ] -> ?source_map:Source_map.t - -> Pretty_print.t -> Parse_bytecode.Debug.t -> Code.program - -> Source_map.t option + -> 'result val f' : ?standalone:bool @@ -48,6 +54,13 @@ val from_string : -> Pretty_print.t -> unit +val link_and_pack : + ?standalone:bool + -> ?wrap_with_fun:[ `Iife | `Anonymous | `Named of string ] + -> ?link:[ `All | `All_from of string list | `Needed | `No ] + -> Javascript.statement_list + -> Javascript.statement_list + val configure : Pretty_print.t -> unit val profiles : (int * profile) list diff --git a/compiler/lib/eval.ml b/compiler/lib/eval.ml index 24c8dbb5ad..2242ef1ce0 100644 --- a/compiler/lib/eval.ml +++ b/compiler/lib/eval.ml @@ -29,17 +29,49 @@ let set_static_env s value = Hashtbl.add static_env s value let get_static_env s = try Some (Hashtbl.find static_env s) with Not_found -> None -module Int = Int32 +module Int32 = struct + include Int32 -let int_binop l f = - match l with - | [ Int i; Int j ] -> Some (Int (f i j)) - | _ -> None + let int_unop l f = + match l with + | [ Int i ] -> Some (Int (f i)) + | _ -> None -let shift l f = - match l with - | [ Int i; Int j ] -> Some (Int (f i (Int32.to_int j land 0x1f))) - | _ -> None + let int_binop l f = + match l with + | [ Int i; Int j ] -> Some (Int (f i j)) + | _ -> None + + (* For when the underlying function takes an [int] (not [t]) as its second argument *) + let shift_op l f = + match l with + | [ Int i; Int j ] -> Some (Int (f i (to_int j))) + | _ -> None +end + +module Int31 = struct + include Int31 + + let int_unop l f = + match l with + | [ Int i ] -> Some (Int (to_int32 (f (of_int32_warning_on_overflow i)))) + | _ -> None + + let int_binop l f = + match l with + | [ Int i; Int j ] -> + Some + (Int + (to_int32 + (f (of_int32_warning_on_overflow i) (of_int32_warning_on_overflow j)))) + | _ -> None + + let shift_op l f = + match l with + | [ Int i; Int j ] -> + Some (Int (to_int32 (f (of_int32_warning_on_overflow i) (Int32.to_int j)))) + | _ -> None +end let float_binop_aux (l : constant list) (f : float -> float -> 'a) : 'a option = let args = @@ -74,6 +106,16 @@ let float_binop_bool l f = | Some b -> bool b | None -> None +module type Int = sig + include Arith_ops + + val int_unop : constant list -> (t -> t) -> constant option + + val int_binop : constant list -> (t -> t -> t) -> constant option + + val shift_op : constant list -> (t -> int -> t) -> constant option +end + let eval_prim x = match x with | Not, [ Int i ] -> bool Int32.(i = 0l) @@ -84,21 +126,26 @@ let eval_prim x = | Ult, [ Int i; Int j ] -> bool (Int32.(j < 0l) || Int32.(i < j)) | Extern name, l -> ( let name = Primitive.resolve name in + let (module Int : Int) = + match Config.target () with + | `JavaScript -> (module Int32) + | `Wasm -> (module Int31) + in match name, l with (* int *) - | "%int_add", _ -> int_binop l Int.add - | "%int_sub", _ -> int_binop l Int.sub - | "%direct_int_mul", _ -> int_binop l Int.mul + | "%int_add", _ -> Int.int_binop l Int.add + | "%int_sub", _ -> Int.int_binop l Int.sub + | "%direct_int_mul", _ -> Int.int_binop l Int.mul | "%direct_int_div", [ _; Int 0l ] -> None - | "%direct_int_div", _ -> int_binop l Int.div - | "%direct_int_mod", _ -> int_binop l Int.rem - | "%int_and", _ -> int_binop l Int.logand - | "%int_or", _ -> int_binop l Int.logor - | "%int_xor", _ -> int_binop l Int.logxor - | "%int_lsl", _ -> shift l Int.shift_left - | "%int_lsr", _ -> shift l Int.shift_right_logical - | "%int_asr", _ -> shift l Int.shift_right - | "%int_neg", [ Int i ] -> Some (Int (Int.neg i)) + | "%direct_int_div", _ -> Int.int_binop l Int.div + | "%direct_int_mod", _ -> Int.int_binop l Int.rem + | "%int_and", _ -> Int.int_binop l Int.logand + | "%int_or", _ -> Int.int_binop l Int.logor + | "%int_xor", _ -> Int.int_binop l Int.logxor + | "%int_lsl", _ -> Int.shift_op l Int.shift_left + | "%int_lsr", _ -> Int.shift_op l Int.shift_right_logical + | "%int_asr", _ -> Int.shift_op l Int.shift_right + | "%int_neg", _ -> Int.int_unop l Int.neg (* float *) | "caml_eq_float", _ -> float_binop_bool l Float.( = ) | "caml_neq_float", _ -> float_binop_bool l Float.( <> ) @@ -131,9 +178,9 @@ let eval_prim x = | "caml_sqrt_float", _ -> float_unop l sqrt | "caml_tan_float", _ -> float_unop l tan | ("caml_string_get" | "caml_string_unsafe_get"), [ String s; Int pos ] -> - let pos = Int.to_int pos in + let pos = Int32.to_int pos in if Config.Flag.safe_string () && pos >= 0 && pos < String.length s - then Some (Int (Int.of_int (Char.code s.[pos]))) + then Some (Int (Int32.of_int (Char.code s.[pos]))) else None | "caml_string_equal", [ String s1; String s2 ] -> bool (String.equal s1 s2) | "caml_string_notequal", [ String s1; String s2 ] -> @@ -143,7 +190,12 @@ let eval_prim x = | Some env -> Some (String env) | None -> None) | "caml_sys_const_word_size", [ _ ] -> Some (Int 32l) - | "caml_sys_const_int_size", [ _ ] -> Some (Int 32l) + | "caml_sys_const_int_size", [ _ ] -> + Some + (Int + (match Config.target () with + | `JavaScript -> 32l + | `Wasm -> 31l)) | "caml_sys_const_big_endian", [ _ ] -> Some (Int 0l) | "caml_sys_const_naked_pointers_checked", [ _ ] -> Some (Int 0l) | _ -> None) @@ -178,6 +230,9 @@ let is_int info x = (fun x -> match Flow.Info.def info x with | Some (Constant (Int _)) -> Y + | Some (Constant (NativeInt _ | Int32 _)) -> + assert (Poly.equal (Config.target ()) `Wasm); + N | Some (Block (_, _, _, _) | Constant _) -> N | None | Some _ -> Unknown) Unknown @@ -188,6 +243,9 @@ let is_int info x = | _ -> Unknown) x | Pc (Int _) -> Y + | Pc (NativeInt _ | Int32 _) -> + assert (Poly.equal (Config.target ()) `Wasm); + N | Pc _ -> N let the_tag_of info x get = @@ -330,7 +388,12 @@ let eval_instr info ((x, loc) as i) = | None -> [ i ]) | Let (x, Prim (Extern "caml_sys_const_backend_type", [ _ ])) -> let jsoo = Code.Var.fresh () in - [ Let (jsoo, Constant (String "js_of_ocaml")), noloc + let backend_name = + match Config.target () with + | `JavaScript -> "js_of_ocaml" + | `Wasm -> "wasm_of_ocaml" + in + [ Let (jsoo, Constant (String backend_name)), noloc ; Let (x, Block (0, [| jsoo |], NotArray, Immutable)), loc ] | Let (_, Prim (Extern ("%resume" | "%perform" | "%reperform"), _)) -> @@ -359,14 +422,17 @@ let eval_instr info ((x, loc) as i) = ( x , Prim ( prim - , List.map2 prim_args prim_args' ~f:(fun arg c -> - match c with - | Some ((Int _ | Float _ | NativeString _) as c) -> Pc c - | Some (String _ as c) when Config.Flag.use_js_string () -> Pc c - | Some _ + , List.map2 prim_args prim_args' ~f:(fun arg (c : constant option) -> + match c, Config.target () with + | ( Some ((Int _ | Int32 _ | NativeInt _ | NativeString _) as c) + , _ ) -> Pc c + | Some (Float _ as c), `JavaScript -> Pc c + | Some (String _ as c), `JavaScript + when Config.Flag.use_js_string () -> Pc c + | Some _, _ (* do not be duplicated other constant as they're not represented with constant in javascript. *) - | None -> arg) ) ) + | None, _ -> arg) ) ) , loc ) ]) | _ -> [ i ] diff --git a/compiler/lib/inline.ml b/compiler/lib/inline.ml index c6e8dd4b8e..e6707e50d5 100644 --- a/compiler/lib/inline.ml +++ b/compiler/lib/inline.ml @@ -167,7 +167,7 @@ let rec args_equal xs ys = | x :: xs, Pv y :: ys -> Code.Var.compare x y = 0 && args_equal xs ys | _ -> false -let inline live_vars closures name pc (outer, p) = +let inline ~first_class_primitives live_vars closures name pc (outer, p) = let block = Addr.Map.find pc p.blocks in let body, (outer, branch, p) = List.fold_right @@ -300,7 +300,7 @@ let inline live_vars closures name pc (outer, p) = , (outer, (Branch (fresh_addr, args), No), { p with blocks; free_pc }) ) | _ -> i :: rem, state) - | Let (x, Closure (l, (pc, []))), loc when not (Config.Flag.effects ()) -> ( + | Let (x, Closure (l, (pc, []))), loc when first_class_primitives -> ( let block = Addr.Map.find pc p.blocks in match block with | { body = [ (Let (y, Prim (Extern prim, args)), _loc) ] @@ -323,6 +323,11 @@ let inline live_vars closures name pc (outer, p) = let times = Debug.find "times" let f p live_vars = + let first_class_primitives = + match Config.target () with + | `JavaScript -> not (Config.Flag.effects ()) + | `Wasm -> false + in Code.invariant p; let t = Timer.make () in let closures = get_closures p in @@ -333,7 +338,7 @@ let f p live_vars = let traverse outer = Code.traverse { fold = Code.fold_children } - (inline live_vars closures name) + (inline ~first_class_primitives live_vars closures name) pc p.blocks (outer, p) diff --git a/compiler/lib/link_js.ml b/compiler/lib/link_js.ml index e4d3d2989e..4fa3778fb9 100644 --- a/compiler/lib/link_js.ml +++ b/compiler/lib/link_js.ml @@ -412,7 +412,13 @@ let link ~output ~linkall ~mklib ~toplevel ~files ~resolve_sourcemap_url ~source List.fold_left units ~init:StringSet.empty ~f:(fun acc (u : Unit_info.t) -> StringSet.union acc (StringSet.of_list u.primitives)) in - let code = Parse_bytecode.link_info ~symbols:!sym ~primitives ~crcs:[] in + let code = + Parse_bytecode.link_info + ~target:`JavaScript + ~symbols:!sym + ~primitives + ~crcs:[] + in let b = Buffer.create 100 in let fmt = Pretty_print.to_buffer b in Driver.configure fmt; diff --git a/compiler/lib/ocaml_compiler.ml b/compiler/lib/ocaml_compiler.ml index 12fcb3ab63..d8020a8411 100644 --- a/compiler/lib/ocaml_compiler.ml +++ b/compiler/lib/ocaml_compiler.ml @@ -18,11 +18,15 @@ open! Stdlib -let rec constant_of_const : _ -> Code.constant = +let rec constant_of_const ~target c : Code.constant = let open Lambda in let open Asttypes in - function - | Const_base (Const_int i) -> Int (Int32.of_int_warning_on_overflow i) + match c with + | Const_base (Const_int i) -> + Int + (match target with + | `JavaScript -> Int32.of_int_warning_on_overflow i + | `Wasm -> Int31.(of_int_warning_on_overflow i |> to_int32)) | Const_base (Const_char c) -> Int (Int32.of_int (Char.code c)) | ((Const_base (Const_string (s, _))) [@if ocaml_version < (4, 11, 0)]) | ((Const_base (Const_string (s, _, _))) [@if ocaml_version >= (4, 11, 0)]) -> String s @@ -35,9 +39,12 @@ let rec constant_of_const : _ -> Code.constant = let l = List.map ~f:(fun f -> float_of_string f) sl in Float_array (Array.of_list l) | ((Const_pointer i) [@if ocaml_version < (4, 12, 0)]) -> - Int (Int32.of_int_warning_on_overflow i) + Int + (match target with + | `JavaScript -> Int32.of_int_warning_on_overflow i + | `Wasm -> Int31.(of_int_warning_on_overflow i |> to_int32)) | Const_block (tag, l) -> - let l = Array.of_list (List.map l ~f:constant_of_const) in + let l = Array.of_list (List.map l ~f:(fun c -> constant_of_const ~target c)) in Tuple (tag, l, Unknown) let rec find_loc_in_summary ident' = function diff --git a/compiler/lib/ocaml_compiler.mli b/compiler/lib/ocaml_compiler.mli index 227f1b9f31..409381a562 100644 --- a/compiler/lib/ocaml_compiler.mli +++ b/compiler/lib/ocaml_compiler.mli @@ -16,7 +16,8 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) -val constant_of_const : Lambda.structured_constant -> Code.constant +val constant_of_const : + target:[ `JavaScript | `Wasm ] -> Lambda.structured_constant -> Code.constant val find_loc_in_summary : Ident.t -> Env.summary -> Location.t option diff --git a/compiler/lib/parse_bytecode.ml b/compiler/lib/parse_bytecode.ml index 18ef20b8df..4611259751 100644 --- a/compiler/lib/parse_bytecode.ml +++ b/compiler/lib/parse_bytecode.ml @@ -443,7 +443,7 @@ end (* Parse constants *) module Constants : sig - val parse : Obj.t -> Code.constant + val parse : target:[ `JavaScript | `Wasm ] -> Obj.t -> Code.constant val inlined : Code.constant -> bool end = struct @@ -477,7 +477,7 @@ end = struct let ident_native = ident_of_custom (Obj.repr 0n) - let rec parse x = + let rec parse ~target x = if Obj.is_block x then let tag = Obj.tag x in @@ -503,11 +503,17 @@ end = struct | None -> assert false else if tag < Obj.no_scan_tag then - Tuple (tag, Array.init (Obj.size x) ~f:(fun i -> parse (Obj.field x i)), Unknown) + Tuple + ( tag + , Array.init (Obj.size x) ~f:(fun i -> parse ~target (Obj.field x i)) + , Unknown ) else assert false else let i : int = Obj.magic x in - Int (Int32.of_int_warning_on_overflow i) + Int + (match target with + | `JavaScript -> Int32.of_int_warning_on_overflow i + | `Wasm -> Int31.(of_int_warning_on_overflow i |> to_int32)) let inlined = function | String _ | NativeString _ -> false @@ -763,8 +769,25 @@ let access_global g i = g.vars.(i) <- Some x; x -let register_global ?(force = false) g i loc rem = - if force || g.is_exported.(i) +let register_global ~target ?(force = false) g i loc rem = + if g.is_exported.(i) + && + match target with + | `Wasm -> true + | `JavaScript -> false + then ( + let name = + match g.named_value.(i) with + | None -> assert false + | Some name -> name + in + Code.Var.name (access_global g i) name; + ( Let + ( Var.fresh () + , Prim (Extern "caml_set_global", [ Pc (String name); Pv (access_global g i) ]) ) + , loc ) + :: rem) + else if force || g.is_exported.(i) then let args = match g.named_value.(i) with @@ -782,25 +805,40 @@ let register_global ?(force = false) g i loc rem = :: rem else rem -let get_global state instrs i loc = +let get_global ~target state instrs i loc = State.size_globals state (i + 1); let g = State.globals state in match g.vars.(i) with | Some x -> if debug_parser () then Format.printf "(global access %a)@." Var.print x; x, State.set_accu state x loc, instrs - | None -> + | None -> ( if i < Array.length g.constants && Constants.inlined g.constants.(i) then let x, state = State.fresh_var state loc in let cst = g.constants.(i) in x, state, (Let (x, Constant cst), loc) :: instrs - else ( + else if i < Array.length g.constants + || + match target with + | `Wasm -> false + | `JavaScript -> true + then ( g.is_const.(i) <- true; let x, state = State.fresh_var state loc in if debug_parser () then Format.printf "%a = CONST(%d)@." Var.print x i; g.vars.(i) <- Some x; x, state, instrs) + else + match g.named_value.(i) with + | None -> assert false + | Some name -> + let x, state = State.fresh_var state loc in + if debug_parser () then Format.printf "%a = get_global(%s)@." Var.print x name; + ( x + , state + , (Let (x, Prim (Extern "caml_get_global", [ Pc (String name) ])), loc) + :: instrs )) let tagged_blocks = ref Addr.Map.empty @@ -815,6 +853,7 @@ type compile_info = ; code : string ; limit : int ; debug : Debug.t + ; target : [ `JavaScript | `Wasm ] } let string_of_addr debug_data addr = @@ -842,7 +881,7 @@ let ( ||| ) x y = | No -> y | _ -> x -let rec compile_block blocks debug_data code pc state = +let rec compile_block blocks debug_data ~target code pc state = match Addr.Map.find_opt pc !tagged_blocks with | Some old_state -> ( (* Check that the shape of the stack is compatible with the one used to compile the block *) @@ -874,7 +913,7 @@ let rec compile_block blocks debug_data code pc state = let state = State.start_block pc state in tagged_blocks := Addr.Map.add pc state !tagged_blocks; let instr, last, state' = - compile { blocks; code; limit; debug = debug_data } pc state [] + compile { blocks; code; limit; debug = debug_data; target } pc state [] in assert (not (Addr.Map.mem pc !compiled_blocks)); (* When jumping to a block that was already visited and the @@ -903,10 +942,11 @@ let rec compile_block blocks debug_data code pc state = in compiled_blocks := Addr.Map.add pc (state, List.rev instr, last) !compiled_blocks; match fst last with - | Branch (pc', _) -> compile_block blocks debug_data code pc' (adjust_state pc') + | Branch (pc', _) -> + compile_block blocks debug_data ~target code pc' (adjust_state pc') | Cond (_, (pc1, _), (pc2, _)) -> - compile_block blocks debug_data code pc1 (adjust_state pc1); - compile_block blocks debug_data code pc2 (adjust_state pc2) + compile_block blocks debug_data ~target code pc1 (adjust_state pc1); + compile_block blocks debug_data ~target code pc2 (adjust_state pc2) | Poptrap (_, _) -> () | Switch (_, _) -> () | Raise _ | Return _ | Stop -> () @@ -1248,7 +1288,7 @@ and compile infos pc state instrs = let params, state' = State.make_stack nparams state' loc in if debug_parser () then Format.printf ") {@."; let state' = State.clear_accu state' in - compile_block infos.blocks infos.debug code addr state'; + compile_block infos.blocks infos.debug ~target:infos.target code addr state'; if debug_parser () then Format.printf "}@."; let args = State.stack_vars state' in let state'', _, _ = Addr.Map.find addr !compiled_blocks in @@ -1305,7 +1345,7 @@ and compile infos pc state instrs = let params, state' = State.make_stack nparams state' loc in if debug_parser () then Format.printf ") {@."; let state' = State.clear_accu state' in - compile_block infos.blocks infos.debug code addr state'; + compile_block infos.blocks infos.debug ~target:infos.target code addr state'; if debug_parser () then Format.printf "}@."; let args = State.stack_vars state' in let state'', _, _ = Addr.Map.find addr !compiled_blocks in @@ -1335,16 +1375,16 @@ and compile infos pc state instrs = compile infos (pc + 2) (State.env_acc n state) instrs | GETGLOBAL -> let i = getu code (pc + 1) in - let _, state, instrs = get_global state instrs i loc in + let _, state, instrs = get_global ~target:infos.target state instrs i loc in compile infos (pc + 2) state instrs | PUSHGETGLOBAL -> let state = State.push state loc in let i = getu code (pc + 1) in - let _, state, instrs = get_global state instrs i loc in + let _, state, instrs = get_global ~target:infos.target state instrs i loc in compile infos (pc + 2) state instrs | GETGLOBALFIELD -> let i = getu code (pc + 1) in - let x, state, instrs = get_global state instrs i loc in + let x, state, instrs = get_global ~target:infos.target state instrs i loc in let j = getu code (pc + 2) in let y, state = State.fresh_var state loc in if debug_parser () then Format.printf "%a = %a[%d]@." Var.print y Var.print x j; @@ -1353,7 +1393,7 @@ and compile infos pc state instrs = let state = State.push state loc in let i = getu code (pc + 1) in - let x, state, instrs = get_global state instrs i loc in + let x, state, instrs = get_global ~target:infos.target state instrs i loc in let j = getu code (pc + 2) in let y, state = State.fresh_var state loc in if debug_parser () then Format.printf "%a = %a[%d]@." Var.print y Var.print x j; @@ -1378,7 +1418,7 @@ and compile infos pc state instrs = in let x, state = State.fresh_var state loc in if debug_parser () then Format.printf "%a = 0@." Var.print x; - let instrs = register_global g i loc instrs in + let instrs = register_global ~target:infos.target g i loc instrs in compile infos (pc + 2) state ((Let (x, const 0l), loc) :: instrs) | ATOM0 -> let x, state = State.fresh_var state loc in @@ -1726,9 +1766,9 @@ and compile infos pc state instrs = let it = Array.init isize ~f:(fun i -> base + gets code (base + i)) in let bt = Array.init bsize ~f:(fun i -> base + gets code (base + isize + i)) in Array.iter it ~f:(fun pc' -> - compile_block infos.blocks infos.debug code pc' state); + compile_block infos.blocks infos.debug ~target:infos.target code pc' state); Array.iter bt ~f:(fun pc' -> - compile_block infos.blocks infos.debug code pc' state); + compile_block infos.blocks infos.debug ~target:infos.target code pc' state); match isize, bsize with | _, 0 -> instrs, (Switch (x, Array.map it ~f:(fun pc -> pc, [])), loc), state | 0, _ -> @@ -1799,10 +1839,17 @@ and compile infos pc state instrs = , (handler_addr, State.stack_vars handler_state) ) , loc ) ) !compiled_blocks; - compile_block infos.blocks infos.debug code handler_addr handler_state; compile_block infos.blocks infos.debug + ~target:infos.target + code + handler_addr + handler_state; + compile_block + infos.blocks + infos.debug + ~target:infos.target code body_addr { (State.push_handler handler_ctx_state) with @@ -1820,6 +1867,7 @@ and compile infos pc state instrs = compile_block infos.blocks infos.debug + ~target:infos.target code addr (State.pop 4 (State.pop_handler state)); @@ -2503,7 +2551,7 @@ type one = ; debug : Debug.t } -let parse_bytecode code globals debug_data = +let parse_bytecode code globals debug_data ~target = let state = State.initial globals in Code.Var.reset (); let blocks = Blocks.analyse debug_data code in @@ -2518,7 +2566,7 @@ let parse_bytecode code globals debug_data = if not (Blocks.is_empty blocks') then ( let start = 0 in - compile_block blocks' debug_data code start state; + compile_block blocks' debug_data ~target code start state; let blocks = Addr.Map.mapi (fun _ (state, instr, last) -> @@ -2638,6 +2686,7 @@ type bytesections = [@@ocaml.warning "-unused-field"] let from_exe + ~target ?(includes = []) ~linkall ~link_info @@ -2651,7 +2700,7 @@ let from_exe let primitive_table = Array.of_list primitives in let code = Toc.read_code toc ic in let init_data = Toc.read_data toc ic in - let init_data = Array.map ~f:Constants.parse init_data in + let init_data = Array.map ~f:(Constants.parse ~target) init_data in let orig_symbols = Toc.read_symb toc ic in let orig_crcs = Toc.read_crcs toc ic in let keeps = @@ -2706,12 +2755,12 @@ let from_exe Ocaml_compiler.Symtable.GlobalMap.iter symbols ~f:(fun id n -> globals.named_value.(n) <- Some (Ocaml_compiler.Symtable.Global.name id); globals.is_exported.(n) <- true); - let p = parse_bytecode code globals debug_data in + let p = parse_bytecode code globals debug_data ~target in (* register predefined exception *) let body = List.fold_left predefined_exceptions ~init:[] ~f:(fun body (i, name) -> globals.named_value.(i) <- Some name; - let body = register_global ~force:true globals i noloc body in + let body = register_global ~target ~force:true globals i noloc body in globals.is_exported.(i) <- false; body) in @@ -2719,7 +2768,7 @@ let from_exe Array.fold_right_i globals.constants ~init:body ~f:(fun i _ l -> match globals.vars.(i) with | Some x when globals.is_const.(i) -> - let l = register_global globals i noloc l in + let l = register_global ~target globals i noloc l in (Let (x, Constant globals.constants.(i)), noloc) :: l | _ -> l) in @@ -2738,8 +2787,8 @@ let from_exe let gdata = Var.fresh () in let need_gdata = ref false in let infos = - [ "sections", Constants.parse (Obj.repr sections) - ; "symbols", Constants.parse (Obj.repr symbols_array) + [ "sections", Constants.parse ~target (Obj.repr sections) + ; "symbols", Constants.parse ~target (Obj.repr symbols_array) ; "prim_count", Int (Int32.of_int (Array.length globals.primitives)) ] in @@ -2830,7 +2879,7 @@ let from_bytes ~prims ~debug (code : bytecode) = t in let globals = make_globals 0 [||] prims in - let p = parse_bytecode code globals debug_data in + let p = parse_bytecode code globals debug_data ~target:`JavaScript in let gdata = Var.fresh_n "global_data" in let need_gdata = ref false in let find_name i = @@ -2895,13 +2944,13 @@ module Reloc = struct let constant_of_const x = Constants.parse x [@@if ocaml_version >= (5, 1, 0)] (* We currently rely on constants to be relocated before globals. *) - let step1 t compunit code = + let step1 ~target t compunit code = if t.step2_started then assert false; let open Cmo_format in List.iter compunit.cu_primitives ~f:(fun name -> Hashtbl.add t.primitives name (Hashtbl.length t.primitives)); let slot_for_literal sc = - t.constants <- constant_of_const sc :: t.constants; + t.constants <- constant_of_const ~target sc :: t.constants; let pos = t.pos in t.pos <- succ t.pos; pos @@ -2969,16 +3018,16 @@ module Reloc = struct globals end -let from_compilation_units ~includes:_ ~include_cmis ~debug_data l = +let from_compilation_units ~target ~includes:_ ~include_cmis ~debug_data l = let reloc = Reloc.create () in - List.iter l ~f:(fun (compunit, code) -> Reloc.step1 reloc compunit code); + List.iter l ~f:(fun (compunit, code) -> Reloc.step1 ~target reloc compunit code); List.iter l ~f:(fun (compunit, code) -> Reloc.step2 reloc compunit code); let globals = Reloc.make_globals reloc in let code = let l = List.map l ~f:(fun (_, c) -> Bytes.to_string c) in String.concat ~sep:"" l in - let prog = parse_bytecode code globals debug_data in + let prog = parse_bytecode code globals debug_data ~target in let gdata = Var.fresh_n "global_data" in let need_gdata = ref false in let body = @@ -2987,7 +3036,7 @@ let from_compilation_units ~includes:_ ~include_cmis ~debug_data l = | Some x when globals.is_const.(i) -> ( match globals.named_value.(i) with | None -> - let l = register_global globals i noloc l in + let l = register_global ~target globals i noloc l in let cst = globals.constants.(i) in (match cst, Code.Var.get_name x with | String str, None -> Code.Var.name x (Printf.sprintf "cst_%s" str) @@ -3020,7 +3069,8 @@ let from_compilation_units ~includes:_ ~include_cmis ~debug_data l = in { code = prepend prog body; cmis; debug = debug_data } -let from_cmo ?(includes = []) ?(include_cmis = false) ?(debug = false) compunit ic = +let from_cmo ~target ?(includes = []) ?(include_cmis = false) ?(debug = false) compunit ic + = let debug_data = Debug.create ~include_cmis debug in seek_in ic compunit.Cmo_format.cu_pos; let code = Bytes.create compunit.Cmo_format.cu_codesize in @@ -3031,11 +3081,13 @@ let from_cmo ?(includes = []) ?(include_cmis = false) ?(debug = false) compunit seek_in ic compunit.Cmo_format.cu_debug; Debug.read_event_list debug_data ~crcs:[] ~includes ~orig:0 ic); if times () then Format.eprintf " read debug events: %a@." Timer.print t; - let p = from_compilation_units ~includes ~include_cmis ~debug_data [ compunit, code ] in + let p = + from_compilation_units ~target ~includes ~include_cmis ~debug_data [ compunit, code ] + in Code.invariant p.code; p -let from_cma ?(includes = []) ?(include_cmis = false) ?(debug = false) lib ic = +let from_cma ~target ?(includes = []) ?(include_cmis = false) ?(debug = false) lib ic = let debug_data = Debug.create ~include_cmis debug in let orig = ref 0 in let t = ref 0. in @@ -3054,7 +3106,7 @@ let from_cma ?(includes = []) ?(include_cmis = false) ?(debug = false) lib ic = compunit, code) in if times () then Format.eprintf " read debug events: %.2f@." !t; - let p = from_compilation_units ~includes ~include_cmis ~debug_data units in + let p = from_compilation_units ~target ~includes ~include_cmis ~debug_data units in Code.invariant p.code; p @@ -3099,7 +3151,7 @@ let from_channel ic = `Exe | _ -> raise Magic_number.(Bad_magic_number (to_string magic))) -let predefined_exceptions () = +let predefined_exceptions ~target = let body = let open Code in List.map predefined_exceptions ~f:(fun (index, name) -> @@ -3108,25 +3160,45 @@ let predefined_exceptions () = let v_name = Var.fresh () in let v_name_js = Var.fresh () in let v_index = Var.fresh () in - [ Let (v_name, Constant (String name)), noloc - ; Let (v_name_js, Constant (NativeString (Native_string.of_string name))), noloc - ; ( Let - ( v_index - , Constant - (Int - ((* Predefined exceptions are registered in - Symtable.init with [-index - 1] *) - Int32.of_int - (-index - 1))) ) - , noloc ) - ; Let (exn, Block (248, [| v_name; v_index |], NotArray, Immutable)), noloc - ; ( Let - ( Var.fresh () - , Prim - ( Extern "caml_register_global" - , [ Pc (Int (Int32.of_int index)); Pv exn; Pv v_name_js ] ) ) - , noloc ) - ]) + [ Let (v_name, Constant (String name)), noloc ] + @ (match target with + | `Wasm -> [] + | `JavaScript -> + [ ( Let (v_name_js, Constant (NativeString (Native_string.of_string name))) + , noloc ) + ]) + @ [ ( Let + ( v_index + , Constant + (Int + ((* Predefined exceptions are registered in + Symtable.init with [-index - 1] *) + Int32.of_int + (-index - 1))) ) + , noloc ) + ; Let (exn, Block (248, [| v_name; v_index |], NotArray, Immutable)), noloc + ; ( Let + ( Var.fresh () + , Prim + ( Extern "caml_register_global" + , [ Pc (Int (Int32.of_int index)) + ; Pv exn + ; Pv + (match target with + | `JavaScript -> v_name_js + | `Wasm -> v_name) + ] ) ) + , noloc ) + ] + @ + match target with + | `JavaScript -> [] + | `Wasm -> + [ ( Let + ( Var.fresh () + , Prim (Extern "caml_set_global", [ Pc (String name); Pv exn ]) ) + , noloc ) + ]) |> List.concat in let block = { params = []; body; branch = Stop, noloc } in @@ -3141,7 +3213,7 @@ let predefined_exceptions () = in { start = 0; blocks = Addr.Map.singleton 0 block; free_pc = 1 }, unit_info -let link_info ~symbols ~primitives ~crcs = +let link_info ~target ~symbols ~primitives ~crcs = let gdata = Code.Var.fresh_n "global_data" in let symbols_array = Ocaml_compiler.Symtable.GlobalMap.fold @@ -3159,8 +3231,8 @@ let link_info ~symbols ~primitives ~crcs = (* Include linking information *) let sections = { symb = symbols; crcs; prim = primitives; dlpt = [] } in let infos = - [ "sections", Constants.parse (Obj.repr sections) - ; "symbols", Constants.parse (Obj.repr symbols_array) + [ "sections", Constants.parse ~target (Obj.repr sections) + ; "symbols", Constants.parse ~target (Obj.repr symbols_array) ; "prim_count", Int (Int32.of_int (List.length primitives)) ] in diff --git a/compiler/lib/parse_bytecode.mli b/compiler/lib/parse_bytecode.mli index 627f65fdd0..33edf53f1c 100644 --- a/compiler/lib/parse_bytecode.mli +++ b/compiler/lib/parse_bytecode.mli @@ -52,7 +52,8 @@ end val read_primitives : Toc.t -> in_channel -> string list val from_exe : - ?includes:string list + target:[ `JavaScript | `Wasm ] + -> ?includes:string list -> linkall:bool -> link_info:bool -> include_cmis:bool @@ -62,7 +63,8 @@ val from_exe : -> one val from_cmo : - ?includes:string list + target:[ `JavaScript | `Wasm ] + -> ?includes:string list -> ?include_cmis:bool -> ?debug:bool -> Cmo_format.compilation_unit @@ -70,7 +72,8 @@ val from_cmo : -> one val from_cma : - ?includes:string list + target:[ `JavaScript | `Wasm ] + -> ?includes:string list -> ?include_cmis:bool -> ?debug:bool -> Cmo_format.library @@ -87,10 +90,11 @@ val from_string : -> string -> Code.program * Debug.t -val predefined_exceptions : unit -> Code.program * Unit_info.t +val predefined_exceptions : target:[ `JavaScript | `Wasm ] -> Code.program * Unit_info.t val link_info : - symbols:Ocaml_compiler.Symtable.GlobalMap.t + target:[ `JavaScript | `Wasm ] + -> symbols:Ocaml_compiler.Symtable.GlobalMap.t -> primitives:StringSet.t -> crcs:(string * Digest.t option) list -> Code.program diff --git a/compiler/lib/specialize_js.ml b/compiler/lib/specialize_js.ml index 9fdce562be..162b877582 100644 --- a/compiler/lib/specialize_js.ml +++ b/compiler/lib/specialize_js.ml @@ -23,47 +23,49 @@ open Code open Flow let specialize_instr info i = - match i with - | Let (x, Prim (Extern "caml_format_int", [ y; z ])) -> ( + match i, Config.target () with + | Let (x, Prim (Extern "caml_format_int", [ y; z ])), `JavaScript -> ( match the_string_of info y with | Some "%d" -> ( match the_int info z with | Some i -> Let (x, Constant (String (Int32.to_string i))) | None -> Let (x, Prim (Extern "%caml_format_int_special", [ z ]))) | _ -> i) - | Let (x, Prim (Extern "%caml_format_int_special", [ z ])) -> ( + | Let (x, Prim (Extern "%caml_format_int_special", [ z ])), `JavaScript -> ( match the_int info z with | Some i -> Let (x, Constant (String (Int32.to_string i))) | None -> i) (* inline the String constant argument so that generate.ml can attempt to parse it *) - | Let - ( x - , Prim - ( Extern (("caml_js_var" | "caml_js_expr" | "caml_pure_js_expr") as prim) - , [ (Pv _ as y) ] ) ) + | ( Let + ( x + , Prim + ( Extern (("caml_js_var" | "caml_js_expr" | "caml_pure_js_expr") as prim) + , [ (Pv _ as y) ] ) ) + , _ ) when Config.Flag.safe_string () -> ( match the_string_of info y with | Some s -> Let (x, Prim (Extern prim, [ Pc (String s) ])) | _ -> i) - | Let (x, Prim (Extern ("caml_register_named_value" as prim), [ y; z ])) -> ( + | Let (x, Prim (Extern ("caml_register_named_value" as prim), [ y; z ])), `JavaScript + -> ( match the_string_of info y with | Some s when Primitive.need_named_value s -> Let (x, Prim (Extern prim, [ Pc (String s); z ])) | Some _ -> Let (x, Constant (Int 0l)) | None -> i) - | Let (x, Prim (Extern "caml_js_call", [ f; o; a ])) -> ( + | Let (x, Prim (Extern "caml_js_call", [ f; o; a ])), _ -> ( match the_def_of info a with | Some (Block (_, a, _, _)) -> let a = Array.map a ~f:(fun x -> Pv x) in Let (x, Prim (Extern "%caml_js_opt_call", f :: o :: Array.to_list a)) | _ -> i) - | Let (x, Prim (Extern "caml_js_fun_call", [ f; a ])) -> ( + | Let (x, Prim (Extern "caml_js_fun_call", [ f; a ])), _ -> ( match the_def_of info a with | Some (Block (_, a, _, _)) -> let a = Array.map a ~f:(fun x -> Pv x) in Let (x, Prim (Extern "%caml_js_opt_fun_call", f :: Array.to_list a)) | _ -> i) - | Let (x, Prim (Extern "caml_js_meth_call", [ o; m; a ])) -> ( + | Let (x, Prim (Extern "caml_js_meth_call", [ o; m; a ])), _ -> ( match the_string_of info m with | Some m when Javascript.is_ident m -> ( match the_def_of info a with @@ -78,13 +80,13 @@ let specialize_instr info i = :: Array.to_list a ) ) | _ -> i) | _ -> i) - | Let (x, Prim (Extern "caml_js_new", [ c; a ])) -> ( + | Let (x, Prim (Extern "caml_js_new", [ c; a ])), _ -> ( match the_def_of info a with | Some (Block (_, a, _, _)) -> let a = Array.map a ~f:(fun x -> Pv x) in Let (x, Prim (Extern "%caml_js_opt_new", c :: Array.to_list a)) | _ -> i) - | Let (x, Prim (Extern "caml_js_object", [ a ])) -> ( + | Let (x, Prim (Extern "caml_js_object", [ a ])), _ -> ( try let a = match the_def_of info a with @@ -109,43 +111,44 @@ let specialize_instr info i = in Let (x, Prim (Extern "%caml_js_opt_object", List.flatten (Array.to_list a))) with Exit -> i) - | Let (x, Prim (Extern "caml_js_get", [ o; (Pv _ as f) ])) -> ( + | Let (x, Prim (Extern "caml_js_get", [ o; (Pv _ as f) ])), _ -> ( match the_native_string_of info f with | Some s -> Let (x, Prim (Extern "caml_js_get", [ o; Pc (NativeString s) ])) | _ -> i) - | Let (x, Prim (Extern "caml_js_set", [ o; (Pv _ as f); v ])) -> ( + | Let (x, Prim (Extern "caml_js_set", [ o; (Pv _ as f); v ])), _ -> ( match the_native_string_of info f with | Some s -> Let (x, Prim (Extern "caml_js_set", [ o; Pc (NativeString s); v ])) | _ -> i) - | Let (x, Prim (Extern "caml_js_delete", [ o; (Pv _ as f) ])) -> ( + | Let (x, Prim (Extern "caml_js_delete", [ o; (Pv _ as f) ])), _ -> ( match the_native_string_of info f with | Some s -> Let (x, Prim (Extern "caml_js_delete", [ o; Pc (NativeString s) ])) | _ -> i) - | Let (x, Prim (Extern ("caml_jsstring_of_string" | "caml_js_from_string"), [ y ])) -> ( + | Let (x, Prim (Extern ("caml_jsstring_of_string" | "caml_js_from_string"), [ y ])), _ + -> ( match the_string_of info y with | Some s when String.is_valid_utf_8 s -> Let (x, Constant (NativeString (Native_string.of_string s))) | Some _ | None -> i) - | Let (x, Prim (Extern "caml_jsbytes_of_string", [ y ])) -> ( + | Let (x, Prim (Extern "caml_jsbytes_of_string", [ y ])), _ -> ( match the_string_of info y with | Some s -> Let (x, Constant (NativeString (Native_string.of_bytestring s))) | None -> i) - | Let (x, Prim (Extern "%int_mul", [ y; z ])) -> ( + | Let (x, Prim (Extern "%int_mul", [ y; z ])), `JavaScript -> ( match the_int info y, the_int info z with | Some j, _ when Int32.(abs j < 0x200000l) -> Let (x, Prim (Extern "%direct_int_mul", [ y; z ])) | _, Some j when Int32.(abs j < 0x200000l) -> Let (x, Prim (Extern "%direct_int_mul", [ y; z ])) | _ -> i) - | Let (x, Prim (Extern "%int_div", [ y; z ])) -> ( + | Let (x, Prim (Extern "%int_div", [ y; z ])), _ -> ( match the_int info z with | Some j when Int32.(j <> 0l) -> Let (x, Prim (Extern "%direct_int_div", [ y; z ])) | _ -> i) - | Let (x, Prim (Extern "%int_mod", [ y; z ])) -> ( + | Let (x, Prim (Extern "%int_mod", [ y; z ])), _ -> ( match the_int info z with | Some j when Int32.(j <> 0l) -> Let (x, Prim (Extern "%direct_int_mod", [ y; z ])) | _ -> i) - | _ -> i + | _, _ -> i let equal2 a b = Code.Var.equal a b diff --git a/compiler/lib/stdlib.ml b/compiler/lib/stdlib.ml index d67f4b0483..abd4bc9afd 100644 --- a/compiler/lib/stdlib.ml +++ b/compiler/lib/stdlib.ml @@ -341,7 +341,51 @@ module Int32 = struct n end -module Int31 = struct +module type Arith_ops = sig + type t + + val neg : t -> t + + val add : t -> t -> t + + val sub : t -> t -> t + + val mul : t -> t -> t + + val div : t -> t -> t + + val rem : t -> t -> t + + val logand : t -> t -> t + + val logor : t -> t -> t + + val logxor : t -> t -> t + + val shift_left : t -> int -> t + + val shift_right : t -> int -> t + + val shift_right_logical : t -> int -> t +end + +module Int31 : sig + type t = private int32 + + include Arith_ops with type t := t + + val of_int_warning_on_overflow : int -> t + + val of_nativeint_warning_on_overflow : nativeint -> t + + val to_int32 : t -> int32 + + val of_int32_warning_on_overflow : int32 -> t + + val to_int : t -> int +end = struct + type t = int32 + let wrap i = Int32.(shift_right (shift_left i 1) 1) let of_int_warning_on_overflow i = @@ -361,6 +405,50 @@ module Int31 = struct ~to_dec:(Printf.sprintf "%nd") ~to_hex:(Printf.sprintf "%nx") n + + let of_int32_warning_on_overflow n = + Int32.convert_warning_on_overflow + ~to_int32:(fun i -> wrap i) + ~of_int32:Fun.id + ~equal:Int32.equal + ~to_dec:(Printf.sprintf "%ld") + ~to_hex:(Printf.sprintf "%lx") + n + + let neg = Int32.neg + + let int_binop wrap f x y = wrap (f x y) + + let add = int_binop wrap Int32.add + + let sub = int_binop wrap Int32.sub + + let mul = int_binop wrap Int32.mul + + let div = int_binop wrap Int32.div + + let rem = int_binop wrap Int32.rem + + let logand = int_binop wrap Int32.logand + + let logor = int_binop wrap Int32.logor + + let logxor = int_binop wrap Int32.logxor + + let shift_op wrap truncate f x y = + (* Limit the shift offset to [0, 31] *) + wrap (f (truncate x) (y land 0x1f)) + + let shift_left = shift_op wrap Fun.id Int32.shift_left + + let shift_right = shift_op wrap Fun.id Int32.shift_right + + let shift_right_logical = + shift_op wrap (fun i -> Int32.logand i 0x7fffffffl) Int32.shift_right_logical + + let to_int32 (x : t) : int32 = x + + let to_int (x : t) = Int32.to_int x end module Option = struct