Import Upstream version 20180207
[hcoop/debian/mlton.git] / mlton / ssa / restore2.fun
CommitLineData
7f918cf1
CE
1(* Copyright (C) 2009,2017 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(* Restore SSA
11 *
12 * Based primarily on Section 19.1 of Appel's "Modern Compiler Implementation in ML",
13 * (but see the caveats in the comments below).
14 * The main deviation is the calculation of liveness of the violating variables,
15 * which is used to predicate the insertion of phi arguments. This is due to
16 * the algorithm's bias towards imperative languages, for which it makes the
17 * assumption that all variables are defined in the start block and all variables
18 * are "used" at exit.
19 * This is "optimized" for restoration of functions with small numbers of violating
20 * variables -- use bool vectors to represent sets of violating variables.
21 * Also, we use a Promise.t to suspend part of the dominance frontier computation.
22 *
23 * Requirements: no violation in globals; this is checked.
24 *)
25
26functor Restore2 (S: RESTORE2_STRUCTS): RESTORE2 =
27struct
28
29structure Control =
30 struct
31 open Control
32 fun diagnostics _ = ()
33 end
34
35open S
36open Exp Transfer
37
38structure LabelInfo =
39 struct
40 datatype t = T of {args: (Var.t * Type.t) vector ref,
41 preds: Label.t list ref,
42 defs: bool vector ref,
43 uses: bool vector ref,
44 live: bool array ref,
45 dtindex: int ref,
46 df: Label.t vector Promise.t ref,
47 phi: Var.t list ref,
48 phiArgs: Var.t vector ref,
49 queued: bool ref}
50
51 fun layout (T {preds, defs, uses, live, dtindex, df, phiArgs, ...})
52 = let open Layout
53 in record [("preds", List.layout Label.layout (!preds)),
54 ("defs", Vector.layout Bool.layout (!defs)),
55 ("uses", Vector.layout Bool.layout (!uses)),
56 ("live", Array.layout Bool.layout (!live)),
57 ("dtindex", Int.layout (!dtindex)),
58 ("df", Promise.layout (Vector.layout Label.layout) (!df)),
59 ("phiArgs", Vector.layout Var.layout (!phiArgs))]
60 end
61
62 local
63 fun make f (T r) = f r
64 fun make' f = (make f, ! o (make f))
65 in
66 val (args, args') = make' #args
67 val (preds, preds') = make' #preds
68 val (defs, defs') = make' #defs
69 val (uses, uses') = make' #uses
70 val (live, live') = make' #live
71 val (dtindex, dtindex') = make' #dtindex
72 val (df, df') = make' #df
73 val (phi, _) = make' #phi
74 val (phiArgs, phiArgs') = make' #phiArgs
75 val (queued, _) = make' #queued
76 end
77
78 fun new (): t = T {args = ref (Vector.new0 ()),
79 preds = ref [],
80 defs = ref (Vector.new0 ()),
81 uses = ref (Vector.new0 ()),
82 live = ref (Array.new0 ()),
83 dtindex = ref ~1,
84 df = ref (Promise.delay (fn () => Vector.new0 ())),
85 phi = ref [],
86 phiArgs = ref (Vector.new0 ()),
87 queued = ref false}
88 end
89
90structure Cardinality =
91 struct
92 structure L = ThreePointLattice(val bottom = "zero"
93 val mid = "one"
94 val top = "many")
95 open L
96
97 val isZero = isBottom
98 val isOne = isMid
99 val makeOne = makeMid
100 val isMany = isTop
101 val makeMany = makeTop
102 val whenMany = whenTop
103
104 val inc: t -> unit
105 = fn c => if isZero c
106 then makeOne c
107 else if isOne c
108 then makeMany c
109 else ()
110 end
111
112structure VarInfo =
113 struct
114 datatype t = T of {defs: Cardinality.t,
115 ty: Type.t ref,
116 index: int ref,
117 defSites: Label.t list ref,
118 useSites: Label.t list ref,
119 vars: Var.t list ref}
120
121 fun layout (T {defs, index, defSites, useSites, vars, ...})
122 = let open Layout
123 in record [("defs", Cardinality.layout defs),
124 ("index", Int.layout (!index)),
125 ("defSites", List.layout Label.layout (!defSites)),
126 ("useSites", List.layout Label.layout (!useSites)),
127 ("vars", List.layout Var.layout (!vars))]
128 end
129
130 local
131 fun make f (T r) = f r
132 fun make' f = (make f, ! o (make f))
133 in
134 val defs = make #defs
135 val (index,index') = make' #index
136 val (_,defSites') = make' #defSites
137 val (_,useSites') = make' #useSites
138 val (ty,ty') = make' #ty
139 end
140 fun addDef (T {defs, ...}) = Cardinality.inc defs
141 fun addDefSite (T {defSites, ...}, l) = List.push(defSites, l)
142 fun addUseSite (T {useSites, ...}, l) = List.push(useSites, l)
143 val violates = Cardinality.isMany o defs
144 fun whenViolates (T {defs, ...}, th) = Cardinality.whenMany (defs, th)
145
146 fun new (): t = T {defs = Cardinality.new (),
147 index = ref ~1,
148 defSites = ref [],
149 useSites = ref [],
150 ty = ref Type.unit,
151 vars = ref []}
152
153 fun pushVar (T {vars, ...}, var) = List.push (vars, var)
154 fun popVar (T {vars, ...}) = ignore (List.pop vars)
155 fun peekVar (T {vars, ...}) = case !vars
156 of [] => NONE
157 | h::_ => SOME h
158 end
159
160fun restoreFunction {globals: Statement.t vector}
161 = let
162 exception NoViolations
163
164 val {get = varInfo: Var.t -> VarInfo.t, ...}
165 = Property.get
166 (Var.plist, Property.initFun (fn _ => VarInfo.new ()))
167
168 val {get = labelInfo: Label.t -> LabelInfo.t, ...}
169 = Property.get
170 (Label.plist, Property.initFun (fn _ => LabelInfo.new ()))
171
172 fun mkQueue ()
173 = let
174 val todo = ref []
175 in
176 {enque = fn (l, li) => let
177 val queued = LabelInfo.queued li
178 in
179 if !queued
180 then ()
181 else (queued := true ;
182 List.push (todo, (l,li)))
183 end,
184 deque = fn () => case !todo
185 of [] => NONE
186 | (l,li)::todo'
187 => (todo := todo';
188 LabelInfo.queued li := false;
189 SOME (l,li))}
190 end
191
192 fun mkPost ()
193 = let
194 val post = ref []
195 in
196 {addPost = fn th => List.push (post, th),
197 post = fn () => List.foreach(!post, fn th => th ())}
198 end
199
200 (* check for violations in globals *)
201 fun addDef (x, ty)
202 = let
203 val vi = varInfo x
204 in
205 VarInfo.ty vi := ty ;
206 VarInfo.addDef vi ;
207 VarInfo.whenViolates
208 (vi, fn () => Error.bug "Restore2.restore: violation in globals")
209 end
210 val _
211 = Vector.foreach
212 (globals, fn Statement.T {var, ty, ...} =>
213 Option.app (var, fn x => addDef (x, ty)))
214 in
215 fn (f: Function.t) =>
216 let
217 val {args, blocks, name, returns, raises, start} = Function.dest f
218 (* check for violations *)
219 val violations = ref []
220 fun addDef (x, ty)
221 = let
222 val vi = varInfo x
223 in
224 if VarInfo.violates vi
225 then ()
226 else (VarInfo.ty vi := ty ;
227 VarInfo.addDef vi ;
228 if VarInfo.violates vi
229 then List.push (violations, x)
230 else ())
231 end
232 val _ = Function.foreachVar (f, addDef)
233
234 (* escape early *)
235 val _ = if List.isEmpty (!violations)
236 then (Control.diagnostics
237 (fn display =>
238 let
239 open Layout
240 in
241 display (seq [Func.layout name,
242 str " NoViolations"])
243 end);
244 raise NoViolations)
245 else ()
246
247 (* init violations *)
248 val index = ref 0
249 val violations
250 = Vector.fromListMap
251 (!violations, fn x =>
252 let
253 val vi = varInfo x
254 val _ = VarInfo.index vi := (!index)
255 val _ = Int.inc index
256 in
257 x
258 end)
259 val numViolations = !index
260
261 (* Diagnostics *)
262 val _ = Control.diagnostics
263 (fn display =>
264 let
265 open Layout
266 in
267 display (seq [Func.layout name,
268 str " Violations: ",
269 Vector.layout Var.layout violations])
270 end)
271
272 (* init entryBlock *)
273 val entry = Label.newNoname ()
274 val entryBlock = Block.T {label = entry,
275 args = args,
276 statements = Vector.new0 (),
277 transfer = Goto {dst = start,
278 args = Vector.new0 ()}}
279
280 (* compute dominator tree *)
281 val dt = Function.dominatorTree f
282 val dt' = Tree.T (entryBlock, Vector.new1 dt)
283
284 (* compute df (dominance frontier) *)
285 (* based on section 19.1 of Appel's "Modern Compiler Implementation in ML" *)
286 (* also computes defSites and useSites of violating variables *)
287 (* also computes preds, defs, and uses *)
288 val dtindex = ref 0
289 fun doitTree (Tree.T (Block.T {label, args, statements, transfer},
290 children))
291 = let
292 val li = labelInfo label
293
294 val _ = LabelInfo.args li := args
295
296 val _ = Transfer.foreachLabel
297 (transfer, fn l =>
298 List.push (LabelInfo.preds (labelInfo l), label))
299
300 val defs = Array.new (numViolations, false)
301 val uses = Array.new (numViolations, false)
302 fun addDef x
303 = let
304 val vi = varInfo x
305 in
306 if VarInfo.violates vi
307 then let
308 val index = VarInfo.index' vi
309 in
310 VarInfo.addDefSite (varInfo x, label);
311 Array.update (defs, index, true);
312 Array.update (uses, index, false)
313 end
314 else ()
315 end
316 fun addUse x
317 = let
318 val vi = varInfo x
319 in
320 if VarInfo.violates vi
321 then let
322 val index = VarInfo.index' vi
323 in
324 VarInfo.addUseSite (varInfo x, label);
325 Array.update (uses, index, true)
326 end
327 else ()
328 end
329 val _ = Transfer.foreachVar (transfer, addUse)
330 val _ = Vector.foreachr
331 (statements, fn Statement.T {var, exp, ...} =>
332 (Option.app (var, addDef);
333 Exp.foreachVar (exp, addUse)))
334 val _ = Vector.foreach (args, addDef o #1)
335 val _ = LabelInfo.defs li := Array.toVector defs
336 val _ = LabelInfo.uses li := Array.toVector uses
337 val _ = LabelInfo.live li := Array.new (numViolations, false)
338
339 val _ = Int.inc dtindex
340 val dtindexMin = !dtindex
341 val _ = LabelInfo.dtindex li := dtindexMin
342 val _ = Vector.foreach(children, doitTree)
343 val dtindexMax = !dtindex
344 fun dominates l
345 = let val dtindex = LabelInfo.dtindex' (labelInfo l)
346 in dtindexMin < dtindex andalso dtindex <= dtindexMax
347 end
348
349 fun promise ()
350 = let
351 val df = ref []
352 fun addDF l
353 = if List.contains(!df, l, Label.equals)
354 then ()
355 else List.push(df,l)
356 val _ = Transfer.foreachLabel
357 (transfer, fn l =>
358 if Vector.exists
359 (children, fn Tree.T (b, _) =>
360 Label.equals (Block.label b, l))
361 then ()
362 else addDF l)
363 val _ = Vector.foreach
364 (children, fn Tree.T (Block.T {label, ...}, _) =>
365 let
366 val li = labelInfo label
367 in
368 Vector.foreach
369 (Promise.force (LabelInfo.df' li), fn l =>
370 if dominates l
371 then ()
372 else addDF l)
373 end)
374 in
375 Vector.fromList (!df)
376 end
377 val _ = LabelInfo.df li := Promise.delay promise
378 in
379 ()
380 end
381 val _ = doitTree dt'
382
383 (* compute liveness *)
384 val _
385 = Vector.foreach
386 (violations, fn x =>
387 let
388 val {enque, deque} = mkQueue ()
389 val enque = fn l => enque (l, labelInfo l)
390
391 val vi = varInfo x
392 val index = VarInfo.index' vi
393 val useSites = VarInfo.useSites' vi
394 val _ = List.foreach (useSites, enque)
395
396 fun doit (_,li)
397 = let
398 val uses = LabelInfo.uses' li
399 val defs = LabelInfo.defs' li
400 val live = LabelInfo.live' li
401 in
402 if Array.sub (live, index)
403 orelse
404 (Vector.sub(defs, index)
405 andalso
406 not (Vector.sub (uses, index)))
407 then ()
408 else (Array.update(live, index, true) ;
409 List.foreach (LabelInfo.preds' li, enque))
410 end
411 fun loop ()
412 = case deque ()
413 of NONE => ()
414 | SOME (l,li) => (doit (l, li); loop ())
415 in
416 loop ()
417 end)
418
419 (* insert phi-functions *)
420 (* based on section 19.1 of Appel's "Modern Compiler Implementation in ML"
421 * (beware: Alg. 19.6 (both in the book and as corrected by the
422 * errata) has numerous typos; and this implementation computes sets of
423 * variables that must have phi-functions at a node, which is close to
424 * the algorithm in the book, but the reverse of the algorithm as
425 * corrected by the errata, which computes sets of nodes that must have
426 * a phi-functions for a variable.)
427 *)
428 val _
429 = Vector.foreach
430 (violations, fn x =>
431 let
432 val {enque, deque} = mkQueue ()
433
434 val vi = varInfo x
435 val index = VarInfo.index' vi
436 val defSites = VarInfo.defSites' vi
437 val _ = List.foreach
438 (defSites, fn l =>
439 enque (l, labelInfo l))
440
441 fun doit (_,li)
442 = Vector.foreach
443 (Promise.force (LabelInfo.df' li), fn l =>
444 let
445 val li = labelInfo l
446 val live = LabelInfo.live' li
447 val phi = LabelInfo.phi li
448 in
449 if Array.sub(live, index)
450 andalso
451 not (List.contains(!phi, x, Var.equals))
452 then (List.push(phi, x);
453 enque (l, li))
454 else ()
455 end)
456 fun loop ()
457 = case deque ()
458 of NONE => ()
459 | SOME (l,li) => (doit (l, li); loop ())
460 in
461 loop ()
462 end)
463
464 (* finalize phi args *)
465 fun visitBlock (Block.T {label, ...})
466 = let
467 val li = labelInfo label
468 val phi = LabelInfo.phi li
469 val phiArgs = LabelInfo.phiArgs li
470 in
471 phiArgs := Vector.fromList (!phi) ;
472 phi := []
473 end
474 val _ = visitBlock entryBlock
475 val _ = Vector.foreach (blocks, visitBlock)
476
477 (* Diagnostics *)
478 val _ = Control.diagnostics
479 (fn display =>
480 let
481 open Layout
482 in
483 Vector.foreach
484 (violations, fn x =>
485 display (seq [Var.layout x,
486 str " ",
487 VarInfo.layout (varInfo x)]));
488 Vector.foreach
489 (blocks, fn Block.T {label, ...} =>
490 display (seq [Label.layout label,
491 str " ",
492 LabelInfo.layout (labelInfo label)]))
493 end)
494
495 (* rewrite *)
496 val blocks = ref []
497 fun rewriteVar (x: Var.t)
498 = case VarInfo.peekVar (varInfo x)
499 of NONE => x
500 | SOME x' => x'
501 fun rewriteStatement addPost (Statement.T {var, ty, exp})
502 = let
503 val exp = Exp.replaceVar (exp, rewriteVar)
504 val var
505 = case var
506 of NONE => NONE
507 | SOME x => let
508 val vi = varInfo x
509 in
510 if VarInfo.violates vi
511 then let
512 val x' = Var.new x
513 in
514 addPost (fn _ => VarInfo.popVar vi) ;
515 VarInfo.pushVar (vi, x');
516 SOME x'
517 end
518 else SOME x
519 end
520 in
521 Statement.T {var = var,
522 ty = ty,
523 exp = exp}
524 end
525 local
526 type t = {dst: Label.t,
527 phiArgs: Var.t vector,
528 route: Label.t,
529 hash: Word.t}
530 val routeTable : t HashSet.t = HashSet.new {hash = #hash}
531 in
532 fun route dst
533 = let
534 val li = labelInfo dst
535 val phiArgs = LabelInfo.phiArgs' li
536 in
537 if Vector.isEmpty phiArgs
538 then dst
539 else let
540 val phiArgs = Vector.map
541 (phiArgs, valOf o VarInfo.peekVar o varInfo)
542 val hash = Vector.fold
543 (phiArgs, Label.hash dst, fn (x, h) =>
544 Word.xorb(Var.hash x, h))
545 val {route, ...}
546 = HashSet.lookupOrInsert
547 (routeTable, hash,
548 fn {dst = dst', phiArgs = phiArgs', ... } =>
549 Label.equals (dst, dst')
550 andalso
551 Vector.equals (phiArgs, phiArgs', Var.equals),
552 fn () =>
553 let
554 val route = Label.new dst
555 val args = Vector.map
556 (LabelInfo.args' li, fn (x,ty) =>
557 (Var.new x, ty))
558 val args' = Vector.concat
559 [Vector.map(args, #1),
560 phiArgs]
561 val block = Block.T
562 {label = route,
563 args = args,
564 statements = Vector.new0 (),
565 transfer = Goto {dst = dst,
566 args = args'}}
567 val _ = List.push (blocks, block)
568 in
569 {dst = dst,
570 phiArgs = phiArgs,
571 route = route,
572 hash = hash}
573 end)
574 in
575 route
576 end
577 end
578 end
579 fun rewriteTransfer (t: Transfer.t)
580 = Transfer.replaceLabelVar (t, route, rewriteVar)
581 fun visitBlock' (Block.T {label, args, statements, transfer})
582 = let
583 val {addPost, post} = mkPost ()
584 val li = labelInfo label
585 fun doit x = let
586 val vi = varInfo x
587 val ty = VarInfo.ty' vi
588 in
589 if VarInfo.violates vi
590 then let
591 val x' = Var.new x
592 in
593 addPost (fn _ => VarInfo.popVar vi) ;
594 VarInfo.pushVar (vi, x') ;
595 (x', ty)
596 end
597 else (x, ty)
598 end
599 val args = Vector.map
600 (args, fn (x, _) => doit x)
601 val phiArgs = Vector.map
602 (LabelInfo.phiArgs' li, fn x => doit x)
603 val args = Vector.concat [args, phiArgs]
604 val statements
605 = if Vector.exists(LabelInfo.defs' li, fn b => b)
606 orelse
607 Vector.exists(LabelInfo.uses' li, fn b => b)
608 then Vector.map (statements, rewriteStatement addPost)
609 else statements
610 val transfer = rewriteTransfer transfer
611 val block = Block.T {label = label,
612 args = args,
613 statements = statements,
614 transfer = transfer}
615 in
616 (block, post)
617 end
618 fun visitBlock block
619 = let val (block, post) = visitBlock' block
620 in List.push (blocks, block) ; post
621 end
622 fun rewrite ()
623 = let
624 local
625 val (Block.T {label, args, statements, transfer}, post)
626 = visitBlock' entryBlock
627 val entryBlock = Block.T {label = label,
628 args = Vector.new0 (),
629 statements = statements,
630 transfer = transfer}
631 val _ = List.push (blocks, entryBlock)
632 in
633 val args = args
634 val post = post
635 end
636 val _ = Tree.traverse (Function.dominatorTree f, visitBlock)
637 val _ = post ()
638 in
639 Function.new {args = args,
640 blocks = Vector.fromList (!blocks),
641 name = name,
642 raises = raises,
643 returns = returns,
644 start = entry}
645 end
646 val f = rewrite ()
647 in
648 f
649 end
650 handle NoViolations => f
651 end
652
653val traceRestoreFunction
654 = Trace.trace ("Restore2.restoreFunction",
655 Func.layout o Function.name,
656 Func.layout o Function.name)
657
658val restoreFunction
659 = fn g =>
660 let
661 val r = restoreFunction g
662 in
663 fn f => traceRestoreFunction r f
664 end
665
666fun restore (Program.T {datatypes, globals, functions, main})
667 = let
668 val r = restoreFunction globals
669 in
670 Program.T {datatypes = datatypes,
671 globals = globals,
672 functions = List.revMap (functions, r),
673 main = main}
674 end
675end