-
Notifications
You must be signed in to change notification settings - Fork 63
/
Copy pathSimplifyMerge.ml
346 lines (297 loc) · 10.4 KB
/
SimplifyMerge.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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
(* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. *)
(* Licensed under the Apache 2.0 and MIT Licenses. *)
(** Merging variables together to avoid un-necessary let-bindings *)
open Ast
open DeBruijn
open PrintAst
module S = Set.Make(Atom)
module M = Map.Make(Atom)
let debug = false
type env = (Atom.t * (typ * bool * ident)) list
let keys (e: env): S.t =
List.fold_left (fun acc (k, _) -> S.add k acc) S.empty e
let extend x y (env: env) =
(x, y) :: env
let find =
List.assoc
(* Print elements of x based on atom -> ident mapping in env *)
let p (env: env) (x: S.t) =
let l = List.map (fun x ->
try let _, _, i = find x env in i with Not_found -> "?"
) (S.elements x) in
String.concat ", " l
(* Only meaningful when all binders are open *)
let replace_atom (a: Atom.t) (a': Atom.t) (e: expr) =
(object
inherit [_] map
method! visit_EOpen _ i' a'' =
let a = if a = a'' then a' else a'' in
EOpen (i', a)
end)#visit_expr_w () e
(* This function receives:
* - env, an associative list of all the variables in scope to their types
* - the set U of variables from env that are no longer dead because they
* were used and assigned to at some point
* - an expression e to rewrite
* it returns:
* - a rewritten term e'
* - the set D of variables from env that were dead in e pre-rewriting
* - an updated set U'.
* Therefore, the set of dead variables in e' (rewritten) is D - U'.
* Therefore, the set of candidates for reusing a variable slot is env - U'. *)
let rec merge' (env: env) (u: S.t) (e: expr): S.t * S.t * expr =
let w = with_type e.typ in
match e.node with
| ETApp _
| ETuple _
| EMatch _
| ECons _
| ESequence _
| EFun _
| EBound _ ->
failwith "impossible (simplified before)"
| EQualified _
| EConstant _
| EUnit
| EBool _
| EString _
| EAny
| EOp _
| EPolyComp _
| EPushFrame
| EPopFrame
| EEnum _
| EStandaloneComment _
| EAbort _
| EBufNull ->
keys env, u, e
| EOpen (_, a) ->
S.remove a (keys env), u, e
| ECast (e, t) ->
let d, u, e = merge env u e in
d, u, w (ECast (e, t))
| EIgnore e ->
let d, u, e = merge env u e in
d, u, w (EIgnore e)
| EApp (e, es) ->
let d, u, e = merge env u e in
let d, u, es = merge_list env d u es in
d, u, w (EApp (e, es))
| ELet (b, e1, e2) ->
(* Following the reverse order of control-flow for u *)
let b, e2 = open_binder b e2 in
let has_storage t e1 =
match t, e1.node with
| TArray _, _
| _, EBufCreate (Stack, _, _)
| _, EBufCreateL (Stack, _) ->
true
| _ ->
false
in
let env' = extend b.node.atom (b.typ, has_storage b.typ e1, b.node.name) env in
let d2, u, e2 = merge env' u e2 in
let candidate =
match e1.node with
| EBufCreateL _ | EBufCreate (_, _, _) ->
(* I guess we could allow heap-allocated buffers to reuse variables,
* but CStarToC11 doesn't allow it (why?). *)
None
| _ ->
let rec common_prefix i s1 s2 =
if String.length s1 = i || String.length s2 = i then
i
else if s1.[i] = s2.[i] then
common_prefix (i + 1) s1 s2
else
i
in
let fits x t has_storage i =
(* Compatible type *)
t = b.typ &&
(* Hasn't been used further down the control-flow *)
not (S.mem x u) &&
(* No self-assignments *)
not (Atom.equal x b.node.atom) &&
(* Must be dead *)
S.mem x d2 &&
(* Ignore sequence let-bindings *)
not (t = TUnit) &&
(* Array types are not assignable *)
not has_storage &&
(* If in prefix mode, must find a common prefix *)
(Options.(!merge_variables <> Prefix) || common_prefix 0 i b.node.name > 0)
in
List.find_map (fun (x, (t, h, i)) -> if fits x t h i then Some (x, i) else None) env
in
(* For later *)
let d1, u_final, e1 = merge env u e1 in
let d_final = S.inter d1 d2 in
(* Note: thanks to the fact we encode the environment as a list, we can
* apply a heuristic, which is to use the nearest binding. *)
begin match candidate with
| Some (y, i) ->
(* We are now rewriting:
* let x: t = e1 in e2
* into:
* let _ = y := e1 in // will be made a sequence later
* e2 *)
let e2 = replace_atom b.node.atom y e2 in
if debug then
KPrint.bprintf "In the let-binding for %s:\n\
Dead in e2: %s\n\
Used in e2: %s\n\
Chose: %s\n\
e2 now: %a\n\n" b.node.name (p env d2) (p env u) i pexpr e2;
let self_assignment =
match e1.node with
| EOpen (_, x1) when Atom.equal x1 y ->
true
| _ ->
false
in
let e =
if e1.node = EAny then
e2.node
else if self_assignment then
e2.node
else
ELet (Helpers.sequence_binding (),
with_type TUnit (EAssign (
with_type b.typ (EOpen (i, y)),
e1)),
e2)
in
d_final, S.add y u_final, w e
| None ->
if debug then
KPrint.bprintf "In the let-binding for %s:\n\
Dead in e2: %s\n\
Used in e2: %s\n\
No candidate\n\n" b.node.name (p env d2) (p env u);
let mut = if S.mem b.node.atom u_final then true else b.node.mut in
let b = { b with node = { b.node with mut }} in
let e = ELet (b, e1, close_binder b e2) in
d_final, u_final, w e
end
| EIfThenElse (e1, e2, e3) ->
let d2, u2, e2 = merge env u e2 in
let d3, u3, e3 = merge env u e3 in
let d1, u, e1 = merge env (S.union u2 u3) e1 in
S.inter (S.inter d1 d2) d3, u, w (EIfThenElse (e1, e2, e3))
| EAssign (e1, e2) ->
let d2, u, e2 = merge env u e2 in
let d1, u, e1 = merge env u e1 in
S.inter d1 d2, u, w (EAssign (e1, e2))
| EBufRead (e1, e2) ->
let d2, u, e2 = merge env u e2 in
let d1, u, e1 = merge env u e1 in
S.inter d1 d2, u, w (EBufRead (e1, e2))
| EBufCreate (l, e1, e2) ->
let d2, u, e2 = merge env u e2 in
let d1, u, e1 = merge env u e1 in
S.inter d1 d2, u, w (EBufCreate (l, e1, e2))
| EBufCreateL (l, es) ->
let d, u, es = merge_list env (keys env) u es in
d, u, w (EBufCreateL (l, es))
| EBufWrite (e1, e2, e3) ->
let d1, u, e1 = merge env u e1 in
let d2, u, e2 = merge env u e2 in
let d3, u, e3 = merge env u e3 in
S.inter (S.inter d1 d2) d3, u, w (EBufWrite (e1, e2, e3))
| EBufSub (e1, e2) ->
let d1, u, e1 = merge env u e1 in
let d2, u, e2 = merge env u e2 in
S.inter d1 d2, u, w (EBufSub (e1, e2))
| EBufDiff (e1, e2) ->
let d1, u, e1 = merge env u e1 in
let d2, u, e2 = merge env u e2 in
S.inter d1 d2, u, w (EBufDiff (e1, e2))
| EBufBlit (e1, e2, e3, e4, e5) ->
let d1, u, e1 = merge env u e1 in
let d2, u, e2 = merge env u e2 in
let d3, u, e3 = merge env u e3 in
let d4, u, e4 = merge env u e4 in
let d5, u, e5 = merge env u e5 in
KList.reduce S.inter [ d1; d2; d3; d4; d5 ], u, w (EBufBlit (e1, e2, e3, e4, e5))
| EBufFill (e1, e2, e3) ->
let d1, u, e1 = merge env u e1 in
let d2, u, e2 = merge env u e2 in
let d3, u, e3 = merge env u e3 in
S.inter (S.inter d1 d2) d3, u, w (EBufFill (e1, e2, e3))
| EBufFree e1 ->
let d1, u, e1 = merge env u e1 in
d1, u, w (EBufFree e1)
| ESwitch (e, es) ->
let ds, us, es = List.fold_left (fun (ds, us, es) (c, e) ->
let d, u, e = merge env u e in
d :: ds, u :: us, (c, e) :: es
) ([], [], []) es in
let es = List.rev es in
let u = KList.reduce S.union us in
let d, u, e = merge env u e in
let d = KList.reduce S.inter (d :: ds) in
d, u, w (ESwitch (e, es))
| EFlat fieldexprs ->
let fs, es = List.split fieldexprs in
let d, u, es = merge_list env (keys env) u es in
let fieldexprs = List.combine fs es in
d, u, w (EFlat fieldexprs)
| EField (e, f) ->
let d, u, e = merge env u e in
d, u, w (EField (e, f))
| EWhile (e1, e2) ->
let d2, u, e2 = merge env u e2 in
let d1, u, e1 = merge env u e1 in
S.inter d1 d2, u, w (EWhile (e1, e2))
| EBreak ->
keys env, u, w EBreak
| EContinue ->
keys env, u, w EContinue
| EReturn e ->
let d, u, e = merge env u e in
d, u, w (EReturn e)
| EFor (b, e1, e2, e3, e4) ->
let binder, s = opening_binder b in
let e2 = s e2 and e3 = s e3 and e4 = s e4 in
let d4, u, e4 = merge env u e4 in
let d3, u, e3 = merge env u e3 in
let d2, u, e2 = merge env u e2 in
let d1, u, e1 = merge env u e1 in
let s = closing_binder binder in
KList.reduce S.inter [ d1; d2; d3; d4 ], u, w (EFor (b, e1, s e2, s e3, s e4))
| EAddrOf e ->
let d, u, e = merge env u e in
d, u, w (EAddrOf e)
and merge (env: env) (u: S.t) (e: expr): S.t * S.t * expr =
let d, u, e = merge' env u e in
if debug then
KPrint.bprintf "After visiting %a: D=%s\n" pexpr e (p env d);
d, u, e
and merge_list env d u es =
let d, u, es =
List.fold_left (fun (d, u, es) e ->
let d', u, e = merge env u e in
S.inter d d', u, e :: es
) (d, u, []) es
in
d, u, List.rev es
let merge_visitor = object(_)
inherit [_] map
method! visit_DFunction () cc flags n_cgs n ret name binders body =
if debug then
KPrint.bprintf "Variable merge: visiting %a\n%a\n" plid name ppexpr body;
let binders, body = open_binders binders body in
let _, _, body = merge [] S.empty body in
let body = close_binders binders body in
DFunction (cc, flags, n_cgs, n, ret, name, binders, body)
end
(* Debug any intermediary AST as follows: *)
(* PPrint.(Print.(print (PrintAst.print_files files ^^ hardline))); *)
let simplify files =
let files = Simplify.sequence_to_let#visit_files () files in
let files = merge_visitor#visit_files () files in
if debug then
PPrint.(Print.(print (PrintAst.print_files files ^^ hardline)));
let files = Simplify.let_to_sequence#visit_files () files in
files