Skip to content

Commit 7009978

Browse files
author
Hongbo Zhang
committed
enhance cross module inlining, add a stream test case
1 parent 8889176 commit 7009978

Some content is hidden

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

87 files changed

+1853
-648
lines changed

jscomp/compiler.mllib

+1
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

+8
Original file line numberDiff line numberDiff line change
@@ -457,3 +457,11 @@ 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+
(* TODO: We can relax this a bit later,
461+
but decide whether to inline it later in the call site
462+
*)
463+
let safe_to_inline (lam : Lambda.lambda) =
464+
match lam with
465+
| Lfunction _ -> true
466+
| Lconst (Const_pointer _ | Const_immstring _ ) -> true
467+
| _ -> false

jscomp/lam_analysis.mli

+1
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,4 @@ val small_inline_size : int
6565
val exit_inline_size : int
6666

6767

68+
val safe_to_inline : Lambda.lambda -> bool

jscomp/lam_compile.ml

+20-3
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
| _ ->
@@ -145,8 +164,6 @@ and get_exp_with_args (cxt : Lam_compile_defs.cxt) lam args_lambda
145164
(match id, name, args with
146165
| {name = "Pervasives"; _}, "^", [ e0 ; e1] ->
147166
E.string_append e0 e1
148-
| {name = "Pervasives"; _}, "string_of_int", [e]
149-
-> E.int_to_string e
150167
| {name = "Pervasives"; _}, "print_endline", ([ _ ] as args) ->
151168
E.seq (E.dump Log args) (E.unit ())
152169
| {name = "Pervasives"; _}, "prerr_endline", ([ _ ] as args) ->

jscomp/lam_compile_group.ml

+43-21
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

jscomp/lam_dispatch_primitive.ml

+9-3
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_stats_export.ml

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
(* OCamlScript compiler
2+
* Copyright (C) 2015-2016 Bloomberg Finance L.P.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License as published by
6+
* the Free Software Foundation, with linking exception;
7+
* either version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17+
*)
18+
19+
(* Author: Hongbo Zhang *)
20+
21+
let pp = Format.fprintf
22+
(* we should exclude meaninglist names and do the convert as well *)
23+
24+
let meaningless_names = ["*opt*"; "param";]
25+
26+
let rec dump_ident fmt (id : Ident.t) (arity : Lam_stats.function_arities) =
27+
pp fmt "@[<2>export var %s:@ %a@ ;@]" (Ext_ident.convert id.name ) dump_arity arity
28+
29+
and dump_arity fmt (arity : Lam_stats.function_arities) =
30+
match arity with
31+
| NA -> pp fmt "any"
32+
| Determin (_, [], _) -> pp fmt "any"
33+
| Determin (_, (_,args)::xs, _) ->
34+
pp fmt "@[(%a)@ =>@ any@]"
35+
(Format.pp_print_list
36+
~pp_sep:(fun fmt _ ->
37+
Format.pp_print_string fmt ",";
38+
Format.pp_print_space fmt ();
39+
)
40+
(fun fmt ident -> pp fmt "@[%s@ :@ any@]"
41+
(Ext_ident.convert @@ Ident.name ident))
42+
) args
43+
44+
45+
(* Note that
46+
[lambda_exports] is
47+
lambda expression to be exported
48+
for the js backend, we compile to js
49+
for the inliner, we try to seriaize it --
50+
relies on other optimizations to make this happen
51+
{[
52+
exports.Make = function () {.....}
53+
]}
54+
TODO: check that we don't do this in browser environment
55+
*)
56+
let export_to_cmj
57+
(meta : Lam_stats.meta )
58+
maybe_pure
59+
external_ids
60+
export_map
61+
62+
: Js_cmj_format.cmj_table =
63+
let values =
64+
65+
List.fold_left
66+
(fun acc (x : Ident.t) ->
67+
let arity = Lam_stats_util.get_arity meta (Lvar x) in
68+
match Ident_map.find x export_map with
69+
| lambda ->
70+
if Lam_analysis.safe_to_inline lambda
71+
(* when inlning a non function, we have to be very careful,
72+
only truly immutable values can be inlined
73+
*)
74+
then
75+
let closed_lambda =
76+
if Lam_inline_util.should_be_functor x.name lambda (* can also be submodule *)
77+
then
78+
if Lam_analysis.is_closed lambda (* TODO: seriealize more*)
79+
then Some lambda
80+
else None
81+
else
82+
let lam_size = Lam_analysis.size lambda in
83+
let free_variables =
84+
Lam_analysis.free_variables Ident_set.empty Ident_map.empty
85+
lambda in
86+
if lam_size < Lam_analysis.small_inline_size &&
87+
Ident_map.is_empty free_variables
88+
(* TODO:
89+
1. global need re-assocate when do the beta reduction
90+
2. [lambda_exports] is not precise
91+
*)
92+
then
93+
begin
94+
(* Ext_log.dwarn __LOC__ "%s recorded for inlining @." x.name ; *)
95+
Some lambda
96+
end
97+
else
98+
begin
99+
(* Ext_log.dwarn __LOC__ "%s : %d : {%s} not inlined @." *)
100+
(* x.name lam_size *)
101+
(* (String.concat ", " @@ *)
102+
(* List.map (fun x -> x.Ident.name) @@ Ident_map.keys free_variables) ; *)
103+
None
104+
end
105+
in
106+
String_map.add x.name Js_cmj_format.{arity ; closed_lambda } acc
107+
else
108+
String_map.add x.name Js_cmj_format.{arity ; closed_lambda = None } acc
109+
| exception Not_found
110+
-> String_map.add x.name Js_cmj_format.{arity ; closed_lambda = None} acc
111+
)
112+
String_map.empty
113+
meta.exports
114+
115+
116+
in
117+
118+
let rec dump fmt ids =
119+
(* TODO: also use {[Ext_pp]} module instead *)
120+
match ids with
121+
| [] -> ()
122+
| x::xs ->
123+
dump_ident fmt x (Lam_stats_util.get_arity meta (Lvar x)) ;
124+
Format.pp_print_space fmt ();
125+
dump fmt xs in
126+
127+
let () =
128+
if not @@ Ext_string.is_empty meta.filename then
129+
Ext_pervasives.with_file_as_pp
130+
(Ext_filename.chop_extension ~loc:__LOC__ meta.filename ^ ".d.ts")
131+
@@ fun fmt ->
132+
pp fmt "@[<v>%a@]@." dump meta.exports
133+
in
134+
let pure =
135+
match maybe_pure with
136+
| None ->
137+
Ext_option.bind ( Ext_list.for_all_ret
138+
(fun (id : Lam_module_ident.t) ->
139+
Lam_compile_env.query_and_add_if_not_exist id meta.env
140+
~not_found:(fun _ -> false ) ~found:(fun i ->
141+
i.pure)
142+
) external_ids) (fun x -> Lam_module_ident.name x)
143+
| Some _ -> maybe_pure
144+
145+
in
146+
{values; pure }
147+

jscomp/lam_stats_export.mli

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
(* OCamlScript compiler
2+
* Copyright (C) 2015-2016 Bloomberg Finance L.P.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License as published by
6+
* the Free Software Foundation, with linking exception;
7+
* either version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17+
*)
18+
19+
(* Author: Hongbo Zhang *)
20+
21+
val export_to_cmj :
22+
Lam_stats.meta ->
23+
Js_cmj_format.effect ->
24+
Lam_module_ident.t list ->
25+
Lambda.lambda Ident_map.t -> Js_cmj_format.cmj_table
26+

0 commit comments

Comments
 (0)