Skip to content

Commit c9d7aa6

Browse files
committedJan 2, 2025·
cleaned up div/mod
1 parent 4d9f427 commit c9d7aa6

File tree

1 file changed

+99
-73
lines changed

1 file changed

+99
-73
lines changed
 

‎backend/cmm_helpers.ml

+99-73
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 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
647674
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
684+
then
685+
let l = Misc.log2_nativeint n in
648686
(* Algorithm:
649687
650688
t = shift-right-signed(c1, l - 1)
@@ -661,11 +699,8 @@ let rec div_int c1 c2 dbg =
661699
add_int c1 t dbg);
662700
Cconst_int (l, dbg) ],
663701
dbg )
664-
else if n < 0
665-
then
666-
sub_int (Cconst_int (0, dbg)) (div_int c1 (Cconst_int (-n, dbg)) dbg) dbg
667702
else
668-
let m, p = divimm_parameters (Nativeint.of_int n) in
703+
let m, p = divimm_parameters n in
669704
(* Algorithm:
670705
671706
t = multiply-high-signed(c1, m) if m < 0,
@@ -685,18 +720,36 @@ let rec div_int c1 c2 dbg =
685720
if p > 0 then Cop (Casr, [t; Cconst_int (p, dbg)], dbg) else t
686721
in
687722
add_int t (lsr_int c1 (Cconst_int (Nativeint.size - 1, dbg)) dbg) dbg)
688-
| c1, c2 -> Cop (Cdivi, [c1; c2], dbg)
723+
| _, _ ->
724+
make_safe_divmod ?dividend_cannot_be_min_int ~if_divisor_is_negative_one
725+
Cdivi c1 c2 ~dbg
689726

690-
let mod_int c1 c2 dbg =
691-
match c1, c2 with
692-
| c1, Cconst_int (0, _) ->
693-
Csequence (c1, raise_symbol dbg "caml_exn_Division_by_zero")
694-
| c1, Cconst_int ((1 | -1), _) -> Csequence (c1, Cconst_int (0, dbg))
695-
| Cconst_int (n1, _), Cconst_int (n2, _) -> Cconst_int (n1 mod n2, dbg)
696-
| c1, (Cconst_int (n, _) as c2) when n <> min_int ->
697-
let l = Misc.log2 n in
698-
if n = 1 lsl l
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
699740
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
751+
then
752+
let l = Misc.log2_nativeint n in
700753
(* Algorithm:
701754
702755
t = shift-right-signed(c1, l - 1)
@@ -713,62 +766,25 @@ let mod_int c1 c2 dbg =
713766
let t = asr_int c1 (Cconst_int (l - 1, dbg)) dbg in
714767
let t = lsr_int t (Cconst_int (Nativeint.size - l, dbg)) dbg in
715768
let t = add_int c1 t dbg in
716-
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
717770
sub_int c1 t dbg)
718771
else
719772
bind "dividend" c1 (fun c1 ->
720773
sub_int c1 (mul_int (div_int c1 c2 dbg) c2 dbg) dbg)
721-
| c1, c2 ->
722-
(* Flambda already generates the tests for zero*)
723-
Cop (Cmodi, [c1; c2], dbg)
724-
725-
(* Division or modulo on boxed integers. The overflow case min_int / -1 can
726-
occur, in which case we force x / -1 = -x and x mod -1 = 0. (PR#5513). *)
727-
728-
(* Division or modulo on boxed integers. The overflow case min_int / -1 can
729-
occur, in which case we force x / -1 = -x and x mod -1 = 0. (PR#5513). *)
730-
731-
let safe_divmod_bi mkop mkm1 ?(dividend_cannot_be_min_int = false) dividend
732-
divisor dbg =
733-
let is_different_from x = function
734-
| Cconst_int (n, _) -> Nativeint.of_int n <> x
735-
| Cconst_natint (n, _) -> n <> x
736-
| _ -> false
737-
in
738-
bind "divisor" divisor (fun divisor ->
739-
bind "dividend" dividend (fun dividend ->
740-
let c = mkop dividend divisor dbg in
741-
if not Arch.division_crashes_on_overflow
742-
then c
743-
else
744-
let dividend_cannot_be_min_int =
745-
dividend_cannot_be_min_int
746-
|| is_different_from Nativeint.min_int dividend
747-
in
748-
let divisor_cannot_be_negative_one =
749-
is_different_from (-1n) divisor
750-
in
751-
if dividend_cannot_be_min_int || divisor_cannot_be_negative_one
752-
then c
753-
else
754-
Cifthenelse
755-
( Cop (Ccmpi Cne, [divisor; Cconst_int (-1, dbg)], dbg),
756-
dbg,
757-
c,
758-
dbg,
759-
mkm1 dividend dbg,
760-
dbg,
761-
Any )))
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
762778

763779
let div_int ?dividend_cannot_be_min_int c1 c2 dbg =
764-
safe_divmod_bi ?dividend_cannot_be_min_int div_int
765-
(fun c1 dbg -> Cop (Csubi, [Cconst_int (0, dbg); c1], dbg))
766-
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))
767783

768784
let mod_int ?dividend_cannot_be_min_int c1 c2 dbg =
769-
safe_divmod_bi ?dividend_cannot_be_min_int mod_int
770-
(fun _ dbg -> Cconst_int (0, dbg))
771-
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))
772788

773789
(* Bool *)
774790

@@ -3544,11 +3560,21 @@ let mul_int_caml arg1 arg2 dbg =
35443560
incr_int (mul_int (untag_int c1 dbg) (decr_int c2 dbg) dbg) dbg
35453561
| c1, c2 -> incr_int (mul_int (decr_int c1 dbg) (untag_int c2 dbg) dbg) dbg
35463562

3563+
(* Since caml integers are tagged, we know that they when they're untagged, they
3564+
can't be [Nativeint.min_int] *)
3565+
let caml_integers_are_tagged = true
3566+
35473567
let div_int_caml arg1 arg2 dbg =
3548-
tag_int (div_int (untag_int arg1 dbg) (untag_int arg2 dbg) dbg) dbg
3568+
tag_int
3569+
(div_int ~dividend_cannot_be_min_int:caml_integers_are_tagged
3570+
(untag_int arg1 dbg) (untag_int arg2 dbg) dbg)
3571+
dbg
35493572

35503573
let mod_int_caml arg1 arg2 dbg =
3551-
tag_int (mod_int (untag_int arg1 dbg) (untag_int arg2 dbg) dbg) dbg
3574+
tag_int
3575+
(mod_int ~dividend_cannot_be_min_int:caml_integers_are_tagged
3576+
(untag_int arg1 dbg) (untag_int arg2 dbg) dbg)
3577+
dbg
35523578

35533579
let and_int_caml arg1 arg2 dbg = and_int arg1 arg2 dbg
35543580

0 commit comments

Comments
 (0)
Please sign in to comment.