1 (**************************************************************************)
5 (* François Pottier, INRIA Rocquencourt *)
6 (* Yann Régis-Gianas, PPS, Université Paris Diderot *)
8 (* Copyright 2005-2008 Institut National de Recherche en Informatique *)
9 (* et en Automatique. All rights reserved. This file is distributed *)
10 (* under the terms of the Q Public License version 1.0, with the change *)
11 (* described in file LICENSE. *)
13 (**************************************************************************)
18 (* In the following, we only inline global functions. In order to
19 avoid unintended capture, as we traverse terms, we keep track of
20 local identifiers that hide global ones. The following little class
21 helps do that. (The pathological case where a local binding hides a
22 global one probably does not arise very often. Fortunately,
23 checking against it in this way is quite cheap, and lets me sleep
26 class locals table
= object(self
)
28 method pvar
(locals
: StringSet.t
) (id
: string) =
29 if Hashtbl.mem table id
then StringSet.add id locals
else locals
33 (* Here is the inliner. *)
35 let inline ({ valdefs
= defs
} as p
: program
) =
37 (* Create a table of all global definitions. *)
39 let before, table
= Traverse.tabulate_defs defs
in
41 (* Prepare to count how many times each function is used, including
42 inside its own definition. The public functions serve as starting
43 points for this discovery phase. *)
45 let queue : valdef
Queue.t
=
47 and usage
: int StringMap.t
ref =
51 (* [visit] is called at every identifier occurrence. *)
54 if StringSet.mem id locals
then
55 (* This is a local identifier. Do nothing. *)
59 let _, def
= Hashtbl.find table id
in
61 (* This is a globally defined identifier. Increment its usage
62 count. If it was never visited, enqueue its definition for
67 StringMap.find id
!usage
72 usage
:= StringMap.add id
(n + 1) !usage
75 (* This identifier is not global. It is either local or a
76 reference to some external library, e.g. ocaml's standard
81 (* Look for occurrences of identifiers inside expressions. *)
85 inherit [ StringSet.t
, unit ] Traverse.fold
87 method evar locals
() id
=
92 (* Initialize the queue with all public definitions, and work from
93 there. We assume that the left-hand side of every definition is
96 List.iter
(fun { valpublic
= public
; valpat
= p
} ->
98 visit StringSet.empty
(pat2var p
)
100 Misc.qfold
(o#valdef
StringSet.empty
) () queue;
101 let usage = !usage in
103 (* Now, inline every function that is called at most once. At the
104 same time, every function that is never called is dropped. The
105 public functions again serve as starting points for the
108 let queue : valdef
Queue.t
=
115 let id = pat2var def
.valpat
in
116 if not
(StringSet.mem
id !emitted
) then begin
117 emitted
:= StringSet.add
id !emitted
;
122 (* A simple application is an application of a variable to a number
123 of variables, constants, or record accesses out of variables. *)
125 let rec is_simple_arg = function
128 | ERecordAccess
(EVar
_, _) ->
136 let is_simple_app = function
137 | EApp
(EVar
_, actuals
) ->
138 List.for_all
is_simple_arg actuals
143 (* Taking a fresh instance of a type scheme. Ugly. *)
149 tv
, Printf.sprintf
"freshtv%d" !count
152 let mapping = List.map
fresh scheme
.quantifiers
in
159 TypVar
(List.assoc v
mapping)
163 | TypApp
(f
, typs
) ->
164 TypApp
(f
, List.map
sub typs
)
166 TypTuple
(List.map
sub typs
)
167 | TypArrow
(typ1
, typ2
) ->
168 TypArrow
(sub typ1
, sub typ2
)
173 (* Destructuring a type annotation. *)
175 let rec annotate formals body typ
=
176 match formals
, typ
with
178 [], EAnnot
(body
, type2scheme typ
)
179 | formal
:: formals
, TypArrow
(targ
, tres
) ->
180 let formals, body
= annotate formals body tres
in
181 PAnnot
(formal
, targ
) :: formals, body
183 (* Type annotation has insufficient arity. *)
187 (* The heart of the inliner: rewriting a function call to a [let]
190 If there was a type annotation at the function definition site,
191 it is dropped, provided [--infer] was enabled. Otherwise, it is
192 kept, because, due to the presence of [EMagic] expressions in the
193 code, dropping a type annotation could cause an ill-typed program
194 to become apparently well-typed. Keeping a type annotation
195 requires taking a fresh instance of the type scheme, because
196 OCaml doesn't have support for locally and existentially bound
197 type variables. Yuck. *)
199 let inline formals actuals body oscheme
=
200 assert (List.length actuals
= List.length
formals);
203 when not
Settings.infer
->
205 let formals, body
= annotate formals body
(instance scheme
) in
206 mlet
formals actuals body
209 mlet
formals actuals body
212 (* Look for occurrences of identifiers inside expressions, branches,
213 etc. and replace them with their definitions if they have only
214 one use site or if their definitions are sufficiently simple. *)
218 inherit [ StringSet.t
] Traverse.map
as super
220 method eapp locals e actuals
=
223 (Hashtbl.mem table
id) && (* a global identifier *)
224 (not
(StringSet.mem
id locals
)) (* not hidden by a local identifier *)
227 let _, def
= Hashtbl.find table
id in (* cannot fail, thanks to the above check *)
229 let formals, body
, oscheme
=
231 | { valval
= EFun
(formals, body
) } ->
233 | { valval
= EAnnot
(EFun
(formals, body
), scheme
) } ->
234 formals, body
, Some scheme
236 (* The definition is not a function definition. This should not
237 happen in the kind of code that we generate. *)
241 assert (StringMap.mem
id usage);
242 if StringMap.find
id usage = 1 || is_simple_app body
then
244 (* The definition can be inlined, with beta reduction. *)
246 inline formals (self#exprs locals actuals
) (EComment
(id, self#expr locals body
)) oscheme
250 (* The definition cannot be inlined. *)
253 super#eapp locals e actuals
258 (* The thing in function position is not a reference to a global. *)
259 super#eapp locals e actuals
264 (* Initialize the queue with all public definitions, and work from
267 List.iter
(function { valpublic
= public
} as def
->
273 Misc.qfold
(fun defs def
->
274 o#valdef
StringSet.empty def
:: defs
278 Error.logC
1 (fun f
->
279 Printf.fprintf f
"%d functions before inlining, %d functions after inlining.\n"
280 before (List.length
valdefs));
282 Time.tick
"Inlining";
284 { p
with valdefs = valdefs }
286 (* The external entry point. *)
289 if Settings.code_inlining
then inline p
else p