Import Debian changes 20180207-1
[hcoop/debian/mlton.git] / mlton / ssa / redundant-tests.fun
CommitLineData
7f918cf1
CE
1(* Copyright (C) 2009 Matthew Fluet.
2 * Copyright (C) 1999-2006 Henry Cejtin, Matthew Fluet, Suresh
3 * Jagannathan, and Stephen Weeks.
4 * Copyright (C) 1997-2000 NEC Research Institute.
5 *
6 * MLton is released under a BSD-style license.
7 * See the file MLton-LICENSE for details.
8 *)
9
10functor RedundantTests (S: SSA_TRANSFORM_STRUCTS): SSA_TRANSFORM =
11struct
12
13open S
14
15structure Rel =
16 struct
17 datatype t =
18 EQ
19 | LT of {signed: bool}
20 | LE of {signed: bool}
21 | NE
22
23 val equals: t * t -> bool = op =
24
25 val toString =
26 fn EQ => "="
27 | LT _ => "<"
28 | LE _ => "<="
29 | NE => "<>"
30
31 val layout = Layout.str o toString
32 end
33
34structure Oper =
35 struct
36 datatype t =
37 Const of Const.t
38 | Var of Var.t
39
40 val layout =
41 fn Const c => Const.layout c
42 | Var x => Var.layout x
43
44 val equals =
45 fn (Const c, Const c') => Const.equals (c, c')
46 | (Var x, Var x') => Var.equals (x, x')
47 | _ => false
48 end
49
50structure Fact =
51 struct
52 datatype t = T of {rel: Rel.t,
53 lhs: Oper.t,
54 rhs: Oper.t}
55
56 fun layout (T {rel, lhs, rhs}) =
57 let open Layout
58 in seq [Oper.layout lhs, str " ", Rel.layout rel,
59 str " ", Oper.layout rhs]
60 end
61
62 fun equals (T {rel, lhs = l, rhs = r},
63 T {rel = rel', lhs = l', rhs = r'}) =
64 Rel.equals (rel, rel')
65 andalso Oper.equals (l, l')
66 andalso Oper.equals (r, r')
67
68 fun negate (T {rel, lhs, rhs}): t =
69 let
70 datatype z = datatype Rel.t
71 val rel =
72 case rel of
73 EQ => NE
74 | LT s => LE s
75 | LE s => LT s
76 | NE => EQ
77 in
78 T {rel = rel, lhs = rhs, rhs = lhs}
79 end
80
81 datatype result = False | True | Unknown
82
83 fun determine (facts: t list, f: t): result =
84 if List.contains (facts, f, equals)
85 then True
86 else if List.contains (facts, negate f, equals)
87 then False
88 else Unknown
89 end
90
91open Exp Transfer
92
93fun transform (Program.T {globals, datatypes, functions, main}) =
94 let
95 datatype varInfo =
96 Const of Const.t
97 | Fact of Fact.t
98 | None
99 | Or of Fact.t * Fact.t
100 val {get = varInfo: Var.t -> varInfo, set = setVarInfo, ...} =
101 Property.getSetOnce (Var.plist, Property.initConst None)
102 val setVarInfo =
103 Trace.trace ("RedundantTests.setVarInfo",
104 Var.layout o #1,
105 Unit.layout)
106 setVarInfo
107 datatype z = datatype Fact.result
108 datatype z = datatype Rel.t
109 fun makeVarInfo {args, prim, targs = _}: varInfo =
110 let
111 fun arg i =
112 let
113 val x = Vector.sub (args, i)
114 in
115 case varInfo x of
116 Const c => Oper.Const c
117 | _ => Oper.Var x
118 end
119 fun z (r, a, b) =
120 Fact (Fact.T {rel = r,
121 lhs = arg a,
122 rhs = arg b})
123 fun doit rel = z (rel, 0, 1)
124 datatype z = datatype Prim.Name.t
125 in
126 case Prim.name prim of
127 MLton_eq => doit EQ
128 | Word_equal _ => doit EQ
129 | Word_lt (_, sg) => doit (LT sg)
130 | _ => None
131 end
132 fun setConst (x, c) = setVarInfo (x, Const c)
133 val _ =
134 Vector.foreach
135 (globals, fn Statement.T {var, exp, ...} =>
136 case exp of
137 Exp.Const c => Option.app (var, fn x => setConst (x, c))
138 | _ => ())
139 local
140 fun make c =
141 let
142 val x = Var.newNoname ()
143 in
144 (x,
145 Statement.T {var = SOME x,
146 ty = Type.bool,
147 exp = ConApp {con = c, args = Vector.new0 ()}})
148 end
149 in
150 val (trueVar, t) = make Con.truee
151 val (falseVar, f) = make Con.falsee
152 end
153 local
154 val statements = ref []
155 in
156 val one =
157 WordSize.memoize
158 (fn s =>
159 let
160 val one = Var.newNoname ()
161 val () =
162 List.push
163 (statements,
164 Statement.T {exp = Exp.Const (Const.word (WordX.one s)),
165 ty = Type.word s,
166 var = SOME one})
167 in
168 one
169 end)
170 val ones = Vector.fromList (!statements)
171 end
172 val globals = Vector.concat [Vector.new2 (t, f), ones, globals]
173 val shrink = shrinkFunction {globals = globals}
174 val numSimplified = ref 0
175 fun simplifyFunction f =
176 let
177 val {args, blocks, mayInline, name, raises, returns, start} =
178 Function.dest f
179 val _ =
180 Control.diagnostic
181 (fn () =>
182 let open Layout
183 in seq [str "processing ", Func.layout name]
184 end)
185 val {get = labelInfo: Label.t -> {ancestor: Label.t option ref,
186 facts: Fact.t list ref,
187 inDeg: int ref},
188 ...} =
189 Property.get
190 (Label.plist, Property.initFun (fn _ => {ancestor = ref NONE,
191 facts = ref [],
192 inDeg = ref 0}))
193 (* Set up inDeg. *)
194 fun inc l = Int.inc (#inDeg (labelInfo l))
195 val () = inc start
196 val _ =
197 Vector.foreach
198 (blocks, fn Block.T {transfer, ...} =>
199 Transfer.foreachLabel (transfer, inc))
200 (* Perform analysis, set up facts, and set up ancestor. *)
201 fun loop (Tree.T (Block.T {label, statements, transfer, ...},
202 children),
203 ancestor') =
204 let
205 val _ =
206 Vector.foreach
207 (statements, fn Statement.T {var, exp, ...} =>
208 case exp of
209 Exp.Const c =>
210 Option.app (var, fn x => setConst (x, c))
211 | Exp.PrimApp pa =>
212 Option.app (var, fn x =>
213 setVarInfo (x, makeVarInfo pa))
214 | _ => ())
215 val _ =
216 case transfer of
217 Case {test, cases, default, ...} =>
218 let
219 fun add (l, f) =
220 let
221 val {facts, inDeg, ...} = labelInfo l
222 in
223 if !inDeg = 1
224 then List.push (facts, f)
225 else ()
226 end
227 fun falseTrue () =
228 case cases of
229 Cases.Con v =>
230 let
231 fun ca i = Vector.sub (v, i)
232 in
233 case (Vector.length v, default) of
234 (1, SOME l') =>
235 let
236 val (c, l) = ca 0
237 in
238 if Con.equals (c, Con.truee)
239 then (l', l)
240 else (l, l')
241 end
242 | (2, _) =>
243 let
244 val (c, l) = ca 0
245 val (_, l') = ca 1
246 in
247 if Con.equals (c, Con.truee)
248 then (l', l)
249 else (l, l')
250 end
251 | _ => Error.bug "RedundantTests.simplifyFunction: expected two branches"
252 end
253 | _ => Error.bug "RedundantTests.simplifyFunction: expected con"
254 in
255 case varInfo test of
256 Fact f =>
257 let
258 val (l, l') = falseTrue ()
259 in
260 add (l, Fact.negate f)
261 ; add (l', f)
262 end
263 | Or (f, f') =>
264 let
265 val (l, _) = falseTrue ()
266 in
267 add (l, Fact.negate f)
268 ; add (l, Fact.negate f')
269 end
270 | _ => ()
271 end
272 | _ => ()
273
274 val {ancestor, facts, ...} = labelInfo label
275 val _ = ancestor := ancestor'
276 val ancestor' = if List.isEmpty (!facts)
277 then ancestor'
278 else SOME label
279 in
280 Vector.foreach
281 (children, fn tree => loop (tree, ancestor'))
282 end
283 val _ = loop (Function.dominatorTree f, NONE)
284 (* Diagnostic. *)
285 val _ =
286 Control.diagnostics
287 (fn display =>
288 Vector.foreach
289 (blocks, fn Block.T {label, ...} =>
290 let open Layout
291 in display (seq [Label.layout label,
292 str " ",
293 List.layout Fact.layout
294 (! (#facts (labelInfo label)))])
295 end))
296 (* Transformation. *)
297 fun isFact (l: Label.t, p: Fact.t -> bool): bool =
298 let
299 fun loop (l: Label.t) =
300 let
301 val {ancestor, facts, ...} = labelInfo l
302 in
303 List.exists (!facts, p)
304 orelse (case !ancestor of
305 NONE => false
306 | SOME l => loop l)
307 end
308 in
309 loop l
310 end
311 fun determine (l: Label.t, f: Fact.t) =
312 let
313 fun loop {ancestor, facts, ...} =
314 case Fact.determine (!facts, f) of
315 Unknown =>
316 (case !ancestor of
317 NONE => Unknown
318 | SOME l => loop (labelInfo l))
319 | r => r
320 in
321 loop (labelInfo l)
322 end
323 val blocks =
324 Vector.map
325 (blocks, fn Block.T {label, args, statements, transfer} =>
326 let
327 val statements =
328 Vector.map
329 (statements, fn statement as Statement.T {ty, var, ...} =>
330 let
331 fun doit x =
332 (Int.inc numSimplified
333 ; Control.diagnostic
334 (fn () =>
335 let open Layout
336 in seq [Option.layout Var.layout var,
337 str " -> ",
338 Var.layout x]
339 end)
340 ; Statement.T {var = var,
341 ty = ty,
342 exp = Var x})
343 fun falsee () = doit falseVar
344 fun truee () = doit trueVar
345 in
346 case var of
347 NONE => statement
348 | SOME var =>
349 (case varInfo var of
350 Or (f, f') =>
351 (case determine (label, f) of
352 False =>
353 (case determine (label, f') of
354 False => falsee ()
355 | True => truee ()
356 | Unknown => statement)
357 | True => truee ()
358 | Unknown => statement)
359 | Fact f =>
360 (case determine (label, f) of
361 False => falsee ()
362 | True => truee ()
363 | Unknown => statement)
364 | _ => statement)
365 end)
366 val noChange = (statements, transfer)
367 fun arith (args: Var.t vector,
368 prim: Type.t Prim.t,
369 success: Label.t)
370 : Statement.t vector * Transfer.t =
371 let
372 fun simplify (prim: Type.t Prim.t,
373 x: Var.t,
374 s: WordSize.t) =
375 let
376 val res = Var.newNoname ()
377 in
378 (Vector.concat
379 [statements,
380 Vector.new1
381 (Statement.T
382 {exp = PrimApp {args = Vector.new2 (x, one s),
383 prim = prim,
384 targs = Vector.new0 ()},
385 ty = Type.word s,
386 var = SOME res})],
387 Goto {args = Vector.new1 res,
388 dst = success})
389 end
390 fun add1 (x: Var.t, s: WordSize.t, sg) =
391 if isFact (label, fn Fact.T {lhs, rel, rhs} =>
392 case (lhs, rel, rhs) of
393 (Oper.Var x', Rel.LT sg', _) =>
394 Var.equals (x, x')
395 andalso sg = sg'
396 | (Oper.Var x', Rel.LE sg',
397 Oper.Const c) =>
398 Var.equals (x, x')
399 andalso sg = sg'
400 andalso
401 (case c of
402 Const.Word w =>
403 WordX.lt
404 (w, WordX.max (s, sg), sg)
405 | _ => Error.bug "RedundantTests.add1: strange fact")
406 | _ => false)
407 then simplify (Prim.wordAdd s, x, s)
408 else noChange
409 fun sub1 (x: Var.t, s: WordSize.t, sg) =
410 if isFact (label, fn Fact.T {lhs, rel, rhs} =>
411 case (lhs, rel, rhs) of
412 (_, Rel.LT sg', Oper.Var x') =>
413 Var.equals (x, x')
414 andalso sg = sg'
415 | (Oper.Const c, Rel.LE sg',
416 Oper.Var x') =>
417 Var.equals (x, x')
418 andalso sg = sg'
419 andalso
420 (case c of
421 Const.Word w =>
422 WordX.gt
423 (w, WordX.min (s, sg), sg)
424 | _ => Error.bug "RedundantTests.sub1: strange fact")
425 | _ => false)
426 then simplify (Prim.wordSub s, x, s)
427 else noChange
428 fun add (c: Const.t, x: Var.t, (s, sg as {signed})) =
429 case c of
430 Const.Word i =>
431 if WordX.isOne i
432 then add1 (x, s, sg)
433 else if signed andalso WordX.isNegOne i
434 then sub1 (x, s, sg)
435 else noChange
436 | _ => Error.bug "RedundantTests.add: strange const"
437 datatype z = datatype Prim.Name.t
438 in
439 case Prim.name prim of
440 Word_addCheck s =>
441 let
442 val x1 = Vector.sub (args, 0)
443 val x2 = Vector.sub (args, 1)
444 in
445 case varInfo x1 of
446 Const c => add (c, x2, s)
447 | _ => (case varInfo x2 of
448 Const c => add (c, x1, s)
449 | _ => noChange)
450 end
451 | Word_subCheck (s, sg as {signed}) =>
452 let
453 val x1 = Vector.sub (args, 0)
454 val x2 = Vector.sub (args, 1)
455 in
456 case varInfo x2 of
457 Const c =>
458 (case c of
459 Const.Word w =>
460 if WordX.isOne w
461 then sub1 (x1, s, sg)
462 else
463 if (signed
464 andalso WordX.isNegOne w)
465 then add1 (x1, s, sg)
466 else noChange
467 | _ =>
468 Error.bug "RedundantTests.sub: strage const")
469 | _ => noChange
470 end
471 | _ => noChange
472 end
473 val (statements, transfer) =
474 case transfer of
475 Arith {args, prim, success, ...} =>
476 arith (args, prim, success)
477 | _ => noChange
478 in
479 Block.T {label = label,
480 args = args,
481 statements = statements,
482 transfer = transfer}
483 end)
484 in
485 shrink (Function.new {args = args,
486 blocks = blocks,
487 mayInline = mayInline,
488 name = name,
489 raises = raises,
490 returns = returns,
491 start = start})
492 end
493 val _ =
494 Control.diagnostic
495 (fn () =>
496 let open Layout
497 in seq [str "numSimplified = ", Int.layout (!numSimplified)]
498 end)
499 val functions = List.revMap (functions, simplifyFunction)
500 val program =
501 Program.T {datatypes = datatypes,
502 globals = globals,
503 functions = functions,
504 main = main}
505 val _ = Program.clearTop program
506 in
507 program
508 end
509end