1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
open Stdlib
type st =
{ pos : int
; visited_variables : int Code.Var.Map.t
; globals : Code.Var.Set.t
; closures : Closure_conversion.closure Code.Var.Map.t
; constants : Code.Var.Set.t
}
let threshold = 1000
let rec globalize st x =
if Code.Var.Set.mem x st.globals
then st
else
let st = { st with globals = Code.Var.Set.add x st.globals } in
globalize_closure st x
and globalize_closure st x =
match Code.Var.Map.find x st.closures with
| { free_variables; _ } ->
List.fold_left
~f:(fun st x ->
if Code.Var.Map.mem x st.visited_variables then globalize st x else st)
~init:st
free_variables
| exception Not_found -> st
let use x st =
match Code.Var.Map.find x st.visited_variables with
| pos -> if st.pos > pos + threshold then globalize st x else st
| exception Not_found -> st
let declare x st =
{ st with visited_variables = Code.Var.Map.add x st.pos st.visited_variables }
let traverse_expression x e st =
match e with
| Code.Apply { f; args; _ } ->
st |> use f |> fun st -> List.fold_left ~f:(fun st x -> use x st) ~init:st args
| Block (_, a, _, _) -> Array.fold_right ~f:use a ~init:st
| Field (x, _, _) -> st |> use x
| Closure _ ->
List.fold_left
~f:(fun st x -> use x st)
~init:st
(Code.Var.Map.find x st.closures).Closure_conversion.free_variables
| Constant _ -> { st with constants = Code.Var.Set.add x st.constants }
| Special _ -> st
| Prim (_, args) ->
List.fold_left
~f:(fun st a ->
match a with
| Code.Pv x -> st |> use x
| Pc _ -> st)
~init:st
args
let traverse_instruction st i =
let st = { st with pos = st.pos + 1 } in
match i with
| Code.Let (x, e) -> st |> declare x |> traverse_expression x e
| Assign (_, x) | Offset_ref (x, _) -> st |> use x
| Set_field (x, _, _, y) -> st |> use x |> use y
| Array_set (x, y, z) -> st |> use x |> use y |> use z
| Event _ -> st
let traverse_block p st pc =
let b = Code.Addr.Map.find pc p.Code.blocks in
let st = List.fold_left ~f:(fun st x -> declare x st) ~init:st b.Code.params in
List.fold_left ~f:(fun st i -> traverse_instruction st i) ~init:st b.Code.body
let available x st = Code.Var.Set.mem x st.globals || Code.Var.Set.mem x st.constants
let propagate_instruction st i =
match i with
| Code.Let (x, Block (_, a, _, _)) when not (Code.Var.Set.mem x st.globals) ->
let non_available =
Array.fold_right ~f:(fun v n -> if available v st then n else n + 1) a ~init:0
in
if 3 * non_available <= Array.length a + 1 then globalize st x else st
| Code.Let (x, Closure _) when not (Code.Var.Set.mem x st.globals) -> (
match Code.Var.Map.find x st.closures with
| { free_variables; _ } ->
if List.for_all ~f:(fun v -> available v st) free_variables
then globalize st x
else st
| exception Not_found -> st)
| _ -> st
let propagate_block p st pc =
let b = Code.Addr.Map.find pc p.Code.blocks in
List.fold_left ~f:(fun st i -> propagate_instruction st i) ~init:st b.Code.body
let f p g closures =
let l = Structure.blocks_in_reverse_post_order g in
let in_loop = Freevars.find_loops_in_closure p p.Code.start in
let st =
List.fold_left
~f:(fun st pc ->
if Code.Addr.Map.mem pc in_loop then st else traverse_block p st pc)
~init:
{ pos = 0
; visited_variables = Code.Var.Map.empty
; globals = Code.Var.Set.empty
; closures
; constants = Code.Var.Set.empty
}
l
in
let st =
List.fold_left
~f:(fun st pc ->
if Code.Addr.Map.mem pc in_loop then st else propagate_block p st pc)
~init:st
l
in
st.globals