Skip to content

Commit 23d4a99

Browse files
committed
Global flow analysis: keep track of which fields are mutable
1 parent 713bfba commit 23d4a99

File tree

1 file changed

+72
-22
lines changed

1 file changed

+72
-22
lines changed

compiler/lib/global_flow.ml

Lines changed: 72 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,21 @@ type escape_status =
8585
| Escape_constant (* Escapes but we know the value is not modified *)
8686
| No
8787

88+
type mutable_fields =
89+
| No_field
90+
| Some_fields of IntSet.t
91+
| All_fields
92+
8893
type state =
8994
{ vars : Var.ISet.t (* Set of all veriables considered *)
9095
; deps : Var.t list Var.Tbl.t (* Dependency between variables *)
9196
; defs : def array (* Definition of each variable *)
9297
; variable_may_escape : escape_status array
9398
(* Any value bound to this variable may escape *)
94-
; variable_possibly_mutable : Var.ISet.t
99+
; variable_mutable_fields : mutable_fields array
95100
(* Any value bound to this variable may be mutable *)
96101
; may_escape : escape_status array (* This value may escape *)
97-
; possibly_mutable : Var.ISet.t (* This value may be mutable *)
102+
; mutable_fields : mutable_fields array (* This value may be mutable *)
98103
; return_values : Var.Set.t Var.Map.t
99104
(* Set of variables holding return values of each function *)
100105
; functions_from_returned_value : Var.t list Var.Hashtbl.t
@@ -154,7 +159,14 @@ let cont_deps blocks st ?ignore (pc, args) =
154159

155160
let do_escape st level x = st.variable_may_escape.(Var.idx x) <- level
156161

157-
let possibly_mutable st x = Var.ISet.add st.variable_possibly_mutable x
162+
let possibly_mutable st x = st.variable_mutable_fields.(Var.idx x) <- All_fields
163+
164+
let field_possibly_mutable st x n =
165+
match st.variable_mutable_fields.(Var.idx x) with
166+
| No_field -> st.variable_mutable_fields.(Var.idx x) <- Some_fields (IntSet.singleton n)
167+
| Some_fields s ->
168+
st.variable_mutable_fields.(Var.idx x) <- Some_fields (IntSet.add n s)
169+
| All_fields -> ()
158170

159171
let expr_deps blocks st x e =
160172
match e with
@@ -259,7 +271,10 @@ let program_deps st { start; blocks; _ } =
259271
add_expr_def st x e;
260272
expr_deps blocks st x e
261273
| Assign (x, y) -> add_assign_def st x y
262-
| Set_field (x, _, _, y) | Array_set (x, _, y) ->
274+
| Set_field (x, n, _, y) ->
275+
field_possibly_mutable st x n;
276+
do_escape st Escape y
277+
| Array_set (x, _, y) ->
263278
possibly_mutable st x;
264279
do_escape st Escape y
265280
| Event _ | Offset_ref _ -> ());
@@ -352,7 +367,7 @@ module Domain = struct
352367
Array.iter ~f:(fun y -> variable_escape ~update ~st ~approx s y) a;
353368
match s, mut with
354369
| Escape, Maybe_mutable ->
355-
Var.ISet.add st.possibly_mutable x;
370+
st.mutable_fields.(Var.idx x) <- All_fields;
356371
update ~children:true x
357372
| (Escape_constant | No), _ | Escape, Immutable -> ())
358373
| Expr (Closure (params, _, _)) ->
@@ -397,18 +412,28 @@ module Domain = struct
397412
s
398413
(if o then others else bot)
399414

