(**************************************************************************) (* *) (* 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 A = Simple_value_approx module C = Inlining_cost module I = Simplify_boxed_integer_ops module S = Simplify_common let phys_equal (approxs:A.t list) = match approxs with | [] | [_] | _ :: _ :: _ :: _ -> Misc.fatal_error "wrong number of arguments for equality" | [a1; a2] -> (* N.B. The following would be incorrect if the variables are not bound in the environment: match a1.var, a2.var with | Some v1, Some v2 when Variable.equal v1 v2 -> true | _ -> ... *) match a1.symbol, a2.symbol with | Some (s1, None), Some (s2, None) -> Symbol.equal s1 s2 | Some (s1, Some f1), Some (s2, Some f2) -> Symbol.equal s1 s2 && f1 = f2 | _ -> false let is_known_to_be_some_kind_of_int (arg:A.descr) = match arg with | Value_int _ | Value_char _ -> true | Value_block (_, _) | Value_float _ | Value_set_of_closures _ | Value_closure _ | Value_string _ | Value_float_array _ | A.Value_boxed_int _ | Value_unknown _ | Value_extern _ | Value_symbol _ | Value_unresolved _ | Value_bottom -> false let is_known_to_be_some_kind_of_block (arg:A.descr) = match arg with | Value_block _ | Value_float _ | Value_float_array _ | A.Value_boxed_int _ | Value_closure _ | Value_string _ -> true | Value_set_of_closures _ | Value_int _ | Value_char _ | Value_unknown _ | Value_extern _ | Value_symbol _ | Value_unresolved _ | Value_bottom -> false let rec structurally_different (arg1:A.t) (arg2:A.t) = match arg1.descr, arg2.descr with | (Value_int n1), (Value_int n2) when n1 <> n2 -> true | Value_block (tag1, fields1), Value_block (tag2, fields2) -> not (Tag.equal tag1 tag2) || (Array.length fields1 <> Array.length fields2) || Misc.Stdlib.Array.exists2 structurally_different fields1 fields2 | descr1, descr2 -> (* This is not very precise as this won't allow to distinguish blocks from strings for instance. This can be improved if it is deemed valuable. *) (is_known_to_be_some_kind_of_int descr1 && is_known_to_be_some_kind_of_block descr2) || (is_known_to_be_some_kind_of_block descr1 && is_known_to_be_some_kind_of_int descr2) let phys_different (approxs:A.t list) = match approxs with | [] | [_] | _ :: _ :: _ :: _ -> Misc.fatal_error "wrong number of arguments for equality" | [a1; a2] -> structurally_different a1 a2 let is_empty = function | [] -> true | _ :: _ -> false let is_pisint = function | Clambda_primitives.Pisint -> true | _ -> false let is_pstring_length = function | Clambda_primitives.Pstringlength -> true | _ -> false let is_pbytes_length = function | Clambda_primitives.Pbyteslength -> true | _ -> false let is_pstringrefs = function | Clambda_primitives.Pstringrefs -> true | _ -> false let is_pbytesrefs = function | Clambda_primitives.Pbytesrefs -> true | _ -> false let primitive (p : Clambda_primitives.primitive) (args, approxs) expr dbg ~size_int : Flambda.named * A.t * Inlining_cost.Benefit.t = let fpc = !Clflags.float_const_prop in match p with | Pmakeblock(tag_int, Asttypes.Immutable, shape) -> let tag = Tag.create_exn tag_int in let shape = match shape with | None -> List.map (fun _ -> Lambda.Pgenval) args | Some shape -> shape in let approxs = List.map2 A.augment_with_kind approxs shape in let shape = List.map2 A.augment_kind_with_approx approxs shape in Prim (Pmakeblock(tag_int, Asttypes.Immutable, Some shape), args, dbg), A.value_block tag (Array.of_list approxs), C.Benefit.zero | Praise _ -> expr, A.value_bottom, C.Benefit.zero | Pmakearray(_, _) when is_empty approxs -> Prim (Pmakeblock(0, Asttypes.Immutable, Some []), [], dbg), A.value_block (Tag.create_exn 0) [||], C.Benefit.zero | Pmakearray (Pfloatarray, Mutable) -> let approx = A.value_mutable_float_array ~size:(List.length args) in expr, approx, C.Benefit.zero | Pmakearray (Pfloatarray, Immutable) -> let approx = A.value_immutable_float_array (Array.of_list approxs) in expr, approx, C.Benefit.zero | Pintcomp Ceq when phys_equal approxs -> S.const_bool_expr expr true | Pintcomp Cne when phys_equal approxs -> S.const_bool_expr expr false (* N.B. Having [not (phys_equal approxs)] would not on its own tell us anything about whether the two values concerned are unequal. To judge that, it would be necessary to prove that the approximations are different, which would in turn entail them being completely known. It may seem that in the case where we have two approximations each annotated with a symbol that we should be able to judge inequality even if part of the approximation description(s) are unknown. This is unfortunately not the case. Here is an example: let a = f 1 let b = f 1 let c = a, a let d = b, b If [Share_constants] is run before [f] is completely inlined (assuming [f] always generates the same result; effects of [f] aren't in fact relevant) then [c] and [d] will not be shared. However if [f] is inlined later, [a] and [b] could be shared and thus [c] and [d] could be too. As such, any intermediate non-aliasing judgement would be invalid. *) | Pintcomp Ceq when phys_different approxs -> S.const_bool_expr expr false | Pintcomp Cne when phys_different approxs -> S.const_bool_expr expr true (* If two values are structurally different we are certain they can never be shared*) | _ -> match A.descrs approxs with | [Value_int x] -> begin match p with | Pnot -> S.const_bool_expr expr (x = 0) | Pnegint -> S.const_int_expr expr (-x) | Pbswap16 -> S.const_int_expr expr (S.swap16 x) | Pisint -> S.const_bool_expr expr true | Poffsetint y -> S.const_int_expr expr (x + y) | Pfloatofint when fpc -> S.const_float_expr expr (float_of_int x) | Pbintofint Pnativeint -> S.const_boxed_int_expr expr Nativeint (Nativeint.of_int x) | Pbintofint Pint32 -> S.const_boxed_int_expr expr Int32 (Int32.of_int x) | Pbintofint Pint64 -> S.const_boxed_int_expr expr Int64 (Int64.of_int x) | _ -> expr, A.value_unknown Other, C.Benefit.zero end | [Value_int x; Value_int y] -> let shift_precond = 0 <= y && y < 8 * size_int in begin match p with | Paddint -> S.const_int_expr expr (x + y) | Psubint -> S.const_int_expr expr (x - y) | Pmulint -> S.const_int_expr expr (x * y) | Pdivint _ when y <> 0 -> S.const_int_expr expr (x / y) | Pmodint _ when y <> 0 -> S.const_int_expr expr (x mod y) | Pandint -> S.const_int_expr expr (x land y) | Porint -> S.const_int_expr expr (x lor y) | Pxorint -> S.const_int_expr expr (x lxor y) | Plslint when shift_precond -> S.const_int_expr expr (x lsl y) | Plsrint when shift_precond -> S.const_int_expr expr (x lsr y) | Pasrint when shift_precond -> S.const_int_expr expr (x asr y) | Pintcomp cmp -> S.const_integer_comparison_expr expr cmp x y | Pcompare_ints -> S.const_int_expr expr (compare x y) | Pisout -> S.const_bool_expr expr (y > x || y < 0) | _ -> expr, A.value_unknown Other, C.Benefit.zero end | [Value_char x; Value_char y] -> begin match p with | Pintcomp cmp -> S.const_integer_comparison_expr expr cmp x y | Pcompare_ints -> S.const_int_expr expr (Char.compare x y) | _ -> expr, A.value_unknown Other, C.Benefit.zero end | [Value_float (Some x)] when fpc -> begin match p with | Pintoffloat -> S.const_int_expr expr (int_of_float x) | Pnegfloat -> S.const_float_expr expr (-. x) | Pabsfloat -> S.const_float_expr expr (abs_float x) | _ -> expr, A.value_unknown Other, C.Benefit.zero end | [Value_float (Some n1); Value_float (Some n2)] when fpc -> begin match p with | Paddfloat -> S.const_float_expr expr (n1 +. n2) | Psubfloat -> S.const_float_expr expr (n1 -. n2) | Pmulfloat -> S.const_float_expr expr (n1 *. n2) | Pdivfloat -> S.const_float_expr expr (n1 /. n2) | Pfloatcomp c -> S.const_float_comparison_expr expr c n1 n2 | Pcompare_floats -> S.const_int_expr expr (Float.compare n1 n2) | _ -> expr, A.value_unknown Other, C.Benefit.zero end | [A.Value_boxed_int(A.Nativeint, n)] -> I.Simplify_boxed_nativeint.simplify_unop p Nativeint expr n | [A.Value_boxed_int(A.Int32, n)] -> I.Simplify_boxed_int32.simplify_unop p Int32 expr n | [A.Value_boxed_int(A.Int64, n)] -> I.Simplify_boxed_int64.simplify_unop p Int64 expr n | [A.Value_boxed_int(A.Nativeint, n1); A.Value_boxed_int(A.Nativeint, n2)] -> I.Simplify_boxed_nativeint.simplify_binop p Nativeint expr n1 n2 | [A.Value_boxed_int(A.Int32, n1); A.Value_boxed_int(A.Int32, n2)] -> I.Simplify_boxed_int32.simplify_binop p Int32 expr n1 n2 | [A.Value_boxed_int(A.Int64, n1); A.Value_boxed_int(A.Int64, n2)] -> I.Simplify_boxed_int64.simplify_binop p Int64 expr n1 n2 | [A.Value_boxed_int(A.Nativeint, n1); Value_int n2] -> I.Simplify_boxed_nativeint.simplify_binop_int p Nativeint expr n1 n2 ~size_int | [A.Value_boxed_int(A.Int32, n1); Value_int n2] -> I.Simplify_boxed_int32.simplify_binop_int p Int32 expr n1 n2 ~size_int | [A.Value_boxed_int(A.Int64, n1); Value_int n2] -> I.Simplify_boxed_int64.simplify_binop_int p Int64 expr n1 n2 ~size_int | [Value_block _] when is_pisint p -> S.const_bool_expr expr false | [Value_string { size }] when (is_pstring_length p || is_pbytes_length p) -> S.const_int_expr expr size | [Value_string { size; contents = Some s }; (Value_int x)] when x >= 0 && x < size -> begin match p with | Pstringrefu | Pstringrefs | Pbytesrefu | Pbytesrefs -> S.const_char_expr (Prim(Pstringrefu, args, dbg)) s.[x] | _ -> expr, A.value_unknown Other, C.Benefit.zero end | [Value_string { size; contents = None }; (Value_int x)] when x >= 0 && x < size && is_pstringrefs p -> Flambda.Prim (Pstringrefu, args, dbg), A.value_unknown Other, (* we improved it, but there is no way to account for that: *) C.Benefit.zero | [Value_string { size; contents = None }; (Value_int x)] when x >= 0 && x < size && is_pbytesrefs p -> Flambda.Prim (Pbytesrefu, args, dbg), A.value_unknown Other, (* we improved it, but there is no way to account for that: *) C.Benefit.zero | [Value_float_array { size; contents }] -> begin match p with | Parraylength _ -> S.const_int_expr expr size | Pfloatfield i -> begin match contents with | A.Contents a when i >= 0 && i < size -> begin match A.check_approx_for_float a.(i) with | None -> expr, a.(i), C.Benefit.zero | Some v -> S.const_float_expr expr v end | Contents _ | Unknown_or_mutable -> expr, A.value_unknown Other, C.Benefit.zero end | _ -> expr, A.value_unknown Other, C.Benefit.zero end | _ -> match Semantics_of_primitives.return_type_of_primitive p with | Float -> expr, A.value_any_float, C.Benefit.zero | Other -> expr, A.value_unknown Other, C.Benefit.zero