Skip to content

Commit 1ed2368

Browse files
committed
Compiler: propagate arity across unit boundary
Propagate shape information through the flow analysis Function arity from shapes: take advantage of flow analysis
1 parent fea3899 commit 1ed2368

22 files changed

+2772
-2841
lines changed

compiler/bin-js_of_ocaml/build_fs.ml

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ function jsoo_create_file_extern(name,content){
7474
let code = Code.prepend Code.empty instr in
7575
Filename.gen_file output_file (fun chan ->
7676
let pfs_fmt = Pretty_print.to_out_channel chan in
77-
let (_ : Source_map.info) =
77+
let (_ : Source_map.info * Shape.t StringMap.t) =
7878
Driver.f
7979
~standalone:true
8080
~wrap_with_fun:`Iife

compiler/bin-js_of_ocaml/cmd_arg.ml

+9
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type t =
5353
; static_env : (string * string) list
5454
; wrap_with_fun : [ `Iife | `Named of string | `Anonymous ]
5555
; target_env : Target_env.t
56+
; shape_files : string list
5657
; (* toplevel *)
5758
dynlink : bool
5859
; linkall : bool
@@ -102,6 +103,10 @@ let options =
102103
let doc = "Set output file name to [$(docv)]." in
103104
Arg.(value & opt (some string) None & info [ "o" ] ~docv:"FILE" ~doc)
104105
in
106+
let shape_files =
107+
let doc = "load shape file [$(docv)]." in
108+
Arg.(value & opt_all string [] & info [ "load" ] ~docv:"FILE" ~doc)
109+
in
105110
let input_file =
106111
let doc =
107112
"Compile the bytecode program [$(docv)]. "
@@ -279,6 +284,7 @@ let options =
279284
output_file
280285
input_file
281286
js_files
287+
shape_files
282288
keep_unit_names =
283289
let inline_source_content = not sourcemap_don't_inline_content in
284290
let chop_extension s = try Filename.chop_extension s with Invalid_argument _ -> s in
@@ -341,6 +347,7 @@ let options =
341347
; bytecode
342348
; source_map
343349
; keep_unit_names
350+
; shape_files
344351
}
345352
in
346353
let t =
@@ -371,6 +378,7 @@ let options =
371378
$ output_file
372379
$ input_file
373380
$ js_files
381+
$ shape_files
374382
$ keep_unit_names)
375383
in
376384
Term.ret t
@@ -567,6 +575,7 @@ let options_runtime_only =
567575
; bytecode = `None
568576
; source_map
569577
; keep_unit_names = false
578+
; shape_files = []
570579
}
571580
in
572581
let t =

compiler/bin-js_of_ocaml/cmd_arg.mli

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type t =
3737
| `Anonymous
3838
]
3939
; target_env : Target_env.t
40+
; shape_files : string list
4041
; (* toplevel *)
4142
dynlink : bool
4243
; linkall : bool

compiler/bin-js_of_ocaml/compile.ml

+35-17
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,13 @@ let output_gen ~standalone ~custom_header ~build_info ~source_map output_file f
5252
Driver.configure fmt;
5353
if standalone then header ~custom_header fmt;
5454
if Config.Flag.header () then jsoo_header fmt build_info;
55-
let sm = f ~standalone ~source_map (k, fmt) in
55+
let sm, shapes = f ~standalone ~source_map (k, fmt) in
56+
(match output_file with
57+
| `Stdout -> ()
58+
| `Name name ->
59+
Shape.Store.save'
60+
(Filename.remove_extension name ^ Shape.Store.ext)
61+
(StringMap.bindings shapes));
5662
match source_map, sm with
5763
| No_sourcemap, _ | _, None -> ()
5864
| ((Inline | File _) as output), Some sm ->
@@ -70,7 +76,6 @@ let output_gen ~standalone ~custom_header ~build_info ~source_map output_file f
7076
Pretty_print.newline fmt;
7177
Pretty_print.string fmt (Printf.sprintf "//# sourceMappingURL=%s\n" urlData)
7278
in
73-
7479
match output_file with
7580
| `Stdout -> f stdout `Stdout
7681
| `Name name -> Filename.gen_file name (fun chan -> f chan `File)
@@ -130,6 +135,11 @@ let sourcemap_of_infos ~base l =
130135