400-
let mark_mutable ~update ~st a =
415+
let mark_mutable ~update ~st a mutable_fields =
401416
match a with
402417
| Top -> ()
403418
| Values { known; _ } ->
404419
Var.Set.iter
405420
(fun x ->
406421
match st.defs.(Var.idx x) with
407-
| Expr (Block (_, _, _, Maybe_mutable)) ->
408-
if not (Var.ISet.mem st.possibly_mutable x)
409-
then (
410-
Var.ISet.add st.possibly_mutable x;
411-
update ~children:true x)
422+
| Expr (Block (_, _, _, Maybe_mutable)) -> (
423+
match st.mutable_fields.(Var.idx x), mutable_fields with
424+
| _, No_field -> ()
425+
| No_field, _ ->
426+
st.mutable_fields.(Var.idx x) <- mutable_fields;
427+
update ~children:true x
428+
| Some_fields s, Some_fields s' ->
429+
if IntSet.exists (fun i -> not (IntSet.mem i s)) s'
430+
then (
431+
st.mutable_fields.(Var.idx x) <- Some_fields (IntSet.union s s');
432+
update ~children:true x)
433+
| Some_fields _, All_fields ->
434+
st.mutable_fields.(Var.idx x) <- All_fields;
435+
update ~children:true x
436+
| All_fields, _ -> ())
412437
| Expr (Block (_, _, _, Immutable)) | Expr (Closure _) -> ()
413438
| Phi _ | Expr _ -> assert false)
414439
known
@@ -444,7 +469,12 @@ let propagate st ~update approx x =
444469
| Some tags -> List.memq t ~set:tags
445470
| None -> true ->
446471
let t = a.(n) in
447-
let m = Var.ISet.mem st.possibly_mutable z in
472+
let m =
473+
match st.mutable_fields.(Var.idx z) with
474+
| No_field -> false
475+
| Some_fields s -> IntSet.mem n s
476+
| All_fields -> true
477+
in
448478
if not m then add_dep st x z;
449479
add_dep st x t;
450480
let a = Var.Tbl.get approx t in
@@ -472,7 +502,11 @@ let propagate st ~update approx x =
472502
(fun z ->
473503
match st.defs.(Var.idx z) with
474504
| Expr (Block (_, lst, _, _)) ->
475-
let m = Var.ISet.mem st.possibly_mutable z in
505+
let m =
506+
match st.mutable_fields.(Var.idx z) with
507+
| No_field -> false
508+
| Some_fields _ | All_fields -> true
509+
in
476510
if not m then add_dep st x z;
477511
Array.iter ~f:(fun t -> add_dep st x t) lst;
478512
let a =
@@ -566,8 +600,9 @@ let propagate st ~update approx x =
566600
(match st.variable_may_escape.(Var.idx x) with
567601
| (Escape | Escape_constant) as s -> Domain.approx_escape ~update ~st ~approx s res
568602
| No -> ());
569-
if Var.ISet.mem st.variable_possibly_mutable x
570-
then Domain.mark_mutable ~update ~st res;
603+
(match st.variable_mutable_fields.(Var.idx x) with
604+
| No_field -> ()
605+
| (Some_fields _ | All_fields) as s -> Domain.mark_mutable ~update ~st res s);
571606
res
572607
| Top -> Top
573608

@@ -645,9 +680,9 @@ let f ~fast p =
645680
let deps = Var.Tbl.make () [] in
646681
let defs = Array.make nv undefined in
647682
let variable_may_escape = Array.make nv No in
648-
let variable_possibly_mutable = Var.ISet.empty () in
683+
let variable_mutable_fields = Array.make nv No_field in
649684
let may_escape = Array.make nv No in
650-
let possibly_mutable = Var.ISet.empty () in
685+
let mutable_fields = Array.make nv No_field in
651686
let functions_from_returned_value = Var.Hashtbl.create 128 in
652687
Var.Map.iter
653688
(fun f s -> Var.Set.iter (fun x -> add_to_list functions_from_returned_value x f) s)
@@ -659,9 +694,9 @@ let f ~fast p =
659694
; return_values = rets
660695
; functions_from_returned_value
661696
; variable_may_escape
662-
; variable_possibly_mutable
697+
; variable_mutable_fields
663698
; may_escape
664-
; possibly_mutable
699+
; mutable_fields
665700
; known_cases = Var.Hashtbl.create 16
666701
; applied_functions = Hashtbl.create 16
667702
; fast
@@ -690,13 +725,28 @@ let f ~fast p =
690725
match a with
691726
| Top -> Format.fprintf f "top"
692727
| Values _ ->
728+
let print_mutable_fields f s =
729+
match s with
730+
| No_field -> Format.fprintf f "no"
731+
| Some_fields s ->
732+
Format.fprintf
733+
f
734+
"{%a}"
735+
(Format.pp_print_list
736+
~pp_sep:(fun f () -> Format.fprintf f ", ")
737+
(fun f i -> Format.fprintf f "%d" i))
738+
(IntSet.elements s)
739+
| All_fields -> Format.fprintf f "yes"
740+
in
693741
Format.fprintf
694742
f
695-
"%a mut:%b vmut:%b vesc:%s esc:%s"
743+
"%a mut:%a vmut:%a vesc:%s esc:%s"
696744
(print_approx st)
697745
a
698-
(Var.ISet.mem st.possibly_mutable x)
699-
(Var.ISet.mem st.variable_possibly_mutable x)
746+
print_mutable_fields
747+
st.mutable_fields.(Var.idx x)
748+
print_mutable_fields
749+
st.variable_mutable_fields.(Var.idx x)
700750
(match st.variable_may_escape.(Var.idx x) with
701751
| Escape -> "Y"
702752
| Escape_constant -> "y"

0 commit comments

Comments
 (0)