Skip to content

Commit ae7a6e4

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 493c064 commit ae7a6e4

24 files changed

+2706
-2447
lines changed

compiler/bin-js_of_ocaml/build_fs.ml

Lines changed: 1 addition & 1 deletion
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

Lines changed: 9 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 35 additions & 17 deletions
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

Lines changed: 6 additions & 2 deletions
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/config.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ module Flag = struct
101101
let auto_link = o ~name:"auto-link" ~default:true
102102

103103
let es6 = o ~name:"es6" ~default:false
104+
105+
let load_shapes_auto = o ~name:"load-shapes-auto" ~default:false
104106
end
105107

106108
module Param = struct

compiler/lib/config.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ module Flag : sig
7575

7676
val es6 : unit -> bool
7777

78+
val load_shapes_auto : unit -> bool
79+
7880
val enable : string -> unit
7981

8082
val disable : string -> unit

compiler/lib/driver.ml

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@ let debug = Debug.find "main"
2323

2424
let times = Debug.find "times"
2525

26+
let debug_shapes = Debug.find "shapes"
27+
2628
type optimized_result =
2729
{ program : Code.program
2830
; variable_uses : Deadcode.variable_uses
2931
; trampolined_calls : Effects.trampolined_calls
3032
; in_cps : Effects.in_cps
3133
; deadcode_sentinal : Code.Var.t
34+
; shapes : Shape.t StringMap.t
3235
}
3336

3437
type profile =
@@ -95,7 +98,9 @@ let phi p =
9598

9699
let ( +> ) f g x = g (f x)
97100

98-
let map_fst f (x, y, z) = f x, y, z
101+
let map_fst4 f (x, y, z, t) = f x, y, z, t
102+
103+
let map_fst3 f (x, y, z) = f x, y, z
99104

100105
let effects ~deadcode_sentinal p =
101106
if Config.Flag.effects ()
@@ -112,7 +117,7 @@ let effects ~deadcode_sentinal p =
112117
Deadcode.f p
113118
else p, live_vars
114119
in
115-
p |> Effects.f ~flow_info:info ~live_vars +> map_fst Lambda_lifting.f)
120+
p |> Effects.f ~flow_info:info ~live_vars +> map_fst3 Lambda_lifting.f)
116121
else
117122
( p
118123
, (Code.Var.Set.empty : Effects.trampolined_calls)
@@ -202,7 +207,13 @@ let generate
202207
~exported_runtime
203208
~wrap_with_fun
204209
~warn_on_unhandled_effect
205-
{ program; variable_uses; trampolined_calls; deadcode_sentinal; in_cps = _ } =
210+
{ program
211+
; variable_uses
212+
; trampolined_calls
213+
; deadcode_sentinal
214+
; in_cps = _
215+
; shapes = _
216+
} =
206217
if times () then Format.eprintf "Start Generation...@.";
207218
let should_export = should_export wrap_with_fun in
208219
Generate.f
@@ -659,6 +670,30 @@ if (typeof module === 'object' && module.exports) {
659670
if times () then Format.eprintf " optimizing: %a@." Timer.print t;
660671
js
661672

673+
let collects_shapes p =
674+
let _, info = Flow.f p in
675+
let pure = Pure_fun.f p in
676+
let l = ref StringMap.empty in
677+
Code.Addr.Map.iter
678+
(fun _ block ->
679+
List.iter block.Code.body ~f:(fun i ->
680+
match i with
681+
| Code.Let
682+
( _
683+
, Prim
684+
( Extern "caml_register_global"
685+
, [ _code; Pv block; Pc (NativeString name) ] ) ) ->
686+
let shape = Flow.the_shape_of ~pure info block in
687+
let name =
688+
match name with
689+
| Byte s -> s
690+
| Utf (Utf8 s) -> s
691+
in
692+
l := StringMap.add name shape !l
693+
| _ -> ()))
694+
p.blocks;
695+
!l
696+
662697
let configure formatter =
663698
let pretty = Config.Flag.pretty () in
664699
Pretty_print.set_compact formatter (not pretty);
@@ -689,18 +724,21 @@ let optimize ~profile p =
689724
| O2 -> o2
690725
| O3 -> o3)
691726
+> exact_calls ~deadcode_sentinal profile
692-
+> effects ~deadcode_sentinal
693-
+> map_fst
727+
+> (fun p -> p, collects_shapes p)
728+
+> (fun (p, shapes) ->
729+
let p, trampolined_calls, cps = effects ~deadcode_sentinal p in
730+
p, trampolined_calls, cps, shapes)
731+
+> map_fst4
694732
(match Config.target (), Config.Flag.effects () with
695733
| `JavaScript, false -> Generate_closure.f
696734
| `JavaScript, true | `Wasm, _ -> Fun.id)
697-
+> map_fst deadcode'
735+
+> map_fst4 deadcode'
698736
in
699737
if times () then Format.eprintf "Start Optimizing...@.";
700738
let t = Timer.make () in
701-
let (program, variable_uses), trampolined_calls, in_cps = opt p in
739+
let (program, variable_uses), trampolined_calls, in_cps, shapes = opt p in
702740
let () = if times () then Format.eprintf " optimizations : %a@." Timer.print t in
703-
{ program; variable_uses; trampolined_calls; in_cps; deadcode_sentinal }
741+
{ program; variable_uses; trampolined_calls; in_cps; deadcode_sentinal; shapes }
704742

705743
let full ~standalone ~wrap_with_fun ~profile ~link ~source_map ~formatter d p =
706744
let optimized_code = optimize ~profile p in
@@ -710,10 +748,20 @@ let full ~standalone ~wrap_with_fun ~profile ~link ~source_map ~formatter d p =
710748
+> link_and_pack ~standalone ~wrap_with_fun ~link
711749
+> output formatter ~source_map ()
712750
in
713-
emit formatter optimized_code
751+
let shapes = optimized_code.shapes in
752+
if debug_shapes ()
753+
then
754+
StringMap.iter
755+
(fun name shape ->
756+
Shape.Store.set ~name shape;
757+
Pretty_print.string
758+
formatter
759+
(Printf.sprintf "//# shape: %s:%s\n" name (Shape.to_string shape)))
760+
shapes;
761+
emit formatter optimized_code, shapes
714762

715763
let full_no_source_map ~formatter ~standalone ~wrap_with_fun ~profile ~link d p =
716-
let (_ : Source_map.info) =
764+
let (_ : Source_map.info * _) =
717765
full ~standalone ~wrap_with_fun ~profile ~link ~source_map:false ~formatter d p
718766
in
719767
()

0 commit comments

Comments
 (0)