@@ -164,6 +164,10 @@ let blocks_in_loop p pc =
164164
165165type 'a cache = 'a option ref
166166
167+ (* Information about a function candidate for inlining. Some
168+ information / statistics about this function are computed lazily
169+ and stored there. *)
170+
167171type info =
168172 { f : Var .t
169173 ; params : Var .t list
@@ -180,16 +184,18 @@ type info =
180184 }
181185
182186type context =
183- { profile : Profile .t
187+ { profile : Profile .t (* * Aggressive inlining? *)
184188 ; p : program
185- ; live_vars : int array
186- ; inline_count : int ref
187- ; env : info Var.Map .t
188- ; in_loop : bool
189- ; has_closures : bool ref
190- ; current_function : Var .t option
189+ ; live_vars : int array (* * Occurence count of all variables *)
190+ ; inline_count : int ref (* * Inlining statistics *)
191+ ; env : info Var.Map .t (* * Functions that are candidate for inlining *)
192+ ; in_loop : bool (* * Whether the current block is in a loop *)
193+ ; has_closures : bool ref (* * Whether the current function contains closures *)
194+ ; current_function : Var .t option (* * Name of the current function *)
191195 ; enclosing_function : Var .t option
196+ (* * Name of the function enclosing the current function *)
192197 }
198+ (* * Current context into which we consider inlining some functions. *)
193199
194200let cache ~info :{ cont = pc , _ ; _ } ref f =
195201 match ! ref with
@@ -199,6 +205,7 @@ let cache ~info:{ cont = pc, _; _ } ref f =
199205 ref := Some v;
200206 v
201207
208+ (* * Does the function contain a loop? *)
202209let contains_loop ~context info =
203210 cache ~info info.loops (fun pc ->
204211 let rec traverse pc ((visited , loop ) as accu ) : _ * bool =
@@ -244,8 +251,10 @@ let rec block_size ~recurse ~context { branch; body; _ } =
244251
245252and size ~recurse ~context = sum ~context (block_size ~recurse ~context )
246253
254+ (* * Size of the function body *)
247255let body_size ~context info = cache ~info info.body_size (size ~recurse: false ~context )
248256
257+ (* * Size of the function, including the size of the closures it contains *)
249258let full_size ~context info = cache ~info info.full_size (size ~recurse: true ~context )
250259
251260let closure_count_uncached ~context =
@@ -258,9 +267,12 @@ let closure_count_uncached ~context =
258267 ~init: 0
259268 body)
260269
270+ (* * Number of closures contained in the function *)
261271let closure_count ~context info =
262272 cache ~info info.closure_count (closure_count_uncached ~context )
263273
274+ (* * Number of instructions in the function which look like
275+ initialization code. *)
264276let count_init_code ~context info =
265277 cache
266278 ~info
@@ -276,6 +288,7 @@ let count_init_code ~context info =
276288 ~init: 0
277289 body)
278290
291+ (* * Whether the function returns a block. *)
279292let returns_a_block ~context info =
280293 cache ~info info.returns_a_block (fun pc ->
281294 let blocks = context.p.blocks in
@@ -295,6 +308,8 @@ let returns_a_block ~context info =
295308 blocks
296309 true )
297310
311+ (* * List of parameters that corresponds to functions called once in
312+ the function body. *)
298313let interesting_parameters ~context info =
299314 let params = info.params in
300315 cache ~info info.interesting_params (fun pc ->
@@ -334,7 +349,10 @@ let functor_like ~context info =
334349 && (not (contains_loop ~context info))
335350 && returns_a_block ~context info
336351 && count_init_code ~context info * 2 > body_size ~context info
337- && full_size ~context info - body_size ~context info < = 20 * closure_count ~context info
352+ (* A large portion of the body is initialization code *)
353+ &&
354+ (* The closures defined in this function are small on average *)
355+ full_size ~context info - body_size ~context info < = 20 * closure_count ~context info
338356
339357let trivial_function ~context info =
340358 body_size ~context info < = 1 && closure_count ~context info = 0
@@ -373,13 +391,16 @@ let rec small_function ~context info args =
373391 with Exit -> true
374392
375393and should_inline ~context info args =
376- (* A closure contains a pointer to (recursively) the contexts of its
377- enclosing functions. The context of a function contains the
378- variables bound in this function which are referred to from one
379- of the enclosed function. To limit the risk of memory leaks, we
380- don't inline functions containing closure if this makes these
381- closures capture additional contexts shared with other
382- closures. *)
394+ (* Typically, in JavaScript implementations, a closure contains a
395+ pointer to (recursively) the contexts of its enclosing functions.
396+ The context of a function contains the variables bound in this
397+ function which are referred to from one of the enclosed function.
398+ To limit the risk of memory leaks, we try to avoid inlining functions
399+ containing closures if this makes these closures capture
400+ additional contexts shared with other closures.
401+ We still inline into toplevel functions ([Option.is_none
402+ context.enclosing_function]) since this results in significant
403+ performance improvements. *)
383404 (match Config. target () , Config. effects () with
384405 | `JavaScript , (`Disabled | `Cps ) ->
385406 closure_count ~context info = 0
@@ -394,8 +415,8 @@ and should_inline ~context info args =
394415 &&
395416 match Config. target () with
396417 | `Wasm when context.in_loop ->
397- (* Avoid inlining in loop since, if the loop is not hot, the
398- code might never get optimized *)
418+ (* Avoid inlining in a loop since, if the loop is not hot,
419+ the code might never get optimized *)
399420 body_size ~context info < 30 && not (contains_loop ~context info)
400421 | `JavaScript
401422 when Option. is_none context.current_function && contains_loop ~context info ->
0 commit comments