Skip to content

Commit 54a164c

Browse files
lpw25stedolan
andauthored
Create fewer regions, according to typechecking (#59)
Instead of creating one at each expression in local mode, regions are now only created where typechecking indicates they should go: - Function bodies (except for local-returning functions) - Loop bodies - Toplevel expressions (module-level let, certain cases of class initialisation) - Inlined overapplications, where the inlined function returns local but the whole application does not The last one of these is tricky, and fixes a bug: a call `f a b c` may be in heap mode, but actually correspond to an overapplication `(f a) b c`, where the one-argument function `f` returns a local closure. Previously, this local closure would leak, and the newly inserted region fixes this. This fix requires tracking the mode of an application all the way through to Cmm, since the `caml_applyN` stubs are split into local and non-local versions according to whether this region should be present in the slow-path. A couple of optimisations are updated to correctly handle regions: - All default arguments are now global so that the worker-wrapper transformation is always valid. - The local functions transformation in simplif now understands tailcalls within regions. Finally, there is an optimisation in Lambda to prevent unnecessary regions being created. Co-authored-by: Stephen Dolan <[email protected]>
1 parent ce34678 commit 54a164c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+1192
-606
lines changed

asmcomp/cmm.ml

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -272,34 +272,34 @@ let iter_shallow_tail f = function
272272
| Cop _ ->
273273
false
274274

275-
let rec map_tail f = function
275+
let map_shallow_tail f = function
276276
| Clet(id, exp, body) ->
277-
Clet(id, exp, map_tail f body)
277+
Clet(id, exp, f body)
278278
| Clet_mut(id, kind, exp, body) ->
279-
Clet_mut(id, kind, exp, map_tail f body)
279+
Clet_mut(id, kind, exp, f body)
280280
| Cphantom_let(id, exp, body) ->
281-
Cphantom_let (id, exp, map_tail f body)
281+
Cphantom_let (id, exp, f body)
282282
| Cifthenelse(cond, ifso_dbg, ifso, ifnot_dbg, ifnot, dbg) ->
283283
Cifthenelse
284284
(
285285
cond,
286-
ifso_dbg, map_tail f ifso,
287-
ifnot_dbg, map_tail f ifnot,
286+
ifso_dbg, f ifso,
287+
ifnot_dbg, f ifnot,
288288
dbg
289289
)
290290
| Csequence(e1, e2) ->
291-
Csequence(e1, map_tail f e2)
291+
Csequence(e1, f e2)
292292
| Cswitch(e, tbl, el, dbg') ->
293-
Cswitch(e, tbl, Array.map (fun (e, dbg) -> map_tail f e, dbg) el, dbg')
293+
Cswitch(e, tbl, Array.map (fun (e, dbg) -> f e, dbg) el, dbg')
294294
| Ccatch(rec_flag, handlers, body) ->
295-
let map_h (n, ids, handler, dbg) = (n, ids, map_tail f handler, dbg) in
296-
Ccatch(rec_flag, List.map map_h handlers, map_tail f body)
295+
let map_h (n, ids, handler, dbg) = (n, ids, f handler, dbg) in
296+
Ccatch(rec_flag, List.map map_h handlers, f body)
297297
| Ctrywith(e1, id, e2, dbg) ->
298-
Ctrywith(map_tail f e1, id, map_tail f e2, dbg)
298+
Ctrywith(f e1, id, f e2, dbg)
299299
| Cregion e ->
300-
Cregion(map_tail f e)
300+
Cregion(f e)
301301
| Ctail e ->
302-
Ctail(map_tail f e)
302+
Ctail(f e)
303303
| Cexit _ | Cop (Craise _, _, _) as cmm ->
304304
cmm
305305
| Cconst_int _
@@ -309,8 +309,22 @@ let rec map_tail f = function
309309
| Cvar _
310310
| Cassign _
311311
| Ctuple _
312-
| Cop _ as c ->
313-
f c
312+
| Cop _ as cmm -> cmm
313+
314+
let map_tail f =
315+
let rec loop = function
316+
| Cconst_int _
317+
| Cconst_natint _
318+
| Cconst_float _
319+
| Cconst_symbol _
320+
| Cvar _
321+
| Cassign _
322+
| Ctuple _
323+
| Cop _ as c ->
324+
f c
325+
| cmm -> map_shallow_tail loop cmm
326+
in
327+
loop
314328

315329
let iter_shallow f = function
316330
| Clet (_id, e1, e2) ->

asmcomp/cmm.mli

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,15 @@ val iter_shallow_tail: (expression -> unit) -> expression -> bool
242242
considered to be in tail position (because their result become
243243
the final result for the expression). *)
244244

245+
val map_shallow_tail: (expression -> expression) -> expression -> expression
246+
(** Apply the transformation to those immediate sub-expressions of an
247+
expression that are in tail position, using the same definition of "tail"
248+
as [iter_shallow_tail] *)
249+
245250
val map_tail: (expression -> expression) -> expression -> expression
246251
(** Apply the transformation to an expression, trying to push it
247-
to all inner sub-expressions that can produce the final result.
248-
Same disclaimer as for [iter_shallow_tail] about the notion
249-
of "tail" sub-expression. *)
252+
to all inner sub-expressions that can produce the final result,
253+
by recursively applying map_shallow_tail *)
250254

251255
val iter_shallow: (expression -> unit) -> expression -> unit
252256
(** Apply the transformation to each immediate sub-expression. *)

asmcomp/cmm_helpers.ml

Lines changed: 75 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -807,12 +807,16 @@ let lookup_label obj lab dbg =
807807
let table = Cop (Cload (Word_val, Mutable), [obj], dbg) in
808808
addr_array_ref table lab dbg)
809809

810-
let call_cached_method obj tag cache pos args apos dbg =
810+
let send_function_name n (mode : Lambda.alloc_mode) =
811+
let suff = match mode with Alloc_heap -> "" | Alloc_local -> "L" in
812+
"caml_send" ^ Int.to_string n ^ suff
813+
814+
let call_cached_method obj tag cache pos args (apos,mode) dbg =
811815
let arity = List.length args in
812816
let cache = array_indexing log2_size_addr cache pos dbg in
813-
Compilenv.need_send_fun arity;
817+
Compilenv.need_send_fun arity mode;
814818
Cop(Capply(typ_val, apos),
815-
Cconst_symbol("caml_send" ^ Int.to_string arity, dbg) ::
819+
Cconst_symbol(send_function_name arity mode, dbg) ::
816820
obj :: tag :: cache :: args,
817821
dbg)
818822

@@ -859,10 +863,13 @@ let make_checkbound dbg = function
859863
Cop(Ccheckbound, args, dbg)
860864

861865
(* Record application and currying functions *)
862-
863-
let apply_function_sym n =
866+
let apply_function_name (n, (mode : Lambda.alloc_mode)) =
867+
let suff = match mode with Alloc_heap -> "" | Alloc_local -> "L" in
868+
"caml_apply" ^ Int.to_string n ^ suff
869+
let apply_function_sym n mode =
864870
assert (n > 0);
865-
Compilenv.need_apply_fun n; "caml_apply" ^ Int.to_string n
871+
Compilenv.need_apply_fun n mode;
872+
apply_function_name (n, mode)
866873
let curry_function_sym ar =
867874
Compilenv.need_curry_fun ar;
868875
match ar with
@@ -1713,10 +1720,10 @@ let ptr_offset ptr offset dbg =
17131720
then ptr
17141721
else Cop(Caddv, [ptr; Cconst_int(offset * size_addr, dbg)], dbg)
17151722

1716-
let direct_apply lbl args pos dbg =
1723+
let direct_apply lbl args (pos, _mode) dbg =
17171724
Cop(Capply(typ_val, pos), Cconst_symbol (lbl, dbg) :: args, dbg)
17181725

1719-
let generic_apply mut clos args pos dbg =
1726+
let generic_apply mut clos args (pos, mode) dbg =
17201727
match args with
17211728
| [arg] ->
17221729
bind "fun" clos (fun clos ->
@@ -1725,23 +1732,23 @@ let generic_apply mut clos args pos dbg =
17251732
| _ ->
17261733
let arity = List.length args in
17271734
let cargs =
1728-
Cconst_symbol(apply_function_sym arity, dbg) :: args @ [clos]
1735+
Cconst_symbol(apply_function_sym arity mode, dbg) :: args @ [clos]
17291736
in
17301737
Cop(Capply(typ_val, pos), cargs, dbg)
17311738

1732-
let send kind met obj args apos dbg =
1739+
let send kind met obj args akind dbg =
17331740
let call_met obj args clos =
17341741
(* met is never a simple expression, so it never gets turned into an
17351742
Immutable load *)
1736-
generic_apply Asttypes.Mutable clos (obj :: args) apos dbg
1743+
generic_apply Asttypes.Mutable clos (obj :: args) akind dbg
17371744
in
17381745
bind "obj" obj (fun obj ->
17391746
match (kind : Lambda.meth_kind), args with
17401747
Self, _ ->
17411748
bind "met" (lookup_label obj met dbg)
17421749
(call_met obj args)
17431750
| Cached, cache :: pos :: args ->
1744-
call_cached_method obj met cache pos args apos dbg
1751+
call_cached_method obj met cache pos args akind dbg
17451752
| _ ->
17461753
bind "met" (lookup_tag obj met dbg)
17471754
(call_met obj args))
@@ -1807,23 +1814,51 @@ let cache_public_method meths tag cache dbg =
18071814
Csequence(Cop (Cstore (Word_int, Assignment), [cache; Cvar tagged], dbg),
18081815
Cvar tagged)))))
18091816

