Source file recover_match.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
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
open Wax_lang
open Ast
let is_block i = match i.desc with Block _ -> true | _ -> false
let is_chain i =
match i.desc with Br_on_cast _ | Br_on_null _ -> true | _ -> false
let rec chain_tests e =
match e.desc with
| Br_on_cast (l, rt, operand) ->
let tests, scrut = chain_tests operand in
(tests @ [ (l, `Cast rt) ], scrut)
| Br_on_null (l, operand) ->
let tests, scrut = chain_tests operand in
(tests @ [ (l, `Null) ], scrut)
| _ -> ([], e)
let split_decls stmts =
let rec aux acc = function
| ({ desc = Let ([ (Some _, _) ], None); _ } as d) :: rest ->
aux (d :: acc) rest
| rest -> (List.rev acc, rest)
in
aux [] stmts
let consume_step stmts =
let decls, stmts = split_decls stmts in
match stmts with
| { desc = Let ([ (Some x, _) ], Some inner); _ } :: body when is_block inner
->
Some (decls, Some x, inner, body)
| { desc = Set (x, _, inner); _ } :: body when is_block inner ->
Some (decls, Some x, inner, body)
| { desc = Let ([ (None, _) ], Some inner); _ } :: body when is_block inner ->
Some (decls, None, inner, body)
| ({ desc = Block { typ; _ }; _ } as inner) :: body
when typ.params = [||] && typ.results = [||] ->
Some (decls, None, inner, body)
| _ -> None
let rec descend blk =
match blk.desc with
| Block { label = Some lbl; block = { desc = body; _ }; _ } -> (
let decls0, body = split_decls body in
match body with
| [
{ desc = Let ([ (None, _) ], Some chain); _ };
{ desc = Br (escape, None); _ };
]
when is_chain chain ->
Some ([], lbl, chain, escape, decls0)
| _ -> (
match consume_step body with
| Some (decls1, binding, inner, arm_body) -> (
match descend inner with
| Some (levels, inner_lbl, chain, escape, decls) ->
Some
( (lbl, binding, arm_body) :: levels,
inner_lbl,
chain,
escape,
decls0 @ decls1 @ decls )
| None -> None)
| None -> None))
| _ -> None
let rec same_scrut a b =
match (a.desc, b.desc) with
| Get x, Get y -> x.desc = y.desc
| Null, Null -> true
| Int s, Int t -> s = t
| NonNull e, NonNull f -> same_scrut e f
| Cast (e, s), Cast (f, t) -> s = t && same_scrut e f
| Test (e, s), Test (f, t) -> s = t && same_scrut e f
| StructGet (e, x), StructGet (f, y) -> x.desc = y.desc && same_scrut e f
| ArrayGet (e, i), ArrayGet (f, j) -> same_scrut e f && same_scrut i j
| _ -> false
let rec diverges_instr i =
match i.desc with
| Return _ | Br _ | Br_table _ | Unreachable | Throw _ | ThrowRef _
| TailCall _ ->
true
| If { if_block; else_block = Some else_block; _ } ->
diverges_list if_block.desc && diverges_list else_block.desc
| Match { arms; default; _ } ->
List.for_all
(fun (_, (b : (_ instr list, _) Ast.annotated)) -> diverges_list b.desc)
arms
&& diverges_list default.desc
| Loop { block; _ } ->
diverges_list block.desc
| _ -> false
and diverges_list l =
match List.rev l with [] -> false | last :: _ -> diverges_instr last
let arm_block stmt =
match stmt.desc with
| Let
( [ (None, _) ],
Some
{
desc =
Block
{ label = Some self; typ; block = { desc = test :: body; _ } };
_;
} )
when typ.params = [||] && Array.length typ.results = 1 && diverges_list body
-> (
match test.desc with
| Let ([ (Some x, _) ], Some { desc = Br_on_cast_fail (l, rt, scrut); _ })
when l.desc = self.desc ->
Some (MatchCast (Some x, rt), scrut, body, `Fused)
| Set (x, _, { desc = Br_on_cast_fail (l, rt, scrut); _ })
when l.desc = self.desc ->
Some (MatchCast (Some x, rt), scrut, body, `Decl x)
| Let ([ (None, _) ], Some { desc = Br_on_cast_fail (l, rt, scrut); _ })
when l.desc = self.desc ->
Some (MatchCast (None, rt), scrut, body, `Fused)
| Br_on_non_null (l, scrut) when l.desc = self.desc ->
Some (MatchNull, scrut, body, `Fused)
| _ -> None)
| _ -> None
let compat scrut s =
match scrut with None -> true | Some s0 -> same_scrut s0 s
let rec collect_arms scrut stmts =
let take pat body s rest =
let scrut = match scrut with None -> Some s | some -> some in
let arms, scrut, hoisted, rest = collect_arms scrut rest in
((pat, body) :: arms, scrut, hoisted, rest)
in
match stmts with
| ({ desc = Let ([ (Some x, _) ], None); _ } as decl) :: rest -> (
match rest with
| blk :: rest'
when match arm_block blk with
| Some (_, s, _, `Decl y) -> y.desc = x.desc && compat scrut s
| _ -> false ->
let pat, s, body =
match arm_block blk with
| Some (pat, s, body, _) -> (pat, s, body)
| None -> assert false
in
take pat body s rest'
| _ ->
let arms, scrut, hoisted, trailing = collect_arms scrut rest in
if arms = [] then ([], scrut, [], stmts)
else (arms, scrut, decl :: hoisted, trailing))
| blk :: rest -> (
match arm_block blk with
| Some (pat, s, body, `Fused) when compat scrut s -> take pat body s rest
| _ -> ([], scrut, [], stmts))
| [] -> ([], scrut, [], stmts)
let rec rewrite_instr ~faithful (i : location instr) : location instr =
let d = rewrite_desc ~faithful i.desc in
if d == i.desc then i else { i with desc = d }
and try_fold ~faithful (i : location instr) (trailing : location instr list) :
(location instr list * location instr) option =
match descend i with
| None -> None
| Some (levels, inner_lbl, chain, escape, decls) ->
let tests, scrut = chain_tests chain in
let n = List.length tests in
let block_labels =
inner_lbl :: List.rev_map (fun (l, _, _) -> l) levels
in
let rec take k = function
| x :: r when k > 0 -> x :: take (k - 1) r
| _ -> []
in
let label_names = List.map (fun (l : label) -> l.desc) block_labels in
let distinct =
List.length (List.sort_uniq compare label_names)
= List.length label_names
in
let chain_ok =
List.length levels = n
&& List.map (fun ((l : label), _) -> l.desc) tests = take n label_names
&&
match List.rev block_labels with
| last :: _ -> last.desc = escape.desc
| [] -> false
in
if n < 1 || (not distinct) || not chain_ok then None
else
let arm (_, pat_kind) (_, binding, body) =
let pat =
match (pat_kind, binding) with
| `Cast rt, Some x -> Some (MatchCast (Some x, rt))
| `Cast rt, None -> Some (MatchCast (None, rt))
| `Null, None -> Some MatchNull
| `Null, Some _ -> None
in
Option.map
(fun pat -> (pat, no_loc (rewrite_list ~faithful body)))
pat
in
let arms = List.map2 arm tests (List.rev levels) in
if List.exists Option.is_none arms then None
else
let arms = List.filter_map Fun.id arms in
let bound =
List.filter_map
(fun (p, _) ->
match p with MatchCast (Some x, _) -> Some x.desc | _ -> None)
arms
in
let hoisted =
List.filter
(fun d ->
match d.desc with
| Let ([ (Some x, _) ], None) -> not (List.mem x.desc bound)
| _ -> true)
decls
in
Some
( hoisted,
{
i with
desc =
Match
{
scrutinee = rewrite_instr ~faithful scrut;
arms;
default = no_loc (rewrite_list ~faithful trailing);
};
} )
and rewrite_list ~faithful stmts =
match stmts with
| [] -> []
| i :: rest -> (
match try_fold ~faithful i rest with
| Some (hoisted, m) -> List.map (rewrite_instr ~faithful) hoisted @ [ m ]
| None -> (
match
if faithful then ([], None, [], []) else collect_arms None stmts
with
| (_ :: _ as arms), Some scrut, hoisted, trailing ->
List.map (rewrite_instr ~faithful) hoisted
@ [
{
i with
desc =
Match
{
scrutinee = rewrite_instr ~faithful scrut;
arms =
List.map
(fun (p, b) ->
(p, no_loc (rewrite_list ~faithful b)))
arms;
default = no_loc (rewrite_list ~faithful trailing);
};
};
]
| _ ->
let i' = rewrite_instr ~faithful i
and rest' = rewrite_list ~faithful rest in
if i' == i && rest' == rest then stmts else i' :: rest'))
and rewrite_desc ~faithful (desc : location instr_desc) : location instr_desc =
Ast_utils.map_desc ~instr:(rewrite_instr ~faithful)
~block:(rewrite_list ~faithful) desc
let rec field_desc ~faithful (f : location modulefield) =
let map_fields =
List.map (fun (a : (location modulefield, _) Ast.annotated) ->
{ a with desc = field_desc ~faithful a.desc })
in
match f with
| Func ({ body = label, instrs; _ } as r) ->
Func { r with body = (label, rewrite_list ~faithful instrs) }
| Conditional ({ then_fields; else_fields; _ } as r) ->
Conditional
{
r with
then_fields = { then_fields with desc = map_fields then_fields.desc };
else_fields =
Option.map
(fun (b :
( (location modulefield, location) Ast.annotated list,
location )
Ast.annotated) -> { b with desc = map_fields b.desc })
else_fields;
}
| ( Type _ | Module_annotation _ | Import _ | Import_group _ | Global _
| Tag _ | Memory _ | Data _ | Table _ | Elem _ ) as f ->
f
let module_ ?(faithful = false) (m : location module_) : location module_ =
List.map
(fun (a : (location modulefield, location) Ast.annotated) ->
{ a with desc = field_desc ~faithful a.desc })
m