@@ -38,89 +38,60 @@ let add_required_modules ( x : Ident.t list) (meta : Lam_stats.t) =
38
38
*)
39
39
40
40
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
124
95
125
96
let alias_ident_or_global (meta : Lam_stats.t ) (k :Ident.t ) (v :Ident.t )
126
97
(v_kind : Lam_id_kind.t ) =
@@ -263,11 +234,3 @@ let is_var (lam : Lam.t) id =
263
234
lapply (let a = 3 in let b = 4 in fun x y -> x + y) 2 3
264
235
]}
265
236
*)
266
-
267
-
268
-
269
-
270
-
271
-
272
-
273
-
0 commit comments