1810-
let region e =
1811-
(* [Cregion e] is equivalent to [e] if [e] contains no local allocs *)
1812-
let rec check_local_allocs = function
1813-
| Cregion _ ->
1814-
(* Local allocations within a nested region do not affect this region.
1815-
Note this prevents O(n^2) behaviour with many nested regions. *)
1816-
()
1817+
let has_local_allocs e =
1818+
let rec loop = function
1819+
| Cregion e ->
1820+
(* Local allocations within a nested region do not affect this region,
1821+
except inside a Ctail block *)
1822+
loop_until_tail e
18171823
| Cop (Calloc Alloc_local, _, _)
18181824
| Cop ((Cextcall _ | Capply _), _, _) ->
1819-
raise Exit
1825+
raise Exit
1826+
| e ->
1827+
iter_shallow loop e
1828+
and loop_until_tail = function
1829+
| Ctail e -> loop e
1830+
| Cregion _ -> ()
1831+
| e -> ignore (iter_shallow_tail loop_until_tail e)
1832+
in
1833+
match loop e with
1834+
| () -> false
1835+
| exception Exit -> true
1836+
1837+
let remove_region_tail e =
1838+
let rec has_tail = function
1839+
| Ctail _
1840+
| Cop(Capply(_, Apply_tail), _, _) -> raise Exit
1841+
| Cregion _ -> ()
1842+
| e -> ignore (iter_shallow_tail has_tail e)
1843+
in
1844+
let rec remove_tail = function
1845+
| Ctail e -> e
1846+
| Cop(Capply(mach, Apply_tail), args, dbg) ->
1847+
Cop(Capply(mach, Apply_nontail), args, dbg)
1848+
| Cregion _ as e -> e
18201849
| e ->
1821-
iter_shallow check_local_allocs e
1850+
map_shallow_tail remove_tail e
18221851
in
1823-
match check_local_allocs e with
1852+
match has_tail e with
18241853
| () -> e
1825-
| exception Exit -> Cregion e
1854+
| exception Exit -> remove_tail e
18261855

