diff --git a/CHANGES.md b/CHANGES.md index 8b7eb42da0..697b0cfe65 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ ## Features/Changes * Compiler/wasm: omit code pointer from closures when not used (#2059) +* Compiler/wasm: unbox numbers within functions (#2069) # 6.2.0 (2025-07-30) - Lille diff --git a/compiler/lib-wasm/curry.ml b/compiler/lib-wasm/curry.ml index b6d5ab0cab..32b339e5be 100644 --- a/compiler/lib-wasm/curry.ml +++ b/compiler/lib-wasm/curry.ml @@ -293,14 +293,7 @@ module Make (Target : Target_sig.S) = struct (fun ~typ closure -> let* l = expression_list load l in call ?typ ~cps:true ~arity closure l) - (let* args = - (* We don't need the deadcode sentinal when the tag is 0 *) - Memory.allocate - ~tag:0 - ~deadcode_sentinal:(Code.Var.fresh ()) - ~load - (List.map ~f:(fun x -> `Var x) (List.tl l)) - in + (let* args = Memory.allocate ~tag:0 (expression_list load (List.tl l)) in let* make_iterator = register_import ~name:"caml_apply_continuation" (Fun (Type.primitive_type 1)) in diff --git a/compiler/lib-wasm/gc_target.ml b/compiler/lib-wasm/gc_target.ml index 306846aae5..9e51787ec8 100644 --- a/compiler/lib-wasm/gc_target.ml +++ b/compiler/lib-wasm/gc_target.ml @@ -666,33 +666,21 @@ module Memory = struct let* ty = Type.float_type in wasm_struct_get ty (wasm_cast ty e) 0 - let allocate ~tag ~deadcode_sentinal ~load l = - if tag = 254 - then - let* l = - expression_list - (fun v -> - match v with - | `Var y -> - if Code.Var.equal y deadcode_sentinal - then return (W.Const (F64 0.)) - else unbox_float (load y) - | `Expr e -> unbox_float (return e)) - l - in - let* ty = Type.float_array_type in - return (W.ArrayNewFixed (ty, l)) - else - let* l = - expression_list - (fun v -> - match v with - | `Var y -> load y - | `Expr e -> return e) - l - in - let* ty = Type.block_type in - return (W.ArrayNewFixed (ty, RefI31 (Const (I32 (Int32.of_int tag))) :: l)) + let allocate ~tag l = + assert (tag <> 254); + let* l = l in + let* ty = Type.block_type in + return (W.ArrayNewFixed (ty, RefI31 (Const (I32 (Int32.of_int tag))) :: l)) + + let allocate_float_array ~deadcode_sentinal ~load l = + let* l = + expression_list + (fun y -> + if Code.Var.equal y deadcode_sentinal then return (W.Const (F64 0.)) else load y) + l + in + let* ty = Type.float_array_type in + return (W.ArrayNewFixed (ty, l)) let tag e = wasm_array_get e (Arith.const 0l) @@ -741,10 +729,9 @@ module Memory = struct let array_set e e' e'' = wasm_array_set e Arith.(e' + const 1l) e'' - let float_array_get e e' = box_float (wasm_array_get ~ty:Type.float_array_type e e') + let float_array_get e e' = wasm_array_get ~ty:Type.float_array_type e e' - let float_array_set e e' e'' = - wasm_array_set ~ty:Type.float_array_type e e' (unbox_float e'') + let float_array_set e e' e'' = wasm_array_set ~ty:Type.float_array_type e e' e'' let gen_array_get e e' = let a = Code.Var.fresh_n "a" in @@ -1047,9 +1034,12 @@ module Constant = struct let* e = Memory.make_int32 ~kind:`Nativeint (return (W.Const (I32 i))) in return (Const, e) - let translate c = + let translate ~unboxed c = match c with | Code.Int i -> return (W.Const (I32 (Targetint.to_int32 i))) + | Float f when unboxed -> return (W.Const (F64 (Int64.float_of_bits f))) + | Int64 i when unboxed -> return (W.Const (I64 i)) + | (Int32 i | NativeInt i) when unboxed -> return (W.Const (I32 i)) | _ -> ( let* const, c = translate_rec c in match const with diff --git a/compiler/lib-wasm/generate.ml b/compiler/lib-wasm/generate.ml index f8c9484485..3dd7d27b32 100644 --- a/compiler/lib-wasm/generate.ml +++ b/compiler/lib-wasm/generate.ml @@ -84,22 +84,6 @@ module Generate (Target : Target_sig.S) = struct let specialized_primitive_type (_, params, result) = { W.params = List.map ~f:repr_type params; result = [ repr_type result ] } - let box_value r e = - match r with - | Value -> e - | Float -> Memory.box_float e - | Int32 -> Memory.box_int32 e - | Nativeint -> Memory.box_nativeint e - | Int64 -> Memory.box_int64 e - - let unbox_value r e = - match r with - | Value -> e - | Float -> Memory.unbox_float e - | Int32 -> Memory.unbox_int32 e - | Nativeint -> Memory.unbox_nativeint e - | Int64 -> Memory.unbox_int64 e - let specialized_primitives = let h = String.Hashtbl.create 18 in List.iter @@ -130,54 +114,34 @@ module Generate (Target : Target_sig.S) = struct ]; h - let float_bin_op' op f g = - Memory.box_float (op (Memory.unbox_float f) (Memory.unbox_float g)) - let float_bin_op op f g = - let* f = Memory.unbox_float f in - let* g = Memory.unbox_float g in - Memory.box_float (return (W.BinOp (F64 op, f, g))) - - let float_un_op' op f = Memory.box_float (op (Memory.unbox_float f)) + let* f = f in + let* g = g in + return (W.BinOp (F64 op, f, g)) let float_un_op op f = - let* f = Memory.unbox_float f in - Memory.box_float (return (W.UnOp (F64 op, f))) - - let float_comparison op f g = - let* f = Memory.unbox_float f in - let* g = Memory.unbox_float g in - return (W.BinOp (F64 op, f, g)) + let* f = f in + return (W.UnOp (F64 op, f)) let int32_bin_op op f g = - let* f = Memory.unbox_int32 f in - let* g = Memory.unbox_int32 g in - Memory.box_int32 (return (W.BinOp (I32 op, f, g))) - - let int32_shift_op op f g = - let* f = Memory.unbox_int32 f in + let* f = f in let* g = g in - Memory.box_int32 (return (W.BinOp (I32 op, f, g))) + return (W.BinOp (I32 op, f, g)) let int64_bin_op op f g = - let* f = Memory.unbox_int64 f in - let* g = Memory.unbox_int64 g in - Memory.box_int64 (return (W.BinOp (I64 op, f, g))) + let* f = f in + let* g = g in + return (W.BinOp (I64 op, f, g)) let int64_shift_op op f g = - let* f = Memory.unbox_int64 f in + let* f = f in let* g = g in - Memory.box_int64 (return (W.BinOp (I64 op, f, I64ExtendI32 (S, g)))) + return (W.BinOp (I64 op, f, I64ExtendI32 (S, g))) let nativeint_bin_op op f g = - let* f = Memory.unbox_nativeint f in - let* g = Memory.unbox_nativeint g in - Memory.box_nativeint (return (W.BinOp (I32 op, f, g))) - - let nativeint_shift_op op f g = - let* f = Memory.unbox_nativeint f in + let* f = f in let* g = g in - Memory.box_nativeint (return (W.BinOp (I32 op, f, g))) + return (W.BinOp (I32 op, f, g)) let get_var_type ctx x = Var.Tbl.get ctx.types x @@ -192,6 +156,15 @@ module Generate (Target : Target_sig.S) = struct | Int (Normalized | Unnormalized), Int (Normalized | Unnormalized) -> e | _, Int (Normalized | Unnormalized) -> Value.int_val e | Int (Unnormalized | Normalized), _ -> Value.val_int e + | Number (_, Unboxed), Number (_, Unboxed) -> e + | _, Number (Int32, Unboxed) -> Memory.unbox_int32 e + | _, Number (Int64, Unboxed) -> Memory.unbox_int64 e + | _, Number (Nativeint, Unboxed) -> Memory.unbox_nativeint e + | _, Number (Float, Unboxed) -> Memory.unbox_float e + | Number (Int32, Unboxed), _ -> Memory.box_int32 e + | Number (Int64, Unboxed), _ -> Memory.box_int64 e + | Number (Nativeint, Unboxed), _ -> Memory.box_nativeint e + | Number (Float, Unboxed), _ -> Memory.box_float e | _ -> e let load_and_box ctx x = convert ~from:(get_var_type ctx x) ~into:Top (load x) @@ -202,7 +175,7 @@ module Generate (Target : Target_sig.S) = struct ~into:typ (match x with | Pv x -> load x - | Pc c -> Constant.translate c) + | Pc c -> Constant.translate ~unboxed:false c) let translate_int_comparison ctx op x y = match get_type ctx x, get_type ctx y with @@ -302,11 +275,6 @@ module Generate (Target : Target_sig.S) = struct | _ -> invalid_arity name l ~expected:3) let () = - register_bin_prim - "caml_array_unsafe_get" - `Mutable - ~ty:(Int Normalized) - Memory.gen_array_get; register_bin_prim "caml_floatarray_unsafe_get" `Mutable @@ -316,8 +284,11 @@ module Generate (Target : Target_sig.S) = struct seq (Memory.gen_array_set x y z) Value.unit); register_tern_prim "caml_array_unsafe_set_addr" ~ty:(Int Normalized) (fun x y z -> seq (Memory.array_set x y z) Value.unit); - register_tern_prim "caml_floatarray_unsafe_set" ~ty:(Int Normalized) (fun x y z -> - seq (Memory.float_array_set x y z) Value.unit); + register_tern_prim + "caml_floatarray_unsafe_set" + ~ty:(Int Normalized) + ~tz:(Number (Float, Unboxed)) + (fun x y z -> seq (Memory.float_array_set x y z) Value.unit); register_bin_prim "caml_string_unsafe_get" `Pure ~ty:(Int Normalized) Memory.bytes_get; register_bin_prim "caml_bytes_unsafe_get" @@ -478,91 +449,252 @@ module Generate (Target : Target_sig.S) = struct let* cond = Arith.uge y (Memory.float_array_length (load a)) in instr (W.Br_if (label, cond))) x); - register_bin_prim "caml_add_float" `Pure (fun f g -> float_bin_op Add f g); - register_bin_prim "caml_sub_float" `Pure (fun f g -> float_bin_op Sub f g); - register_bin_prim "caml_mul_float" `Pure (fun f g -> float_bin_op Mul f g); - register_bin_prim "caml_div_float" `Pure (fun f g -> float_bin_op Div f g); - register_bin_prim "caml_copysign_float" `Pure (fun f g -> float_bin_op CopySign f g); - register_un_prim "caml_signbit_float" `Pure (fun f -> - let* f = Memory.unbox_float f in + register_bin_prim + "caml_add_float" + `Pure + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + (fun f g -> float_bin_op Add f g); + register_bin_prim + "caml_sub_float" + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + `Pure + (fun f g -> float_bin_op Sub f g); + register_bin_prim + "caml_mul_float" + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + `Pure + (fun f g -> float_bin_op Mul f g); + register_bin_prim + "caml_div_float" + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + `Pure + (fun f g -> float_bin_op Div f g); + register_bin_prim + "caml_copysign_float" + `Pure + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + (fun f g -> float_bin_op CopySign f g); + register_un_prim + "caml_signbit_float" + `Pure + ~typ:(Number (Float, Unboxed)) + (fun f -> + let* f = f in let sign = W.BinOp (F64 CopySign, Const (F64 1.), f) in return (W.BinOp (F64 Lt, sign, Const (F64 0.)))); - register_un_prim "caml_neg_float" `Pure (fun f -> float_un_op Neg f); - register_un_prim "caml_abs_float" `Pure (fun f -> float_un_op Abs f); - register_un_prim "caml_ceil_float" `Pure (fun f -> float_un_op Ceil f); - register_un_prim "caml_floor_float" `Pure (fun f -> float_un_op Floor f); - register_un_prim "caml_trunc_float" `Pure (fun f -> float_un_op Trunc f); - register_un_prim "caml_round_float" `Pure (fun f -> float_un_op' Math.round f); - register_un_prim "caml_sqrt_float" `Pure (fun f -> float_un_op Sqrt f); - register_bin_prim "caml_eq_float" `Pure (fun f g -> float_comparison Eq f g); - register_bin_prim "caml_neq_float" `Pure (fun f g -> float_comparison Ne f g); - register_bin_prim "caml_ge_float" `Pure (fun f g -> float_comparison Ge f g); - register_bin_prim "caml_le_float" `Pure (fun f g -> float_comparison Le f g); - register_bin_prim "caml_gt_float" `Pure (fun f g -> float_comparison Gt f g); - register_bin_prim "caml_lt_float" `Pure (fun f g -> float_comparison Lt f g); - register_un_prim "caml_int_of_float" `Pure (fun f -> - let* f = Memory.unbox_float f in + register_un_prim + "caml_neg_float" + `Pure + ~typ:(Number (Float, Unboxed)) + (fun f -> float_un_op Neg f); + register_un_prim + "caml_abs_float" + `Pure + ~typ:(Number (Float, Unboxed)) + (fun f -> float_un_op Abs f); + register_un_prim + "caml_ceil_float" + `Pure + ~typ:(Number (Float, Unboxed)) + (fun f -> float_un_op Ceil f); + register_un_prim + "caml_floor_float" + `Pure + ~typ:(Number (Float, Unboxed)) + (fun f -> float_un_op Floor f); + register_un_prim + "caml_trunc_float" + `Pure + ~typ:(Number (Float, Unboxed)) + (fun f -> float_un_op Trunc f); + register_un_prim "caml_round_float" `Pure ~typ:(Number (Float, Unboxed)) Math.round; + register_un_prim + "caml_sqrt_float" + `Pure + ~typ:(Number (Float, Unboxed)) + (fun f -> float_un_op Sqrt f); + register_bin_prim + "caml_eq_float" + `Pure + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + (fun f g -> float_bin_op Eq f g); + register_bin_prim + "caml_neq_float" + `Pure + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + (fun f g -> float_bin_op Ne f g); + register_bin_prim + "caml_ge_float" + `Pure + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + (fun f g -> float_bin_op Ge f g); + register_bin_prim + "caml_le_float" + `Pure + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + (fun f g -> float_bin_op Le f g); + register_bin_prim + "caml_gt_float" + `Pure + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + (fun f g -> float_bin_op Gt f g); + register_bin_prim + "caml_lt_float" + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + `Pure + (fun f g -> float_bin_op Lt f g); + register_un_prim + "caml_int_of_float" + `Pure + ~typ:(Number (Float, Unboxed)) + (fun f -> + let* f = f in return (W.UnOp (I32 (TruncSatF64 S), f))); register_un_prim "caml_float_of_int" `Pure ~typ:(Int Normalized) (fun n -> let* n = n in - Memory.box_float (return (W.UnOp (F64 (Convert (`I32, S)), n)))); - register_un_prim "caml_cos_float" `Pure (fun f -> float_un_op' Math.cos f); - register_un_prim "caml_sin_float" `Pure (fun f -> float_un_op' Math.sin f); - register_un_prim "caml_tan_float" `Pure (fun f -> float_un_op' Math.tan f); - register_un_prim "caml_acos_float" `Pure (fun f -> float_un_op' Math.acos f); - register_un_prim "caml_asin_float" `Pure (fun f -> float_un_op' Math.asin f); - register_un_prim "caml_atan_float" `Pure (fun f -> float_un_op' Math.atan f); - register_bin_prim "caml_atan2_float" `Pure (fun f g -> float_bin_op' Math.atan2 f g); - register_un_prim "caml_cosh_float" `Pure (fun f -> float_un_op' Math.cosh f); - register_un_prim "caml_sinh_float" `Pure (fun f -> float_un_op' Math.sinh f); - register_un_prim "caml_tanh_float" `Pure (fun f -> float_un_op' Math.tanh f); - register_un_prim "caml_acosh_float" `Pure (fun f -> float_un_op' Math.acosh f); - register_un_prim "caml_asinh_float" `Pure (fun f -> float_un_op' Math.asinh f); - register_un_prim "caml_atanh_float" `Pure (fun f -> float_un_op' Math.atanh f); - register_un_prim "caml_cbrt_float" `Pure (fun f -> float_un_op' Math.cbrt f); - register_un_prim "caml_exp_float" `Pure (fun f -> float_un_op' Math.exp f); - register_un_prim "caml_exp2_float" `Pure (fun f -> float_un_op' Math.exp2 f); - register_un_prim "caml_log_float" `Pure (fun f -> float_un_op' Math.log f); - register_un_prim "caml_expm1_float" `Pure (fun f -> float_un_op' Math.expm1 f); - register_un_prim "caml_log1p_float" `Pure (fun f -> float_un_op' Math.log1p f); - register_un_prim "caml_log2_float" `Pure (fun f -> float_un_op' Math.log2 f); - register_un_prim "caml_log10_float" `Pure (fun f -> float_un_op' Math.log10 f); - register_bin_prim "caml_power_float" `Pure (fun f g -> float_bin_op' Math.power f g); - register_bin_prim "caml_hypot_float" `Pure (fun f g -> float_bin_op' Math.hypot f g); - register_bin_prim "caml_fmod_float" `Pure (fun f g -> float_bin_op' Math.fmod f g); - register_un_prim "caml_int32_bits_of_float" `Pure (fun f -> - let* f = Memory.unbox_float f in - Memory.box_int32 (return (W.UnOp (I32 ReinterpretF, F32DemoteF64 f)))); - register_un_prim "caml_int32_float_of_bits" `Pure (fun i -> - let* i = Memory.unbox_int32 i in - Memory.box_float (return (W.F64PromoteF32 (UnOp (F32 ReinterpretI, i))))); - register_un_prim "caml_int32_of_float" `Pure (fun f -> - let* f = Memory.unbox_float f in - Memory.box_int32 (return (W.UnOp (I32 (TruncSatF64 S), f)))); - register_un_prim "caml_int32_to_float" `Pure (fun n -> - let* n = Memory.unbox_int32 n in - Memory.box_float (return (W.UnOp (F64 (Convert (`I32, S)), n)))); - register_un_prim "caml_int32_neg" `Pure (fun i -> - let* i = Memory.unbox_int32 i in - Memory.box_int32 (return (W.BinOp (I32 Sub, Const (I32 0l), i)))); - register_bin_prim "caml_int32_add" `Pure (fun i j -> int32_bin_op Add i j); - register_bin_prim "caml_int32_sub" `Pure (fun i j -> int32_bin_op Sub i j); - register_bin_prim "caml_int32_mul" `Pure (fun i j -> int32_bin_op Mul i j); - register_bin_prim "caml_int32_and" `Pure (fun i j -> int32_bin_op And i j); - register_bin_prim "caml_int32_or" `Pure (fun i j -> int32_bin_op Or i j); - register_bin_prim "caml_int32_xor" `Pure (fun i j -> int32_bin_op Xor i j); - register_bin_prim_ctx "caml_int32_div" (fun context i j -> + return (W.UnOp (F64 (Convert (`I32, S)), n))); + register_un_prim "caml_cos_float" `Pure ~typ:(Number (Float, Unboxed)) Math.cos; + register_un_prim "caml_sin_float" `Pure ~typ:(Number (Float, Unboxed)) Math.sin; + register_un_prim "caml_tan_float" `Pure ~typ:(Number (Float, Unboxed)) Math.tan; + register_un_prim "caml_acos_float" `Pure ~typ:(Number (Float, Unboxed)) Math.acos; + register_un_prim "caml_asin_float" `Pure ~typ:(Number (Float, Unboxed)) Math.asin; + register_un_prim "caml_atan_float" `Pure ~typ:(Number (Float, Unboxed)) Math.atan; + register_bin_prim + "caml_atan2_float" + `Pure + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + Math.atan2; + register_un_prim "caml_cosh_float" `Pure ~typ:(Number (Float, Unboxed)) Math.cosh; + register_un_prim "caml_sinh_float" `Pure ~typ:(Number (Float, Unboxed)) Math.sinh; + register_un_prim "caml_tanh_float" `Pure ~typ:(Number (Float, Unboxed)) Math.tanh; + register_un_prim "caml_acosh_float" `Pure ~typ:(Number (Float, Unboxed)) Math.acosh; + register_un_prim "caml_asinh_float" `Pure ~typ:(Number (Float, Unboxed)) Math.asinh; + register_un_prim "caml_atanh_float" `Pure ~typ:(Number (Float, Unboxed)) Math.atanh; + register_un_prim "caml_cbrt_float" `Pure ~typ:(Number (Float, Unboxed)) Math.cbrt; + register_un_prim "caml_exp_float" `Pure ~typ:(Number (Float, Unboxed)) Math.exp; + register_un_prim "caml_exp2_float" `Pure ~typ:(Number (Float, Unboxed)) Math.exp2; + register_un_prim "caml_log_float" `Pure ~typ:(Number (Float, Unboxed)) Math.log; + register_un_prim "caml_expm1_float" `Pure ~typ:(Number (Float, Unboxed)) Math.expm1; + register_un_prim "caml_log1p_float" `Pure ~typ:(Number (Float, Unboxed)) Math.log1p; + register_un_prim "caml_log2_float" `Pure ~typ:(Number (Float, Unboxed)) Math.log2; + register_un_prim "caml_log10_float" `Pure ~typ:(Number (Float, Unboxed)) Math.log10; + register_bin_prim + "caml_power_float" + `Pure + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + Math.power; + register_bin_prim + "caml_hypot_float" + `Pure + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + Math.hypot; + register_bin_prim + "caml_fmod_float" + `Pure + ~tx:(Number (Float, Unboxed)) + ~ty:(Number (Float, Unboxed)) + Math.fmod; + register_un_prim + "caml_int32_bits_of_float" + `Pure + ~typ:(Number (Float, Unboxed)) + (fun f -> + let* f = f in + return (W.UnOp (I32 ReinterpretF, F32DemoteF64 f))); + register_un_prim + "caml_int32_float_of_bits" + `Pure + ~typ:(Number (Int32, Unboxed)) + (fun i -> + let* i = i in + return (W.F64PromoteF32 (UnOp (F32 ReinterpretI, i)))); + register_un_prim + "caml_int32_of_float" + `Pure + ~typ:(Number (Float, Unboxed)) + (fun f -> + let* f = f in + return (W.UnOp (I32 (TruncSatF64 S), f))); + register_un_prim + "caml_int32_to_float" + `Pure + ~typ:(Number (Int32, Unboxed)) + (fun n -> + let* n = n in + return (W.UnOp (F64 (Convert (`I32, S)), n))); + register_un_prim + "caml_int32_neg" + `Pure + ~typ:(Number (Int32, Unboxed)) + (fun i -> + let* i = i in + return (W.BinOp (I32 Sub, Const (I32 0l), i))); + register_bin_prim + "caml_int32_add" + `Pure + ~tx:(Number (Int32, Unboxed)) + ~ty:(Number (Int32, Unboxed)) + (fun i j -> int32_bin_op Add i j); + register_bin_prim + "caml_int32_sub" + `Pure + ~tx:(Number (Int32, Unboxed)) + ~ty:(Number (Int32, Unboxed)) + (fun i j -> int32_bin_op Sub i j); + register_bin_prim + "caml_int32_mul" + `Pure + ~tx:(Number (Int32, Unboxed)) + ~ty:(Number (Int32, Unboxed)) + (fun i j -> int32_bin_op Mul i j); + register_bin_prim + "caml_int32_and" + `Pure + ~tx:(Number (Int32, Unboxed)) + ~ty:(Number (Int32, Unboxed)) + (fun i j -> int32_bin_op And i j); + register_bin_prim + "caml_int32_or" + `Pure + ~tx:(Number (Int32, Unboxed)) + ~ty:(Number (Int32, Unboxed)) + (fun i j -> int32_bin_op Or i j); + register_bin_prim + "caml_int32_xor" + `Pure + ~tx:(Number (Int32, Unboxed)) + ~ty:(Number (Int32, Unboxed)) + (fun i j -> int32_bin_op Xor i j); + register_bin_prim_ctx + "caml_int32_div" + ~tx:(Number (Int32, Unboxed)) + ~ty:(Number (Int32, Unboxed)) + (fun context 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* () = store ~typ:I32 j' 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 + let* () = store ~typ:I32 i' i in if_ { params = []; result = [] } Arith.( @@ -578,65 +710,137 @@ module Generate (Target : Target_sig.S) = struct (let* i = load i' in let* j = load j' in return (W.BinOp (I32 (Div S), i, j))))) - (Memory.box_int32 (load res))); - register_bin_prim_ctx "caml_int32_mod" (fun context i j -> + (load res)); + register_bin_prim_ctx + "caml_int32_mod" + ~tx:(Number (Int32, Unboxed)) + ~ty:(Number (Int32, Unboxed)) + (fun context i j -> let j' = Var.fresh () in seq - (let* () = store ~typ:I32 j' (Memory.unbox_int32 j) in + (let* () = store ~typ:I32 j' 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* i = i in let* j = load j' in - Memory.box_int32 (return (W.BinOp (I32 (Rem S), i, j))))); - register_bin_prim "caml_int32_shift_left" `Pure ~ty:(Int Unnormalized) (fun i j -> - int32_shift_op Shl i j); - register_bin_prim "caml_int32_shift_right" `Pure ~ty:(Int Unnormalized) (fun i j -> - int32_shift_op (Shr S) i j); + return (W.BinOp (I32 (Rem S), i, j)))); + register_bin_prim + "caml_int32_shift_left" + `Pure + ~tx:(Number (Int32, Unboxed)) + ~ty:(Int Unnormalized) + (fun i j -> int32_bin_op Shl i j); + register_bin_prim + "caml_int32_shift_right" + `Pure + ~tx:(Number (Int32, Unboxed)) + ~ty:(Int Unnormalized) + (fun i j -> int32_bin_op (Shr S) i j); register_bin_prim "caml_int32_shift_right_unsigned" `Pure + ~tx:(Number (Int32, Unboxed)) ~ty:(Int Unnormalized) - (fun i j -> int32_shift_op (Shr U) i j); - register_un_prim "caml_int32_to_int" `Pure (fun i -> Memory.unbox_int32 i); - register_un_prim "caml_int32_of_int" `Pure ~typ:(Int Normalized) (fun i -> - Memory.box_int32 i); - register_un_prim "caml_nativeint_of_int32" `Pure (fun i -> - Memory.box_nativeint (Memory.unbox_int32 i)); - register_un_prim "caml_nativeint_to_int32" `Pure (fun i -> - Memory.box_int32 (Memory.unbox_nativeint i)); - register_un_prim "caml_int64_bits_of_float" `Pure (fun f -> - let* f = Memory.unbox_float f in - Memory.box_int64 (return (W.UnOp (I64 ReinterpretF, f)))); - register_un_prim "caml_int64_float_of_bits" `Pure (fun i -> - let* i = Memory.unbox_int64 i in - Memory.box_float (return (W.UnOp (F64 ReinterpretI, i)))); - register_un_prim "caml_int64_of_float" `Pure (fun f -> - let* f = Memory.unbox_float f in - Memory.box_int64 (return (W.UnOp (I64 (TruncSatF64 S), f)))); - register_un_prim "caml_int64_to_float" `Pure (fun n -> - let* n = Memory.unbox_int64 n in - Memory.box_float (return (W.UnOp (F64 (Convert (`I64, S)), n)))); - register_un_prim "caml_int64_neg" `Pure (fun i -> - let* i = Memory.unbox_int64 i in - Memory.box_int64 (return (W.BinOp (I64 Sub, Const (I64 0L), i)))); - register_bin_prim "caml_int64_add" `Pure (fun i j -> int64_bin_op Add i j); - register_bin_prim "caml_int64_sub" `Pure (fun i j -> int64_bin_op Sub i j); - register_bin_prim "caml_int64_mul" `Pure (fun i j -> int64_bin_op Mul i j); - register_bin_prim "caml_int64_and" `Pure (fun i j -> int64_bin_op And i j); - register_bin_prim "caml_int64_or" `Pure (fun i j -> int64_bin_op Or i j); - register_bin_prim "caml_int64_xor" `Pure (fun i j -> int64_bin_op Xor i j); - register_bin_prim_ctx "caml_int64_div" (fun context i j -> + (fun i j -> int32_bin_op (Shr U) i j); + register_un_prim "caml_int32_to_int" `Pure ~typ:(Number (Int32, Unboxed)) (fun i -> i); + register_un_prim "caml_int32_of_int" `Pure ~typ:(Int Normalized) (fun i -> i); + register_un_prim + "caml_nativeint_of_int32" + `Pure + ~typ:(Number (Int32, Unboxed)) + (fun i -> i); + register_un_prim + "caml_nativeint_to_int32" + `Pure + ~typ:(Number (Nativeint, Unboxed)) + (fun i -> i); + register_un_prim + "caml_int64_bits_of_float" + `Pure + ~typ:(Number (Float, Unboxed)) + (fun f -> + let* f = f in + return (W.UnOp (I64 ReinterpretF, f))); + register_un_prim + "caml_int64_float_of_bits" + `Pure + ~typ:(Number (Int64, Unboxed)) + (fun i -> + let* i = i in + return (W.UnOp (F64 ReinterpretI, i))); + register_un_prim + "caml_int64_of_float" + `Pure + ~typ:(Number (Float, Unboxed)) + (fun f -> + let* f = f in + return (W.UnOp (I64 (TruncSatF64 S), f))); + register_un_prim + "caml_int64_to_float" + `Pure + ~typ:(Number (Int64, Unboxed)) + (fun n -> + let* n = n in + return (W.UnOp (F64 (Convert (`I64, S)), n))); + register_un_prim + "caml_int64_neg" + `Pure + ~typ:(Number (Int64, Unboxed)) + (fun i -> + let* i = i in + return (W.BinOp (I64 Sub, Const (I64 0L), i))); + register_bin_prim + "caml_int64_add" + `Pure + ~tx:(Number (Int64, Unboxed)) + ~ty:(Number (Int64, Unboxed)) + (fun i j -> int64_bin_op Add i j); + register_bin_prim + "caml_int64_sub" + `Pure + ~tx:(Number (Int64, Unboxed)) + ~ty:(Number (Int64, Unboxed)) + (fun i j -> int64_bin_op Sub i j); + register_bin_prim + "caml_int64_mul" + `Pure + ~tx:(Number (Int64, Unboxed)) + ~ty:(Number (Int64, Unboxed)) + (fun i j -> int64_bin_op Mul i j); + register_bin_prim + "caml_int64_and" + `Pure + ~tx:(Number (Int64, Unboxed)) + ~ty:(Number (Int64, Unboxed)) + (fun i j -> int64_bin_op And i j); + register_bin_prim + "caml_int64_or" + `Pure + ~tx:(Number (Int64, Unboxed)) + ~ty:(Number (Int64, Unboxed)) + (fun i j -> int64_bin_op Or i j); + register_bin_prim + "caml_int64_xor" + `Pure + ~tx:(Number (Int64, Unboxed)) + ~ty:(Number (Int64, Unboxed)) + (fun i j -> int64_bin_op Xor i j); + register_bin_prim_ctx + "caml_int64_div" + ~tx:(Number (Int64, Unboxed)) + ~ty:(Number (Int64, Unboxed)) + (fun context 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* () = store ~typ:I64 j' 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 + let* () = store ~typ:I64 i' i in if_ { params = []; result = [] } Arith.( @@ -652,80 +856,166 @@ module Generate (Target : Target_sig.S) = struct (let* i = load i' in let* j = load j' in return (W.BinOp (I64 (Div S), i, j))))) - (Memory.box_int64 (load res))); - register_bin_prim_ctx "caml_int64_mod" (fun context i j -> + (load res)); + register_bin_prim_ctx + "caml_int64_mod" + ~tx:(Number (Int64, Unboxed)) + ~ty:(Number (Int64, Unboxed)) + (fun context i j -> let j' = Var.fresh () in seq - (let* () = store ~typ:I64 j' (Memory.unbox_int64 j) in + (let* () = store ~typ:I64 j' 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* i = i in let* j = load j' in - Memory.box_int64 (return (W.BinOp (I64 (Rem S), i, j))))); - register_bin_prim "caml_int64_shift_left" `Pure ~ty:(Int Unnormalized) (fun i j -> - int64_shift_op Shl i j); - register_bin_prim "caml_int64_shift_right" `Pure ~ty:(Int Unnormalized) (fun i j -> - int64_shift_op (Shr S) i j); + return (W.BinOp (I64 (Rem S), i, j)))); + register_bin_prim + "caml_int64_shift_left" + `Pure + ~tx:(Number (Int64, Unboxed)) + ~ty:(Int Unnormalized) + (fun i j -> int64_shift_op Shl i j); + register_bin_prim + "caml_int64_shift_right" + `Pure + ~tx:(Number (Int64, Unboxed)) + ~ty:(Int Unnormalized) + (fun i j -> int64_shift_op (Shr S) i j); register_bin_prim "caml_int64_shift_right_unsigned" + ~tx:(Number (Int64, Unboxed)) ~ty:(Int Unnormalized) `Pure (fun i j -> int64_shift_op (Shr U) i j); - register_un_prim "caml_int64_to_int" `Pure (fun i -> - let* i = Memory.unbox_int64 i in + register_un_prim + "caml_int64_to_int" + `Pure + ~typ:(Number (Int64, Unboxed)) + (fun i -> + let* i = i in return (W.I32WrapI64 i)); register_un_prim "caml_int64_of_int" `Pure ~typ:(Int Normalized) (fun i -> let* i = i in - Memory.box_int64 - (return - (match i with - | Const (I32 i) -> W.Const (I64 (Int64.of_int32 i)) - | _ -> W.I64ExtendI32 (S, i)))); - register_un_prim "caml_int64_to_int32" `Pure (fun i -> - let* i = Memory.unbox_int64 i in - Memory.box_int32 (return (W.I32WrapI64 i))); - register_un_prim "caml_int64_of_int32" `Pure (fun i -> - let* i = Memory.unbox_int32 i in - Memory.box_int64 (return (W.I64ExtendI32 (S, i)))); - register_un_prim "caml_int64_to_nativeint" `Pure (fun i -> - let* i = Memory.unbox_int64 i in - Memory.box_nativeint (return (W.I32WrapI64 i))); - register_un_prim "caml_int64_of_nativeint" `Pure (fun i -> - let* i = Memory.unbox_nativeint i in - Memory.box_int64 (return (W.I64ExtendI32 (S, i)))); - register_un_prim "caml_nativeint_bits_of_float" `Pure (fun f -> - let* f = Memory.unbox_float f in - Memory.box_nativeint (return (W.UnOp (I32 ReinterpretF, F32DemoteF64 f)))); - register_un_prim "caml_nativeint_float_of_bits" `Pure (fun i -> - let* i = Memory.unbox_int64 i in - Memory.box_float (return (W.F64PromoteF32 (UnOp (I32 ReinterpretF, i))))); - register_un_prim "caml_nativeint_of_float" `Pure (fun f -> - let* f = Memory.unbox_float f in - Memory.box_nativeint (return (W.UnOp (I32 (TruncSatF64 S), f)))); - register_un_prim "caml_nativeint_to_float" `Pure (fun n -> - let* n = Memory.unbox_nativeint n in - Memory.box_float (return (W.UnOp (F64 (Convert (`I32, S)), n)))); - register_un_prim "caml_nativeint_neg" `Pure (fun i -> - let* i = Memory.unbox_nativeint i in - Memory.box_nativeint (return (W.BinOp (I32 Sub, Const (I32 0l), i)))); - register_bin_prim "caml_nativeint_add" `Pure (fun i j -> nativeint_bin_op Add i j); - register_bin_prim "caml_nativeint_sub" `Pure (fun i j -> nativeint_bin_op Sub i j); - register_bin_prim "caml_nativeint_mul" `Pure (fun i j -> nativeint_bin_op Mul i j); - register_bin_prim "caml_nativeint_and" `Pure (fun i j -> nativeint_bin_op And i j); - register_bin_prim "caml_nativeint_or" `Pure (fun i j -> nativeint_bin_op Or i j); - register_bin_prim "caml_nativeint_xor" `Pure (fun i j -> nativeint_bin_op Xor i j); - register_bin_prim_ctx "caml_nativeint_div" (fun context i j -> + return + (match i with + | Const (I32 i) -> W.Const (I64 (Int64.of_int32 i)) + | _ -> W.I64ExtendI32 (S, i))); + register_un_prim + "caml_int64_to_int32" + `Pure + ~typ:(Number (Int64, Unboxed)) + (fun i -> + let* i = i in + return (W.I32WrapI64 i)); + register_un_prim + "caml_int64_of_int32" + `Pure + ~typ:(Number (Int32, Unboxed)) + (fun i -> + let* i = i in + return (W.I64ExtendI32 (S, i))); + register_un_prim + "caml_int64_to_nativeint" + `Pure + ~typ:(Number (Int64, Unboxed)) + (fun i -> + let* i = i in + return (W.I32WrapI64 i)); + register_un_prim + "caml_int64_of_nativeint" + `Pure + ~typ:(Number (Nativeint, Unboxed)) + (fun i -> + let* i = i in + return (W.I64ExtendI32 (S, i))); + register_un_prim + "caml_nativeint_bits_of_float" + `Pure + ~typ:(Number (Float, Unboxed)) + (fun f -> + let* f = f in + return (W.UnOp (I32 ReinterpretF, F32DemoteF64 f))); + register_un_prim + "caml_nativeint_float_of_bits" + `Pure + ~typ:(Number (Nativeint, Unboxed)) + (fun i -> + let* i = i in + return (W.F64PromoteF32 (UnOp (I32 ReinterpretF, i)))); + register_un_prim + "caml_nativeint_of_float" + `Pure + ~typ:(Number (Float, Unboxed)) + (fun f -> + let* f = f in + return (W.UnOp (I32 (TruncSatF64 S), f))); + register_un_prim + "caml_nativeint_to_float" + `Pure + ~typ:(Number (Nativeint, Unboxed)) + (fun n -> + let* n = n in + return (W.UnOp (F64 (Convert (`I32, S)), n))); + register_un_prim + "caml_nativeint_neg" + `Pure + ~typ:(Number (Nativeint, Unboxed)) + (fun i -> + let* i = i in + return (W.BinOp (I32 Sub, Const (I32 0l), i))); + register_bin_prim + "caml_nativeint_add" + `Pure + ~tx:(Number (Nativeint, Unboxed)) + ~ty:(Number (Nativeint, Unboxed)) + (fun i j -> nativeint_bin_op Add i j); + register_bin_prim + "caml_nativeint_sub" + `Pure + ~tx:(Number (Nativeint, Unboxed)) + ~ty:(Number (Nativeint, Unboxed)) + (fun i j -> nativeint_bin_op Sub i j); + register_bin_prim + "caml_nativeint_mul" + `Pure + ~tx:(Number (Nativeint, Unboxed)) + ~ty:(Number (Nativeint, Unboxed)) + (fun i j -> nativeint_bin_op Mul i j); + register_bin_prim + "caml_nativeint_and" + `Pure + ~tx:(Number (Nativeint, Unboxed)) + ~ty:(Number (Nativeint, Unboxed)) + (fun i j -> nativeint_bin_op And i j); + register_bin_prim + "caml_nativeint_or" + `Pure + ~tx:(Number (Nativeint, Unboxed)) + ~ty:(Number (Nativeint, Unboxed)) + (fun i j -> nativeint_bin_op Or i j); + register_bin_prim + "caml_nativeint_xor" + `Pure + ~tx:(Number (Nativeint, Unboxed)) + ~ty:(Number (Nativeint, Unboxed)) + (fun i j -> nativeint_bin_op Xor i j); + register_bin_prim_ctx + "caml_nativeint_div" + ~tx:(Number (Nativeint, Unboxed)) + ~ty:(Number (Nativeint, Unboxed)) + (fun context 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* () = store ~typ:I32 j' 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 + let* () = store ~typ:I32 i' i in if_ { params = []; result = [] } Arith.( @@ -741,31 +1031,44 @@ module Generate (Target : Target_sig.S) = struct (let* i = load i' in let* j = load j' in return (W.BinOp (I32 (Div S), i, j))))) - (Memory.box_nativeint (load res))); - register_bin_prim_ctx "caml_nativeint_mod" (fun context i j -> + (load res)); + register_bin_prim_ctx + "caml_nativeint_mod" + ~tx:(Number (Nativeint, Unboxed)) + ~ty:(Number (Nativeint, Unboxed)) + (fun context i j -> let j' = Var.fresh () in seq - (let* () = store ~typ:I32 j' (Memory.unbox_nativeint j) in + (let* () = store ~typ:I32 j' 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* i = i in let* j = load j' in - Memory.box_nativeint (return (W.BinOp (I32 (Rem S), i, j))))); - register_bin_prim "caml_nativeint_shift_left" `Pure ~ty:(Int Unnormalized) (fun i j -> - nativeint_shift_op Shl i j); + return (W.BinOp (I32 (Rem S), i, j)))); + register_bin_prim + "caml_nativeint_shift_left" + `Pure + ~tx:(Number (Nativeint, Unboxed)) + ~ty:(Int Unnormalized) + (fun i j -> nativeint_bin_op Shl i j); register_bin_prim "caml_nativeint_shift_right" `Pure + ~tx:(Number (Nativeint, Unboxed)) ~ty:(Int Unnormalized) - (fun i j -> nativeint_shift_op (Shr S) i j); + (fun i j -> nativeint_bin_op (Shr S) i j); register_bin_prim "caml_nativeint_shift_right_unsigned" `Pure + ~tx:(Number (Nativeint, Unboxed)) ~ty:(Int Unnormalized) - (fun i j -> nativeint_shift_op (Shr U) i j); - register_un_prim "caml_nativeint_to_int" `Pure (fun i -> Memory.unbox_nativeint i); - register_un_prim "caml_nativeint_of_int" `Pure ~typ:(Int Normalized) (fun i -> - Memory.box_nativeint i); + (fun i j -> nativeint_bin_op (Shr U) i j); + register_un_prim + "caml_nativeint_to_int" + `Pure + ~typ:(Number (Nativeint, Unboxed)) + (fun i -> i); + register_un_prim "caml_nativeint_of_int" `Pure ~typ:(Int Normalized) (fun i -> i); register_bin_prim "caml_int_compare" `Pure @@ -773,16 +1076,20 @@ module Generate (Target : Target_sig.S) = struct ~ty:(Int Normalized) (fun i j -> Arith.((j < i) - (i < j))); register_prim "%js_array" `Pure (fun ctx _ l -> - let* l = - List.fold_right - ~f:(fun x acc -> - let* x = transl_prim_arg ctx x in - let* acc = acc in - return (`Expr x :: acc)) - l - ~init:(return []) - in - Memory.allocate ~tag:0 ~deadcode_sentinal:ctx.deadcode_sentinal ~load l) + Memory.allocate ~tag:0 (expression_list (fun x -> transl_prim_arg ctx x) l)) + + let unboxed_type ty : W.value_type option = + match ty with + | Typing.Int (Normalized | Unnormalized) | Number ((Int32 | Nativeint), Unboxed) -> + Some I32 + | Number (Int64, Unboxed) -> Some I64 + | Number (Float, Unboxed) -> Some F64 + | _ -> None + + let box_number_if_needed ctx x e = + match get_var_type ctx x with + | Number (n, Boxed) as into -> convert ~from:(Number (n, Unboxed)) ~into e + | _ -> e let rec translate_expr ctx context x e = match e with @@ -822,16 +1129,23 @@ module Generate (Target : Target_sig.S) = struct in return (W.Call (apply, args @ [ closure ])) | Block (tag, a, _, _) -> - Memory.allocate - ~deadcode_sentinal:ctx.deadcode_sentinal - ~tag - ~load:(fun x -> load_and_box ctx x) - (List.map ~f:(fun x -> `Var x) (Array.to_list a)) - | Field (x, n, Non_float) -> Memory.field (load_and_box ctx x) n - | Field (x, n, Float) -> + if tag = 254 + then + Memory.allocate_float_array + ~deadcode_sentinal:ctx.deadcode_sentinal + ~load:(fun x -> + convert ~from:(get_var_type ctx x) ~into:(Number (Float, Unboxed)) (load x)) + (Array.to_list a) + else + Memory.allocate + ~tag + (expression_list (fun x -> load_and_box ctx x) (Array.to_list a)) + | Field (y, n, Non_float) -> Memory.field (load_and_box ctx y) n + | Field (y, n, Float) -> Memory.float_array_get - (load_and_box ctx x) - (Constant.translate (Int (Targetint.of_int_warning_on_overflow n))) + (load_and_box ctx y) + (return (W.Const (I32 (Int32.of_int n)))) + |> box_number_if_needed ctx x | Closure _ -> Closure.translate ~context:ctx.global_context @@ -839,7 +1153,13 @@ module Generate (Target : Target_sig.S) = struct ~cps:(Var.Set.mem x ctx.in_cps) ~no_code_pointer:(Call_graph_analysis.direct_calls_only ctx.fun_info x) x - | Constant c -> Constant.translate c + | Constant c -> + Constant.translate + ~unboxed: + (match get_var_type ctx x with + | Number (_, Unboxed) -> true + | _ -> false) + c | Special (Alias_prim _) -> assert false | Prim (Extern "caml_alloc_dummy_function", [ _; Pc (Int arity) ]) -> (* Removed in OCaml 5.2 *) @@ -886,40 +1206,55 @@ module Generate (Target : Target_sig.S) = struct Memory.array_get (transl_prim_arg ctx x) (transl_prim_arg ctx ~typ:(Int Normalized) y) + | Prim (Extern "caml_array_unsafe_get", [ x; y ]) -> + Memory.gen_array_get + (transl_prim_arg ctx x) + (transl_prim_arg ctx ~typ:(Int Normalized) y) | Prim (p, l) -> ( match p with | Extern name when String.Hashtbl.mem internal_primitives name -> snd (String.Hashtbl.find internal_primitives name) ctx context l + |> box_number_if_needed ctx x + | Extern name when String.Hashtbl.mem specialized_primitives name -> + let ((_, arg_typ, _) as typ) = + String.Hashtbl.find specialized_primitives name + in + let* f = register_import ~name (Fun (specialized_primitive_type typ)) in + let rec loop acc arg_typ l = + match arg_typ, l with + | [], [] -> return (W.Call (f, List.rev acc)) + | repr :: rem, x :: r -> + let* x = + transl_prim_arg + ctx + ?typ: + (match repr with + | Value -> None + | Float -> Some (Number (Float, Unboxed)) + | Int32 -> Some (Number (Int32, Unboxed)) + | Nativeint -> Some (Number (Nativeint, Unboxed)) + | Int64 -> Some (Number (Int64, Unboxed))) + x + in + loop (x :: acc) rem r + | [], _ :: _ | _ :: _, [] -> assert false + in + loop [] arg_typ l |> box_number_if_needed ctx x | _ -> ( let l = List.map ~f:(fun x -> transl_prim_arg ctx x) l in match p, l with - | Extern name, l -> ( - try - let ((_, arg_typ, res_typ) as typ) = - String.Hashtbl.find specialized_primitives name - in - let* f = register_import ~name (Fun (specialized_primitive_type typ)) in - let rec loop acc arg_typ l = - match arg_typ, l with - | [], [] -> box_value res_typ (return (W.Call (f, List.rev acc))) - | repr :: rem, x :: r -> - let* x = unbox_value repr x in - loop (x :: acc) rem r - | [], _ :: _ | _ :: _, [] -> assert false - in - loop [] arg_typ l - with Not_found -> - let* f = - register_import ~name (Fun (Type.primitive_type (List.length l))) - in - let rec loop acc l = - match l with - | [] -> return (W.Call (f, List.rev acc)) - | x :: r -> - let* x = x in - loop (x :: acc) r - in - loop [] l) + | Extern name, l -> + let* f = + register_import ~name (Fun (Type.primitive_type (List.length l))) + in + let rec loop acc l = + match l with + | [] -> return (W.Call (f, List.rev acc)) + | x :: r -> + let* x = x in + loop (x :: acc) r + in + loop [] l | IsInt, [ x ] -> Value.is_int x | Vectlength, [ x ] -> Memory.gen_array_length x | (Not | Lt | Le | Eq | Neq | Ult | Array_get | IsInt | Vectlength), _ -> @@ -934,10 +1269,7 @@ module Generate (Target : Target_sig.S) = struct then drop (translate_expr ctx context x e) else store - ?typ: - (match get_var_type ctx x with - | Int (Normalized | Unnormalized) -> Some I32 - | _ -> None) + ?typ:(unboxed_type (get_var_type ctx x)) x (translate_expr ctx context x e) | Set_field (x, n, Non_float, y) -> @@ -945,8 +1277,8 @@ module Generate (Target : Target_sig.S) = struct | Set_field (x, n, Float, y) -> Memory.float_array_set (load_and_box ctx x) - (Constant.translate (Int (Targetint.of_int_warning_on_overflow n))) - (load y) + (return (W.Const (I32 (Int32.of_int n)))) + (convert ~from:(get_var_type ctx y) ~into:(Number (Float, Unboxed)) (load y)) | Offset_ref (x, n) -> Memory.set_field (load x) @@ -1010,14 +1342,7 @@ module Generate (Target : Target_sig.S) = struct l ~f:(fun continuation (y, ty, x, tx) -> let* () = continuation in - store - ~always:true - ?typ: - (match ty with - | Typing.Int (Normalized | Unnormalized) -> Some I32 - | _ -> None) - y - (convert ~from:tx ~into:ty (load x))) + store ~always:true ?typ:(unboxed_type ty) y (convert ~from:tx ~into:ty (load x))) ~init:(return ()) let exception_name = "ocaml_exception" diff --git a/compiler/lib-wasm/target_sig.ml b/compiler/lib-wasm/target_sig.ml index f3ee8be13c..197bf6e690 100644 --- a/compiler/lib-wasm/target_sig.ml +++ b/compiler/lib-wasm/target_sig.ml @@ -20,11 +20,12 @@ module type S = sig type expression = Code_generation.expression module Memory : sig - val allocate : - tag:int - -> deadcode_sentinal:Code.Var.t + val allocate : tag:int -> Wasm_ast.expression list Code_generation.t -> expression + + val allocate_float_array : + deadcode_sentinal:Code.Var.t -> load:(Code.Var.t -> expression) - -> [ `Expr of Wasm_ast.expression | `Var of Wasm_ast.var ] list + -> Wasm_ast.var list -> expression val load_function_pointer : @@ -166,7 +167,7 @@ module type S = sig end module Constant : sig - val translate : Code.constant -> expression + val translate : unboxed:bool -> Code.constant -> expression end module Closure : sig diff --git a/compiler/lib-wasm/typing.ml b/compiler/lib-wasm/typing.ml index 2e1be67c99..412b8d462e 100644 --- a/compiler/lib-wasm/typing.ml +++ b/compiler/lib-wasm/typing.ml @@ -25,10 +25,14 @@ type boxed_number = | Nativeint | Float +type boxed_status = + | Boxed + | Unboxed + type typ = | Top | Int of Integer.kind - | Number of boxed_number + | Number of boxed_number * boxed_status | Tuple of typ array (** This value is a block or an integer; if it's an integer, an overapproximation of the possible values of each of its @@ -42,7 +46,15 @@ module Domain = struct match t, t' with | Bot, t | t, Bot -> t | Int r, Int r' -> Int (Integer.join r r') - | Number n, Number n' -> if Poly.equal n n' then t else Top + | Number (n, b), Number (n', b') -> + if Poly.equal n n' + then + Number + ( n + , match b, b' with + | Unboxed, _ | _, Unboxed -> Unboxed + | Boxed, Boxed -> Boxed ) + else Top | Tuple t, Tuple t' -> let l = Array.length t in let l' = Array.length t' in @@ -64,7 +76,7 @@ module Domain = struct match t, t' with | Top, Top | Bot, Bot -> true | Int t, Int t' -> Poly.equal t t' - | Number t, Number t' -> Poly.equal t t' + | Number (t, b), Number (t', b') -> Poly.equal t t' && Poly.equal b b' | Tuple t, Tuple t' -> Array.length t = Array.length t' && Array.for_all2 ~f:equal t t' | (Top | Tuple _ | Int _ | Number _ | Bot), _ -> false @@ -91,6 +103,7 @@ module Domain = struct let box t = match t with | Int _ -> Int Ref + | Number (n, _) -> Number (n, Boxed) | _ -> t let rec print f t = @@ -105,10 +118,18 @@ module Domain = struct | Ref -> "ref" | Normalized -> "normalized" | Unnormalized -> "unnormalized") - | Number Int32 -> Format.fprintf f "int32" - | Number Int64 -> Format.fprintf f "int64" - | Number Nativeint -> Format.fprintf f "nativeint" - | Number Float -> Format.fprintf f "float" + | Number (n, b) -> + Format.fprintf + f + "%s{%s}" + (match n with + | Int32 -> "int32" + | Int64 -> "int64" + | Nativeint -> "nativeint" + | Float -> "float") + (match b with + | Boxed -> "boxed" + | Unboxed -> "unboxed") | Tuple t -> Format.fprintf f @@ -156,10 +177,10 @@ type st = let rec constant_type (c : constant) = match c with | Int _ -> Int Normalized - | Int32 _ -> Number Int32 - | Int64 _ -> Number Int64 - | NativeInt _ -> Number Nativeint - | Float _ -> Number Float + | Int32 _ -> Number (Int32, Unboxed) + | Int64 _ -> Number (Int64, Unboxed) + | NativeInt _ -> Number (Nativeint, Unboxed) + | Float _ -> Number (Float, Unboxed) | Tuple (_, a, _) -> Tuple (Array.map ~f:(fun c' -> Domain.box (constant_type c')) a) | _ -> Top @@ -194,22 +215,22 @@ let prim_type ~approx prim args = | "caml_lessequal" | "caml_equal" | "caml_compare" -> Int Ref - | "caml_int32_bswap" -> Number Int32 - | "caml_nativeint_bswap" -> Number Nativeint - | "caml_int64_bswap" -> Number Int64 + | "caml_int32_bswap" -> Number (Int32, Unboxed) + | "caml_nativeint_bswap" -> Number (Nativeint, Unboxed) + | "caml_int64_bswap" -> Number (Int64, Unboxed) | "caml_int32_compare" | "caml_nativeint_compare" | "caml_int64_compare" -> Int Ref - | "caml_string_get32" -> Number Int32 - | "caml_string_get64" -> Number Int64 - | "caml_bytes_get32" -> Number Int32 - | "caml_bytes_get64" -> Number Int64 - | "caml_lxm_next" -> Number Int64 - | "caml_ba_uint8_get32" -> Number Int32 - | "caml_ba_uint8_get64" -> Number Int64 - | "caml_nextafter_float" -> Number Float + | "caml_string_get32" -> Number (Int32, Unboxed) + | "caml_string_get64" -> Number (Int64, Unboxed) + | "caml_bytes_get32" -> Number (Int32, Unboxed) + | "caml_bytes_get64" -> Number (Int64, Unboxed) + | "caml_lxm_next" -> Number (Int64, Unboxed) + | "caml_ba_uint8_get32" -> Number (Int32, Unboxed) + | "caml_ba_uint8_get64" -> Number (Int64, Unboxed) + | "caml_nextafter_float" -> Number (Float, Unboxed) | "caml_classify_float" -> Int Ref - | "caml_ldexp_float" | "caml_erf_float" | "caml_erfc_float" -> Number Float + | "caml_ldexp_float" | "caml_erf_float" | "caml_erfc_float" -> Number (Float, Unboxed) | "caml_float_compare" -> Int Ref - | "caml_floatarray_unsafe_get" -> Number Float + | "caml_floatarray_unsafe_get" -> Number (Float, Unboxed) | "caml_bytes_unsafe_get" | "caml_string_unsafe_get" | "caml_bytes_get" @@ -221,7 +242,7 @@ let prim_type ~approx prim args = | "caml_sub_float" | "caml_mul_float" | "caml_div_float" - | "caml_copysign_float" -> Number Float + | "caml_copysign_float" -> Number (Float, Unboxed) | "caml_signbit_float" -> Int Normalized | "caml_neg_float" | "caml_abs_float" @@ -229,7 +250,7 @@ let prim_type ~approx prim args = | "caml_floor_float" | "caml_trunc_float" | "caml_round_float" - | "caml_sqrt_float" -> Number Float + | "caml_sqrt_float" -> Number (Float, Unboxed) | "caml_eq_float" | "caml_neq_float" | "caml_ge_float" @@ -261,11 +282,11 @@ let prim_type ~approx prim args = | "caml_log10_float" | "caml_power_float" | "caml_hypot_float" - | "caml_fmod_float" -> Number Float - | "caml_int32_bits_of_float" -> Number Int32 - | "caml_int32_float_of_bits" -> Number Float - | "caml_int32_of_float" -> Number Int32 - | "caml_int32_to_float" -> Number Float + | "caml_fmod_float" -> Number (Float, Unboxed) + | "caml_int32_bits_of_float" -> Number (Int32, Unboxed) + | "caml_int32_float_of_bits" -> Number (Float, Unboxed) + | "caml_int32_of_float" -> Number (Int32, Unboxed) + | "caml_int32_to_float" -> Number (Float, Unboxed) | "caml_int32_neg" | "caml_int32_add" | "caml_int32_sub" @@ -277,15 +298,15 @@ let prim_type ~approx prim args = | "caml_int32_mod" | "caml_int32_shift_left" | "caml_int32_shift_right" - | "caml_int32_shift_right_unsigned" -> Number Int32 + | "caml_int32_shift_right_unsigned" -> Number (Int32, Unboxed) | "caml_int32_to_int" -> Int Unnormalized - | "caml_int32_of_int" -> Number Int32 - | "caml_nativeint_of_int32" -> Number Nativeint - | "caml_nativeint_to_int32" -> Number Int32 - | "caml_int64_bits_of_float" -> Number Int64 - | "caml_int64_float_of_bits" -> Number Float - | "caml_int64_of_float" -> Number Int64 - | "caml_int64_to_float" -> Number Float + | "caml_int32_of_int" -> Number (Int32, Unboxed) + | "caml_nativeint_of_int32" -> Number (Nativeint, Unboxed) + | "caml_nativeint_to_int32" -> Number (Int32, Unboxed) + | "caml_int64_bits_of_float" -> Number (Int64, Unboxed) + | "caml_int64_float_of_bits" -> Number (Float, Unboxed) + | "caml_int64_of_float" -> Number (Int64, Unboxed) + | "caml_int64_to_float" -> Number (Float, Unboxed) | "caml_int64_neg" | "caml_int64_add" | "caml_int64_sub" @@ -297,17 +318,17 @@ let prim_type ~approx prim args = | "caml_int64_mod" | "caml_int64_shift_left" | "caml_int64_shift_right" - | "caml_int64_shift_right_unsigned" -> Number Int64 + | "caml_int64_shift_right_unsigned" -> Number (Int64, Unboxed) | "caml_int64_to_int" -> Int Unnormalized - | "caml_int64_of_int" -> Number Int64 - | "caml_int64_to_int32" -> Number Int32 - | "caml_int64_of_int32" -> Number Int64 - | "caml_int64_to_nativeint" -> Number Nativeint - | "caml_int64_of_nativeint" -> Number Int64 - | "caml_nativeint_bits_of_float" -> Number Nativeint - | "caml_nativeint_float_of_bits" -> Number Float - | "caml_nativeint_of_float" -> Number Nativeint - | "caml_nativeint_to_float" -> Number Float + | "caml_int64_of_int" -> Number (Int64, Unboxed) + | "caml_int64_to_int32" -> Number (Int32, Unboxed) + | "caml_int64_of_int32" -> Number (Int64, Unboxed) + | "caml_int64_to_nativeint" -> Number (Nativeint, Unboxed) + | "caml_int64_of_nativeint" -> Number (Int64, Unboxed) + | "caml_nativeint_bits_of_float" -> Number (Nativeint, Unboxed) + | "caml_nativeint_float_of_bits" -> Number (Float, Unboxed) + | "caml_nativeint_of_float" -> Number (Nativeint, Unboxed) + | "caml_nativeint_to_float" -> Number (Float, Unboxed) | "caml_nativeint_neg" | "caml_nativeint_add" | "caml_nativeint_sub" @@ -319,9 +340,9 @@ let prim_type ~approx prim args = | "caml_nativeint_mod" | "caml_nativeint_shift_left" | "caml_nativeint_shift_right" - | "caml_nativeint_shift_right_unsigned" -> Number Nativeint + | "caml_nativeint_shift_right_unsigned" -> Number (Nativeint, Unboxed) | "caml_nativeint_to_int" -> Int Unnormalized - | "caml_nativeint_of_int" -> Number Nativeint + | "caml_nativeint_of_int" -> Number (Nativeint, Unboxed) | "caml_int_compare" -> Int Normalized | _ -> Top @@ -345,7 +366,7 @@ let propagate st approx x : Domain.t = | Some_fields _ | No_field -> Domain.limit (Domain.box (Var.Tbl.get approx y))) lst) - | Field (_, _, Float) -> Number Float + | Field (_, _, Float) -> Number (Float, Unboxed) | Field (y, n, Non_float) -> ( match Var.Tbl.get approx y with | Tuple t -> if n < Array.length t then t.(n) else Bot @@ -421,12 +442,190 @@ let solver st = in Solver.f () g (propagate st) +(* These are primitives which are handled internally by the compiler, + plus the specialized primitives listed in Generate. *) +let primitives_with_unboxed_parameters = + let h = String.Hashtbl.create 256 in + List.iter + ~f:(fun s -> String.Hashtbl.add h s ()) + [ "caml_int32_bswap" + ; "caml_nativeint_bswap" + ; "caml_int64_bswap" + ; "caml_int32_compare" + ; "caml_nativeint_compare" + ; "caml_int64_compare" + ; "caml_nextafter_float" + ; "caml_classify_float" + ; "caml_ldexp_float" + ; "caml_erf_float" + ; "caml_erfc_float" + ; "caml_float_compare" + ; "caml_add_float" + ; "caml_sub_float" + ; "caml_mul_float" + ; "caml_div_float" + ; "caml_copysign_float" + ; "caml_signbit_float" + ; "caml_neg_float" + ; "caml_abs_float" + ; "caml_ceil_float" + ; "caml_floor_float" + ; "caml_trunc_float" + ; "caml_round_float" + ; "caml_sqrt_float" + ; "caml_eq_float" + ; "caml_neq_float" + ; "caml_ge_float" + ; "caml_le_float" + ; "caml_gt_float" + ; "caml_lt_float" + ; "caml_int_of_float" + ; "caml_cos_float" + ; "caml_sin_float" + ; "caml_tan_float" + ; "caml_acos_float" + ; "caml_asin_float" + ; "caml_atan_float" + ; "caml_atan2_float" + ; "caml_cosh_float" + ; "caml_sinh_float" + ; "caml_tanh_float" + ; "caml_acosh_float" + ; "caml_asinh_float" + ; "caml_atanh_float" + ; "caml_cbrt_float" + ; "caml_exp_float" + ; "caml_exp2_float" + ; "caml_log_float" + ; "caml_expm1_float" + ; "caml_log1p_float" + ; "caml_log2_float" + ; "caml_log10_float" + ; "caml_power_float" + ; "caml_hypot_float" + ; "caml_fmod_float" + ; "caml_int32_bits_of_float" + ; "caml_int32_float_of_bits" + ; "caml_int32_of_float" + ; "caml_int32_to_float" + ; "caml_int32_neg" + ; "caml_int32_add" + ; "caml_int32_sub" + ; "caml_int32_mul" + ; "caml_int32_and" + ; "caml_int32_or" + ; "caml_int32_xor" + ; "caml_int32_div" + ; "caml_int32_mod" + ; "caml_int32_shift_left" + ; "caml_int32_shift_right" + ; "caml_int32_shift_right_unsigned" + ; "caml_int32_to_int" + ; "caml_nativeint_of_int32" + ; "caml_nativeint_to_int32" + ; "caml_int64_bits_of_float" + ; "caml_int64_float_of_bits" + ; "caml_int64_of_float" + ; "caml_int64_to_float" + ; "caml_int64_neg" + ; "caml_int64_add" + ; "caml_int64_sub" + ; "caml_int64_mul" + ; "caml_int64_and" + ; "caml_int64_or" + ; "caml_int64_xor" + ; "caml_int64_div" + ; "caml_int64_mod" + ; "caml_int64_shift_left" + ; "caml_int64_shift_right" + ; "caml_int64_shift_right_unsigned" + ; "caml_int64_to_int" + ; "caml_int64_to_int32" + ; "caml_int64_of_int32" + ; "caml_int64_to_nativeint" + ; "caml_int64_of_nativeint" + ; "caml_nativeint_bits_of_float" + ; "caml_nativeint_float_of_bits" + ; "caml_nativeint_of_float" + ; "caml_nativeint_to_float" + ; "caml_nativeint_neg" + ; "caml_nativeint_add" + ; "caml_nativeint_sub" + ; "caml_nativeint_mul" + ; "caml_nativeint_and" + ; "caml_nativeint_or" + ; "caml_nativeint_xor" + ; "caml_nativeint_div" + ; "caml_nativeint_mod" + ; "caml_nativeint_shift_left" + ; "caml_nativeint_shift_right" + ; "caml_nativeint_shift_right_unsigned" + ; "caml_nativeint_to_int" + ; "caml_floatarray_unsafe_set" + ]; + h + +let box_numbers p st types = + (* We box numbers eagerly if the boxed value is ever used. *) + let should_box = Var.ISet.empty () in + let rec box y = + if not (Var.ISet.mem should_box y) + then ( + Var.ISet.add should_box y; + let typ = Var.Tbl.get types y in + (match typ with + | Number (n, Unboxed) -> Var.Tbl.set types y (Number (n, Boxed)) + | _ -> ()); + match typ with + | Number (_, Unboxed) | Top -> ( + match st.state.defs.(Var.idx y) with + | Expr _ -> () + | Phi { known; _ } -> Var.Set.iter box known) + | Number (_, Boxed) | Int _ | Tuple _ | Bot -> ()) + in + Addr.Map.iter + (fun _ b -> + List.iter + ~f:(fun i -> + match i with + | Let (_, e) -> ( + match e with + | Apply { args; _ } -> List.iter ~f:box args + | Block (tag, lst, _, _) -> if tag <> 254 then Array.iter ~f:box lst + | Prim (Extern s, args) -> + if not (String.Hashtbl.mem primitives_with_unboxed_parameters s) + then + List.iter + ~f:(fun a -> + match a with + | Pv y -> box y + | Pc _ -> ()) + args + | Prim ((Eq | Neq), args) -> + List.iter + ~f:(fun a -> + match a with + | Pv y -> box y + | Pc _ -> ()) + args + | Prim ((Vectlength | Array_get | Not | IsInt | Lt | Le | Ult), _) + | Field _ | Closure _ | Constant _ | Special _ -> ()) + | Set_field (_, _, Non_float, y) | Array_set (_, _, y) -> box y + | Assign _ | Offset_ref _ | Set_field (_, _, Float, _) | Event _ -> ()) + b.body; + match b.branch with + | Return y -> box y + | Raise _ | Stop | Branch _ | Cond _ | Switch _ | Pushtrap _ | Poptrap _ -> ()) + p.blocks + let f ~state ~info ~deadcode_sentinal p = let t = Timer.make () in update_deps state p; let function_parameters = mark_function_parameters p in - let typ = solver { state; info; function_parameters } in + let st = { state; info; function_parameters } in + let typ = solver st in Var.Tbl.set typ deadcode_sentinal (Int Normalized); + box_numbers p st typ; if times () then Format.eprintf " type analysis: %a@." Timer.print t; if debug () then ( diff --git a/compiler/lib-wasm/typing.mli b/compiler/lib-wasm/typing.mli index 1860b4ac7c..d962438bbe 100644 --- a/compiler/lib-wasm/typing.mli +++ b/compiler/lib-wasm/typing.mli @@ -11,10 +11,14 @@ type boxed_number = | Nativeint | Float +type boxed_status = + | Boxed + | Unboxed + type typ = | Top | Int of Integer.kind - | Number of boxed_number + | Number of boxed_number * boxed_status | Tuple of typ array | Bot diff --git a/compiler/lib-wasm/wasm_output.ml b/compiler/lib-wasm/wasm_output.ml index 0617b0878c..88595ca03a 100644 --- a/compiler/lib-wasm/wasm_output.ml +++ b/compiler/lib-wasm/wasm_output.ml @@ -550,7 +550,9 @@ end = struct List.iter ~f:(fun e' -> output_expression st ch e') l; output_byte ch 0x10; output_uint ch (Code.Var.Hashtbl.find st.func_names f) - | Seq _ -> assert false + | Seq (l, e') -> + List.iter ~f:(fun i' -> output_instruction st ch i') l; + output_expression st ch e' | Pop _ -> () | RefFunc f -> Feature.require reference_types; @@ -939,7 +941,9 @@ end = struct List.fold_left ~f:(fun set i -> instr_function_references i set) ~init:set l | Call (_, l) | ArrayNewFixed (_, l) | StructNew (_, l) -> List.fold_left ~f:(fun set i -> expr_function_references i set) ~init:set l - | Seq _ -> assert false + | Seq (l, e) -> + List.fold_left ~f:(fun set i -> instr_function_references i set) ~init:set l + |> expr_function_references e | RefFunc f -> Code.Var.Set.add f set | Call_ref (_, e', l) -> List.fold_left