Skip to content

Commit f3e92af

Browse files
OlivierNicolehhugo
authored andcommitted
Lambda lifting: only lift functions that have free variables
This addresses the second remark in #1792: lifted functions that have no free variables don't need to be wrapped.
1 parent 3d05b15 commit f3e92af

File tree

2 files changed

+58
-34
lines changed

2 files changed

+58
-34
lines changed

compiler/lib/lambda_lifting_simple.ml

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -147,41 +147,61 @@ and rewrite_body
147147
let s =
148148
Var.Set.fold (fun x m -> Var.Map.add x (Var.fork x) m) free_vars Var.Map.empty
149149
in
150-
let program = Subst.Excluding_Binders.cont (Subst.from_map s) pc' program in
151-
let f' = try Var.Map.find f s with Not_found -> Var.fork f in
152-
let s = Var.Map.bindings (Var.Map.remove f s) in
153-
let f'' = Var.fork f in
154-
if debug ()
155-
then
156-
Format.eprintf
157-
"LIFT %s (depth:%d free_vars:%d inner_depth:%d)@."
158-
(Code.Var.to_string f'')
159-
depth
160-
(Var.Set.cardinal free_vars)
161-
(compute_depth program pc');
162-
let pc'' = program.free_pc in
163-
let bl = { params = []; body = [ Let (f', cl) ]; branch = Return f' } in
164-
let program =
165-
{ program with free_pc = pc'' + 1; blocks = Addr.Map.add pc'' bl program.blocks }
166-
in
167-
(* Add to returned list of lifter functions definitions *)
168-
let functions =
169-
Let (f'', Closure (List.map s ~f:snd, (pc'', []), None)) :: functions
170-
in
171-
let lifters = Var.Map.add f f' lifters in
172-
rewrite_body
173-
~to_lift
174-
~inside_lifted
175-
~current_contiguous:[]
176-
~st:(program, functions, lifters)
177-
~var_depth
178-
~acc_instr:
179-
(* Replace closure with application of the lifter function *)
180-
(Let (f, Apply { f = f''; args = List.map ~f:fst s; exact = true }) :: acc_instr)
181-
~depth
182-
rem
150+
if not Var.Map.(is_empty (remove f s))
151+
then (
152+
let program = Subst.Excluding_Binders.cont (Subst.from_map s) pc' program in
153+
let f' = try Var.Map.find f s with Not_found -> Var.fork f in
154+
let f'' = Var.fork f in
155+
let s = Var.Map.bindings (Var.Map.remove f s) in
156+
if debug ()
157+
then
158+
Format.eprintf
159+
"LIFT %s (depth:%d free_vars:%d inner_depth:%d)@."
160+
(Code.Var.to_string f'')
161+
depth
162+
(Var.Set.cardinal free_vars)
163+
(compute_depth program pc');
164+
let pc'' = program.free_pc in
165+
let bl = { params = []; body = [ Let (f', cl) ]; branch = Return f' } in
166+
let program =
167+
{ program with
168+
free_pc = pc'' + 1
169+
; blocks = Addr.Map.add pc'' bl program.blocks
170+
}
171+
in
172+
(* Add to returned list of lifter functions definitions *)
173+
let functions =
174+
Let (f'', Closure (List.map s ~f:snd, (pc'', []), None)) :: functions in
175+
let lifters = Var.Map.add f f' lifters in
176+
rewrite_body
177+
~to_lift
178+
~inside_lifted
179+
~current_contiguous:[]
180+
~st:(program, functions, lifters)
181+
~var_depth
182+
~acc_instr:
183+
(* Replace closure with application of the lifter function *)
184+
(Let (f, Apply { f = f''; args = List.map ~f:fst s; exact = true })
185+
:: acc_instr)
186+
~depth
187+
rem)
188+
else
189+
(* The closure doesn't have free variables, and thus doesn't need a lifter
190+
function. Just make sure it's a top-level function. *)
191+
let functions = Let (f, cl) :: functions in
192+
rewrite_body
193+
~to_lift
194+
~inside_lifted
195+
~var_depth
196+
~current_contiguous:[]
197+
~st:(program, functions, lifters)
198+
~acc_instr
199+
~depth
200+
rem
183201
| Let (cname, Closure (params, (pc', args), cloc)) :: rem ->
184-
(* More closure definitions follow: accumulate and lift later *)
202+
(* We do not lift an isolated closure: either more closure definitions follow, or
203+
the closure doesn't need to be lifted. In both cases, we accumulate it and will
204+
lift (or not) later. *)
185205
let st =
186206
rewrite_blocks
187207
~to_lift

compiler/lib/lambda_lifting_simple.mli

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,8 @@ val f : to_lift:Var.Set.t -> program -> program * Var.t Var.Map.t
5050
fib 42
5151
5252
[fib_l] is the lifted version of [fib], [fib'] is the lifting closure.
53+
54+
Note that putting a function's name in [to_lift] is not a guarantee that
55+
it will be lambda-lifted: a function may end up unlifted if it has no
56+
free variables.
5357
*)

0 commit comments

Comments
 (0)