Skip to content

Commit fba595d

Browse files
committed
Backport linear computation of closure environments
1 parent 31f23fe commit fba595d

File tree

2 files changed

+382
-0
lines changed

2 files changed

+382
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Linear computation of closure environments
2+
3+
- <https://github.com/ocaml/ocaml/pull/12222>
4+
- <https://discuss.ocaml.org/t/scaling-factors-when-compiling-mutually-recursive-definitions/14708/2>
Lines changed: 378 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,378 @@
1+
diff --git a/bytecomp/bytegen.ml b/bytecomp/bytegen.ml
2+
index 27e170ffce..12690f7061 100644
3+
--- a/bytecomp/bytegen.ml
4+
+++ b/bytecomp/bytegen.ml
5+
@@ -34,20 +34,53 @@ let new_label () =
6+
(**** Operations on compilation environments. ****)
7+
8+
let empty_env =
9+
- { ce_stack = Ident.empty; ce_heap = Ident.empty; ce_rec = Ident.empty }
10+
+ { ce_stack = Ident.empty; ce_closure = Not_in_closure }
11+
12+
(* Add a stack-allocated variable *)
13+
14+
let add_var id pos env =
15+
{ ce_stack = Ident.add id pos env.ce_stack;
16+
- ce_heap = env.ce_heap;
17+
- ce_rec = env.ce_rec }
18+
+ ce_closure = env.ce_closure }
19+
20+
let rec add_vars idlist pos env =
21+
match idlist with
22+
[] -> env
23+
| id :: rem -> add_vars rem (pos + 1) (add_var id pos env)
24+
25+
+(* Compute the closure environment *)
26+
+
27+
+let rec add_positions entries pos_to_entry ~pos ~delta = function
28+
+ | [] -> entries, pos
29+
+ | id :: rem ->
30+
+ let entries =
31+
+ Ident.add id (pos_to_entry pos) entries
32+
+ in
33+
+ add_positions entries pos_to_entry ~pos:(pos + delta) ~delta rem
34+
+
35+
+type function_definition =
36+
+ | Single_non_recursive
37+
+ | Multiple_recursive of Ident.t list
38+
+
39+
+let closure_entries fun_defs fvs =
40+
+ let funct_entries, pos_end_functs =
41+
+ match fun_defs with
42+
+ | Single_non_recursive ->
43+
+ (* No need to store the function in the environment, but we still need to
44+
+ reserve a slot in the closure block *)
45+
+ Ident.empty, 3
46+
+ | Multiple_recursive functs ->
47+
+ add_positions Ident.empty (fun pos -> Function pos) ~pos:0 ~delta:3 functs
48+
+ in
49+
+ (* Note: [pos_end_functs] is the position where we would store the next
50+
+ function if there was one, and points after an eventual infix tag.
51+
+ Since that was the last function, we don't need the last infix tag
52+
+ and start storing free variables at [pos_end_functs - 1]. *)
53+
+ let all_entries, _end_pos =
54+
+ add_positions funct_entries (fun pos -> Free_variable pos)
55+
+ ~pos:(pos_end_functs - 1) ~delta:1 fvs
56+
+ in
57+
+ all_entries
58+
+
59+
(**** Examination of the continuation ****)
60+
61+
(* Return a label to the beginning of the given continuation.
62+
@@ -354,9 +387,8 @@ type function_to_compile =
63+
{ params: Ident.t list; (* function parameters *)
64+
body: lambda; (* the function body *)
65+
label: label; (* the label of the function entry *)
66+
- free_vars: Ident.t list; (* free variables of the function *)
67+
- num_defs: int; (* number of mutually recursive definitions *)
68+
- rec_vars: Ident.t list; (* mutually recursive fn names *)
69+
+ entries: closure_entry Ident.tbl; (* the offsets for the free variables
70+
+ and mutually recursive functions *)
71+
rec_pos: int } (* rank in recursive definition *)
72+
73+
let functions_to_compile = (Stack.create () : function_to_compile Stack.t)
74+
@@ -545,15 +577,18 @@ let rec comp_expr env exp sz cont =
75+
let pos = Ident.find_same id env.ce_stack in
76+
Kacc(sz - pos) :: cont
77+
with Not_found ->
78+
- try
79+
- let pos = Ident.find_same id env.ce_heap in
80+
- Kenvacc(pos) :: cont
81+
- with Not_found ->
82+
- try
83+
- let ofs = Ident.find_same id env.ce_rec in
84+
- Koffsetclosure(ofs) :: cont
85+
- with Not_found ->
86+
+ let not_found () =
87+
fatal_error ("Bytegen.comp_expr: var " ^ Ident.unique_name id)
88+
+ in
89+
+ match env.ce_closure with
90+
+ | Not_in_closure -> not_found ()
91+
+ | In_closure { entries; env_pos } ->
92+
+ match Ident.find_same id entries with
93+
+ | Free_variable pos ->
94+
+ Kenvacc(pos - env_pos) :: cont
95+
+ | Function pos ->
96+
+ Koffsetclosure(pos - env_pos) :: cont
97+
+ | exception Not_found -> not_found ()
98+
end
99+
| Lconst cst ->
100+
Kconst cst :: cont
101+
@@ -601,9 +636,10 @@ let rec comp_expr env exp sz cont =
102+
let cont = add_pseudo_event loc !compunit_name cont in
103+
let lbl = new_label() in
104+
let fv = Ident.Set.elements(free_variables exp) in
105+
+ let entries = closure_entries Single_non_recursive fv in
106+
let to_compile =
107+
{ params = List.map fst params; body = body; label = lbl;
108+
- free_vars = fv; num_defs = 1; rec_vars = []; rec_pos = 0 } in
109+
+ entries = entries; rec_pos = 0 } in
110+
Stack.push to_compile functions_to_compile;
111+
comp_args env (List.map (fun n -> Lvar n) fv) sz
112+
(Kclosure(lbl, List.length fv) :: cont)
113+
@@ -620,14 +656,16 @@ let rec comp_expr env exp sz cont =
114+
let fv =
115+
Ident.Set.elements (free_variables (Lletrec(decl, lambda_unit))) in
116+
let rec_idents = List.map (fun (id, _lam) -> id) decl in
117+
+ let entries =
118+
+ closure_entries (Multiple_recursive rec_idents) fv
119+
+ in
120+
let rec comp_fun pos = function
121+
[] -> []
122+
| (_id, Lfunction{params; body}) :: rem ->
123+
let lbl = new_label() in
124+
let to_compile =
125+
{ params = List.map fst params; body = body; label = lbl;
126+
- free_vars = fv; num_defs = ndecl; rec_vars = rec_idents;
127+
- rec_pos = pos} in
128+
+ entries = entries; rec_pos = pos} in
129+
Stack.push to_compile functions_to_compile;
130+
lbl :: comp_fun (pos + 1) rem
131+
| _ -> assert false in
132+
@@ -1060,13 +1098,15 @@ let comp_block env exp sz cont =
133+
134+
let comp_function tc cont =
135+
let arity = List.length tc.params in
136+
- let rec positions pos delta = function
137+
- [] -> Ident.empty
138+
- | id :: rem -> Ident.add id pos (positions (pos + delta) delta rem) in
139+
+ let ce_stack, _last_pos =
140+
+ add_positions Ident.empty Fun.id ~pos:arity ~delta:(-1) tc.params
141+
+ in
142+
let env =
143+
- { ce_stack = positions arity (-1) tc.params;
144+
- ce_heap = positions (3 * (tc.num_defs - tc.rec_pos) - 1) 1 tc.free_vars;
145+
- ce_rec = positions (-3 * tc.rec_pos) 3 tc.rec_vars } in
146+
+ { ce_stack;
147+
+ ce_closure =
148+
+ In_closure { entries = tc.entries; env_pos = 3 * tc.rec_pos }
149+
+ }
150+
+ in
151+
let cont =
152+
comp_block env tc.body arity (Kreturn arity :: cont) in
153+
if arity > 1 then
154+
diff --git a/bytecomp/instruct.ml b/bytecomp/instruct.ml
155+
index c684bedf99..c1ecf1f51a 100644
156+
--- a/bytecomp/instruct.ml
157+
+++ b/bytecomp/instruct.ml
158+
@@ -15,10 +15,20 @@
159+
160+
open Lambda
161+
162+
+type closure_entry =
163+
+ | Free_variable of int
164+
+ | Function of int
165+
+
166+
+type closure_env =
167+
+ | Not_in_closure
168+
+ | In_closure of {
169+
+ entries: closure_entry Ident.tbl;
170+
+ env_pos: int;
171+
+ }
172+
+
173+
type compilation_env =
174+
{ ce_stack: int Ident.tbl;
175+
- ce_heap: int Ident.tbl;
176+
- ce_rec: int Ident.tbl }
177+
+ ce_closure: closure_env }
178+
179+
type debug_event =
180+
{ mutable ev_pos: int; (* Position in bytecode *)
181+
diff --git a/bytecomp/instruct.mli b/bytecomp/instruct.mli
182+
index e1cae776b0..a95990f153 100644
183+
--- a/bytecomp/instruct.mli
184+
+++ b/bytecomp/instruct.mli
185+
@@ -19,21 +19,35 @@ open Lambda
186+
187+
(* Structure of compilation environments *)
188+
189+
+type closure_entry =
190+
+ | Free_variable of int
191+
+ | Function of int
192+
+
193+
+type closure_env =
194+
+ | Not_in_closure
195+
+ | In_closure of {
196+
+ entries: closure_entry Ident.tbl; (* Offsets of the free variables and
197+
+ recursive functions from the start of
198+
+ the block *)
199+
+ env_pos: int; (* Offset of the current function from
200+
+ the start of the block *)
201+
+ }
202+
+
203+
type compilation_env =
204+
- { ce_stack: int Ident.tbl; (* Positions of variables in the stack *)
205+
- ce_heap: int Ident.tbl; (* Structure of the heap-allocated env *)
206+
- ce_rec: int Ident.tbl } (* Functions bound by the same let rec *)
207+
+ { ce_stack: int Ident.tbl; (* Positions of variables in the stack *)
208+
+ ce_closure: closure_env } (* Structure of the heap-allocated env *)
209+
210+
(* The ce_stack component gives locations of variables residing
211+
in the stack. The locations are offsets w.r.t. the origin of the
212+
stack frame.
213+
- The ce_heap component gives the positions of variables residing in the
214+
- heap-allocated environment.
215+
- The ce_rec component associates offsets to identifiers for functions
216+
- bound by the same let rec as the current function. The offsets
217+
- are used by the OFFSETCLOSURE instruction to recover the closure
218+
- pointer of the desired function from the env register (which
219+
- points to the closure for the current function). *)
220+
+ The ce_closure component gives the positions of variables residing in the
221+
+ heap-allocated environment. The env_pos component gives the position of
222+
+ the current function from the start of the closure block, and the entries
223+
+ component gives the positions of free variables and functions bound by the
224+
+ same let rec as the current function, from the start of the closure block.
225+
+ These are used by the ENVACC and OFFSETCLOSURE instructions to recover the
226+
+ relevant value from the env register (which points to the current function).
227+
+*)
228+
229+
(* Debugging events *)
230+
231+
diff --git a/debugger/eval.ml b/debugger/eval.ml
232+
index a47f381de5..9d9a8c090b 100644
233+
--- a/debugger/eval.ml
234+
+++ b/debugger/eval.ml
235+
@@ -47,20 +47,28 @@ let rec address path event = function
236+
Debugcom.Remote_value.global (Symtable.get_global_position id)
237+
with Symtable.Error _ -> raise(Error(Unbound_identifier id))
238+
else
239+
+ let not_found () =
240+
+ raise(Error(Unbound_identifier id))
241+
+ in
242+
begin match event with
243+
Some {ev_ev = ev} ->
244+
begin try
245+
let pos = Ident.find_same id ev.ev_compenv.ce_stack in
246+
Debugcom.Remote_value.local (ev.ev_stacksize - pos)
247+
with Not_found ->
248+
- try
249+
- let pos = Ident.find_same id ev.ev_compenv.ce_heap in
250+
- Debugcom.Remote_value.from_environment pos
251+
- with Not_found ->
252+
- raise(Error(Unbound_identifier id))
253+
+ match ev.ev_compenv.ce_closure with
254+
+ | Not_in_closure -> not_found ()
255+
+ | In_closure { entries; env_pos } ->
256+
+ match Ident.find_same id entries with
257+
+ | Free_variable pos ->
258+
+ Debugcom.Remote_value.from_environment (pos - env_pos)
259+
+ | Function _pos ->
260+
+ (* Recursive functions seem to be unhandled *)
261+
+ not_found ()
262+
+ | exception Not_found -> not_found ()
263+
end
264+
| None ->
265+
- raise(Error(Unbound_identifier id))
266+
+ not_found ()
267+
end
268+
| Env.Adot(root, pos) ->
269+
let v = address path event root in
270+
diff --git a/middle_end/closure/closure.ml b/middle_end/closure/closure.ml
271+
index ac18435189..0fb50cb880 100644
272+
--- a/middle_end/closure/closure.ml
273+
+++ b/middle_end/closure/closure.ml
274+
@@ -50,13 +50,6 @@ let rec split_list n l =
275+
| a::l -> let (l1, l2) = split_list (n-1) l in (a::l1, l2)
276+
end
277+
278+
-let rec build_closure_env env_param pos = function
279+
- [] -> V.Map.empty
280+
- | id :: rem ->
281+
- V.Map.add id
282+
- (Uprim(P.Pfield pos, [Uvar env_param], Debuginfo.none))
283+
- (build_closure_env env_param (pos+1) rem)
284+
-
285+
(* Auxiliary for accessing globals. We change the name of the global
286+
to the name of the corresponding asm symbol. This is done here
287+
and no longer in Cmmgen so that approximations stored in .cmx files
288+
@@ -701,9 +694,21 @@ let rec substitute loc ((backend, fpc) as st) sb rn ulam =
289+
| Uunreachable ->
290+
Uunreachable
291+
292+
+type closure_entry =
293+
+ | Free_variable of int
294+
+ | Function of int
295+
+
296+
+type closure_env =
297+
+ | Not_in_closure
298+
+ | In_closure of {
299+
+ entries: closure_entry V.Map.t;
300+
+ env_param: V.t;
301+
+ env_pos: int;
302+
+ }
303+
+
304+
type env = {
305+
backend : (module Backend_intf.S);
306+
- cenv : ulambda V.Map.t;
307+
+ cenv : closure_env;
308+
fenv : value_approximation V.Map.t;
309+
mutable_vars : V.Set.t;
310+
}
311+
@@ -882,8 +887,20 @@ let close_approx_var { fenv; cenv } id =
312+
match approx with
313+
Value_const c -> make_const c
314+
| approx ->
315+
- let subst = try V.Map.find id cenv with Not_found -> Uvar id in
316+
- (subst, approx)
317+
+ match cenv with
318+
+ | Not_in_closure -> Uvar id, approx
319+
+ | In_closure { entries; env_param; env_pos } ->
320+
+ let subst =
321+
+ match V.Map.find id entries with
322+
+ | Free_variable fv_pos ->
323+
+ Uprim(P.Pfield (fv_pos - env_pos),
324+
+ [Uvar env_param],
325+
+ Debuginfo.none)
326+
+ | Function fun_pos ->
327+
+ Uoffset(Uvar env_param, fun_pos - env_pos)
328+
+ | exception Not_found -> Uvar id
329+
+ in
330+
+ (subst, approx)
331+
332+
let close_var env id =
333+
let (ulam, _app) = close_approx_var env id in ulam
334+
@@ -1296,16 +1313,29 @@ and close_functions { backend; fenv; cenv; mutable_vars } fun_defs =
335+
(* This reference will be set to false if the hypothesis that a function
336+
does not use its environment parameter is invalidated. *)
337+
let useless_env = ref initially_closed in
338+
+ let cenv_entries =
339+
+ let rec free_variables_entries fv_pos = function
340+
+ [] -> V.Map.empty
341+
+ | id :: rem ->
342+
+ V.Map.add id (Free_variable fv_pos)
343+
+ (free_variables_entries (fv_pos+1) rem)
344+
+ in
345+
+ let entries_fv = free_variables_entries fv_pos fv in
346+
+ List.fold_right2
347+
+ (fun (id, _params, _return, _body, _fundesc, _dbg) pos env ->
348+
+ V.Map.add id (Function pos) env)
349+
+ uncurried_defs clos_offsets entries_fv
350+
+ in
351+
(* Translate each function definition *)
352+
let clos_fundef (id, params, return, body, fundesc, dbg) env_pos =
353+
let env_param = V.create_local "env" in
354+
- let cenv_fv =
355+
- build_closure_env env_param (fv_pos - env_pos) fv in
356+
let cenv_body =
357+
- List.fold_right2
358+
- (fun (id, _params, _return, _body, _fundesc, _dbg) pos env ->
359+
- V.Map.add id (Uoffset(Uvar env_param, pos - env_pos)) env)
360+
- uncurried_defs clos_offsets cenv_fv in
361+
+ In_closure {
362+
+ entries = cenv_entries;
363+
+ env_param;
364+
+ env_pos;
365+
+ }
366+
+ in
367+
let (ubody, approx) =
368+
close { backend; fenv = fenv_rec; cenv = cenv_body; mutable_vars } body
369+
in
370+
@@ -1514,7 +1544,7 @@ let intro ~backend ~size lam =
371+
Compilenv.set_global_approx(Value_tuple !global_approx);
372+
let (ulam, _approx) =
373+
close { backend; fenv = V.Map.empty;
374+
- cenv = V.Map.empty; mutable_vars = V.Set.empty } lam
375+
+ cenv = Not_in_closure; mutable_vars = V.Set.empty } lam
376+
in
377+
let opaque =
378+
!Clflags.opaque

0 commit comments

Comments
 (0)