Skip to content

Commit a564d26

Browse files
committed
Cleanup
1 parent eb96936 commit a564d26

File tree

1 file changed

+54
-91
lines changed

1 file changed

+54
-91
lines changed

compiler/core/lam_util.ml

Lines changed: 54 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -38,89 +38,60 @@ let add_required_modules ( x : Ident.t list) (meta : Lam_stats.t) =
3838
*)
3939

4040

41-
(*
42-
It's impossible to have a case like below:
43-
{[
44-
(let export_f = ... in export_f)
45-
]}
46-
Even so, it's still correct
47-
*)
48-
let refine_let
49-
~kind param
50-
(arg : Lam.t) (l : Lam.t) : Lam.t =
51-
52-
match (kind : Lam_compat.let_kind ), arg, l with
53-
| _, _, Lvar w when Ident.same w param
54-
(* let k = xx in k
55-
there is no [rec] so [k] would not appear in [xx]
56-
*)
57-
-> arg (* TODO: optimize here -- it's safe to do substitution here *)
58-
| _, _, Lprim {primitive ; args = [Lvar w]; loc ; _} when Ident.same w param
59-
&& (function | Lam_primitive.Pmakeblock _ -> false | _ -> true) primitive
60-
(* don't inline inside a block *)
61-
-> Lam.prim ~primitive ~args:[arg] loc
62-
(* we can not do this substitution when capttured *)
63-
(* | _, Lvar _, _ -> (\** let u = h in xxx*\) *)
64-
(* (\* assert false *\) *)
65-
(* Ext_log.err "@[substitution >> @]@."; *)
66-
(* let v= subst_lambda (Map_ident.singleton param arg ) l in *)
67-
(* Ext_log.err "@[substitution << @]@."; *)
68-
(* v *)
69-
| _, _, Lapply {ap_func=fn; ap_args = [Lvar w]; ap_info; ap_transformed_jsx} when
70-
Ident.same w param &&
71-
(not (Lam_hit.hit_variable param fn ))
72-
->
73-
(* does not work for multiple args since
74-
evaluation order unspecified, does not apply
75-
for [js] in general, since the scope of js ir is loosen
76-
77-
here we remove the definition of [param]
78-
{[ let k = v in (body) k
79-
]}
80-
#1667 make sure body does not hit k
81-
*)
82-
Lam.apply fn [arg] ap_info ~ap_transformed_jsx
83-
| (Strict | StrictOpt ),
84-
( Lvar _ | Lconst _ |
85-
Lprim {primitive = Pfield (_ , Fld_module _) ;
86-
args = [ Lglobal_module _ | Lvar _ ]; _}) , _ ->
87-
(* (match arg with *)
88-
(* | Lconst _ -> *)
89-
(* Ext_log.err "@[%a %s@]@." *)
90-
(* Ident.print param (string_of_lambda arg) *)
91-
(* | _ -> ()); *)
92-
(* No side effect and does not depend on store,
93-
since function evaluation is always delayed
94-
*)
95-
Lam.let_ Alias param arg l
96-
| (Strict | StrictOpt),
97-
( Lprim {primitive = Psome_not_nest; _} as prim ), _ ->
98-
Lam.let_ Alias param prim l
99-
| ( (Strict | StrictOpt ) ), (Lfunction _ ), _ ->
100-
(*It can be promoted to [Alias], however,
101-
we don't want to do this, since we don't want the
102-
function to be inlined to a block, for example
103-
{[
104-
let f = fun _ -> 1 in
105-
[0, f]
106-
]}
107-
TODO: punish inliner to inline functions
108-
into a block
109-
*)
110-
Lam.let_ StrictOpt param arg l
111-
(* Not the case, the block itself can have side effects
112-
we can apply [no_side_effects] pass
113-
| Some Strict, Lprim(Pmakeblock (_,_,Immutable),_) ->
114-
Llet(StrictOpt, param, arg, l)
115-
*)
116-
| Strict, _ ,_ when Lam_analysis.no_side_effects arg ->
117-
Lam.let_ StrictOpt param arg l
118-
| Variable, _, _ ->
119-
Lam.let_ Variable param arg l
120-
| kind, _, _ ->
121-
Lam.let_ kind param arg l
122-
(* | None , _, _ ->
123-
Lam.let_ Strict param arg l *)
41+
(* refine_let normalises let-bindings so we avoid redundant locals while
42+
preserving evaluation semantics. *)
43+
let refine_let ~kind param (arg : Lam.t) (l : Lam.t) : Lam.t =
44+
let is_block_constructor = function
45+
| Lam_primitive.Pmakeblock _ -> true
46+
| _ -> false
47+
in
48+
let is_alias_candidate (lam : Lam.t) =
49+
match lam with
50+
| Lvar _ | Lconst _ -> true
51+
| Lprim { primitive = Psome_not_nest; _ } -> true
52+
| Lprim { primitive = Pfield (_, Fld_module _); args = [ (Lglobal_module _ | Lvar _) ]; _ } -> true
53+
| _ -> false
54+
in
55+
match (kind : Lam_compat.let_kind), arg, l with
56+
| _, _, Lvar w when Ident.same w param ->
57+
(* If the body immediately returns the binding (e.g. `{ let x = value; x }`),
58+
we skip creating `x` and keep `value`. There is no `rec`, so `value`
59+
cannot refer back to `x`, and we avoid generating a redundant local. *)
60+
arg
61+
| _, _, Lprim { primitive; args = [ Lvar w ]; loc; _ }
62+
when Ident.same w param && not (is_block_constructor primitive) ->
63+
(* When we immediately feed the binding into a primitive, like
64+
`{ let x = value; Array.length(x) }`, we inline the primitive call
65+
with `value`. This only happens for primitives that are pure and do not
66+
allocate new blocks, so evaluation order and side effects stay the same. *)
67+
Lam.prim ~primitive ~args:[arg] loc
68+
| _, _, Lapply { ap_func = fn; ap_args = [ Lvar w ]; ap_info; ap_transformed_jsx }
69+
when Ident.same w param && not (Lam_hit.hit_variable param fn) ->
70+
(* For a function call such as `{ let x = value; someFn(x) }`, we can
71+
rewrite to `someFn(value)` as long as the callee does not capture `x`.
72+
This removes the temporary binding while preserving the call semantics. *)
73+
Lam.apply fn [arg] ap_info ~ap_transformed_jsx
74+
| (Strict | StrictOpt), arg, _ when is_alias_candidate arg ->
75+
(* `Strict` and `StrictOpt` bindings both evaluate the RHS immediately
76+
(with `StrictOpt` allowing later elimination if unused). When that RHS
77+
is pure — `{ let x = Some(value); ... }`, `{ let x = 3; ... }`, or a module
78+
field read — we mark it as an alias so downstream passes can inline the
79+
original expression and drop the temporary. *)
80+
Lam.let_ Alias param arg l
81+
| Strict, Lfunction _, _ ->
82+
(* If we eagerly evaluate a function binding such as
83+
`{ let makeGreeting = () => "hi"; ... }`, we end up allocating the
84+
closure immediately. Downgrading `Strict` to `StrictOpt` preserves the
85+
original laziness while still letting later passes inline when safe. *)
86+
Lam.let_ StrictOpt param arg l
87+
| Strict, _, _ when Lam_analysis.no_side_effects arg ->
88+
(* A strict binding whose expression has no side effects — think
89+
`{ let x = computePure(); use(x); }` — can be relaxed to `StrictOpt`.
90+
This keeps the original semantics yet allows downstream passes to skip
91+
evaluating `x` when it turns out to be unused. *)
92+
Lam.let_ StrictOpt param arg l
93+
| kind, _, _ ->
94+
Lam.let_ kind param arg l
12495
12596
let alias_ident_or_global (meta : Lam_stats.t) (k:Ident.t) (v:Ident.t)
12697
(v_kind : Lam_id_kind.t) =
@@ -263,11 +234,3 @@ let is_var (lam : Lam.t) id =
263234
lapply (let a = 3 in let b = 4 in fun x y -> x + y) 2 3
264235
]}
265236
*)
266-
267-
268-
269-
270-
271-
272-
273-

0 commit comments

Comments
 (0)