(**************************************************************************)
(*                                                                        *)
(*                                 OCaml                                  *)
(*                                                                        *)
(*                       Pierre Chambart, OCamlPro                        *)
(*           Mark Shinwell and Leo White, Jane Street Europe              *)
(*                                                                        *)
(*   Copyright 2013--2016 OCamlPro SAS                                    *)
(*   Copyright 2014--2016 Jane Street Group LLC                           *)
(*                                                                        *)
(*   All rights reserved.  This file is distributed under the terms of    *)
(*   the GNU Lesser General Public License version 2.1, with the          *)
(*   special exception on linking described in the file LICENSE.          *)
(*                                                                        *)
(**************************************************************************)

[@@@ocaml.warning "+a-4-9-30-40-41-42-66"]
open! Int_replace_polymorphic_compare

module S = Simplify_common

(* Simplification of operations on boxed integers (nativeint, Int32, Int64). *)
module Simplify_boxed_integer_operator (I : sig
  type t
  val kind : Lambda.boxed_integer
  val zero : t
  val add : t -> t -> t
  val sub : t -> t -> t
  val mul : t -> t -> t
  val div : t -> t -> t
  val rem : t -> t -> t
  val logand : t -> t -> t
  val logor : t -> t -> t
  val logxor : t -> t -> t
  val shift_left : t -> int -> t
  val shift_right : t -> int -> t
  val shift_right_logical : t -> int -> t
  val to_int : t -> int
  val to_int32 : t -> Int32.t
  val to_int64 : t -> Int64.t
  val neg : t -> t
  val swap : t -> t
  val compare : t -> t -> int
end) : Simplify_boxed_integer_ops_intf.S with type t := I.t = struct
  module A = Simple_value_approx
  module C = Inlining_cost

  let equal_kind = Lambda.equal_boxed_integer

  let simplify_unop (p : Clambda_primitives.primitive)
        (kind : I.t A.boxed_int) expr (n : I.t) =
    let eval op = S.const_boxed_int_expr expr kind (op n) in
    let eval_conv kind op = S.const_boxed_int_expr expr kind (op n) in
    let eval_unboxed op = S.const_int_expr expr (op n) in
    match p with
    | Pintofbint kind when equal_kind kind I.kind -> eval_unboxed I.to_int
    | Pcvtbint (kind, Pint32) when equal_kind kind I.kind ->
      eval_conv A.Int32 I.to_int32
    | Pcvtbint (kind, Pint64) when equal_kind kind I.kind ->
      eval_conv A.Int64 I.to_int64
    | Pnegbint kind when equal_kind kind I.kind -> eval I.neg
    | Pbbswap kind when equal_kind kind I.kind -> eval I.swap
    | _ -> expr, A.value_unknown Other, C.Benefit.zero

  let simplify_binop (p : Clambda_primitives.primitive)
        (kind : I.t A.boxed_int) expr (n1 : I.t) (n2 : I.t) =
    let eval op = S.const_boxed_int_expr expr kind (op n1 n2) in
    let non_zero n = (I.compare I.zero n) <> 0 in
    match p with
    | Paddbint kind when equal_kind kind I.kind -> eval I.add
    | Psubbint kind when equal_kind kind I.kind -> eval I.sub
    | Pmulbint kind when equal_kind kind I.kind -> eval I.mul
    | Pdivbint {size=kind} when equal_kind kind I.kind && non_zero n2 ->
      eval I.div
    | Pmodbint {size=kind} when equal_kind kind I.kind && non_zero n2 ->
      eval I.rem
    | Pandbint kind when equal_kind kind I.kind -> eval I.logand
    | Porbint kind when equal_kind kind I.kind -> eval I.logor
    | Pxorbint kind when equal_kind kind I.kind -> eval I.logxor
    | Pbintcomp (kind, c) when equal_kind kind I.kind ->
      S.const_integer_comparison_expr expr c n1 n2
    | Pcompare_bints kind when equal_kind kind I.kind ->
      S.const_int_expr expr (I.compare n1 n2)
    | _ -> expr, A.value_unknown Other, C.Benefit.zero

  let simplify_binop_int (p : Clambda_primitives.primitive)
        (kind : I.t A.boxed_int) expr (n1 : I.t) (n2 : int) ~size_int =
    let eval op = S.const_boxed_int_expr expr kind (op n1 n2) in
    let precond = 0 <= n2 && n2 < 8 * size_int in
    match p with
    | Plslbint kind when equal_kind kind I.kind && precond -> eval I.shift_left
    | Plsrbint kind when equal_kind kind I.kind && precond ->
      eval I.shift_right_logical
    | Pasrbint kind when equal_kind kind I.kind && precond -> eval I.shift_right
    | _ -> expr, A.value_unknown Other, C.Benefit.zero
end

module Simplify_boxed_nativeint = Simplify_boxed_integer_operator (struct
  include Nativeint
  let to_int64 = Int64.of_nativeint
  let swap = S.swapnative
  let kind = Lambda.Pnativeint
end)

module Simplify_boxed_int32 = Simplify_boxed_integer_operator (struct
  include Int32
  let to_int32 i = i
  let to_int64 = Int64.of_int32
  let swap = S.swap32
  let kind = Lambda.Pint32
end)

module Simplify_boxed_int64 = Simplify_boxed_integer_operator (struct
  include Int64
  let to_int64 i = i
  let swap = S.swap64
  let kind = Lambda.Pint64
end)
