Skip to content

Commit d1acc48

Browse files
committed
Squashed commit of the following:
commit c9d7aa6 Author: Jacob Van Buren <[email protected]> Date: Thu Jan 2 14:49:45 2025 -0500 cleaned up div/mod commit 4d9f427 Author: Jacob Van Buren <[email protected]> Date: Thu Jan 2 14:45:42 2025 -0500 address feedback and simplify division interface
1 parent 625a416 commit d1acc48

File tree

5 files changed

+162
-150
lines changed

5 files changed

+162
-150
lines changed

backend/cmm_helpers.ml

+110-105
Original file line numberDiff line numberDiff line change
@@ -635,16 +635,54 @@ let raise_symbol dbg symb =
635635
Cop
636636
(Craise Lambda.Raise_regular, [Cconst_symbol (global_symbol symb, dbg)], dbg)
637637

638-
let rec div_int c1 c2 is_safe dbg =
639-
match c1, c2 with
640-
| c1, Cconst_int (0, _) ->
641-
Csequence (c1, raise_symbol dbg "caml_exn_Division_by_zero")
642-
| c1, Cconst_int (1, _) -> c1
643-
| Cconst_int (n1, _), Cconst_int (n2, _) -> Cconst_int (n1 / n2, dbg)
644-
| c1, Cconst_int (n, _) when n <> min_int ->
645-
let l = Misc.log2 n in
646-
if n = 1 lsl l
638+
let[@inline] get_const = function
639+
| Cconst_int (i, _) -> Some (Nativeint.of_int i)
640+
| Cconst_natint (i, _) -> Some i
641+
| _ -> None
642+
643+
(** Division or modulo on registers. The overflow case min_int / -1 can
644+
occur, in which case we force x / -1 = -x and x mod -1 = 0. (PR#5513).
645+
In typical cases, [operator] is used to compute the result.
646+
647+
However, if division crashes on overflow, we will insert a runtime check for a divisor
648+
of -1, and fall back to [if_divisor_is_minus_one]. *)
649+
let make_safe_divmod operator ~if_divisor_is_negative_one
650+
?(dividend_cannot_be_min_int = false) c1 c2 ~dbg =
651+
if dividend_cannot_be_min_int || not Arch.division_crashes_on_overflow
652+
then Cop (operator, [c1; c2], dbg)
653+
else
654+
bind "divisor" c2 (fun c2 ->
655+
bind "dividend" c1 (fun c1 ->
656+
Cifthenelse
657+
( Cop (Ccmpi Cne, [c2; Cconst_int (-1, dbg)], dbg),
658+
dbg,
659+
Cop (operator, [c1; c2], dbg),
660+
dbg,
661+
if_divisor_is_negative_one ~dividend:c1 ~dbg,
662+
dbg,
663+
Any )))
664+
665+
let rec div_int ?dividend_cannot_be_min_int c1 c2 dbg =
666+
let if_divisor_is_negative_one ~dividend ~dbg = neg_int dividend dbg in
667+
match get_const c1, get_const c2 with
668+
| _, Some 0n -> Csequence (c1, raise_symbol dbg "caml_exn_Division_by_zero")
669+
| _, Some 1n -> c1
670+
| Some n1, Some n2 -> natint_const_untagged dbg (Nativeint.div n1 n2)
671+
| _, Some -1n -> if_divisor_is_negative_one ~dividend:c1 ~dbg
672+
| _, Some n ->
673+
if n < 0n
674+
then
675+
if n = Nativeint.min_int
676+
then Cop (Ccmpi Ceq, [c1; Cconst_natint (Nativeint.min_int, dbg)], dbg)
677+
else
678+
neg_int
679+
(div_int ?dividend_cannot_be_min_int c1
680+
(Cconst_natint (Nativeint.neg n, dbg))
681+
dbg)
682+
dbg
683+
else if Nativeint.logand n (Nativeint.pred n) = 0n
647684
then
685+
let l = Misc.log2_nativeint n in
648686
(* Algorithm:
649687
650688
t = shift-right-signed(c1, l - 1)
@@ -661,14 +699,8 @@ let rec div_int c1 c2 is_safe dbg =
661699
add_int c1 t dbg);
662700
Cconst_int (l, dbg) ],
663701
dbg )
664-
else if n < 0
665-
then
666-
sub_int
667-
(Cconst_int (0, dbg))
668-
(div_int c1 (Cconst_int (-n, dbg)) is_safe dbg)
669-
dbg
670702
else
671-
let m, p = divimm_parameters (Nativeint.of_int n) in
703+
let m, p = divimm_parameters n in
672704
(* Algorithm:
673705
674706
t = multiply-high-signed(c1, m) if m < 0,
@@ -688,30 +720,36 @@ let rec div_int c1 c2 is_safe dbg =
688720
if p > 0 then Cop (Casr, [t; Cconst_int (p, dbg)], dbg) else t
689721
in
690722
add_int t (lsr_int c1 (Cconst_int (Nativeint.size - 1, dbg)) dbg) dbg)
691-
| c1, c2 when !Clflags.unsafe || is_safe = Lambda.Unsafe ->
692-
Cop (Cdivi, [c1; c2], dbg)
693-
| c1, c2 ->
694-
bind "divisor" c2 (fun c2 ->
695-
bind "dividend" c1 (fun c1 ->
696-
Cifthenelse
697-
( c2,
698-
dbg,
699-
Cop (Cdivi, [c1; c2], dbg),
700-
dbg,
701-
raise_symbol dbg "caml_exn_Division_by_zero",
702-
dbg,
703-
Any )))
704-
705-
let mod_int c1 c2 is_safe dbg =
706-
match c1, c2 with
707-
| c1, Cconst_int (0, _) ->
708-
Csequence (c1, raise_symbol dbg "caml_exn_Division_by_zero")
709-
| c1, Cconst_int ((1 | -1), _) -> Csequence (c1, Cconst_int (0, dbg))
710-
| Cconst_int (n1, _), Cconst_int (n2, _) -> Cconst_int (n1 mod n2, dbg)
711-
| c1, (Cconst_int (n, _) as c2) when n <> min_int ->
712-
let l = Misc.log2 n in
713-
if n = 1 lsl l
723+
| _, _ ->
724+
make_safe_divmod ?dividend_cannot_be_min_int ~if_divisor_is_negative_one
725+
Cdivi c1 c2 ~dbg
726+
727+
let mod_int ?dividend_cannot_be_min_int c1 c2 dbg =
728+
let if_divisor_is_positive_or_negative_one ~dividend ~dbg =
729+
match dividend with
730+
| Cvar _ -> Cconst_int (0, dbg)
731+
| dividend -> Csequence (dividend, Cconst_int (0, dbg))
732+
in
733+
match get_const c1, get_const c2 with
734+
| _, Some 0n -> Csequence (c1, raise_symbol dbg "caml_exn_Division_by_zero")
735+
| _, Some (1n | -1n) ->
736+
if_divisor_is_positive_or_negative_one ~dividend:c1 ~dbg
737+
| Some n1, Some n2 -> natint_const_untagged dbg (Nativeint.rem n1 n2)
738+
| _, Some n ->
739+
if n = Nativeint.min_int
740+
then
741+
bind "dividend" c1 (fun c1 ->
742+
Cifthenelse
743+
( Cop (Ccmpi Ceq, [c1; neg_int c1 dbg], dbg),
744+
dbg,
745+
Cconst_int (0, dbg),
746+
dbg,
747+
Cop (Cor, [c1; Cconst_natint (Nativeint.min_int, dbg)], dbg),
748+
dbg,
749+
Any ))
750+
else if Nativeint.logand n (Nativeint.pred n) = 0n
714751
then
752+
let l = Misc.log2_nativeint n in
715753
(* Algorithm:
716754
717755
t = shift-right-signed(c1, l - 1)
@@ -728,69 +766,25 @@ let mod_int c1 c2 is_safe dbg =
728766
let t = asr_int c1 (Cconst_int (l - 1, dbg)) dbg in
729767
let t = lsr_int t (Cconst_int (Nativeint.size - l, dbg)) dbg in
730768
let t = add_int c1 t dbg in
731-
let t = Cop (Cand, [t; Cconst_int (-n, dbg)], dbg) in
769+
let t = Cop (Cand, [t; Cconst_natint (Nativeint.neg n, dbg)], dbg) in
732770
sub_int c1 t dbg)
733771
else
734772
bind "dividend" c1 (fun c1 ->
735-
sub_int c1 (mul_int (div_int c1 c2 is_safe dbg) c2 dbg) dbg)
736-
| c1, c2 when !Clflags.unsafe || is_safe = Lambda.Unsafe ->
737-
(* Flambda already generates that test *)
738-
Cop (Cmodi, [c1; c2], dbg)
739-
| c1, c2 ->
740-
bind "divisor" c2 (fun c2 ->
741-
bind "dividend" c1 (fun c1 ->
742-
Cifthenelse
743-
( c2,
744-
dbg,
745-
Cop (Cmodi, [c1; c2], dbg),
746-
dbg,
747-
raise_symbol dbg "caml_exn_Division_by_zero",
748-
dbg,
749-
Any )))
750-
751-
(* Division or modulo on boxed integers. The overflow case min_int / -1 can
752-
occur, in which case we force x / -1 = -x and x mod -1 = 0. (PR#5513). *)
753-
754-
(* Division or modulo on boxed integers. The overflow case min_int / -1 can
755-
occur, in which case we force x / -1 = -x and x mod -1 = 0. (PR#5513). *)
756-
757-
let safe_divmod_bi mkop mkm1 ?(dividend_cannot_be_min_int = false) is_safe
758-
dividend divisor dbg =
759-
let is_different_from x = function
760-
| Cconst_int (n, _) -> Nativeint.of_int n <> x
761-
| Cconst_natint (n, _) -> n <> x
762-
| _ -> false
763-
in
764-
bind "divisor" divisor (fun divisor ->
765-
bind "dividend" dividend (fun dividend ->
766-
let c = mkop dividend divisor is_safe dbg in
767-
if not Arch.division_crashes_on_overflow
768-
then c
769-
else
770-
let dividend_cannot_be_min_int =
771-
dividend_cannot_be_min_int
772-
|| is_different_from Nativeint.min_int dividend
773-
in
774-
let divisor_cannot_be_negative_one =
775-
is_different_from (-1n) divisor
776-
in
777-
if dividend_cannot_be_min_int || divisor_cannot_be_negative_one
778-
then c
779-
else
780-
Cifthenelse
781-
( Cop (Ccmpi Cne, [divisor; Cconst_int (-1, dbg)], dbg),
782-
dbg,
783-
c,
784-
dbg,
785-
mkm1 dividend dbg,
786-
dbg,
787-
Any )))
788-
789-
let safe_div_bi =
790-
safe_divmod_bi div_int (fun c1 dbg ->
791-
Cop (Csubi, [Cconst_int (0, dbg); c1], dbg))
773+
sub_int c1 (mul_int (div_int c1 c2 dbg) c2 dbg) dbg)
774+
| _, _ ->
775+
make_safe_divmod ?dividend_cannot_be_min_int
776+
~if_divisor_is_negative_one:if_divisor_is_positive_or_negative_one Cmodi
777+
c1 c2 ~dbg
778+
779+
let div_int ?dividend_cannot_be_min_int c1 c2 dbg =
780+
bind "divisor" c2 (fun c2 ->
781+
bind "dividend" c1 (fun c1 ->
782+
div_int ?dividend_cannot_be_min_int c1 c2 dbg))
792783

793-
let safe_mod_bi = safe_divmod_bi mod_int (fun _ dbg -> Cconst_int (0, dbg))
784+
let mod_int ?dividend_cannot_be_min_int c1 c2 dbg =
785+
bind "divisor" c2 (fun c2 ->
786+
bind "dividend" c1 (fun c1 ->
787+
mod_int ?dividend_cannot_be_min_int c1 c2 dbg))
794788

795789
(* Bool *)
796790

@@ -1985,8 +1979,9 @@ let low_bits ~bits ~(dbg : Debuginfo.t) e =
19851979
| bits -> Misc.fatal_errorf "low_bits not implemented for %d bits" bits
19861980

19871981
let ignore_low_bits ~bits ~dbg:(_ : Debuginfo.t) e =
1988-
assert (0 <= bits && bits <= arch_bits);
1989-
if bits = 0 then e else ignore_low_bit_int e
1982+
if bits = 1
1983+
then ignore_low_bit_int e
1984+
else Misc.fatal_error "ignore_low_bits expected bits=1 for now"
19901985

19911986
let and_int e1 e2 dbg =
19921987
let is_mask32 = function
@@ -3465,11 +3460,21 @@ let mul_int_caml arg1 arg2 dbg =
34653460
incr_int (mul_int (untag_int c1 dbg) (decr_int c2 dbg) dbg) dbg
34663461
| c1, c2 -> incr_int (mul_int (decr_int c1 dbg) (untag_int c2 dbg) dbg) dbg
34673462

3468-
let div_int_caml is_safe arg1 arg2 dbg =
3469-
tag_int (div_int (untag_int arg1 dbg) (untag_int arg2 dbg) is_safe dbg) dbg
3470-
3471-
let mod_int_caml is_safe arg1 arg2 dbg =
3472-
tag_int (mod_int (untag_int arg1 dbg) (untag_int arg2 dbg) is_safe dbg) dbg
3463+
(* Since caml integers are tagged, we know that they when they're untagged, they
3464+
can't be [Nativeint.min_int] *)
3465+
let caml_integers_are_tagged = true
3466+
3467+
let div_int_caml arg1 arg2 dbg =
3468+
tag_int
3469+
(div_int ~dividend_cannot_be_min_int:caml_integers_are_tagged
3470+
(untag_int arg1 dbg) (untag_int arg2 dbg) dbg)
3471+
dbg
3472+
3473+
let mod_int_caml arg1 arg2 dbg =
3474+
tag_int
3475+
(mod_int ~dividend_cannot_be_min_int:caml_integers_are_tagged
3476+
(untag_int arg1 dbg) (untag_int arg2 dbg) dbg)
3477+
dbg
34733478

34743479
let and_int_caml arg1 arg2 dbg = and_int arg1 arg2 dbg
34753480

backend/cmm_helpers.mli

+18-17
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,6 @@ val lsr_int : expression -> expression -> Debuginfo.t -> expression
8282

8383
val asr_int : expression -> expression -> Debuginfo.t -> expression
8484

85-
val div_int :
86-
expression -> expression -> Lambda.is_safe -> Debuginfo.t -> expression
87-
88-
val mod_int :
89-
expression -> expression -> Lambda.is_safe -> Debuginfo.t -> expression
90-
9185
val and_int : expression -> expression -> Debuginfo.t -> expression
9286

9387
val or_int : expression -> expression -> Debuginfo.t -> expression
@@ -101,17 +95,15 @@ val tag_int : expression -> Debuginfo.t -> expression
10195
val untag_int : expression -> Debuginfo.t -> expression
10296

10397
(** Specific division operations for boxed integers *)
104-
val safe_div_bi :
98+
val div_int :
10599
?dividend_cannot_be_min_int:bool ->
106-
Lambda.is_safe ->
107100
expression ->
108101
expression ->
109102
Debuginfo.t ->
110103
expression
111104

112-
val safe_mod_bi :
105+
val mod_int :
113106
?dividend_cannot_be_min_int:bool ->
114-
Lambda.is_safe ->
115107
expression ->
116108
expression ->
117109
Debuginfo.t ->
@@ -389,8 +381,10 @@ val ignore_low_bits : bits:int -> dbg:Debuginfo.t -> expression -> expression
389381
irrelevant *)
390382
val low_bits : bits:int -> dbg:Debuginfo.t -> expression -> expression
391383

384+
(** sign-extend a given integer expression from [bits] bits to an entire register *)
392385
val sign_extend : bits:int -> dbg:Debuginfo.t -> expression -> expression
393386

387+
(** zero-extend a given integer expression from [bits] bits to an entire register *)
394388
val zero_extend : bits:int -> dbg:Debuginfo.t -> expression -> expression
395389

396390
(** Box a given integer, without sharing of constants *)
@@ -477,9 +471,9 @@ val sub_int_caml : binary_primitive
477471

478472
val mul_int_caml : binary_primitive
479473

480-
val div_int_caml : Lambda.is_safe -> binary_primitive
474+
val div_int_caml : binary_primitive
481475

482-
val mod_int_caml : Lambda.is_safe -> binary_primitive
476+
val mod_int_caml : binary_primitive
483477

484478
val and_int_caml : binary_primitive
485479

@@ -702,7 +696,10 @@ val create_ccatch :
702696
body:Cmm.expression ->
703697
Cmm.expression
704698

705-
(** Shift operations. take as first argument a tagged caml integer, and as
699+
(** Shift operations.
700+
Inputs: a tagged caml integer and an untagged machine integer.
701+
Outputs: a tagged caml integer.
702+
ake as first argument a tagged caml integer, and as
706703
second argument an untagged machine intger which is the amount to shift the
707704
first argument by. *)
708705

@@ -1181,10 +1178,14 @@ val unboxed_int64_or_nativeint_array_set :
11811178
Debuginfo.t ->
11821179
expression
11831180

1184-
(** {2 Getters and setters for unboxed int and float32 fields of mixed
1185-
blocks} [immediate_or_pointer] is not needed as the layout is implied from the name,
1186-
and [initialization_or_assignment] is not needed as unboxed ints can always be
1187-
assigned without caml_modify (etc.). *)
1181+
(** {2 Getters and setters for unboxed fields of mixed blocks}
1182+
1183+
The first argument is the heap block to modify a field of.
1184+
The [index_in_words] should be an untagged integer.
1185+
1186+
In constrast to [setfield] and [setfield_computed], [immediate_or_pointer] is not
1187+
needed as the layout is implied from the name, and [initialization_or_assignment] is
1188+
not needed as unboxed ints can always be assigned without caml_modify (etc.). *)
11881189

11891190
val get_field_unboxed :
11901191
dbg:Debuginfo.t ->

0 commit comments

Comments
 (0)