Skip to content

Commit 3a5a2d8

Browse files
committed
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 3d8e70d commit 3a5a2d8

File tree

2 files changed

+54
-32
lines changed

2 files changed

+54
-32
lines changed

compiler/lib/lambda_lifting_simple.ml

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