forked from ocaml/ocaml
-
Notifications
You must be signed in to change notification settings - Fork 0
/
simplify_primitives.ml
302 lines (284 loc) · 12.8 KB
/
simplify_primitives.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
(**************************************************************************)
(* *)
(* 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 _ | Value_constptr _ -> 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_constptr _
| 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_constptr n1), (Value_int n2 | Value_constptr 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 = a, a
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)
| 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_constptr x); (Value_int y | Value_constptr 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
| 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
| _ -> expr, A.value_unknown Other, C.Benefit.zero
end
| [Value_constptr x] ->
begin match p with
(* [Pidentity] should probably never appear, but is here for
completeness. *)
| Pnot -> S.const_bool_expr expr (x = 0)
| Pisint -> S.const_bool_expr expr true
| Poffsetint y -> S.const_ptr_expr expr (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
| _ -> 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 | Value_constptr 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 | Value_constptr 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 | Value_constptr 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