1856+
let region e =
1857+
(* [Cregion e] is equivalent to [e] if [e] contains no local allocs *)
1858+
if has_local_allocs e then
1859+
Cregion e
1860+
else
1861+
remove_region_tail e
18271862

18281863
(* CR mshinwell: These will be filled in by later pull requests. *)
18291864
let placeholder_dbg () = Debuginfo.none
@@ -1840,7 +1875,7 @@ let placeholder_fun_dbg ~human_name:_ = Debuginfo.none
18401875
(app closN-1.code aN closN-1))))
18411876
*)
18421877

1843-
let apply_function_body arity =
1878+
let apply_function_body (arity, (mode : Lambda.alloc_mode)) =
18441879
let dbg = placeholder_dbg in
18451880
let arg = Array.make arity (V.create_local "arg") in
18461881
for i = 1 to arity - 1 do arg.(i) <- V.create_local "arg" done;
@@ -1875,13 +1910,15 @@ let apply_function_body arity =
18751910
:: List.map (fun s -> Cvar s) all_args,
18761911
dbg ()),
18771912
dbg (),
1878-
app_fun clos 0,
1913+
(match mode with
1914+
| Alloc_heap -> Cregion (app_fun clos 0)
1915+
| Alloc_local -> app_fun clos 0),
18791916
dbg ()))
18801917