131136
let sourcemap_of_info ~base info = sourcemap_of_infos ~base [ info ]
132137

138+
let map_fst f (x, y) = f x, y
139+
140+
let merge_shape a b =
141+
StringMap.union (fun _name s1 s2 -> if Shape.equal s1 s2 then Some s1 else None) a b
142+
133143
let run
134144
{ Cmd_arg.common
135145
; profile
@@ -153,6 +163,7 @@ let run
153163
; export_file
154164
; keep_unit_names
155165
; include_runtime
166+
; shape_files
156167
} =
157168
let source_map_base = Option.map ~f:snd source_map in
158169
let source_map =
@@ -172,6 +183,7 @@ let run
172183
| `Name _, _ -> ());
173184
List.iter params ~f:(fun (s, v) -> Config.Param.set s v);
174185
List.iter static_env ~f:(fun (s, v) -> Eval.set_static_env s v);
186+
List.iter shape_files ~f:(fun fn -> Shape.Store.load' fn);
175187
let t = Timer.make () in
176188
let include_dirs =
177189
List.filter_map (include_dirs @ [ "+stdlib/" ]) ~f:(fun d -> Findlib.find [] d)
@@ -381,7 +393,7 @@ let run
381393
~standalone
382394
~link:`All
383395
output_file
384-
|> sourcemap_of_info ~base:source_map_base)
396+
|> map_fst (sourcemap_of_info ~base:source_map_base))
385397
| (`Stdin | `File _) as bytecode ->
386398
let kind, ic, close_ic, include_dirs =
387399
match bytecode with
@@ -427,7 +439,7 @@ let run
427439
~source_map
428440
~link:(if linkall then `All else `Needed)
429441
output_file
430-
|> sourcemap_of_info ~base:source_map_base)
442+
|> map_fst (sourcemap_of_info ~base:source_map_base))
431443
| `Cmo cmo ->
432444
let output_file =
433445
match output_file, keep_unit_names with
@@ -460,12 +472,13 @@ let run
460472
(fun ~standalone ~source_map output ->
461473
match include_runtime with
462474
| true ->
463-
let sm1 = output_partial_runtime ~standalone ~source_map output in
464-
let sm2 = output_partial cmo code ~standalone ~source_map output in
465-
sourcemap_of_infos ~base:source_map_base [ sm1; sm2 ]
475+
let sm1, sh1 = output_partial_runtime ~standalone ~source_map output in
476+
let sm2, sh2 = output_partial cmo code ~standalone ~source_map output in
477+
( sourcemap_of_infos ~base:source_map_base [ sm1; sm2 ]
478+
, merge_shape sh1 sh2 )
466479
| false ->
467480
output_partial cmo code ~standalone ~source_map output
468-
|> sourcemap_of_info ~base:source_map_base)
481+
|> map_fst (sourcemap_of_info ~base:source_map_base))
469482
| `Cma cma when keep_unit_names ->
470483
(if include_runtime
471484
then
@@ -488,7 +501,7 @@ let run
488501
(`Name output_file)
489502
(fun ~standalone ~source_map output ->
490503
output_partial_runtime ~standalone ~source_map output
491-
|> sourcemap_of_info ~base:source_map_base));
504+
|> map_fst (sourcemap_of_info ~base:source_map_base)));
492505
List.iter cma.lib_units ~f:(fun cmo ->
493506
let output_file =
494507
match output_file with
@@ -524,16 +537,16 @@ let run
524537
(`Name output_file)
525538
(fun ~standalone ~source_map output ->
526539
output_partial ~standalone ~source_map cmo code output
527-
|> sourcemap_of_info ~base:source_map_base))
540+
|> map_fst (sourcemap_of_info ~base:source_map_base)))
528541
| `Cma cma ->
529542
let f ~standalone ~source_map output =
530-
let source_map_runtime =
543+
let runtime =
531544
if not include_runtime
532545
then None
533546
else Some (output_partial_runtime ~standalone ~source_map output)
534547
in
535548

536-
let source_map_units =
549+
let units =
537550
List.map cma.lib_units ~f:(fun cmo ->
538551
let t1 = Timer.make () in
539552
let code =
@@ -553,12 +566,17 @@ let run
553566
(Ocaml_compiler.Cmo_format.name cmo);
554567
output_partial ~standalone ~source_map cmo code output)
555568
in
556-
let sm =
557-
match source_map_runtime with
558-
| None -> source_map_units
559-
| Some x -> x :: source_map_units
569+
let sm_and_shapes =
570+
match runtime with
571+
| None -> units
572+
| Some x -> x :: units
573+
in
574+
let shapes =
575+
List.fold_left sm_and_shapes ~init:StringMap.empty ~f:(fun acc (_, s) ->
576+
merge_shape s acc)
560577
in
561-
sourcemap_of_infos ~base:source_map_base sm
578+
( sourcemap_of_infos ~base:source_map_base (List.map sm_and_shapes ~f:fst)
579+
, shapes )
562580
in
563581
output_gen
564582
~standalone:false

compiler/lib/code.ml

+6-2
Original file line numberDiff line numberDiff line change
@@ -556,13 +556,17 @@ module Print = struct
556556
if exact
557557
then Format.fprintf f "%a!(%a)" Var.print g var_list args
558558
else Format.fprintf f "%a(%a)" Var.print g var_list args
559-
| Block (t, a, _, mut) ->
559+
| Block (t, a, k, mut) ->
560560
Format.fprintf
561561
f
562-
"%s{tag=%d"
562+
"{%s%s:tag=%d"
563563
(match mut with
564564
| Immutable -> "imm"
565565
| Maybe_mutable -> "")
566+
(match k with
567+
| Array -> "A"
568+
| NotArray -> "NA"
569+
| Unknown -> "U")
566570
t;
567571
for i = 0 to Array.length a - 1 do
568572
Format.fprintf f "; %d = %a" i Var.print a.(i)

compiler/lib/driver.ml

+54-10
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type optimized_result =
2929
; trampolined_calls : Effects.trampolined_calls
3030
; in_cps : Effects.in_cps
3131
; deadcode_sentinal : Code.Var.t
32+
; shapes : Shape.t StringMap.t
3233
}
3334

3435
type profile =
@@ -95,7 +96,9 @@ let phi p =
9596

9697
let ( +> ) f g x = g (f x)
9798

98-
let map_fst f (x, y, z) = f x, y, z
99+
let map_fst4 f (x, y, z, t) = f x, y, z, t
100+
101+
let map_fst3 f (x, y, z) = f x, y, z
99102

100103
let effects ~deadcode_sentinal p =
101104
if Config.Flag.effects ()
@@ -112,7 +115,7 @@ let effects ~deadcode_sentinal p =
112115
Deadcode.f p
113116
else p, live_vars
114117
in
115-
p |> Effects.f ~flow_info:info ~live_vars +> map_fst Lambda_lifting.f)
118+
p |> Effects.f ~flow_info:info ~live_vars +> map_fst3 Lambda_lifting.f)
116119
else
117120
( p
118121
, (Code.Var.Set.empty : Effects.trampolined_calls)
@@ -202,7 +205,13 @@ let generate
202205
~exported_runtime
203206
~wrap_with_fun
204207
~warn_on_unhandled_effect
205-
{ program; variable_uses; trampolined_calls; deadcode_sentinal; in_cps = _ } =
208+
{ program
209+
; variable_uses
210+
; trampolined_calls
211+
; deadcode_sentinal
212+
; in_cps = _
213+
; shapes = _
214+
} =
206215
if times () then Format.eprintf "Start Generation...@.";
207216
let should_export = should_export wrap_with_fun in
208217
Generate.f
@@ -659,6 +668,30 @@ if (typeof module === 'object' && module.exports) {
659668
if times () then Format.eprintf " optimizing: %a@." Timer.print t;
660669
js
661670

671+
let collects_shapes p =
672+
let _, info = Flow.f p in
673+
let pure = Pure_fun.f p in
674+
let l = ref StringMap.empty in
675+
Code.Addr.Map.iter
676+
(fun _ block ->
677+
List.iter block.Code.body ~f:(fun i ->
678+
match i with
679+
| Code.Let
680+
( _
681+
, Prim
682+
( Extern "caml_register_global"
683+
, [ _code; Pv block; Pc (NativeString name) ] ) ) ->
684+
let shape = Flow.the_shape_of ~pure info block in
685+
let name =
686+
match name with
687+
| Byte s -> s
688+
| Utf (Utf8 s) -> s
689+
in
690+
l := StringMap.add name shape !l
691+
| _ -> ()))
692+
p.blocks;
693+
!l
694+
662695
let configure formatter =
663696
let pretty = Config.Flag.pretty () in
664697
Pretty_print.set_compact formatter (not pretty);
@@ -689,18 +722,21 @@ let optimize ~profile p =
689722
| O2 -> o2
690723
| O3 -> o3)
691724
+> exact_calls ~deadcode_sentinal profile
692-
+> effects ~deadcode_sentinal
693-
+> map_fst
725+
+> (fun p -> p, collects_shapes p)
726+
+> (fun (p, shapes) ->
727+
let p, trampolined_calls, cps = effects ~deadcode_sentinal p in
728+
p, trampolined_calls, cps, shapes)
729+
+> map_fst4
694730
(match Config.target (), Config.Flag.effects () with
695731
| `JavaScript, false -> Generate_closure.f
696732
| `JavaScript, true | `Wasm, _ -> Fun.id)
697-
+> map_fst deadcode'
733+
+> map_fst4 deadcode'
698734
in
699735
if times () then Format.eprintf "Start Optimizing...@.";
700736
let t = Timer.make () in
701-
let (program, variable_uses), trampolined_calls, in_cps = opt p in
737+
let (program, variable_uses), trampolined_calls, in_cps, shapes = opt p in
702738
let () = if times () then Format.eprintf " optimizations : %a@." Timer.print t in
703-
{ program; variable_uses; trampolined_calls; in_cps; deadcode_sentinal }
739+
{ program; variable_uses; trampolined_calls; in_cps; deadcode_sentinal; shapes }
704740

