Skip to content

Commit 3069360

Browse files
committed
Merge pull request #91 from bloomberg/lambda_exports
enhance cross module inlining, add a stream test case
2 parents 8889176 + 585a4ec commit 3069360

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+1940
-692
lines changed

jscomp/compiler.mllib

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ lam_compile_env
3232
lam_dispatch_primitive
3333
lam_stats
3434
lam_stats_util
35+
lam_stats_export
3536
lam_util
3637
lam_pass_alpha_conversion
3738
lam_pass_remove_alias

jscomp/lam_analysis.ml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,3 +457,20 @@ let is_closed lam =
457457
Ident_map.for_all (fun k _ -> Ident.global k)
458458
(free_variables Ident_set.empty Ident_map.empty lam)
459459

460+
461+
let is_closed_with_map exports params body =
462+
let param_map = free_variables exports (param_map_of_list params) body in
463+
let old_count = List.length params in
464+
let new_count = Ident_map.cardinal param_map in
465+
(old_count = new_count, param_map)
466+
467+
468+
469+
(* TODO: We can relax this a bit later,
470+
but decide whether to inline it later in the call site
471+
*)
472+
let safe_to_inline (lam : Lambda.lambda) =
473+
match lam with
474+
| Lfunction _ -> true
475+
| Lconst (Const_pointer _ | Const_immstring _ ) -> true
476+
| _ -> false

jscomp/lam_analysis.mli

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ val is_closed : Lambda.lambda -> bool
4141

4242

4343

44-
4544
type stats =
4645
{
4746
mutable top : bool ;
@@ -57,6 +56,10 @@ type stats =
5756
mutable times : int ;
5857
}
5958

59+
val is_closed_with_map :
60+
Ident_set.t ->
61+
Ident.t list -> Lambda.lambda -> bool * stats Ident_map.t
62+
6063
val param_map_of_list : Ident.t list -> stats Ident_map.t
6164

6265
val free_variables : Ident_set.t -> stats Ident_map.t -> Lambda.lambda -> stats Ident_map.t
@@ -65,3 +68,4 @@ val small_inline_size : int
6568
val exit_inline_size : int
6669

6770

71+
val safe_to_inline : Lambda.lambda -> bool

jscomp/lam_beta_reduce.mli

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,15 @@ val refresh :
5050
Lambda.lambda ->
5151
Lambda.lambda
5252

53+
(**
54+
{[ Lam_beta_reduce.propogate_beta_reduce_with_map meta param_map params body args]}
55+
[param_map] collect the usage of parameters,
56+
it can be produced by
57+
{[!Lam_analysis.free_variables meta.export_idents
58+
(Lam_analysis.param_map_of_list params) body]}
59+
*)
5360
val propogate_beta_reduce_with_map :
5461
Lam_stats.meta ->
5562
Lam_analysis.stats Ident_map.t ->
56-
Ident_map.key list ->
63+
Ident.t list ->
5764
Lambda.lambda -> Lambda.lambda list -> Lambda.lambda

jscomp/lam_compile.ml

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,26 @@ let rec
7575
it is very small
7676
TODO: add comment here, we should try to add comment for
7777
cross module inlining
78-
*)
78+
79+
if we do too agressive inlining here:
80+
81+
if we inline {!List.length} which will call {!A_list.length},
82+
then we if we try inline {!A_list.length}, this means if {!A_list}
83+
is rebuilt, this module should also be rebuilt,
84+
85+
But if the build system is content-based, suppose {!A_list}
86+
is changed, cmj files in {!List} is unchnaged, however,
87+
{!List.length} call {!A_list.length} which is changed, since
88+
[ocamldep] only detect that we depend on {!List}, it will not
89+
get re-built, then we are screwed.
90+
91+
This is okay for stamp based build system.
92+
93+
Another solution is that we add dependencies in the compiler
94+
95+
-: we should not do functor application inlining in a
96+
non-toplevel, it will explode code very quickly
97+
*)
7998
->
8099
compile_lambda cxt lam
81100
| _ ->
@@ -100,25 +119,6 @@ let rec
100119

