1 (* Copyright (C) 2009 Matthew Fluet.
2 * Copyright (C) 1999-2007 Henry Cejtin, Matthew Fluet, Suresh
3 * Jagannathan, and Stephen Weeks.
4 * Copyright (C) 1997-2000 NEC Research Institute.
6 * MLton is released under a BSD-style license.
7 * See the file MLton-LICENSE for details.
10 functor Inline (S: INLINE_STRUCTS): INLINE =
20 fun containsCall (f: Function.t): bool =
24 (Function.blocks f, fn Block.T {transfer, ...} =>
29 fun containsLoop (f: Function.t): bool =
31 val {get, set, destroy} =
32 Property.destGetSet (Label.plist, Property.initConst false)
39 (f, fn (Block.T {label, transfer, ...}) =>
42 Goto {dst, ...} => if get dst then escape true else ()
44 ; fn () => set (label, false)))
53 fun 'a make (dontInlineFunc: Function.t * 'a -> bool)
54 (Program.T {functions, ...}, a: 'a): Func.t -> bool =
56 val {get = shouldInline: Func.t -> bool,
57 set = setShouldInline, ...} =
58 Property.getSetOnce (Func.plist, Property.initConst false)
62 if not (Function.mayInline f) orelse dontInlineFunc (f, a)
64 else setShouldInline (Function.name f, true))
71 val name = Function.name f
72 val shouldInline = shouldInline name
75 (seq [Func.layout name, str ": ",
76 record [("shouldInline", Bool.layout shouldInline)]])
82 val leafOnce = make (fn (f, {size}) =>
83 Option.isNone (Function.sizeMax (f, {max = size,
85 sizeTransfer =Transfer.size}))
86 orelse Function.containsCall f)
87 val leafOnceNoLoop = make (fn (f, {size}) =>
88 Option.isNone (Function.sizeMax (f, {max = size,
90 sizeTransfer =Transfer.size}))
91 orelse Function.containsCall f
92 orelse Function.containsLoop f)
95 structure Graph = DirectedGraph
96 structure Node = Graph.Node
99 fun make (dontInline: Function.t -> bool)
100 (Program.T {functions, ...}, {size: int option}) =
103 type info = {function: Function.t,
105 shouldInline: bool ref,
107 val {get = funcInfo: Func.t -> info,
108 set = setFuncInfo, ...} =
110 (Func.plist, Property.initRaise ("funcInfo", Func.layout))
111 val {get = nodeFunc: unit Node.t -> Func.t,
112 set = setNodeFunc, ...} =
114 (Node.plist, Property.initRaise ("nodeFunc", Node.layout))
115 val graph = Graph.new ()
116 (* initialize the info for each func *)
121 val name = Function.name f
122 val n = Graph.newNode graph
124 setNodeFunc (n, name)
125 ; setFuncInfo (name, {function = f,
127 shouldInline = ref false,
130 (* Build the call graph. *)
135 val {name, blocks, ...} = Function.dest f
136 val {node, ...} = funcInfo name
139 (blocks, fn Block.T {transfer, ...} =>
142 (ignore o Graph.addEdge)
143 (graph, {from = node, to = #node (funcInfo func)})
146 (* Compute strongly-connected components.
147 * Then start at the leaves of the call graph and work up.
151 (rev (Graph.stronglyConnectedComponents graph),
156 val {function, shouldInline, size, ...} =
157 funcInfo (nodeFunc n)
159 if Function.mayInline function
160 andalso not (dontInline function)
174 val {shouldInline, size, ...} =
181 | _ => Transfer.size t})
185 | SOME n => (shouldInline := true
198 val name = Function.name f
199 val {shouldInline, size, ...} = funcInfo name
200 val shouldInline = !shouldInline
204 (seq [Func.layout name, str ": ",
205 record [("shouldInline", Bool.layout shouldInline),
206 ("size", Int.layout size)]])
210 ! o #shouldInline o funcInfo
213 val leafRepeat = make (fn _ => false)
214 val leafRepeatNoLoop = make (fn f => Function.containsLoop f)
217 fun nonRecursive (Program.T {functions, ...}, {small: int, product: int}) =
219 type info = {doesCallSelf: bool ref,
220 function: Function.t,
223 shouldInline: bool ref,
225 val {get = funcInfo: Func.t -> info,
226 set = setFuncInfo, ...} =
228 (Func.plist, Property.initRaise ("funcInfo", Func.layout))
229 val {get = nodeFunc: unit Node.t -> Func.t,
230 set = setNodeFunc, ...} =
232 (Node.plist, Property.initRaise ("nodeFunc", Node.layout))
233 val graph = Graph.new ()
234 (* initialize the info for each func *)
239 val name = Function.name f
240 val n = Graph.newNode graph
242 setNodeFunc (n, name)
243 ; setFuncInfo (name, {doesCallSelf = ref false,
247 shouldInline = ref false,
250 (* Update call counts. *)
255 val {name, blocks, ...} = Function.dest f
256 val {doesCallSelf, ...} = funcInfo name
259 (blocks, fn Block.T {transfer, ...} =>
263 val {numCalls, ...} = funcInfo func
265 if Func.equals (name, func)
266 then doesCallSelf := true
267 else Int.inc numCalls
271 fun mayInline (setSize: bool,
272 {function, doesCallSelf, numCalls, size, ...}: info): bool =
273 Function.mayInline function
274 andalso not (!doesCallSelf)
281 fn t as Call {func, ...} =>
283 val {shouldInline, size, ...} = funcInfo func
289 | t => Transfer.size t})
294 ; (!numCalls - 1) * (n - small) <= product
296 (* Build the call graph. Do not include functions that we already know
297 * will not be inlined.
303 val {name, blocks, ...} = Function.dest f
304 val info as {node, ...} = funcInfo name
306 if mayInline (false, info)
308 (blocks, fn Block.T {transfer, ...} =>
311 if Func.equals (name, func)
313 else (ignore o Graph.addEdge)
314 (graph, {from = node, to = #node (funcInfo func)})
318 (* Compute strongly-connected components.
319 * Then start at the leaves of the call graph and work up.
323 (rev (Graph.stronglyConnectedComponents graph),
324 fn [n] => let val info as {shouldInline, ...} = funcInfo (nodeFunc n)
325 in shouldInline := mayInline (true, info)
335 val name = Function.name f
336 val {numCalls, shouldInline, size, ...} = funcInfo name
337 val numCalls = !numCalls
338 val shouldInline = !shouldInline
342 (seq [Func.layout name, str ": ",
343 record [("numCalls", Int.layout numCalls),
344 ("shouldInline", Bool.layout shouldInline),
345 ("size", Int.layout size)]])
349 ! o #shouldInline o funcInfo
352 fun transform {program as Program.T {datatypes, globals, functions, main},
353 shouldInline: Func.t -> bool,
354 inlineIntoMain: bool} =
356 val {get = funcInfo: Func.t -> {function: Function.t,
357 isCalledByMain: bool ref},
358 set = setFuncInfo, ...} =
360 (Func.plist, Property.initRaise ("Inline.funcInfo", Func.layout))
361 val isCalledByMain: Func.t -> bool =
362 ! o #isCalledByMain o funcInfo
363 val () = List.foreach (functions, fn f =>
364 setFuncInfo (Function.name f,
366 isCalledByMain = ref false}))
369 (#blocks (Function.dest (Program.mainFunction program)),
370 fn Block.T {transfer, ...} =>
372 Transfer.Call {func, ...} =>
373 #isCalledByMain (funcInfo func) := true
375 fun doit (blocks: Block.t vector,
376 return: Return.t) : Block.t vector =
378 val newBlocks = ref []
382 fn block as Block.T {label, args, statements, transfer} =>
385 Block.T {label = label,
387 statements = statements,
391 Call {func, args, return = return'} =>
393 val return = Return.compose (return, return')
399 val {name, args, start, blocks, ...} =
400 (Function.dest o Function.alphaRename)
401 (#function (funcInfo func))
402 val blocks = doit (blocks, return)
403 val _ = List.push (newBlocks, blocks)
405 Label.newString (Func.originalName name)
413 statements = Vector.new0 (),
414 transfer = Goto {dst = start,
415 args = Vector.new0 ()}}))
420 new (Goto {dst = name,
423 else new (Call {func = func,
430 {handler = Handler.Handle handler, ...} =>
431 new (Goto {dst = handler,
436 Return.NonTail {cont, ...} =>
437 new (Goto {dst = cont, args = xs})
442 Vector.concat (blocks::(!newBlocks))
444 val shrink = shrinkFunction {globals = globals}
447 (functions, [], fn (f, ac) =>
449 val {args, blocks, mayInline, name, raises, returns, start} =
453 val blocks = doit (blocks, Return.Tail)
455 shrink (Function.new {args = args,
457 mayInline = mayInline,
465 if Func.equals (name, main)
466 then if inlineIntoMain
473 orelse not (isCalledByMain name)
479 Program.T {datatypes = datatypes,
481 functions = functions,
483 val _ = Program.clearTop program
488 fun inlineLeaf (p, {loops, repeat, size}) =
491 else transform {program = p,
493 case (loops, repeat) of
494 (false, false) => leafOnce (p, {size = size})
495 | (false, true) => leafRepeat (p, {size = size})
496 | (true, false) => leafOnceNoLoop (p, {size = size})
497 | (true, true) => leafRepeatNoLoop (p, {size = size}),
498 inlineIntoMain = true}
499 fun inlineNonRecursive (p, arg) =
500 transform {program = p,
501 shouldInline = nonRecursive (p, arg),
502 inlineIntoMain = !Control.inlineIntoMain}