@@ -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
12596let 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