Skip to content

Commit b9076fb

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 b9076fb

File tree

2 files changed

+57
-32
lines changed

2 files changed

+57
-32
lines changed

compiler/lib/lambda_lifting_simple.ml

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -147,39 +147,60 @@ 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))
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 = Let (f'', Closure (List.map s ~f:snd, (pc'', []))) :: functions in
174+
let lifters = Var.Map.add f f' lifters in
175+
rewrite_body
176+
~to_lift
177+
~inside_lifted
178+
~current_contiguous:[]
179+
~st:(program, functions, lifters)
180+
~var_depth
181+
~acc_instr:
182+
(* Replace closure with application of the lifter function *)
183+
(Let (f, Apply { f = f''; args = List.map ~f:fst s; exact = true })
184+
:: acc_instr)
185+
~depth
186+
rem)
187+
else
188+
(* The closure doesn't have free variables, and thus doesn't need a lifter
189+
function. Just make sure it's a top-level function. *)
190+
let functions = Let (f, cl) :: functions in
191+
rewrite_body
192+
~to_lift
193+
~inside_lifted
194+
~var_depth
195+
~current_contiguous:[]
196+
~st:(program, functions, lifters)
197+
~acc_instr
198+
~depth
199+
rem
181200
| Let (cname, Closure (params, (pc', args))) :: rem ->
182-
(* More closure definitions follow: accumulate and lift later *)
201+
(* We do not lift an isolated closure: either more closure definitions follow, or
202+
the closure doesn't need to be lifted. In both cases, we accumulate it and will
203+
lift (or not) later. *)
183204
let st =
184205
rewrite_blocks
185206
~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)