101120
and get_exp_with_args (cxt : Lam_compile_defs.cxt) lam args_lambda
102121
(id : Ident.t) (pos : int) env : Js_output.t =
103-
let args_code, args =
104-
List.fold_right
105-
(fun (x : Lambda.lambda) (args_code, args) ->
106-
match x with
107-
| Lprim (Pgetglobal i, [] ) ->
108-
(* when module is passed as an argument - unpack to an array
109-
for the function, generative module or functor can be a function,
110-
however it can not be global -- global can only module
111-
*)
112-
113-
args_code, (Lam_compile_global.get_exp (i, env, true) :: args)
114-
| _ ->
115-
begin match compile_lambda {cxt with st = NeedValue; should_return = False} x with
116-
| {block = a; value = Some b} ->
117-
(a @ args_code), (b :: args )
118-
| _ -> assert false
119-
end
120-
) args_lambda ([], []) in
121-
122122
Lam_compile_env.find_and_add_if_not_exist (id,pos) env ~not_found:(fun id ->
123123
(** This can not happen since this id should be already consulted by type checker
124124
Worst case
@@ -127,26 +127,50 @@ and get_exp_with_args (cxt : Lam_compile_defs.cxt) lam args_lambda
127127
]}
128128
shift by one (due to module encoding)
129129
*)
130-
Js_output.handle_block_return cxt.st cxt.should_return lam args_code @@
131-
E.str ~pure:false (Printf.sprintf "Err %s %d %d"
132-
id.name
133-
id.flags
134-
pos
135-
))
130+
(* Js_output.handle_block_return cxt.st cxt.should_return lam args_code @@ *)
131+
(* E.str ~pure:false (Printf.sprintf "Err %s %d %d" *)
132+
(* id.name *)
133+
(* id.flags *)
134+
(* pos *)
135+
(* ) *)
136+
assert false
137+
)
136138

