Skip to content

Commit e0e5df8

Browse files
committed
OxCaml: indexing by unboxed int
1 parent 8efc6d1 commit e0e5df8

File tree

6 files changed

+198
-3
lines changed

6 files changed

+198
-3
lines changed

compiler/lib-wasm/generate.ml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,12 @@ module Generate (Target : Target_sig.S) = struct
317317
| [ x ] -> f (transl_prim_arg ctx ?typ x)
318318
| l -> invalid_arity name l ~expected:1)
319319

320+
let register_un_prim_ctx name ?typ ?ret_typ f =
321+
register_prim name `Mutator ~unbox:(is_unboxed typ) ?ret_typ (fun ctx context l ->
322+
match l with
323+
| [ x ] -> f context (transl_prim_arg ctx ?typ x)
324+
| l -> invalid_arity name l ~expected:1)
325+
320326
let register_bin_prim name k ?tx ?ty ?ret_typ f =
321327
let unbox = is_unboxed tx || is_unboxed ty in
322328
register_prim name k ~unbox ?ret_typ (fun ctx _ l ->
@@ -415,12 +421,44 @@ module Generate (Target : Target_sig.S) = struct
415421
~ty:int_n
416422
~ret_typ:float_u
417423
Memory.float_array_get;
424+
register_bin_prim
425+
"caml_array_unsafe_get_indexed_by_int32"
426+
`Mutable
427+
~ty:int32_u
428+
(fun x y -> Memory.gen_array_get x y);
429+
register_bin_prim
430+
"caml_array_unsafe_get_indexed_by_int64"
431+
`Mutable
432+
~ty:int64_u
433+
(fun x y ->
434+
let y =
435+
let* y = y in
436+
return (W.I32WrapI64 y)
437+
in
438+
Memory.gen_array_get x y);
439+
register_bin_prim
440+
"caml_array_unsafe_get_indexed_by_nativeint"
441+
`Mutable
442+
~ty:nativeint_u
443+
(fun x y -> Memory.gen_array_get x y);
418444
register_tern_prim "caml_array_unsafe_set" ~ty:int_n (fun x y z ->
419445
seq (Memory.gen_array_set x y z) Value.unit);
420446
register_tern_prim "caml_array_unsafe_set_addr" ~ty:int_n (fun x y z ->
421447
seq (Memory.array_set x y z) Value.unit);
422448
register_tern_prim "caml_floatarray_unsafe_set" ~ty:int_n ~tz:float_u (fun x y z ->
423449
seq (Memory.float_array_set x y z) Value.unit);
450+
register_tern_prim "caml_array_unsafe_set_indexed_by_int32" ~ty:int32_u (fun x y z ->
451+
seq (Memory.gen_array_set x y z) Value.unit);
452+
register_tern_prim "caml_array_unsafe_set_indexed_by_int64" ~ty:int64_u (fun x y z ->
453+
let y =
454+
let* y = y in
455+
return (W.I32WrapI64 y)
456+
in
457+
seq (Memory.gen_array_set x y z) Value.unit);
458+
register_tern_prim
459+
"caml_array_unsafe_set_indexed_by_nativeint"
460+
~ty:nativeint_u
461+
(fun x y z -> seq (Memory.gen_array_set x y z) Value.unit);
424462
register_bin_prim
425463
"caml_string_unsafe_get"
426464
`Pure
@@ -514,6 +552,50 @@ module Generate (Target : Target_sig.S) = struct
514552
let* cond = Arith.uge y (Memory.float_array_length (load a)) in
515553
instr (W.Br_if (label, cond)))
516554
x);
555+
register_un_prim_ctx
556+
"caml_checked_int32_to_int"
557+
~typ:int32_u
558+
~ret_typ:int_n
559+
(fun context x ->
560+
let y = Code.Var.fresh () in
561+
seq
562+
(let* () = store y x in
563+
let label = label_index context bound_error_pc in
564+
let* cond = Arith.((load y lsl const 1l) asr const 1l <> load y) in
565+
instr (W.Br_if (label, cond)))
566+
(load y));
567+
register_un_prim_ctx
568+
"caml_checked_nativeint_to_int"
569+
~typ:nativeint_u
570+
~ret_typ:int_n
571+
(fun context x ->
572+
let y = Code.Var.fresh () in
573+
seq
574+
(let* () = store y x in
575+
let label = label_index context bound_error_pc in
576+
let* cond = Arith.((load y lsl const 1l) asr const 1l <> load y) in
577+
instr (W.Br_if (label, cond)))
578+
(load y));
579+
register_un_prim_ctx
580+
"caml_checked_int64_to_int"
581+
~typ:int64_u
582+
~ret_typ:int_n
583+
(fun context x ->
584+
let y = Code.Var.fresh () in
585+
seq
586+
(let* () = store y x in
587+
let* y = load y in
588+
let label = label_index context bound_error_pc in
589+
let cond =
590+
W.BinOp
591+
( I64 Ne
592+
, y
593+
, BinOp (I64 (Shr U), BinOp (I64 Shl, y, Const (I64 33L)), Const (I64 33L))
594+
)
595+
in
596+
instr (W.Br_if (label, cond)))
597+
(let* y = load y in
598+
return (W.I32WrapI64 y)));
517599
register_arith_bin_prim "caml_add_float" `Pure ~typ:float_u (fun f g ->
518600
float_bin_op Add f g);
519601
register_arith_bin_prim "caml_sub_float" ~typ:float_u `Pure (fun f g ->
@@ -1883,6 +1965,9 @@ module Generate (Target : Target_sig.S) = struct
18831965
| "caml_check_bound"
18841966
| "caml_check_bound_gen"
18851967
| "caml_check_bound_float"
1968+
| "caml_checked_int32_to_int"
1969+
| "caml_checked_nativeint_to_int"
1970+
| "caml_checked_int64_to_int"
18861971
| "caml_ba_get_1"
18871972
| "caml_ba_get_2"
18881973
| "caml_ba_get_3"

compiler/lib/eval.ml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,17 @@ let eval_prim ~target x =
298298
| "caml_nativeint_compare", [ NativeInt i; NativeInt j ] ->
299299
Some (Int (Targetint.of_int_exn (Int32.compare i j)))
300300
| "caml_nativeint_to_int", [ Int32 i ] -> Some (Int (Targetint.of_int32_truncate i))
301+
| "caml_checked_int32_to_int", [ Int32 i ]
302+
when Int32.equal i (Targetint.to_int32 (Targetint.of_int32_truncate i)) ->
303+
Some (Int (Targetint.of_int32_truncate i))
304+
| "caml_checked_nativeint_to_int", [ Int32 i ]
305+
when Int32.equal i (Targetint.to_int32 (Targetint.of_int32_truncate i)) ->
306+
Some (Int (Targetint.of_int32_truncate i))
307+
| "caml_checked_int64_to_int", [ Int64 i ]
308+
when let j = Int64.to_int32 i in
309+
Int64.equal i (Int64.of_int32 j)
310+
&& Int32.equal j (Targetint.to_int32 (Targetint.of_int32_truncate j)) ->
311+
Some (Int (Targetint.of_int32_truncate (Int64.to_int32 i)))
301312
| "caml_nativeint_of_int", [ Int i ] -> nativeint (Targetint.to_int32 i)
302313
(* int64 *)
303314
| "caml_int64_bits_of_float", [ Float f ] -> int64 f

compiler/lib/flow.ml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ module Info = struct
5454
let possibly_mutable t x = Code.Var.ISet.mem t.info_possibly_mutable x
5555

5656
let update_def { info_defs; _ } x exp =
57+
(* [Specialize_js] can introduce fresh variables *)
5758
let idx = Code.Var.idx x in
58-
info_defs.(idx) <- Expr exp
59+
if idx < Array.length info_defs then info_defs.(idx) <- Expr exp
5960
end
6061

6162
let undefined = Phi Var.Set.empty
@@ -338,10 +339,11 @@ let get_approx
338339
top
339340
join
340341
x =
341-
let s = Var.Tbl.get info_known_origins x in
342-
if Var.Tbl.get info_maybe_unknown x
342+
(* [Specialize_js] can introduce fresh variables *)
343+
if Var.idx x >= Var.Tbl.length info_known_origins || Var.Tbl.get info_maybe_unknown x
343344
then top
344345
else
346+
let s = Var.Tbl.get info_known_origins x in
345347
match Var.Set.cardinal s with
346348
| 0 -> top
347349
| 1 -> f (Var.Set.choose s)

compiler/lib/generate.ml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,8 @@ let _ =
12591259
[ "caml_array_unsafe_get"
12601260
; "caml_array_unsafe_get_float"
12611261
; "caml_floatarray_unsafe_get"
1262+
; "caml_array_unsafe_get_indexed_by_int32"
1263+
; "caml_array_unsafe_get_indexed_by_nativeint"
12621264
]
12631265
`Mutable
12641266
(fun cx cy _ -> Mlvalue.Array.field cx cy);
@@ -1275,6 +1277,10 @@ let _ =
12751277
]
12761278
`Pure
12771279
(fun cx _ -> cx);
1280+
register_un_prims
1281+
[ "caml_checked_nativeint_to_int"; "caml_checked_int32_to_int" ]
1282+
`Mutator
1283+
(fun cx _ -> cx);
12781284
register_bin_prims
12791285
[ "%int_add"; "caml_int32_add"; "caml_nativeint_add" ]
12801286
`Pure
@@ -1355,6 +1361,8 @@ let _ =
13551361
; "caml_array_unsafe_set_float"
13561362
; "caml_floatarray_unsafe_set"
13571363
; "caml_array_unsafe_set_addr"
1364+
; "caml_array_unsafe_set_indexed_by_int32"
1365+
; "caml_array_unsafe_set_indexed_by_nativeint"
13581366
]
13591367
`Mutator
13601368
(fun cx cy cz _ -> J.EBin (J.Eq, Mlvalue.Array.field cx cy, cz));