1881-
let send_function arity =
1918+
let send_function (arity, mode) =
18821919
let dbg = placeholder_dbg in
18831920
let cconst_int i = Cconst_int (i, dbg ()) in
1884-
let (args, clos', body) = apply_function_body (1+arity) in
1921+
let (args, clos', body) = apply_function_body (1+arity, mode) in
18851922
let cache = V.create_local "cache"
18861923
and obj = List.hd args
18871924
and tag = V.create_local "tag" in
@@ -1915,7 +1952,7 @@ let send_function arity =
19151952
in
19161953
let body = Clet(VP.create clos', clos, body) in
19171954
let cache = cache in
1918-
let fun_name = "caml_send" ^ Int.to_string arity in
1955+
let fun_name = send_function_name arity mode in
19191956
let fun_args =
19201957
[obj, typ_val; tag, typ_int; cache, typ_val]
19211958
@ List.map (fun id -> (id, typ_val)) (List.tl args) in
@@ -1931,7 +1968,7 @@ let send_function arity =
19311968
let apply_function arity =
19321969
let (args, clos, body) = apply_function_body arity in
19331970
let all_args = args @ [clos] in
1934-
let fun_name = "caml_apply" ^ Int.to_string arity in
1971+
let fun_name = apply_function_name arity in
19351972
let fun_dbg = placeholder_fun_dbg ~human_name:fun_name in
19361973
Cfunction
19371974
{fun_name;
@@ -2129,26 +2166,27 @@ let curry_function = function
21292166
assert (n > 0);
21302167
intermediate_curry_functions ~nlocal ~arity:n 0
21312168

2132-
module Int = Numbers.Int
2169+
module ApplyFnSet =
2170+
Set.Make (struct type t = int * Lambda.alloc_mode let compare = compare end)
21332171
module AritySet =
21342172
Set.Make (struct type t = Clambda.arity let compare = compare end)
21352173

2136-
let default_apply = Int.Set.add 2 (Int.Set.add 3 Int.Set.empty)
2174+
let default_apply = ApplyFnSet.of_list [2,Alloc_heap; 3,Alloc_heap]
21372175
(* These apply funs are always present in the main program because
21382176
the run-time system needs them (cf. runtime/<arch>.S) . *)
21392177

21402178
let generic_functions shared units =
21412179
let (apply,send,curry) =
21422180
List.fold_left
21432181
(fun (apply,send,curry) (ui : Cmx_format.unit_infos) ->
2144-
List.fold_right Int.Set.add ui.ui_apply_fun apply,
2145-
List.fold_right Int.Set.add ui.ui_send_fun send,
2182+
List.fold_right ApplyFnSet.add ui.ui_apply_fun apply,
2183+
List.fold_right ApplyFnSet.add ui.ui_send_fun send,
21462184
List.fold_right AritySet.add ui.ui_curry_fun curry)
2147-
(Int.Set.empty,Int.Set.empty,AritySet.empty)
2185+
(ApplyFnSet.empty,ApplyFnSet.empty,AritySet.empty)
21482186
units in
2149-
let apply = if shared then apply else Int.Set.union apply default_apply in
2150-
let accu = Int.Set.fold (fun n accu -> apply_function n :: accu) apply [] in
2151-
let accu = Int.Set.fold (fun n accu -> send_function n :: accu) send accu in
2187+
let apply = if shared then apply else ApplyFnSet.union apply default_apply in
2188+
let accu = ApplyFnSet.fold (fun nr accu -> apply_function nr :: accu) apply [] in
2189+
let accu = ApplyFnSet.fold (fun nr accu -> send_function nr :: accu) send accu in
21522190
AritySet.fold (fun arity accu -> curry_function arity @ accu) curry accu
21532191

21542192
(* Primitives *)

asmcomp/cmm_helpers.mli

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ val lookup_label : expression -> expression -> Debuginfo.t -> expression
281281
- args : the additional arguments to the method call *)
282282
val call_cached_method :
283283
expression -> expression -> expression -> expression -> expression list ->
284-
Lambda.apply_position -> Debuginfo.t -> expression
284+
Clambda.apply_kind -> Debuginfo.t -> expression
285285

286286
(** Allocations *)
287287

@@ -311,7 +311,7 @@ val check_bound :
311311

312312
(** Get the symbol for the generic application with [n] arguments, and
313313
ensure its presence in the set of defined symbols *)
314-
val apply_function_sym : int -> string
314+
val apply_function_sym : int -> Lambda.alloc_mode -> string
315315

316316
(** Get the symbol for the generic currying or tuplifying wrapper with
317317
[n] arguments, and ensure its presence in the set of defined symbols. *)
@@ -543,7 +543,7 @@ val ptr_offset : expression -> int -> Debuginfo.t -> expression
543543

544544
(** Direct application of a function via a symbol *)
545545
val direct_apply :
546-
string -> expression list -> Lambda.apply_position
546+
string -> expression list -> Clambda.apply_kind
547547
-> Debuginfo.t -> expression
548548

549549
(** Generic application of a function to one or several arguments.
@@ -553,7 +553,7 @@ val direct_apply :
553553
the currently defined closure. *)
554554
val generic_apply :
555555
Asttypes.mutable_flag -> expression -> expression list
556-
-> Lambda.apply_position -> Debuginfo.t -> expression
556+
-> Clambda.apply_kind -> Debuginfo.t -> expression
557557

558558
(** Method call : [send kind met obj args dbg]
559559
- [met] is a method identifier, which can be a hashed variant or an index
@@ -564,7 +564,7 @@ val generic_apply :
564564
cache and cache position) *)
565565
val send :
566566
Lambda.meth_kind -> expression -> expression -> expression list
567-
-> Lambda.apply_position -> Debuginfo.t -> expression
567+
-> Clambda.apply_kind -> Debuginfo.t -> expression
568568

569569
(** Construct [Cregion e], eliding some useless regions *)
570570
val region : expression -> expression

asmcomp/cmmgen.ml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ let rec expr_size env = function
171171
| RHS_block blocksize -> RHS_infix { blocksize; offset }
172172
| RHS_nonrec -> RHS_nonrec
173173
| _ -> assert false)
174+
| Uregion exp ->
175+
expr_size env exp
176+
| Utail _ ->
177+
Misc.fatal_error "Utail in non-tail position"
174178
| _ -> RHS_nonrec
175179

176180
(* Translate structured constants to Cmm data items *)
@@ -422,13 +426,13 @@ let rec transl env e =
422426
let ptr = transl env arg in
423427
let dbg = Debuginfo.none in
424428
ptr_offset ptr offset dbg
425-
| Udirect_apply(lbl, args, pos, dbg) ->
429+
| Udirect_apply(lbl, args, kind, dbg) ->
426430
let args = List.map (transl env) args in
427-
direct_apply lbl args pos dbg
428-
| Ugeneric_apply(clos, args, pos, dbg) ->
431+
direct_apply lbl args kind dbg
432+
| Ugeneric_apply(clos, args, kind, dbg) ->
429433
let clos = transl env clos in
430434
let args = List.map (transl env) args in
431-
generic_apply (mut_from_env env clos) clos args pos dbg
435+
generic_apply (mut_from_env env clos) clos args kind dbg
432436
| Usend(kind, met, obj, args, pos, dbg) ->
433437
let met = transl env met in
434438
let obj = transl env obj in

0 commit comments

Comments
 (0)