137139
~found:(fun {id; name;arity; closed_lambda ; _} ->
140+
let args_code, args =
141+
List.fold_right
142+
(fun (x : Lambda.lambda) (args_code, args) ->
143+
match x with
144+
| Lprim (Pgetglobal i, [] ) ->
145+
(* when module is passed as an argument - unpack to an array
146+
for the function, generative module or functor can be a function,
147+
however it can not be global -- global can only module
148+
*)
149+
150+
args_code, (Lam_compile_global.get_exp (i, env, true) :: args)
151+
| _ ->
152+
begin match compile_lambda {cxt with st = NeedValue; should_return = False} x with
153+
| {block = a; value = Some b} ->
154+
(a @ args_code), (b :: args )
155+
| _ -> assert false
156+
end
157+
) args_lambda ([], []) in
158+
159+
138160
match closed_lambda with
139161
| Some (Lfunction (_, params, body))
140162
when Ext_list.same_length params args_lambda ->
163+
(* TODO: serialize it when exporting to save compile time *)
164+
let (_, param_map) =
165+
Lam_analysis.is_closed_with_map Ident_set.empty params body in
141166
compile_lambda cxt
142-
(Lam_beta_reduce.propogate_beta_reduce cxt.meta params body args_lambda)
167+
(Lam_beta_reduce.propogate_beta_reduce_with_map cxt.meta param_map
168+
params body args_lambda)
143169
| _ ->
144170
Js_output.handle_block_return cxt.st cxt.should_return lam args_code @@
145171
(match id, name, args with
146172
| {name = "Pervasives"; _}, "^", [ e0 ; e1] ->
147173
E.string_append e0 e1
148-
| {name = "Pervasives"; _}, "string_of_int", [e]
149-
-> E.int_to_string e
150174
| {name = "Pervasives"; _}, "print_endline", ([ _ ] as args) ->
151175
E.seq (E.dump Log args) (E.unit ())
152176
| {name = "Pervasives"; _}, "prerr_endline", ([ _ ] as args) ->

jscomp/lam_compile_group.ml

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,9 @@ let compile ~filename non_export env _sigs lam =
182182
let lam = Lam_pass_remove_alias.simplify_alias meta lam in
183183
let lam = Lam_group.deep_flatten lam in
184184
let () = Lam_pass_collect.collect_helper meta lam in
185-
let () = ignore @@ _d lam in
186-
187185
let lam =
188186
lam
187+
|> _d
189188
|> Lam_pass_alpha_conversion.alpha_conversion meta
190189
|> Lam_pass_exits.simplify_exits in
191190
let () = Lam_pass_collect.collect_helper meta lam in
@@ -215,19 +214,39 @@ let compile ~filename non_export env _sigs lam =
215214

216215
begin
217216
match (lam : Lambda.lambda) with
218-
| Lprim(Psetglobal id, [biglambda]) (* ATT: might be wrong in toplevel *) ->
217+
| Lprim(Psetglobal id, [biglambda])
218+
->
219+
(* Invariant: The last one is always [exports]
220+
Compile definitions
221+
Compile exports
222+
Assume Pmakeblock(_,_),
223+
lambda_exports are pure
224+
compile each binding with a return value
225+
This might be wrong in toplevel
226+
*)
227+
219228
begin
220229
match Lam_group.flatten [] biglambda with
221230
| Lprim( (Pmakeblock (_,_,_), lambda_exports)), rest ->
222-
let coercion_groups, new_exports =
231+
let coercion_groups, new_exports, new_export_set, export_map =
223232
if non_export then
224-
[], []
233+
[], [], Ident_set.empty, Ident_map.empty
225234
else
226235
List.fold_right2
227-
(fun eid lam (coercions, new_exports) ->
236+
(fun eid lam (coercions, new_exports, new_export_set, export_map) ->
228237
match (lam : Lambda.lambda) with
229-
| Lvar id when Ident.name id = Ident.name eid ->
230-
(coercions, id :: new_exports)
238+
| Lvar id
239+
when Ident.name id = Ident.name eid ->
240+
(* {[ Ident.same id eid]} is more correct,
241+
however, it will introduce
242+
a coercion, which is not necessary,
243+
as long as its name is the same, we want to avoid
244+
another coercion
245+
*)
246+
(coercions,
247+
id :: new_exports,
248+
Ident_set.add id new_export_set,
249+
export_map)
231250
| _ -> (** TODO : bug
232251
check [map.ml] here coercion, we introduced
233252
rebound which is not corrrect
@@ -243,15 +262,25 @@ let compile ~filename non_export env _sigs lam =
243262
however
244263
*)
245264
(Lam_group.Single(Strict ,eid, lam) :: coercions,
246-
eid :: new_exports))
247-
meta.exports lambda_exports ([],[])
265+
eid :: new_exports,
266+
Ident_set.add eid new_export_set,
267+
Ident_map.add eid lam export_map))
268+
meta.exports lambda_exports
269+
([],[], Ident_set.empty, Ident_map.empty)
248270
in
249271

250272
let meta = { meta with
251-
export_idents = Lam_util.ident_set_of_list new_exports;
273+
export_idents = new_export_set;
252274
exports = new_exports
253275
} in
254-
let rest = List.rev_append rest coercion_groups in
276+
let (export_map, rest) =
277+
List.fold_left
278+
(fun (export_map, acc) x ->
279+
(match (x : Lam_group.t) with
280+
| Single (_,id,lam) when Ident_set.mem id new_export_set
281+
-> Ident_map.add id lam export_map
282+
| _ -> export_map), x :: acc ) (export_map, coercion_groups) rest in
283+
255284
let () =
256285
if not @@ Ext_string.is_empty filename
257286
then
@@ -261,13 +290,6 @@ let compile ~filename non_export env _sigs lam =
261290
Format.pp_print_list ~pp_sep:Format.pp_print_newline
262291
(Lam_group.pp_group env) fmt rest ;
263292
in
264-
(* Invariant: The last one is always [exports]
265-
Compile definitions
266-
Compile exports
267-
Assume Pmakeblock(_,_),
268-
lambda_exports are pure
269-
compile each binding with a return value
270-
*)
271293
let rest = Lam_dce.remove meta.exports rest
272294
in
273295
let module E = struct exception Not_pure of string end in
@@ -335,8 +357,8 @@ let compile ~filename non_export env _sigs lam =
335357

336358
(* Exporting ... *)
337359
let v =
338-
Lam_stats_util.export_to_cmj meta maybe_pure external_module_ids
339-
(if non_export then [] else lambda_exports)
360+
Lam_stats_export.export_to_cmj meta maybe_pure external_module_ids
361+
(if non_export then Ident_map.empty else export_map)
340362
in
341363
(if not @@ Ext_string.is_empty filename then
342364
Js_cmj_format.to_file
@@ -357,7 +379,7 @@ let lambda_as_module
357379
(lam : Lambda.lambda) =
358380
begin
359381
Lam_current_unit.set_file filename ;
360-
Lam_current_unit.set_debug_file "pervasives.ml";
382+
Lam_current_unit.iset_debug_file "caml_string.ml";
361383
Ext_pervasives.with_file_as_chan
362384
(Ext_filename.chop_extension ~loc:__LOC__ filename ^ ".js")
363385
(fun chan -> Js_dump.dump_deps_program (compile ~filename false env sigs lam) chan)

jscomp/lam_dispatch_primitive.ml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -517,16 +517,22 @@ let query (prim : Lam_compile_env.primitive_description)
517517
E.runtime_call Js_config.obj_runtime prim.prim_name args
518518

519519
| "caml_format_float"
520-
| "caml_format_int"
520+
521521
| "caml_nativeint_format"
522522
| "caml_int32_format"
523523
| "caml_float_of_string"
524524
| "caml_int_of_string" (* what is the semantics?*)
525525
| "caml_int32_of_string"
526526
| "caml_nativeint_of_string" ->
527527
E.runtime_call Js_config.format prim.prim_name args
528-
529-
528+
| "caml_format_int" ->
529+
begin match args with
530+
| [ {expression_desc = Str (_, "%d"); _}; v]
531+
->
532+
E.int_to_string v
533+
| _ ->
534+
E.runtime_call Js_config.format prim.prim_name args
535+
end
530536
(* "caml_alloc_dummy"; *)
531537
(* TODO: "caml_alloc_dummy_float"; *)
532538
| "caml_update_dummy"

jscomp/lam_pass_remove_alias.ml

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ let simplify_alias
7676
| {closed_lambda=Some Lfunction(Curried, params, body) }
7777
(** be more cautious when do cross module inlining *)
7878
when
79-
(
79+
( Ext_list.same_length params args &&
8080
List.for_all (fun (arg : Lambda.lambda) ->
8181
match arg with
8282
| Lvar p ->
@@ -126,18 +126,19 @@ let simplify_alias
126126
end
127127
else
128128
if lam_size < Lam_analysis.small_inline_size then
129-
let param_fresh_map = Lam_analysis.param_map_of_list params in
129+
130+
(* let param_map = *)
131+
(* Lam_analysis.free_variables meta.export_idents *)
132+
(* (Lam_analysis.param_map_of_list params) body in *)
133+
(* let old_count = List.length params in *)
134+
(* let new_count = Ident_map.cardinal param_map in *)
130135
let param_map =
131-
Lam_analysis.free_variables meta.export_idents param_fresh_map body in
132-
let old_count = List.length params in
133-
let new_count = Ident_map.cardinal param_map in
134-
if
135-
(
136-
not (Ident_set.mem v meta.export_idents)
137-
|| old_count = new_count
138-
)
139-
140-
then
136+
Lam_analysis.is_closed_with_map
137+
meta.export_idents params body in
138+
let is_export_id = Ident_set.mem v meta.export_idents in
139+
match is_export_id, param_map with
140+
| false, (_, param_map)
141+
| true, (true, param_map) ->
141142
if rec_flag = Rec then
142143
begin
143144
(* Ext_log.dwarn __LOC__ "beta rec.. %s/%d" v.name v.stamp ; *)
@@ -153,7 +154,7 @@ let simplify_alias
153154
simpl (Lam_beta_reduce.propogate_beta_reduce_with_map meta param_map params body args)
154155

155156
end
156-
else
157+
| _ ->
157158
Lapply ( simpl l1, List.map simpl args, info)
158159
else
159160
begin

0 commit comments

Comments
 (0)