compiler/lib/specialize_js.ml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,46 @@ let idx_equal (v1, c1) (v2, c2) =
283283
| `Var a, `Var b -> Code.Var.equal a b
284284
| `Cst _, `Var _ | `Var _, `Cst _ -> false
285285

286+
let indexing_primitives l =
287+
let h = String.Hashtbl.create 16 in
288+
List.iter l ~f:(fun prim ->
289+
List.iter [ "int32"; "nativeint"; "int64" ] ~f:(fun int ->
290+
String.Hashtbl.add
291+
h
292+
(prim ^ "_indexed_by_" ^ int)
293+
("caml_checked_" ^ int ^ "_to_int", prim)));
294+
h
295+
296+
let getters =
297+
indexing_primitives
298+
[ "caml_array_get"
299+
; "caml_string_get16"
300+
; "caml_string_get32"
301+
; "caml_string_get64"
302+
; "caml_string_getf32"
303+
; "caml_bytes_get16"
304+
; "caml_bytes_get32"
305+
; "caml_bytes_get64"
306+
; "caml_bytes_getf32"
307+
; "caml_ba_uint8_get16"
308+
; "caml_ba_uint8_get32"
309+
; "caml_ba_uint8_get64"
310+
; "caml_ba_uint8_getf32"
311+
]
312+
313+
let setters =
314+
indexing_primitives
315+
[ "caml_array_set"
316+
; "caml_bytes_set16"
317+
; "caml_bytes_set32"
318+
; "caml_bytes_set64"
319+
; "caml_bytes_setf32"
320+
; "caml_ba_uint8_set16"
321+
; "caml_ba_uint8_set32"
322+
; "caml_ba_uint8_set64"
323+
; "caml_ba_uint8_setf32"
324+
]
325+
286326
let specialize_instrs ~target opt_count info l =
287327
let rec aux info checks l acc =
288328
match l with
@@ -293,6 +333,18 @@ let specialize_instrs ~target opt_count info l =
293333
the array access. The bound checking function returns the array,
294334
which allows to produce more compact code. *)
295335
match i with
336+
| Let (x, Prim (Extern prim, [ y; z ])) when String.Hashtbl.mem getters prim ->
337+
let conv, access = String.Hashtbl.find getters prim in
338+
let z' = Code.Var.fresh () in
339+
let r =
340+
Let (z', Prim (Extern conv, [ z ]))
341+
(* The recursive call to [aux] will optimize
342+
[caml_array_get] into a nominally "unsafe" (but
343+
guarded) access. *)
344+
:: Let (x, Prim (Extern access, [ y; Pv z' ]))
345+
:: r
346+
in
347+
aux info checks r acc
296348
| Let
297349
( x
298350
, Prim
@@ -339,6 +391,18 @@ let specialize_instrs ~target opt_count info l =
339391
incr opt_count;
340392
let acc = instr y' :: Let (y', Prim (Extern check, [ Pv y; z ])) :: acc in
341393
aux info ((y, idx) :: checks) r acc
394+
| Let (x, Prim (Extern prim, [ y; z; w ])) when String.Hashtbl.mem setters prim ->
395+
let conv, setter = String.Hashtbl.find setters prim in
396+
let z' = Code.Var.fresh () in
397+
let r =
398+
Let (z', Prim (Extern conv, [ z ]))
399+
(* The recursive call to [aux] will optimize
400+
[caml_array_set] into a nominally "unsafe" (but
401+
guarded) access. *)
402+
:: Let (x, Prim (Extern setter, [ y; Pv z'; w ]))
403+
:: r
404+
in
405+
aux info checks r acc
342406
| Let
343407
( x
344408
, Prim

runtime/js/int64.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,31 @@ function caml_int64_to_int32(x) {
352352
return x.toInt();
353353
}
354354

355+
//Provides: caml_checked_int64_to_int
356+
//Requires: caml_int64_of_int32, caml_array_bound_error
357+
//Version: >= 5.2, < 5.3
358+
//OxCaml
359+
function caml_checked_int64_to_int(x) {
360+
var y = x.toInt();
361+
if (x.compare(caml_int64_of_int32(y)) !== 0) caml_array_bound_error();
362+
return y;
363+
}
364+
365+
//Provides: caml_array_unsafe_get_indexed_by_int64 mutable (mutable, const)
366+
//Version: >= 5.2, < 5.3
367+
//OxCaml
368+
function caml_array_unsafe_get_indexed_by_int64(array, index) {
369+
return array[index.toInt() + 1];
370+
}
371+
372+
//Provides: caml_array_unsafe_set_indexed_by_int64 (mutable, const, mutable)
373+
//Version: >= 5.2, < 5.3
374+
//OxCaml
375+
function caml_array_unsafe_set_indexed_by_int64(array, index, newval) {
376+
array[index.toInt() + 1] = newval;
377+
return 0;
378+
}
379+
355380
//Provides: caml_int64_to_float const
356381
function caml_int64_to_float(x) {
357382
return x.toFloat();

0 commit comments

Comments
 (0)