open Base
open Stdio
module I = Ocaml_intrinsics_kernel.Conditional

let%expect_test "csel int" =
  let inputs = [ 0; 1; 4; 6; 5 ] in
  List.iter inputs ~f:(fun a ->
    let expect = if a % 2 = 0 then a else a + 1 in
    let actual = I.select_value (a % 2 = 0) a (a + 1) in
    printf "%d %d\n" expect actual);
  [%expect {|
    0 0
    2 2
    4 4
    6 6
    6 6
    |}]
;;

let%expect_test "csel max int value" =
  let inputs = [ 0, 1; 4, 5 ] in
  List.iter inputs ~f:(fun (a, b) ->
    let expect = if a > b then a else b in
    let actual = I.select_value (a > b) a b in
    printf "%d %d\n" expect actual);
  [%expect {|
    1 1
    5 5
    |}]
;;

let%expect_test "csel max float value" =
  let inputs = [ 0.5, Float.neg_infinity; 0.0, 0.1; Float.nan, 5.0 ] in
  List.iter inputs ~f:(fun (a, b) ->
    let expect = if Float.(a > b) then a else b in
    let actual = I.select_value Float.(a > b) a b in
    printf "%f %f\n" expect actual);
  [%expect {|
    0.500000 0.500000
    0.100000 0.100000
    5.000000 5.000000
    |}]
;;

let%expect_test "csel max int untagged" =
  let inputs = [ 0, 1; 4, 5 ] in
  List.iter inputs ~f:(fun (a, b) ->
    let expect = if a > b then a else b in
    let actual = I.select_int (a > b) a b in
    printf "%d %d\n" expect actual);
  [%expect {|
    1 1
    5 5
    |}]
;;

let%expect_test "csel max int64 unboxed" =
  let inputs = [ 0L, 1L; 4L, 5L; Int64.max_value, Int64.min_value ] in
  List.iter inputs ~f:(fun (a, b) ->
    let expect = if Int64.(a > b) then a else b in
    let actual = I.select_int64 Int64.(a > b) a b in
    printf "%Ld %Ld\n" expect actual);
  [%expect {|
    1 1
    5 5
    9223372036854775807 9223372036854775807
    |}]
;;

let%expect_test "csel max int32 unboxed" =
  let inputs = [ 0l, 1l; 4l, 5l; Int32.max_value, Int32.min_value ] in
  List.iter inputs ~f:(fun (a, b) ->
    let expect = if Int32.(a > b) then a else b in
    let actual = I.select_int32 Int32.(a > b) a b in
    printf "%ld %ld\n" expect actual);
  [%expect {|
    1 1
    5 5
    2147483647 2147483647
    |}]
;;

[%%import "config.h"]
[%%ifdef JSC_ARCH_SIXTYFOUR]

let%expect_test "csel max nativeint unboxed" =
  let inputs = [ 0n, 1n; 4n, 5n; Nativeint.max_value, Nativeint.min_value ] in
  List.iter inputs ~f:(fun (a, b) ->
    let expect = if Nativeint.(a > b) then a else b in
    let actual = I.select_nativeint Nativeint.(a > b) a b in
    printf "%nd %nd\n" expect actual);
  [%expect {|
    1 1
    5 5
    9223372036854775807 9223372036854775807
    |}]
;;

[%%else]

let%expect_test "csel max nativeint unboxed" =
  let inputs = [ 0n, 1n; 4n, 5n; Nativeint.max_value, Nativeint.min_value ] in
  List.iter inputs ~f:(fun (a, b) ->
    let expect = if Nativeint.(a > b) then a else b in
    let actual = I.select_nativeint Nativeint.(a > b) a b in
    printf "%nd %nd\n" expect actual);
  [%expect {|
    1 1
    5 5
    2147483647 2147483647
    |}]
;;

[%%endif]

let%expect_test "csel sideffects" =
  let inputs = [ 0, 1; 5, 4 ] in
  List.iter inputs ~f:(fun (a, b) ->
    let expect =
      if a > b
      then (
        printf "hello 0\n";
        a)
      else (
        printf "world 0\n";
        b)
    in
    let actual =
      I.select_value
        (a > b)
        (printf "hello 1\n";
         a)
        (printf "world 1\n";
         b)
    in
    printf "%d %d\n" expect actual);
  [%expect
    {|
    world 0
    world 1
    hello 1
    1 1
    hello 0
    world 1
    hello 1
    5 5
    |}]
;;

let%expect_test "min extra moves" =
  (* Currently [min] emits extra moves:
   *
   * actual:
   *
   * camlT__min_266:
   *   movq	%rax, %rdi
   *   movq	%rbx, %rax
   *   cmpq	%rax, %rdi
   *   cmovl	%rdi, %rax
   *   ret
   *
   * [min2] is
   *
   * camlT__min2_273:
   *    cmpq	%rax, %rbx
   *    cmovl	%rbx, %rax
   * 	ret
   * ret *)
  let[@inline never] min (x : int) (y : int) = I.select_value (x < y) x y in
  let[@inline never] min2 (x : int) (y : int) = I.select_value (y < x) y x in
  let inputs = [ 0, 1; 5, 4 ] in
  List.iter inputs ~f:(fun (a, b) ->
    printf "%d " (min a b);
    printf "%d\n" (min2 a b));
  [%expect {|
    0 0
    4 4
    |}]
;;

let%expect_test "float deadcode" =
  (* Currently [nop_float] emits extra loads, because there is no dead code elimination
   *    after register allocation:
   *
   *    camlT__nop_float_292:
   *      movsd	(%rbx), %xmm0
   *      movsd	(%rax), %xmm1
   *      ret *)
  let[@inline never] nop_float (x : float) (y : float) : float =
    I.select_value Float.(x > y) x x
  in
  let inputs =
    [ 0.5, Float.neg_infinity; 0.0, 0.1; Float.nan, 5.0; Float.infinity, -0.0 ]
  in
  List.iter inputs ~f:(fun (a, b) -> printf "%f " (nop_float a b));
  [%expect {| 0.500000 0.000000 nan inf |}]
;;
