Skip to content

Commit 01d2711

Browse files
authored
Global flow analysis: performance improvements (#1900)
* Global flow analysis: performance improvement Avoid quadratic behavior for functions with many returned values. * More precise global flow analysis with --opt 2
1 parent 3d71bec commit 01d2711

File tree

4 files changed

+33
-71
lines changed

4 files changed

+33
-71
lines changed

compiler/lib/code.ml

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,6 @@ module Var : sig
9898

9999
type 'a t
100100

101-
module DataSet : sig
102-
type 'a t
103-
104-
val iter : ('a -> unit) -> 'a t -> unit
105-
106-
val fold : ('a -> 'acc -> 'acc) -> 'a t -> 'acc -> 'acc
107-
end
108-
109101
type size = unit
110102

111103
val get : 'a t -> key -> 'a
@@ -116,10 +108,6 @@ module Var : sig
116108

117109
val make : size -> 'a -> 'a t
118110

119-
val make_set : size -> 'a DataSet.t t
120-
121-
val add_set : 'a DataSet.t t -> key -> 'a -> unit
122-
123111
val iter : (key -> 'a -> unit) -> 'a t -> unit
124112
end
125113

@@ -203,24 +191,6 @@ end = struct
203191
module Tbl = struct
204192
type 'a t = 'a array
205193

206-
module DataSet = struct
207-
type 'a t =
208-
| Empty
209-
| One of 'a
210-
| Many of ('a, unit) Hashtbl.t
211-
212-
let iter f = function
213-
| Empty -> ()
214-
| One a -> f a
215-
| Many t -> Hashtbl.iter (fun k () -> f k) t
216-
217-
let fold f t acc =
218-
match t with
219-
| Empty -> acc
220-
| One a -> f a acc
221-
| Many t -> Hashtbl.fold (fun k () acc -> f k acc) t acc
222-
end
223-
224194
type key = T.t
225195

226196
type size = unit
@@ -233,18 +203,6 @@ end = struct
233203

234204
let make () v = Array.make (count ()) v
235205

236-
let make_set () = Array.make (count ()) DataSet.Empty
237-
238-
let add_set t x k =
239-
match t.(x) with
240-
| DataSet.Empty -> t.(x) <- One k
241-
| One k' ->
242-
let tbl = Hashtbl.create 0 in
243-
Hashtbl.replace tbl k' ();
244-
Hashtbl.replace tbl k ();
245-
t.(x) <- Many tbl
246-
| Many tbl -> Hashtbl.replace tbl k ()
247-
248206
let iter f t =
249207
for i = 0 to Array.length t - 1 do
250208
f i (Array.unsafe_get t i)

compiler/lib/code.mli

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,6 @@ module Var : sig
8989
module Tbl : sig
9090
type key = t
9191

92-
module DataSet : sig
93-
type 'a t
94-
95-
val iter : ('a -> unit) -> 'a t -> unit
96-
97-
val fold : ('a -> 'acc -> 'acc) -> 'a t -> 'acc -> 'acc
98-
end
99-
10092
type 'a t
10193

10294
type size = unit
@@ -109,10 +101,6 @@ module Var : sig
109101

110102
val make : size -> 'a -> 'a t
111103

112-
val make_set : size -> 'a DataSet.t t
113-
114-
val add_set : 'a DataSet.t t -> key -> 'a -> unit
115-
116104
val iter : (key -> 'a -> unit) -> 'a t -> unit
117105
end
118106

compiler/lib/driver.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ let exact_calls profile ~deadcode_sentinal p =
128128
| `Disabled | `Jspi ->
129129
let fast =
130130
match profile with
131-
| O3 -> false
132-
| O1 | O2 -> true
131+
| O2 | O3 -> false
132+
| O1 -> true
133133
in
134134
let info = Global_flow.f ~fast p in
135135
let p =

compiler/lib/global_flow.ml

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ let times = Debug.find "times"
3131

3232
open Code
3333

34+
let associated_list h x = try Var.Hashtbl.find h x with Not_found -> []
35+
36+
let add_to_list h x v = Var.Hashtbl.replace h x (v :: associated_list h x)
37+
3438
(****)
3539

3640
(* Compute the list of variables containing the return values of each
@@ -83,7 +87,7 @@ type escape_status =
8387

8488
type state =
8589
{ vars : Var.ISet.t (* Set of all veriables considered *)
86-
; deps : Var.t Var.Tbl.DataSet.t Var.Tbl.t (* Dependency between variables *)
90+
; deps : Var.t list Var.Tbl.t (* Dependency between variables *)
8791
; defs : def array (* Definition of each variable *)
8892
; variable_may_escape : escape_status array
8993
(* Any value bound to this variable may escape *)
@@ -93,20 +97,24 @@ type state =
9397
; possibly_mutable : Var.ISet.t (* This value may be mutable *)
9498
; return_values : Var.Set.t Var.Map.t
9599
(* Set of variables holding return values of each function *)
96-
; known_cases : (Var.t, int list) Hashtbl.t
100+
; functions_from_returned_value : Var.t list Var.Hashtbl.t
101+
(* Functions associated to each return value *)
102+
; known_cases : int list Var.Hashtbl.t
97103
(* Possible tags for a block after a [switch]. This is used to
98104
get a more precise approximation of the effect of a field
99105
access [Field] *)
100106
; applied_functions : (Var.t * Var.t, unit) Hashtbl.t
101107
(* Functions that have been already considered at a call site.
102108
This is to avoid repeated computations *)
109+
; function_call_sites : Var.t list Var.Hashtbl.t
110+
(* Known call sites of each functions *)
103111
; fast : bool
104112
}
105113

106114
let add_var st x = Var.ISet.add st.vars x
107115

108116
(* x depends on y *)
109-
let add_dep st x y = Var.Tbl.add_set st.deps y x
117+
let add_dep st x y = Var.Tbl.set st.deps y (x :: Var.Tbl.get st.deps y)
110118

111119
let add_expr_def st x e =
112120
add_var st x;
@@ -223,10 +231,10 @@ let expr_deps blocks st x e =
223231
match st.defs.(Var.idx f) with
224232
| Expr (Closure (params, _)) when List.length args = List.length params ->
225233
Hashtbl.add st.applied_functions (x, f) ();
234+
add_to_list st.function_call_sites f x;
226235
if st.fast
227236
then List.iter ~f:(fun a -> do_escape st Escape a) args
228-
else List.iter2 ~f:(fun p a -> add_assign_def st p a) params args;
229-
Var.Set.iter (fun y -> add_dep st x y) (Var.Map.find f st.return_values)
237+
else List.iter2 ~f:(fun p a -> add_assign_def st p a) params args
230238
| _ -> ())
231239
| Closure (l, cont) ->
232240
List.iter l ~f:(fun x -> add_param_def st x);
@@ -285,7 +293,7 @@ let program_deps st { start; blocks; _ } =
285293
~f:(fun i ->
286294
match i with
287295
| Let (y, Field (x', _, _)) when Var.equal b x' ->
288-
Hashtbl.add st.known_cases y tags
296+
Var.Hashtbl.add st.known_cases y tags
289297
| _ -> ())
290298
block.body)
291299
h
@@ -420,7 +428,7 @@ let propagate st ~update approx x =
420428
match Var.Tbl.get approx y with
421429
| Values { known; others } ->
422430
let tags =
423-
try Some (Hashtbl.find st.known_cases x) with Not_found -> None
431+
try Some (Var.Hashtbl.find st.known_cases x) with Not_found -> None
424432
in
425433
Domain.join_set
426434
~others
@@ -506,6 +514,7 @@ let propagate st ~update approx x =
506514
if not (Hashtbl.mem st.applied_functions (x, g))
507515
then (
508516
Hashtbl.add st.applied_functions (x, g) ();
517+
add_to_list st.function_call_sites g x;
509518
if st.fast
510519
then
511520
List.iter
@@ -518,18 +527,15 @@ let propagate st ~update approx x =
518527
add_assign_def st p a;
519528
update ~children:false p)
520529
params
521-
args;
522-
Var.Set.iter
523-
(fun y -> add_dep st x y)
524-
(Var.Map.find g st.return_values));
530+
args);
525531
Domain.join_set
526532
~update
527533
~st
528534
~approx
529535
(fun y -> Var.Tbl.get approx y)
530536
(Var.Map.find g st.return_values)
531537
| Expr (Closure (_, _)) ->
532-
(* The funciton is partially applied or over applied *)
538+
(* The function is partially applied or over applied *)
533539
List.iter
534540
~f:(fun y -> Domain.variable_escape ~update ~st ~approx Escape y)
535541
args;
@@ -599,7 +605,11 @@ let solver st =
599605
let g =
600606
{ G.domain = st.vars
601607
; G.iter_children =
602-
(fun f x -> Var.Tbl.DataSet.iter (fun k -> f k) (Var.Tbl.get st.deps x))
608+
(fun f x ->
609+
List.iter ~f (Var.Tbl.get st.deps x);
610+
List.iter
611+
~f:(fun g -> List.iter ~f (associated_list st.function_call_sites g))
612+
(associated_list st.functions_from_returned_value x))
603613
}
604614
in
605615
let res = Solver.f' () g (propagate st) in
@@ -632,24 +642,30 @@ let f ~fast p =
632642
let rets = return_values p in
633643
let nv = Var.count () in
634644
let vars = Var.ISet.empty () in
635-
let deps = Var.Tbl.make_set () in
645+
let deps = Var.Tbl.make () [] in
636646
let defs = Array.make nv undefined in
637647
let variable_may_escape = Array.make nv No in
638648
let variable_possibly_mutable = Var.ISet.empty () in
639649
let may_escape = Array.make nv No in
640650
let possibly_mutable = Var.ISet.empty () in
651+
let functions_from_returned_value = Var.Hashtbl.create 128 in
652+
Var.Map.iter
653+
(fun f s -> Var.Set.iter (fun x -> add_to_list functions_from_returned_value x f) s)
654+
rets;
641655
let st =
642656
{ vars
643657
; deps
644658
; defs
645659
; return_values = rets
660+
; functions_from_returned_value
646661
; variable_may_escape
647662
; variable_possibly_mutable
648663
; may_escape
649664
; possibly_mutable
650-
; known_cases = Hashtbl.create 16
665+
; known_cases = Var.Hashtbl.create 16
651666
; applied_functions = Hashtbl.create 16
652667
; fast
668+
; function_call_sites = Var.Hashtbl.create 128
653669
}
654670
in
655671
program_deps st p;

0 commit comments

Comments
 (0)