Commit | Line | Data |
---|---|---|
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 | ||
10 | functor RedundantTests (S: SSA_TRANSFORM_STRUCTS): SSA_TRANSFORM = | |
11 | struct | |
12 | ||
13 | open S | |
14 | ||
15 | structure 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 | ||
34 | structure 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 | ||
50 | structure 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 | ||
91 | open Exp Transfer | |
92 | ||
93 | fun 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 | |
509 | end |