705741
let full ~standalone ~wrap_with_fun ~profile ~link ~source_map ~formatter d p =
706742
let optimized_code = optimize ~profile p in
@@ -710,10 +746,18 @@ let full ~standalone ~wrap_with_fun ~profile ~link ~source_map ~formatter d p =
710746
+> link_and_pack ~standalone ~wrap_with_fun ~link
711747
+> output formatter ~source_map ()
712748
in
713-
emit formatter optimized_code
749+
let shapes = optimized_code.shapes in
750+
StringMap.iter
751+
(fun name shape ->
752+
Shape.Store.set ~name shape;
753+
Pretty_print.string
754+
formatter
755+
(Printf.sprintf "//# shape: %s:%s\n" name (Shape.to_string shape)))
756+
shapes;
757+
emit formatter optimized_code, shapes
714758

715759
let full_no_source_map ~formatter ~standalone ~wrap_with_fun ~profile ~link d p =
716-
let (_ : Source_map.info) =
760+
let (_ : Source_map.info * _) =
717761
full ~standalone ~wrap_with_fun ~profile ~link ~source_map:false ~formatter d p
718762
in
719763
()

compiler/lib/driver.mli

+4-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
1919
*)
2020

21+
open Stdlib
22+
2123
type profile
2224

2325
type optimized_result =
@@ -26,6 +28,7 @@ type optimized_result =
2628
; trampolined_calls : Effects.trampolined_calls
2729
; in_cps : Effects.in_cps
2830
; deadcode_sentinal : Code.Var.t
31+
; shapes : Shape.t StringMap.t
2932
}
3033

3134
val optimize : profile:profile -> Code.program -> optimized_result
@@ -39,7 +42,7 @@ val f :
3942
-> formatter:Pretty_print.t
4043
-> Parse_bytecode.Debug.t
4144
-> Code.program
42-
-> Source_map.info
45+
-> Source_map.info * Shape.t StringMap.t
4346

4447
val f' :
4548
?standalone:bool

0 commit comments

Comments
 (0)