diff --git a/compiler/bin-wasm_of_ocaml/compile.ml b/compiler/bin-wasm_of_ocaml/compile.ml index 6ab6fe175..ddd04e93b 100644 --- a/compiler/bin-wasm_of_ocaml/compile.ml +++ b/compiler/bin-wasm_of_ocaml/compile.ml @@ -162,7 +162,7 @@ let escape_string s = done; Buffer.contents b -let build_js_runtime primitives (strings : string list) wasm_file output_file = +let build_js_runtime primitives (strings, fragments) wasm_file output_file = let always_required_js, primitives = let l = StringSet.fold @@ -200,6 +200,21 @@ let build_js_runtime primitives (strings : string list) wasm_file output_file = strings)) , Javascript.N ) ]); + let fragment_buffer = Buffer.create 1024 in + let f = Pretty_print.to_buffer fragment_buffer in + Pretty_print.set_compact f (not (Config.Flag.pretty ())); + ignore + (Js_output.program + f + [ ( Javascript.Expression_statement + (EObj + (List.map + ~f:(fun (nm, f) -> + let id = Utf8_string.of_string_exn nm in + Javascript.Property (PNI id, f)) + fragments)) + , Javascript.N ) + ]); let s = Wa_runtime.js_runtime in let rec find pat i = if String.equal (String.sub s ~pos:i ~len:(String.length pat)) pat @@ -227,6 +242,8 @@ let build_js_runtime primitives (strings : string list) wasm_file output_file = ^ trim_semi (Buffer.contents b') ^ String.sub s ~pos:(j + 10) ~len:(k - j - 10) ^ trim_semi (Buffer.contents b'') + ^ "," + ^ trim_semi (Buffer.contents fragment_buffer) ^ String.sub s ~pos:(k + 7) ~len:(String.length s - k - 7)) let run { Cmd_arg.common; profile; runtime_files; input_file; output_file; params } = diff --git a/compiler/lib/driver.ml b/compiler/lib/driver.ml index fad0ce0bb..f3cd7837e 100644 --- a/compiler/lib/driver.ml +++ b/compiler/lib/driver.ml @@ -615,13 +615,13 @@ let full ~target ~standalone ~wrap_with_fun ~profile ~linkall ~source_map d p = match target with | `JavaScript formatter -> let source_map = emit formatter r in - source_map, [] + source_map, ([], []) | `Wasm ch -> let (p, live_vars), _, in_cps = r in None, Wa_generate.f ch ~live_vars ~in_cps p let full_no_source_map ~target ~standalone ~wrap_with_fun ~profile ~linkall d p = - let (_ : Source_map.t option * string list) = + let (_ : Source_map.t option * _) = full ~target ~standalone ~wrap_with_fun ~profile ~linkall ~source_map:None d p in () diff --git a/compiler/lib/driver.mli b/compiler/lib/driver.mli index 5ca2b795d..9b27c91f1 100644 --- a/compiler/lib/driver.mli +++ b/compiler/lib/driver.mli @@ -29,7 +29,7 @@ val f : -> ?source_map:Source_map.t -> Parse_bytecode.Debug.t -> Code.program - -> Source_map.t option * string list + -> Source_map.t option * (string list * (string * Javascript.expression) list) val f' : ?standalone:bool diff --git a/compiler/lib/specialize_js.ml b/compiler/lib/specialize_js.ml index 87097c2ec..c562f18be 100644 --- a/compiler/lib/specialize_js.ml +++ b/compiler/lib/specialize_js.ml @@ -53,19 +53,19 @@ let specialize_instr ~target info i = Let (x, Prim (Extern prim, [ Pc (String s); z ])) | Some _ -> Let (x, Constant (Int (Regular, 0l))) | None -> i) - | Let (x, Prim (Extern "caml_js_call", [ f; o; a ])), `JavaScript -> ( + | 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 ])), `JavaScript -> ( + | 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 ])), `JavaScript -> ( + | 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 @@ -80,7 +80,7 @@ let specialize_instr ~target info i = :: Array.to_list a ) ) | _ -> i) | _ -> i) - | Let (x, Prim (Extern "caml_js_new", [ c; a ])), `JavaScript -> ( + | 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 diff --git a/compiler/lib/wasm/wa_code_generation.ml b/compiler/lib/wasm/wa_code_generation.ml index b4eb3cdce..6ef0daca1 100644 --- a/compiler/lib/wasm/wa_code_generation.ml +++ b/compiler/lib/wasm/wa_code_generation.ml @@ -37,6 +37,7 @@ type context = ; mutable string_count : int ; mutable strings : string list ; mutable string_index : int StringMap.t + ; mutable fragments : Javascript.expression StringMap.t } let make_context () = @@ -57,6 +58,7 @@ let make_context () = ; string_count = 0 ; strings = [] ; string_index = StringMap.empty + ; fragments = StringMap.empty } type var = @@ -187,6 +189,12 @@ let register_string s st = context.string_index <- StringMap.add s n context.string_index; n, st +let register_fragment name f st = + let context = st.context in + if not (StringMap.mem name context.fragments) + then context.fragments <- StringMap.add name (f ()) context.fragments; + (), st + let set_closure_env f env st = st.context.closure_envs <- Var.Map.add f env st.context.closure_envs; (), st diff --git a/compiler/lib/wasm/wa_code_generation.mli b/compiler/lib/wasm/wa_code_generation.mli index f8db7bf5d..dec0939dd 100644 --- a/compiler/lib/wasm/wa_code_generation.mli +++ b/compiler/lib/wasm/wa_code_generation.mli @@ -21,6 +21,7 @@ type context = ; mutable string_count : int ; mutable strings : string list ; mutable string_index : int StringMap.t + ; mutable fragments : Javascript.expression StringMap.t } val make_context : unit -> context @@ -141,6 +142,8 @@ val init_code : context -> unit t val register_string : string -> int t +val register_fragment : string -> (unit -> Javascript.expression) -> unit t + val get_context : context t val set_closure_env : Code.Var.t -> Code.Var.t -> unit t diff --git a/compiler/lib/wasm/wa_core_target.ml b/compiler/lib/wasm/wa_core_target.ml index 98cb1f811..fc11dbf30 100644 --- a/compiler/lib/wasm/wa_core_target.ml +++ b/compiler/lib/wasm/wa_core_target.ml @@ -620,6 +620,8 @@ module Math = struct let fmod f g = binary "fmod" f g end +let internal_primitives = Hashtbl.create 0 + let handle_exceptions ~result_typ ~fall_through ~context body x exn_handler = let* ocaml_tag = register_import ~name:"ocaml_exception" (Tag Value.value) in try_ diff --git a/compiler/lib/wasm/wa_gc_target.ml b/compiler/lib/wasm/wa_gc_target.ml index 2da6da774..5cadcfd35 100644 --- a/compiler/lib/wasm/wa_gc_target.ml +++ b/compiler/lib/wasm/wa_gc_target.ml @@ -1230,6 +1230,155 @@ module Math = struct let exp2 x = power (return (W.Const (F64 2.))) x end +module JavaScript = struct + let anyref = W.Ref { nullable = true; typ = Any } + + let invoke_fragment name args = + let* f = + register_import + ~import_module:"fragments" + ~name + (Fun { params = List.map ~f:(fun _ -> anyref) args; result = [ anyref ] }) + in + let* wrap = + register_import ~name:"wrap" (Fun { params = [ anyref ]; result = [ Type.value ] }) + in + let* unwrap = + register_import + ~name:"unwrap" + (Fun { params = [ Type.value ]; result = [ anyref ] }) + in + let* args = + expression_list + (fun e -> + let* e = e in + return (W.Call (unwrap, [ e ]))) + args + in + return (W.Call (wrap, [ Call (f, args) ])) +end + +let internal_primitives = Hashtbl.create 100 + +let () = + let register name f = Hashtbl.add internal_primitives name f in + let module J = Javascript in + register "%caml_js_opt_call" (fun transl_prim_arg l -> + let arity = List.length l - 2 in + let name = Printf.sprintf "call_%d" arity in + let* () = + register_fragment name (fun () -> + let f = Utf8_string.of_string_exn "f" in + let o = Utf8_string.of_string_exn "o" in + let params = + List.init ~len:arity ~f:(fun i -> + Utf8_string.of_string_exn (Printf.sprintf "x%d" i)) + in + EArrow + ( J.fun_ + (List.map ~f:J.ident (f :: params)) + [ ( Return_statement + (Some + (J.call + (J.dot + (EVar (J.ident f)) + (Utf8_string.of_string_exn "call")) + (List.map ~f:(fun x -> J.EVar (J.ident x)) (o :: params)) + N)) + , N ) + ] + N + , AUnknown )) + in + let l = List.map ~f:transl_prim_arg l in + JavaScript.invoke_fragment name l); + register "%caml_js_opt_fun_call" (fun transl_prim_arg l -> + let arity = List.length l - 1 in + let name = Printf.sprintf "fun_call_%d" arity in + let* () = + register_fragment name (fun () -> + let f = Utf8_string.of_string_exn "f" in + let params = + List.init ~len:arity ~f:(fun i -> + Utf8_string.of_string_exn (Printf.sprintf "x%d" i)) + in + EArrow + ( J.fun_ + (List.map ~f:J.ident (f :: params)) + [ ( Return_statement + (Some + (J.call + (EVar (J.ident f)) + (List.map ~f:(fun x -> J.EVar (J.ident x)) params) + N)) + , N ) + ] + N + , AUnknown )) + in + let l = List.map ~f:transl_prim_arg l in + JavaScript.invoke_fragment name l); + register "%caml_js_opt_meth_call" (fun transl_prim_arg l -> + match l with + | o :: Code.Pc (NativeString (Utf meth)) :: args -> + let arity = List.length args in + let name = + let (Utf8 name) = meth in + Printf.sprintf "meth_call_%d_%s" arity name + in + let* () = + register_fragment name (fun () -> + let o = Utf8_string.of_string_exn "o" in + let params = + List.init ~len:arity ~f:(fun i -> + Utf8_string.of_string_exn (Printf.sprintf "x%d" i)) + in + EArrow + ( J.fun_ + (List.map ~f:J.ident (o :: params)) + [ ( Return_statement + (Some + (J.call + (J.dot (EVar (J.ident o)) meth) + (List.map ~f:(fun x -> J.EVar (J.ident x)) params) + N)) + , N ) + ] + N + , AUnknown )) + in + let o = transl_prim_arg o in + let args = List.map ~f:transl_prim_arg args in + JavaScript.invoke_fragment name (o :: args) + | _ -> assert false); + register "%caml_js_opt_new" (fun transl_prim_arg l -> + let arity = List.length l - 1 in + let name = Printf.sprintf "new_%d" arity in + let* () = + register_fragment name (fun () -> + let c = Utf8_string.of_string_exn "c" in + let params = + List.init ~len:arity ~f:(fun i -> + Utf8_string.of_string_exn (Printf.sprintf "x%d" i)) + in + EArrow + ( J.fun_ + (List.map ~f:J.ident (c :: params)) + [ ( Return_statement + (Some + (ENew + ( EVar (J.ident c) + , Some + (List.map ~f:(fun x -> J.Arg (EVar (J.ident x))) params) + ))) + , N ) + ] + N + , AUnknown )) + in + let l = List.map ~f:transl_prim_arg l in + JavaScript.invoke_fragment name l) + let externref = W.Ref { nullable = true; typ = Extern } let handle_exceptions ~result_typ ~fall_through ~context body x exn_handler = diff --git a/compiler/lib/wasm/wa_generate.ml b/compiler/lib/wasm/wa_generate.ml index 4133b4c33..4bda507ba 100644 --- a/compiler/lib/wasm/wa_generate.ml +++ b/compiler/lib/wasm/wa_generate.ml @@ -152,399 +152,457 @@ module Generate (Target : Wa_target_sig.S) = struct | Prim (Extern "caml_alloc_dummy_infix", _) when Poly.(target = `GC) -> Closure.dummy ~cps:(Config.Flag.effects ()) ~arity:1 | Prim (p, l) -> ( - let l = List.map ~f:transl_prim_arg l in - match p, l with - | Extern "caml_array_unsafe_get", [ x; y ] -> Memory.gen_array_get x y - | Extern "caml_floatarray_unsafe_get", [ x; y ] -> Memory.float_array_get x y - | Extern "caml_array_unsafe_set", [ x; y; z ] -> - seq (Memory.gen_array_set x y z) Value.unit - | Extern "caml_array_unsafe_set_addr", [ x; y; z ] -> - seq (Memory.array_set x y z) Value.unit - | Extern "caml_floatarray_unsafe_set", [ x; y; z ] -> - seq (Memory.float_array_set x y z) Value.unit - | Extern ("caml_string_unsafe_get" | "caml_bytes_unsafe_get"), [ x; y ] -> - Memory.bytes_get x y - | Extern ("caml_string_unsafe_set" | "caml_bytes_unsafe_set"), [ x; y; z ] -> - seq (Memory.bytes_set x y z) Value.unit - | Extern ("caml_string_get" | "caml_bytes_get"), [ x; y ] -> - seq - (let* cond = Arith.uge (Value.int_val y) (Memory.bytes_length x) in - instr (W.Br_if (label_index context bound_error_pc, cond))) - (Memory.bytes_get x y) - | Extern ("caml_string_set" | "caml_bytes_set"), [ x; y; z ] -> - seq - (let* cond = Arith.uge (Value.int_val y) (Memory.bytes_length x) in - let* () = instr (W.Br_if (label_index context bound_error_pc, cond)) in - Memory.bytes_set x y z) - Value.unit - | Extern ("caml_ml_string_length" | "caml_ml_bytes_length"), [ x ] -> - Value.val_int (Memory.bytes_length x) - | Extern "%int_add", [ x; y ] -> Value.int_add x y - | Extern "%int_sub", [ x; y ] -> Value.int_sub x y - | Extern ("%int_mul" | "%direct_int_mul"), [ x; y ] -> Value.int_mul x y - | Extern "%direct_int_div", [ x; y ] -> Value.int_div x y - | Extern "%int_div", [ x; y ] -> - seq - (let* cond = Arith.eqz (Value.int_val y) in - instr (W.Br_if (label_index context zero_divide_pc, cond))) - (Value.int_div x y) - | Extern "%int_mod", [ x; y ] -> - seq - (let* cond = Arith.eqz (Value.int_val y) in - instr (W.Br_if (label_index context zero_divide_pc, cond))) - (Value.int_mod x y) - | Extern "%direct_int_mod", [ x; y ] -> Value.int_mod x y - | Extern "%int_neg", [ x ] -> Value.int_neg x - | Extern "%int_or", [ x; y ] -> Value.int_or x y - | Extern "%int_and", [ x; y ] -> Value.int_and x y - | Extern "%int_xor", [ x; y ] -> Value.int_xor x y - | Extern "%int_lsl", [ x; y ] -> Value.int_lsl x y - | Extern "%int_lsr", [ x; y ] -> Value.int_lsr x y - | Extern "%int_asr", [ x; y ] -> Value.int_asr x y - | Extern "caml_check_bound", [ x; y ] -> - seq - (let* cond = Arith.uge (Value.int_val y) (Memory.array_length x) in - instr (W.Br_if (label_index context bound_error_pc, cond))) - x - | Extern "caml_check_bound_gen", [ x; y ] -> - seq - (let* cond = Arith.uge (Value.int_val y) (Memory.gen_array_length x) in - instr (W.Br_if (label_index context bound_error_pc, cond))) - x - | Extern "caml_check_bound_float", [ x; y ] -> - seq - (let* cond = Arith.uge (Value.int_val y) (Memory.float_array_length x) in - instr (W.Br_if (label_index context bound_error_pc, cond))) - x - | Extern "caml_add_float", [ f; g ] -> float_bin_op stack_ctx x Add f g - | Extern "caml_sub_float", [ f; g ] -> float_bin_op stack_ctx x Sub f g - | Extern "caml_mul_float", [ f; g ] -> float_bin_op stack_ctx x Mul f g - | Extern "caml_div_float", [ f; g ] -> float_bin_op stack_ctx x Div f g - | Extern "caml_copysign_float", [ f; g ] -> float_bin_op stack_ctx x CopySign f g - | Extern "caml_signbit_float", [ f ] -> - let* f = Memory.unbox_float f in - let sign = W.BinOp (F64 CopySign, Const (F64 1.), f) in - Value.val_int (return (W.BinOp (F64 Lt, sign, Const (F64 0.)))) - | Extern "caml_neg_float", [ f ] -> float_un_op stack_ctx x Neg f - | Extern "caml_abs_float", [ f ] -> float_un_op stack_ctx x Abs f - | Extern "caml_ceil_float", [ f ] -> float_un_op stack_ctx x Ceil f - | Extern "caml_floor_float", [ f ] -> float_un_op stack_ctx x Floor f - | Extern "caml_trunc_float", [ f ] -> float_un_op stack_ctx x Trunc f - | Extern "caml_round_float", [ f ] -> float_un_op' stack_ctx x Math.round f - | Extern "caml_sqrt_float", [ f ] -> float_un_op stack_ctx x Sqrt f - | Extern "caml_eq_float", [ f; g ] -> float_comparison Eq f g - | Extern "caml_neq_float", [ f; g ] -> float_comparison Ne f g - | Extern "caml_ge_float", [ f; g ] -> float_comparison Ge f g - | Extern "caml_le_float", [ f; g ] -> float_comparison Le f g - | Extern "caml_gt_float", [ f; g ] -> float_comparison Gt f g - | Extern "caml_lt_float", [ f; g ] -> float_comparison Lt f g - | Extern "caml_int_of_float", [ f ] -> - let* f = Memory.unbox_float f in - Value.val_int (return (W.UnOp (I32 (TruncSatF64 S), f))) - | Extern "caml_float_of_int", [ n ] -> - let* n = Value.int_val n in - Memory.box_float stack_ctx x (return (W.UnOp (F64 (Convert (`I32, S)), n))) - | Extern "caml_cos_float", [ f ] -> float_un_op' stack_ctx x Math.cos f - | Extern "caml_sin_float", [ f ] -> float_un_op' stack_ctx x Math.sin f - | Extern "caml_tan_float", [ f ] -> float_un_op' stack_ctx x Math.tan f - | Extern "caml_acos_float", [ f ] -> float_un_op' stack_ctx x Math.acos f - | Extern "caml_asin_float", [ f ] -> float_un_op' stack_ctx x Math.asin f - | Extern "caml_atan_float", [ f ] -> float_un_op' stack_ctx x Math.atan f - | Extern "caml_atan2_float", [ f; g ] -> float_bin_op' stack_ctx x Math.atan2 f g - | Extern "caml_cosh_float", [ f ] -> float_un_op' stack_ctx x Math.cosh f - | Extern "caml_sinh_float", [ f ] -> float_un_op' stack_ctx x Math.sinh f - | Extern "caml_tanh_float", [ f ] -> float_un_op' stack_ctx x Math.tanh f - | Extern "caml_acosh_float", [ f ] -> float_un_op' stack_ctx x Math.acosh f - | Extern "caml_asinh_float", [ f ] -> float_un_op' stack_ctx x Math.asinh f - | Extern "caml_atanh_float", [ f ] -> float_un_op' stack_ctx x Math.atanh f - | Extern "caml_cbrt_float", [ f ] -> float_un_op' stack_ctx x Math.cbrt f - | Extern "caml_exp_float", [ f ] -> float_un_op' stack_ctx x Math.exp f - | Extern "caml_exp2_float", [ f ] -> float_un_op' stack_ctx x Math.exp2 f - | Extern "caml_log_float", [ f ] -> float_un_op' stack_ctx x Math.log f - | Extern "caml_expm1_float", [ f ] -> float_un_op' stack_ctx x Math.expm1 f - | Extern "caml_log1p_float", [ f ] -> float_un_op' stack_ctx x Math.log1p f - | Extern "caml_log2_float", [ f ] -> float_un_op' stack_ctx x Math.log2 f - | Extern "caml_log10_float", [ f ] -> float_un_op' stack_ctx x Math.log10 f - | Extern "caml_power_float", [ f; g ] -> float_bin_op' stack_ctx x Math.power f g - | Extern "caml_hypot_float", [ f; g ] -> float_bin_op' stack_ctx x Math.hypot f g - | Extern "caml_fmod_float", [ f; g ] -> float_bin_op' stack_ctx x Math.fmod f g - | Extern "caml_int32_bits_of_float", [ f ] -> - let* f = Memory.unbox_float f in - Memory.box_int32 - stack_ctx - x - (return (W.UnOp (I32 ReinterpretF, F32DemoteF64 f))) - | Extern "caml_int32_float_of_bits", [ i ] -> - let* i = Memory.unbox_int64 i in - Memory.box_float - stack_ctx - x - (return (W.F64PromoteF32 (UnOp (I32 ReinterpretF, i)))) - | Extern "caml_int32_of_float", [ f ] -> - let* f = Memory.unbox_float f in - Memory.box_int32 stack_ctx x (return (W.UnOp (I32 (TruncSatF64 S), f))) - | Extern "caml_int32_to_float", [ n ] -> - let* n = Memory.unbox_int32 n in - Memory.box_float stack_ctx x (return (W.UnOp (F64 (Convert (`I32, S)), n))) - | Extern "caml_int32_neg", [ i ] -> - let* i = Memory.unbox_int32 i in - Memory.box_int32 stack_ctx x (return (W.BinOp (I32 Sub, Const (I32 0l), i))) - | Extern "caml_int32_add", [ i; j ] -> int32_bin_op stack_ctx x Add i j - | Extern "caml_int32_sub", [ i; j ] -> int32_bin_op stack_ctx x Sub i j - | Extern "caml_int32_mul", [ i; j ] -> int32_bin_op stack_ctx x Mul i j - | Extern "caml_int32_and", [ i; j ] -> int32_bin_op stack_ctx x And i j - | Extern "caml_int32_or", [ i; j ] -> int32_bin_op stack_ctx x Or i j - | Extern "caml_int32_xor", [ i; j ] -> int32_bin_op stack_ctx x Xor i j - | Extern "caml_int32_div", [ i; j ] -> - let res = Var.fresh () in - (*ZZZ Can we do better?*) - let i' = Var.fresh () in - let j' = Var.fresh () in - seq - (let* () = store ~typ:I32 j' (Memory.unbox_int32 j) in - let* () = - let* j = load j' in - instr (W.Br_if (label_index context zero_divide_pc, W.UnOp (I32 Eqz, j))) - in - let* () = store ~typ:I32 i' (Memory.unbox_int32 i) in - if_ - { params = []; result = [] } - Arith.( - (let* j = load j' in - return (W.BinOp (I32 Eq, j, Const (I32 (-1l))))) - land let* i = load i' in - return (W.BinOp (I32 Eq, i, Const (I32 Int32.min_int)))) - (store ~always:true ~typ:I32 res (return (W.Const (I32 Int32.min_int)))) - (store - ~always:true - ~typ:I32 - res - (let* i = load i' in + match p with + | Extern name when Hashtbl.mem internal_primitives name -> + Hashtbl.find internal_primitives name transl_prim_arg l + | _ -> ( + let l = List.map ~f:transl_prim_arg l in + match p, l with + | Extern "caml_array_unsafe_get", [ x; y ] -> Memory.gen_array_get x y + | Extern "caml_floatarray_unsafe_get", [ x; y ] -> Memory.float_array_get x y + | Extern "caml_array_unsafe_set", [ x; y; z ] -> + seq (Memory.gen_array_set x y z) Value.unit + | Extern "caml_array_unsafe_set_addr", [ x; y; z ] -> + seq (Memory.array_set x y z) Value.unit + | Extern "caml_floatarray_unsafe_set", [ x; y; z ] -> + seq (Memory.float_array_set x y z) Value.unit + | Extern ("caml_string_unsafe_get" | "caml_bytes_unsafe_get"), [ x; y ] -> + Memory.bytes_get x y + | Extern ("caml_string_unsafe_set" | "caml_bytes_unsafe_set"), [ x; y; z ] -> + seq (Memory.bytes_set x y z) Value.unit + | Extern ("caml_string_get" | "caml_bytes_get"), [ x; y ] -> + seq + (let* cond = Arith.uge (Value.int_val y) (Memory.bytes_length x) in + instr (W.Br_if (label_index context bound_error_pc, cond))) + (Memory.bytes_get x y) + | Extern ("caml_string_set" | "caml_bytes_set"), [ x; y; z ] -> + seq + (let* cond = Arith.uge (Value.int_val y) (Memory.bytes_length x) in + let* () = instr (W.Br_if (label_index context bound_error_pc, cond)) in + Memory.bytes_set x y z) + Value.unit + | Extern ("caml_ml_string_length" | "caml_ml_bytes_length"), [ x ] -> + Value.val_int (Memory.bytes_length x) + | Extern "%int_add", [ x; y ] -> Value.int_add x y + | Extern "%int_sub", [ x; y ] -> Value.int_sub x y + | Extern ("%int_mul" | "%direct_int_mul"), [ x; y ] -> Value.int_mul x y + | Extern "%direct_int_div", [ x; y ] -> Value.int_div x y + | Extern "%int_div", [ x; y ] -> + seq + (let* cond = Arith.eqz (Value.int_val y) in + instr (W.Br_if (label_index context zero_divide_pc, cond))) + (Value.int_div x y) + | Extern "%int_mod", [ x; y ] -> + seq + (let* cond = Arith.eqz (Value.int_val y) in + instr (W.Br_if (label_index context zero_divide_pc, cond))) + (Value.int_mod x y) + | Extern "%direct_int_mod", [ x; y ] -> Value.int_mod x y + | Extern "%int_neg", [ x ] -> Value.int_neg x + | Extern "%int_or", [ x; y ] -> Value.int_or x y + | Extern "%int_and", [ x; y ] -> Value.int_and x y + | Extern "%int_xor", [ x; y ] -> Value.int_xor x y + | Extern "%int_lsl", [ x; y ] -> Value.int_lsl x y + | Extern "%int_lsr", [ x; y ] -> Value.int_lsr x y + | Extern "%int_asr", [ x; y ] -> Value.int_asr x y + | Extern "caml_check_bound", [ x; y ] -> + seq + (let* cond = Arith.uge (Value.int_val y) (Memory.array_length x) in + instr (W.Br_if (label_index context bound_error_pc, cond))) + x + | Extern "caml_check_bound_gen", [ x; y ] -> + seq + (let* cond = Arith.uge (Value.int_val y) (Memory.gen_array_length x) in + instr (W.Br_if (label_index context bound_error_pc, cond))) + x + | Extern "caml_check_bound_float", [ x; y ] -> + seq + (let* cond = + Arith.uge (Value.int_val y) (Memory.float_array_length x) + in + instr (W.Br_if (label_index context bound_error_pc, cond))) + x + | Extern "caml_add_float", [ f; g ] -> float_bin_op stack_ctx x Add f g + | Extern "caml_sub_float", [ f; g ] -> float_bin_op stack_ctx x Sub f g + | Extern "caml_mul_float", [ f; g ] -> float_bin_op stack_ctx x Mul f g + | Extern "caml_div_float", [ f; g ] -> float_bin_op stack_ctx x Div f g + | Extern "caml_copysign_float", [ f; g ] -> + float_bin_op stack_ctx x CopySign f g + | Extern "caml_signbit_float", [ f ] -> + let* f = Memory.unbox_float f in + let sign = W.BinOp (F64 CopySign, Const (F64 1.), f) in + Value.val_int (return (W.BinOp (F64 Lt, sign, Const (F64 0.)))) + | Extern "caml_neg_float", [ f ] -> float_un_op stack_ctx x Neg f + | Extern "caml_abs_float", [ f ] -> float_un_op stack_ctx x Abs f + | Extern "caml_ceil_float", [ f ] -> float_un_op stack_ctx x Ceil f + | Extern "caml_floor_float", [ f ] -> float_un_op stack_ctx x Floor f + | Extern "caml_trunc_float", [ f ] -> float_un_op stack_ctx x Trunc f + | Extern "caml_round_float", [ f ] -> float_un_op' stack_ctx x Math.round f + | Extern "caml_sqrt_float", [ f ] -> float_un_op stack_ctx x Sqrt f + | Extern "caml_eq_float", [ f; g ] -> float_comparison Eq f g + | Extern "caml_neq_float", [ f; g ] -> float_comparison Ne f g + | Extern "caml_ge_float", [ f; g ] -> float_comparison Ge f g + | Extern "caml_le_float", [ f; g ] -> float_comparison Le f g + | Extern "caml_gt_float", [ f; g ] -> float_comparison Gt f g + | Extern "caml_lt_float", [ f; g ] -> float_comparison Lt f g + | Extern "caml_int_of_float", [ f ] -> + let* f = Memory.unbox_float f in + Value.val_int (return (W.UnOp (I32 (TruncSatF64 S), f))) + | Extern "caml_float_of_int", [ n ] -> + let* n = Value.int_val n in + Memory.box_float + stack_ctx + x + (return (W.UnOp (F64 (Convert (`I32, S)), n))) + | Extern "caml_cos_float", [ f ] -> float_un_op' stack_ctx x Math.cos f + | Extern "caml_sin_float", [ f ] -> float_un_op' stack_ctx x Math.sin f + | Extern "caml_tan_float", [ f ] -> float_un_op' stack_ctx x Math.tan f + | Extern "caml_acos_float", [ f ] -> float_un_op' stack_ctx x Math.acos f + | Extern "caml_asin_float", [ f ] -> float_un_op' stack_ctx x Math.asin f + | Extern "caml_atan_float", [ f ] -> float_un_op' stack_ctx x Math.atan f + | Extern "caml_atan2_float", [ f; g ] -> + float_bin_op' stack_ctx x Math.atan2 f g + | Extern "caml_cosh_float", [ f ] -> float_un_op' stack_ctx x Math.cosh f + | Extern "caml_sinh_float", [ f ] -> float_un_op' stack_ctx x Math.sinh f + | Extern "caml_tanh_float", [ f ] -> float_un_op' stack_ctx x Math.tanh f + | Extern "caml_acosh_float", [ f ] -> float_un_op' stack_ctx x Math.acosh f + | Extern "caml_asinh_float", [ f ] -> float_un_op' stack_ctx x Math.asinh f + | Extern "caml_atanh_float", [ f ] -> float_un_op' stack_ctx x Math.atanh f + | Extern "caml_cbrt_float", [ f ] -> float_un_op' stack_ctx x Math.cbrt f + | Extern "caml_exp_float", [ f ] -> float_un_op' stack_ctx x Math.exp f + | Extern "caml_exp2_float", [ f ] -> float_un_op' stack_ctx x Math.exp2 f + | Extern "caml_log_float", [ f ] -> float_un_op' stack_ctx x Math.log f + | Extern "caml_expm1_float", [ f ] -> float_un_op' stack_ctx x Math.expm1 f + | Extern "caml_log1p_float", [ f ] -> float_un_op' stack_ctx x Math.log1p f + | Extern "caml_log2_float", [ f ] -> float_un_op' stack_ctx x Math.log2 f + | Extern "caml_log10_float", [ f ] -> float_un_op' stack_ctx x Math.log10 f + | Extern "caml_power_float", [ f; g ] -> + float_bin_op' stack_ctx x Math.power f g + | Extern "caml_hypot_float", [ f; g ] -> + float_bin_op' stack_ctx x Math.hypot f g + | Extern "caml_fmod_float", [ f; g ] -> + float_bin_op' stack_ctx x Math.fmod f g + | Extern "caml_int32_bits_of_float", [ f ] -> + let* f = Memory.unbox_float f in + Memory.box_int32 + stack_ctx + x + (return (W.UnOp (I32 ReinterpretF, F32DemoteF64 f))) + | Extern "caml_int32_float_of_bits", [ i ] -> + let* i = Memory.unbox_int64 i in + Memory.box_float + stack_ctx + x + (return (W.F64PromoteF32 (UnOp (I32 ReinterpretF, i)))) + | Extern "caml_int32_of_float", [ f ] -> + let* f = Memory.unbox_float f in + Memory.box_int32 stack_ctx x (return (W.UnOp (I32 (TruncSatF64 S), f))) + | Extern "caml_int32_to_float", [ n ] -> + let* n = Memory.unbox_int32 n in + Memory.box_float + stack_ctx + x + (return (W.UnOp (F64 (Convert (`I32, S)), n))) + | Extern "caml_int32_neg", [ i ] -> + let* i = Memory.unbox_int32 i in + Memory.box_int32 + stack_ctx + x + (return (W.BinOp (I32 Sub, Const (I32 0l), i))) + | Extern "caml_int32_add", [ i; j ] -> int32_bin_op stack_ctx x Add i j + | Extern "caml_int32_sub", [ i; j ] -> int32_bin_op stack_ctx x Sub i j + | Extern "caml_int32_mul", [ i; j ] -> int32_bin_op stack_ctx x Mul i j + | Extern "caml_int32_and", [ i; j ] -> int32_bin_op stack_ctx x And i j + | Extern "caml_int32_or", [ i; j ] -> int32_bin_op stack_ctx x Or i j + | Extern "caml_int32_xor", [ i; j ] -> int32_bin_op stack_ctx x Xor i j + | Extern "caml_int32_div", [ i; j ] -> + let res = Var.fresh () in + (*ZZZ Can we do better?*) + let i' = Var.fresh () in + let j' = Var.fresh () in + seq + (let* () = store ~typ:I32 j' (Memory.unbox_int32 j) in + let* () = let* j = load j' in - return (W.BinOp (I32 (Div S), i, j))))) - (Memory.box_int32 stack_ctx x (load res)) - | Extern "caml_int32_mod", [ i; j ] -> - let j' = Var.fresh () in - seq - (let* () = store ~typ:I32 j' (Memory.unbox_int32 j) in - let* j = load j' in - instr (W.Br_if (label_index context zero_divide_pc, W.UnOp (I32 Eqz, j)))) - (let* i = Memory.unbox_int32 i in - let* j = load j' in - Memory.box_int32 stack_ctx x (return (W.BinOp (I32 (Rem S), i, j)))) - | Extern "caml_int32_shift_left", [ i; j ] -> int32_shift_op stack_ctx x Shl i j - | Extern "caml_int32_shift_right", [ i; j ] -> - int32_shift_op stack_ctx x (Shr S) i j - | Extern "caml_int32_shift_right_unsigned", [ i; j ] -> - int32_shift_op stack_ctx x (Shr U) i j - | Extern "caml_int32_to_int", [ i ] -> Value.val_int (Memory.unbox_int32 i) - | Extern "caml_int32_of_int", [ i ] -> - Memory.box_int32 stack_ctx x (Value.int_val i) - | Extern "caml_int64_bits_of_float", [ f ] -> - let* f = Memory.unbox_float f in - Memory.box_int64 stack_ctx x (return (W.UnOp (I64 ReinterpretF, f))) - | Extern "caml_int64_float_of_bits", [ i ] -> - let* i = Memory.unbox_int64 i in - Memory.box_float stack_ctx x (return (W.UnOp (F64 ReinterpretI, i))) - | Extern "caml_int64_of_float", [ f ] -> - let* f = Memory.unbox_float f in - Memory.box_int64 stack_ctx x (return (W.UnOp (I64 (TruncSatF64 S), f))) - | Extern "caml_int64_to_float", [ n ] -> - let* n = Memory.unbox_int64 n in - Memory.box_float stack_ctx x (return (W.UnOp (F64 (Convert (`I64, S)), n))) - | Extern "caml_int64_neg", [ i ] -> - let* i = Memory.unbox_int64 i in - Memory.box_int64 stack_ctx x (return (W.BinOp (I64 Sub, Const (I64 0L), i))) - | Extern "caml_int64_add", [ i; j ] -> int64_bin_op stack_ctx x Add i j - | Extern "caml_int64_sub", [ i; j ] -> int64_bin_op stack_ctx x Sub i j - | Extern "caml_int64_mul", [ i; j ] -> int64_bin_op stack_ctx x Mul i j - | Extern "caml_int64_and", [ i; j ] -> int64_bin_op stack_ctx x And i j - | Extern "caml_int64_or", [ i; j ] -> int64_bin_op stack_ctx x Or i j - | Extern "caml_int64_xor", [ i; j ] -> int64_bin_op stack_ctx x Xor i j - | Extern "caml_int64_div", [ i; j ] -> - let res = Var.fresh () in - (*ZZZ Can we do better?*) - let i' = Var.fresh () in - let j' = Var.fresh () in - seq - (let* () = store ~typ:I64 j' (Memory.unbox_int64 j) in - let* () = - let* j = load j' in - instr (W.Br_if (label_index context zero_divide_pc, W.UnOp (I64 Eqz, j))) - in - let* () = store ~typ:I64 i' (Memory.unbox_int64 i) in - if_ - { params = []; result = [] } - Arith.( - (let* j = load j' in - return (W.BinOp (I64 Eq, j, Const (I64 (-1L))))) - land let* i = load i' in - return (W.BinOp (I64 Eq, i, Const (I64 Int64.min_int)))) - (store ~always:true ~typ:I64 res (return (W.Const (I64 Int64.min_int)))) - (store - ~always:true - ~typ:I64 - res - (let* i = load i' in + instr + (W.Br_if (label_index context zero_divide_pc, W.UnOp (I32 Eqz, j))) + in + let* () = store ~typ:I32 i' (Memory.unbox_int32 i) in + if_ + { params = []; result = [] } + Arith.( + (let* j = load j' in + return (W.BinOp (I32 Eq, j, Const (I32 (-1l))))) + land let* i = load i' in + return (W.BinOp (I32 Eq, i, Const (I32 Int32.min_int)))) + (store + ~always:true + ~typ:I32 + res + (return (W.Const (I32 Int32.min_int)))) + (store + ~always:true + ~typ:I32 + res + (let* i = load i' in + let* j = load j' in + return (W.BinOp (I32 (Div S), i, j))))) + (Memory.box_int32 stack_ctx x (load res)) + | Extern "caml_int32_mod", [ i; j ] -> + let j' = Var.fresh () in + seq + (let* () = store ~typ:I32 j' (Memory.unbox_int32 j) in + let* j = load j' in + instr + (W.Br_if (label_index context zero_divide_pc, W.UnOp (I32 Eqz, j)))) + (let* i = Memory.unbox_int32 i in + let* j = load j' in + Memory.box_int32 stack_ctx x (return (W.BinOp (I32 (Rem S), i, j)))) + | Extern "caml_int32_shift_left", [ i; j ] -> + int32_shift_op stack_ctx x Shl i j + | Extern "caml_int32_shift_right", [ i; j ] -> + int32_shift_op stack_ctx x (Shr S) i j + | Extern "caml_int32_shift_right_unsigned", [ i; j ] -> + int32_shift_op stack_ctx x (Shr U) i j + | Extern "caml_int32_to_int", [ i ] -> Value.val_int (Memory.unbox_int32 i) + | Extern "caml_int32_of_int", [ i ] -> + Memory.box_int32 stack_ctx x (Value.int_val i) + | Extern "caml_int64_bits_of_float", [ f ] -> + let* f = Memory.unbox_float f in + Memory.box_int64 stack_ctx x (return (W.UnOp (I64 ReinterpretF, f))) + | Extern "caml_int64_float_of_bits", [ i ] -> + let* i = Memory.unbox_int64 i in + Memory.box_float stack_ctx x (return (W.UnOp (F64 ReinterpretI, i))) + | Extern "caml_int64_of_float", [ f ] -> + let* f = Memory.unbox_float f in + Memory.box_int64 stack_ctx x (return (W.UnOp (I64 (TruncSatF64 S), f))) + | Extern "caml_int64_to_float", [ n ] -> + let* n = Memory.unbox_int64 n in + Memory.box_float + stack_ctx + x + (return (W.UnOp (F64 (Convert (`I64, S)), n))) + | Extern "caml_int64_neg", [ i ] -> + let* i = Memory.unbox_int64 i in + Memory.box_int64 + stack_ctx + x + (return (W.BinOp (I64 Sub, Const (I64 0L), i))) + | Extern "caml_int64_add", [ i; j ] -> int64_bin_op stack_ctx x Add i j + | Extern "caml_int64_sub", [ i; j ] -> int64_bin_op stack_ctx x Sub i j + | Extern "caml_int64_mul", [ i; j ] -> int64_bin_op stack_ctx x Mul i j + | Extern "caml_int64_and", [ i; j ] -> int64_bin_op stack_ctx x And i j + | Extern "caml_int64_or", [ i; j ] -> int64_bin_op stack_ctx x Or i j + | Extern "caml_int64_xor", [ i; j ] -> int64_bin_op stack_ctx x Xor i j + | Extern "caml_int64_div", [ i; j ] -> + let res = Var.fresh () in + (*ZZZ Can we do better?*) + let i' = Var.fresh () in + let j' = Var.fresh () in + seq + (let* () = store ~typ:I64 j' (Memory.unbox_int64 j) in + let* () = let* j = load j' in - return (W.BinOp (I64 (Div S), i, j))))) - (Memory.box_int64 stack_ctx x (load res)) - | Extern "caml_int64_mod", [ i; j ] -> - let j' = Var.fresh () in - seq - (let* () = store ~typ:I64 j' (Memory.unbox_int64 j) in - let* j = load j' in - instr (W.Br_if (label_index context zero_divide_pc, W.UnOp (I64 Eqz, j)))) - (let* i = Memory.unbox_int64 i in - let* j = load j' in - Memory.box_int64 stack_ctx x (return (W.BinOp (I64 (Rem S), i, j)))) - | Extern "caml_int64_shift_left", [ i; j ] -> int64_shift_op stack_ctx x Shl i j - | Extern "caml_int64_shift_right", [ i; j ] -> - int64_shift_op stack_ctx x (Shr S) i j - | Extern "caml_int64_shift_right_unsigned", [ i; j ] -> - int64_shift_op stack_ctx x (Shr U) i j - | Extern "caml_int64_to_int", [ i ] -> - let* i = Memory.unbox_int64 i in - Value.val_int (return (W.I32WrapI64 i)) - | Extern "caml_int64_of_int", [ i ] -> - let* i = Value.int_val i in - Memory.box_int64 - stack_ctx - x - (return - (match i with - | Const (I32 i) -> W.Const (I64 (Int64.of_int32 i)) - | _ -> W.I64ExtendI32 (S, i))) - | Extern "caml_int64_to_int32", [ i ] -> - let* i = Memory.unbox_int64 i in - Memory.box_int32 stack_ctx x (return (W.I32WrapI64 i)) - | Extern "caml_int64_of_int32", [ i ] -> - let* i = Memory.unbox_int32 i in - Memory.box_int64 stack_ctx x (return (W.I64ExtendI32 (S, i))) - | Extern "caml_int64_to_nativeint", [ i ] -> - let* i = Memory.unbox_int64 i in - Memory.box_nativeint stack_ctx x (return (W.I32WrapI64 i)) - | Extern "caml_int64_of_nativeint", [ i ] -> - let* i = Memory.unbox_nativeint i in - Memory.box_int64 stack_ctx x (return (W.I64ExtendI32 (S, i))) - | Extern "caml_nativeint_bits_of_float", [ f ] -> - let* f = Memory.unbox_float f in - Memory.box_nativeint - stack_ctx - x - (return (W.UnOp (I32 ReinterpretF, F32DemoteF64 f))) - | Extern "caml_nativeint_float_of_bits", [ i ] -> - let* i = Memory.unbox_int64 i in - Memory.box_float - stack_ctx - x - (return (W.F64PromoteF32 (UnOp (I32 ReinterpretF, i)))) - | Extern "caml_nativeint_of_float", [ f ] -> - let* f = Memory.unbox_float f in - Memory.box_nativeint stack_ctx x (return (W.UnOp (I32 (TruncSatF64 S), f))) - | Extern "caml_nativeint_to_float", [ n ] -> - let* n = Memory.unbox_nativeint n in - Memory.box_float stack_ctx x (return (W.UnOp (F64 (Convert (`I32, S)), n))) - | Extern "caml_nativeint_neg", [ i ] -> - let* i = Memory.unbox_nativeint i in - Memory.box_nativeint - stack_ctx - x - (return (W.BinOp (I32 Sub, Const (I32 0l), i))) - | Extern "caml_nativeint_add", [ i; j ] -> nativeint_bin_op stack_ctx x Add i j - | Extern "caml_nativeint_sub", [ i; j ] -> nativeint_bin_op stack_ctx x Sub i j - | Extern "caml_nativeint_mul", [ i; j ] -> nativeint_bin_op stack_ctx x Mul i j - | Extern "caml_nativeint_and", [ i; j ] -> nativeint_bin_op stack_ctx x And i j - | Extern "caml_nativeint_or", [ i; j ] -> nativeint_bin_op stack_ctx x Or i j - | Extern "caml_nativeint_xor", [ i; j ] -> nativeint_bin_op stack_ctx x Xor i j - | Extern "caml_nativeint_div", [ i; j ] -> - let res = Var.fresh () in - (*ZZZ Can we do better?*) - let i' = Var.fresh () in - let j' = Var.fresh () in - seq - (let* () = store ~typ:I32 j' (Memory.unbox_nativeint j) in - let* () = - let* j = load j' in - instr (W.Br_if (label_index context zero_divide_pc, W.UnOp (I32 Eqz, j))) - in - let* () = store ~typ:I32 i' (Memory.unbox_nativeint i) in - if_ - { params = []; result = [] } - Arith.( - (let* j = load j' in - return (W.BinOp (I32 Eq, j, Const (I32 (-1l))))) - land let* i = load i' in - return (W.BinOp (I32 Eq, i, Const (I32 Int32.min_int)))) - (store ~always:true ~typ:I32 res (return (W.Const (I32 Int32.min_int)))) - (store - ~always:true - ~typ:I32 - res - (let* i = load i' in + instr + (W.Br_if (label_index context zero_divide_pc, W.UnOp (I64 Eqz, j))) + in + let* () = store ~typ:I64 i' (Memory.unbox_int64 i) in + if_ + { params = []; result = [] } + Arith.( + (let* j = load j' in + return (W.BinOp (I64 Eq, j, Const (I64 (-1L))))) + land let* i = load i' in + return (W.BinOp (I64 Eq, i, Const (I64 Int64.min_int)))) + (store + ~always:true + ~typ:I64 + res + (return (W.Const (I64 Int64.min_int)))) + (store + ~always:true + ~typ:I64 + res + (let* i = load i' in + let* j = load j' in + return (W.BinOp (I64 (Div S), i, j))))) + (Memory.box_int64 stack_ctx x (load res)) + | Extern "caml_int64_mod", [ i; j ] -> + let j' = Var.fresh () in + seq + (let* () = store ~typ:I64 j' (Memory.unbox_int64 j) in + let* j = load j' in + instr + (W.Br_if (label_index context zero_divide_pc, W.UnOp (I64 Eqz, j)))) + (let* i = Memory.unbox_int64 i in + let* j = load j' in + Memory.box_int64 stack_ctx x (return (W.BinOp (I64 (Rem S), i, j)))) + | Extern "caml_int64_shift_left", [ i; j ] -> + int64_shift_op stack_ctx x Shl i j + | Extern "caml_int64_shift_right", [ i; j ] -> + int64_shift_op stack_ctx x (Shr S) i j + | Extern "caml_int64_shift_right_unsigned", [ i; j ] -> + int64_shift_op stack_ctx x (Shr U) i j + | Extern "caml_int64_to_int", [ i ] -> + let* i = Memory.unbox_int64 i in + Value.val_int (return (W.I32WrapI64 i)) + | Extern "caml_int64_of_int", [ i ] -> + let* i = Value.int_val i in + Memory.box_int64 + stack_ctx + x + (return + (match i with + | Const (I32 i) -> W.Const (I64 (Int64.of_int32 i)) + | _ -> W.I64ExtendI32 (S, i))) + | Extern "caml_int64_to_int32", [ i ] -> + let* i = Memory.unbox_int64 i in + Memory.box_int32 stack_ctx x (return (W.I32WrapI64 i)) + | Extern "caml_int64_of_int32", [ i ] -> + let* i = Memory.unbox_int32 i in + Memory.box_int64 stack_ctx x (return (W.I64ExtendI32 (S, i))) + | Extern "caml_int64_to_nativeint", [ i ] -> + let* i = Memory.unbox_int64 i in + Memory.box_nativeint stack_ctx x (return (W.I32WrapI64 i)) + | Extern "caml_int64_of_nativeint", [ i ] -> + let* i = Memory.unbox_nativeint i in + Memory.box_int64 stack_ctx x (return (W.I64ExtendI32 (S, i))) + | Extern "caml_nativeint_bits_of_float", [ f ] -> + let* f = Memory.unbox_float f in + Memory.box_nativeint + stack_ctx + x + (return (W.UnOp (I32 ReinterpretF, F32DemoteF64 f))) + | Extern "caml_nativeint_float_of_bits", [ i ] -> + let* i = Memory.unbox_int64 i in + Memory.box_float + stack_ctx + x + (return (W.F64PromoteF32 (UnOp (I32 ReinterpretF, i)))) + | Extern "caml_nativeint_of_float", [ f ] -> + let* f = Memory.unbox_float f in + Memory.box_nativeint + stack_ctx + x + (return (W.UnOp (I32 (TruncSatF64 S), f))) + | Extern "caml_nativeint_to_float", [ n ] -> + let* n = Memory.unbox_nativeint n in + Memory.box_float + stack_ctx + x + (return (W.UnOp (F64 (Convert (`I32, S)), n))) + | Extern "caml_nativeint_neg", [ i ] -> + let* i = Memory.unbox_nativeint i in + Memory.box_nativeint + stack_ctx + x + (return (W.BinOp (I32 Sub, Const (I32 0l), i))) + | Extern "caml_nativeint_add", [ i; j ] -> + nativeint_bin_op stack_ctx x Add i j + | Extern "caml_nativeint_sub", [ i; j ] -> + nativeint_bin_op stack_ctx x Sub i j + | Extern "caml_nativeint_mul", [ i; j ] -> + nativeint_bin_op stack_ctx x Mul i j + | Extern "caml_nativeint_and", [ i; j ] -> + nativeint_bin_op stack_ctx x And i j + | Extern "caml_nativeint_or", [ i; j ] -> nativeint_bin_op stack_ctx x Or i j + | Extern "caml_nativeint_xor", [ i; j ] -> + nativeint_bin_op stack_ctx x Xor i j + | Extern "caml_nativeint_div", [ i; j ] -> + let res = Var.fresh () in + (*ZZZ Can we do better?*) + let i' = Var.fresh () in + let j' = Var.fresh () in + seq + (let* () = store ~typ:I32 j' (Memory.unbox_nativeint j) in + let* () = let* j = load j' in - return (W.BinOp (I32 (Div S), i, j))))) - (Memory.box_nativeint stack_ctx x (load res)) - | Extern "caml_nativeint_mod", [ i; j ] -> - let j' = Var.fresh () in - seq - (let* () = store ~typ:I32 j' (Memory.unbox_nativeint j) in - let* j = load j' in - instr (W.Br_if (label_index context zero_divide_pc, W.UnOp (I32 Eqz, j)))) - (let* i = Memory.unbox_nativeint i in - let* j = load j' in - Memory.box_nativeint stack_ctx x (return (W.BinOp (I32 (Rem S), i, j)))) - | Extern "caml_nativeint_shift_left", [ i; j ] -> - nativeint_shift_op stack_ctx x Shl i j - | Extern "caml_nativeint_shift_right", [ i; j ] -> - nativeint_shift_op stack_ctx x (Shr S) i j - | Extern "caml_nativeint_shift_right_unsigned", [ i; j ] -> - nativeint_shift_op stack_ctx x (Shr U) i j - | Extern "caml_nativeint_to_int", [ i ] -> - Value.val_int (Memory.unbox_nativeint i) - | Extern "caml_nativeint_of_int", [ i ] -> - Memory.box_nativeint stack_ctx x (Value.int_val i) - | Extern "caml_int_compare", [ i; j ] -> - Value.val_int - Arith.( - (Value.int_val j < Value.int_val i) - (Value.int_val i < Value.int_val j)) - | Extern "%js_array", l -> - let* l = - List.fold_right - ~f:(fun x acc -> - let* x = x in - let* acc = acc in - return (`Expr x :: acc)) - l - ~init:(return []) - in - Memory.allocate stack_ctx x ~tag:0 l - | Extern name, l -> - let name = Primitive.resolve name in - (*ZZZ Different calling convention when large number of parameters *) - let* f = register_import ~name (Fun (func_type (List.length l))) in - let* () = Stack.perform_spilling stack_ctx (`Instr x) in - let rec loop acc l = - match l with - | [] -> - Stack.kill_variables stack_ctx; - return (W.Call (f, List.rev acc)) - | x :: r -> - let* x = x in - loop (x :: acc) r - in - loop [] l - | Not, [ x ] -> Value.not x - | Lt, [ x; y ] -> Value.lt x y - | Le, [ x; y ] -> Value.le x y - | Eq, [ x; y ] -> Value.eq x y - | Neq, [ x; y ] -> Value.neq x y - | Ult, [ x; y ] -> Value.ult x y - | Array_get, [ x; y ] -> Memory.array_get x y - | IsInt, [ x ] -> Value.is_int x - | Vectlength, [ x ] -> Value.val_int (Memory.gen_array_length x) - | (Not | Lt | Le | Eq | Neq | Ult | Array_get | IsInt | Vectlength), _ -> - assert false) + instr + (W.Br_if (label_index context zero_divide_pc, W.UnOp (I32 Eqz, j))) + in + let* () = store ~typ:I32 i' (Memory.unbox_nativeint i) in + if_ + { params = []; result = [] } + Arith.( + (let* j = load j' in + return (W.BinOp (I32 Eq, j, Const (I32 (-1l))))) + land let* i = load i' in + return (W.BinOp (I32 Eq, i, Const (I32 Int32.min_int)))) + (store + ~always:true + ~typ:I32 + res + (return (W.Const (I32 Int32.min_int)))) + (store + ~always:true + ~typ:I32 + res + (let* i = load i' in + let* j = load j' in + return (W.BinOp (I32 (Div S), i, j))))) + (Memory.box_nativeint stack_ctx x (load res)) + | Extern "caml_nativeint_mod", [ i; j ] -> + let j' = Var.fresh () in + seq + (let* () = store ~typ:I32 j' (Memory.unbox_nativeint j) in + let* j = load j' in + instr + (W.Br_if (label_index context zero_divide_pc, W.UnOp (I32 Eqz, j)))) + (let* i = Memory.unbox_nativeint i in + let* j = load j' in + Memory.box_nativeint stack_ctx x (return (W.BinOp (I32 (Rem S), i, j)))) + | Extern "caml_nativeint_shift_left", [ i; j ] -> + nativeint_shift_op stack_ctx x Shl i j + | Extern "caml_nativeint_shift_right", [ i; j ] -> + nativeint_shift_op stack_ctx x (Shr S) i j + | Extern "caml_nativeint_shift_right_unsigned", [ i; j ] -> + nativeint_shift_op stack_ctx x (Shr U) i j + | Extern "caml_nativeint_to_int", [ i ] -> + Value.val_int (Memory.unbox_nativeint i) + | Extern "caml_nativeint_of_int", [ i ] -> + Memory.box_nativeint stack_ctx x (Value.int_val i) + | Extern "caml_int_compare", [ i; j ] -> + Value.val_int + Arith.( + (Value.int_val j < Value.int_val i) + - (Value.int_val i < Value.int_val j)) + | Extern "%js_array", l -> + let* l = + List.fold_right + ~f:(fun x acc -> + let* x = x in + let* acc = acc in + return (`Expr x :: acc)) + l + ~init:(return []) + in + Memory.allocate stack_ctx x ~tag:0 l + | Extern name, l -> + let name = Primitive.resolve name in + (*ZZZ Different calling convention when large number of parameters *) + let* f = register_import ~name (Fun (func_type (List.length l))) in + let* () = Stack.perform_spilling stack_ctx (`Instr x) in + let rec loop acc l = + match l with + | [] -> + Stack.kill_variables stack_ctx; + return (W.Call (f, List.rev acc)) + | x :: r -> + let* x = x in + loop (x :: acc) r + in + loop [] l + | Not, [ x ] -> Value.not x + | Lt, [ x; y ] -> Value.lt x y + | Le, [ x; y ] -> Value.le x y + | Eq, [ x; y ] -> Value.eq x y + | Neq, [ x; y ] -> Value.neq x y + | Ult, [ x; y ] -> Value.ult x y + | Array_get, [ x; y ] -> Memory.array_get x y + | IsInt, [ x ] -> Value.is_int x + | Vectlength, [ x ] -> Value.val_int (Memory.gen_array_length x) + | (Not | Lt | Le | Eq | Neq | Ult | Array_get | IsInt | Vectlength), _ -> + assert false)) and translate_instr ctx stack_ctx context (i, _) = match i with @@ -992,7 +1050,8 @@ module Generate (Target : Wa_target_sig.S) = struct ( List.rev_append ctx.global_context.other_fields (imports @ functions @ (start_function :: constant_data)) - , List.rev ctx.global_context.strings ) + , ( List.rev ctx.global_context.strings + , StringMap.bindings ctx.global_context.fragments ) ) end let init () = @@ -1053,11 +1112,11 @@ let f ch (p : Code.program) ~live_vars ~in_cps = match target with | `Core -> let module G = Generate (Wa_core_target) in - let fields, strings = G.f ~live_vars ~in_cps p in + let fields, js_code = G.f ~live_vars ~in_cps p in Wa_asm_output.f ch fields; - strings + js_code | `GC -> let module G = Generate (Wa_gc_target) in - let fields, strings = G.f ~live_vars ~in_cps p in + let fields, js_code = G.f ~live_vars ~in_cps p in Wa_wat_output.f ch fields; - strings + js_code diff --git a/compiler/lib/wasm/wa_generate.mli b/compiler/lib/wasm/wa_generate.mli index f040ba50d..a5138ea82 100644 --- a/compiler/lib/wasm/wa_generate.mli +++ b/compiler/lib/wasm/wa_generate.mli @@ -5,4 +5,4 @@ val f : -> Code.program -> live_vars:int array -> in_cps:Effects.in_cps - -> string list + -> string list * (string * Javascript.expression) list diff --git a/compiler/lib/wasm/wa_target_sig.ml b/compiler/lib/wasm/wa_target_sig.ml index 12758f524..ee43e854a 100644 --- a/compiler/lib/wasm/wa_target_sig.ml +++ b/compiler/lib/wasm/wa_target_sig.ml @@ -276,6 +276,9 @@ module type S = sig val round : expression -> expression end + val internal_primitives : + (string, (Code.prim_arg -> expression) -> Code.prim_arg list -> expression) Hashtbl.t + val handle_exceptions : result_typ:Wa_ast.value_type list -> fall_through:'a diff --git a/runtime/wasm/runtime.js b/runtime/wasm/runtime.js index d9a2ae640..1b990f7dc 100644 --- a/runtime/wasm/runtime.js +++ b/runtime/wasm/runtime.js @@ -1,4 +1,4 @@ -(async function (eval_function, js, strings) { +(async function (eval_function, js, strings, fragments) { "use strict"; const src = 'CODE'; function loadRelative(src) { @@ -345,7 +345,7 @@ map_set:(m,x,v)=>m.set(x,v), log:(x)=>console.log('ZZZZZ', x) } - const imports = {Math:math,bindings,env:{},js,strings} + const imports = {Math:math,bindings,env:{},js,strings,fragments} const wasmModule = isNode?await WebAssembly.instantiate(await code, imports) :await WebAssembly.instantiateStreaming(code,imports)