Source file ref_unboxing.ml
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
open! Stdlib
open Code
let debug = Debug.find "unbox-refs"
let times = Debug.find "times"
let stats = Debug.find "stats"
let rewrite_body unboxed_refs body ref_contents subst =
let ref_contents, subst, l =
List.fold_left
~f:(fun (ref_contents, subst, acc) i ->
match i with
| Let (x, Block (0, [| y |], (NotArray | Unknown), Maybe_mutable))
when Var.Set.mem x unboxed_refs -> Var.Map.add x y ref_contents, subst, acc
| Let (y, Field (x, 0, Non_float)) when Var.Map.mem x ref_contents ->
ref_contents, Var.Map.add y (Var.Map.find x ref_contents) subst, acc
| Offset_ref (x, n) when Var.Map.mem x ref_contents ->
let y = Var.fork x in
( Var.Map.add x y ref_contents
, subst
, Let
( y
, Prim
( Extern "%int_add"
, [ Pv (Var.Map.find x ref_contents)
; Pc (Int (Targetint.of_int_exn n))
] ) )
:: acc )
| Set_field (x, 0, Non_float, y) when Var.Map.mem x ref_contents ->
Var.Map.add x y ref_contents, subst, acc
| Event _ -> (
( ref_contents
, subst
, match acc with
| Event _ :: prev ->
i :: prev
| _ -> i :: acc ))
| _ -> ref_contents, subst, i :: acc)
body
~init:(ref_contents, subst, [])
in
ref_contents, subst, List.rev l
let rewrite_cont relevant_vars ref_contents (pc', args) =
let refs, _ = Int.Hashtbl.find relevant_vars pc' in
let vars = Var.Map.filter (fun x _ -> Var.Set.mem x refs) ref_contents in
pc', List.map ~f:snd (Var.Map.bindings vars) @ args
let rewrite_branch relevant_vars ref_contents branch =
match branch with
| Return _ | Raise _ | Stop -> branch
| Branch cont -> Branch (rewrite_cont relevant_vars ref_contents cont)
| Cond (x, cont, cont') ->
Cond
( x
, rewrite_cont relevant_vars ref_contents cont
, rewrite_cont relevant_vars ref_contents cont' )
| Switch (x, a) ->
Switch (x, Array.map ~f:(fun cont -> rewrite_cont relevant_vars ref_contents cont) a)
| Pushtrap (cont, x, cont') ->
Pushtrap
( rewrite_cont relevant_vars ref_contents cont
, x
, rewrite_cont relevant_vars ref_contents cont' )
| Poptrap cont -> Poptrap (rewrite_cont relevant_vars ref_contents cont)
let rewrite_function p ~unboxed_refs pc subst =
let g = Structure.(dominator_tree (build_graph p.blocks pc)) in
let relevant_vars =
let relevant_vars = Int.Hashtbl.create 16 in
let rec traverse_tree g pc refs =
let block = Addr.Map.find pc p.blocks in
let refs' =
List.fold_left
~f:(fun s i ->
match i with
| Let (x, Block (0, [| _ |], (NotArray | Unknown), Maybe_mutable))
when Var.Hashtbl.mem unboxed_refs x -> Var.Set.add x s
| _ -> s)
~init:refs
block.body
in
Int.Hashtbl.add relevant_vars pc (refs, refs');
Addr.Set.iter (fun pc' -> traverse_tree g pc' refs') (Structure.get_edges g pc)
in
traverse_tree g pc Var.Set.empty;
relevant_vars
in
let rec traverse_tree' g pc blocks subst =
let block = Addr.Map.find pc p.blocks in
let refs, refs' = Int.Hashtbl.find relevant_vars pc in
let ref_contents =
Var.Set.fold (fun x m -> Var.Map.add x (Var.fork x) m) refs Var.Map.empty
in
let params = List.map ~f:snd (Var.Map.bindings ref_contents) @ block.params in
let ref_contents, subst, body = rewrite_body refs' block.body ref_contents subst in
let branch = rewrite_branch relevant_vars ref_contents block.branch in
let blocks = Addr.Map.add pc { params; body; branch } blocks in
Addr.Set.fold
(fun pc' (blocks, subst) -> traverse_tree' g pc' blocks subst)
(Structure.get_edges g pc)
(blocks, subst)
in
let blocks, subst = traverse_tree' g pc p.blocks subst in
{ p with blocks }, subst
let f p =
let t = Timer.make () in
let candidates = Var.Hashtbl.create 128 in
let updated = Var.Hashtbl.create 128 in
let visited = BitSet.create' p.free_pc in
let discard x = Var.Hashtbl.remove candidates x in
let check_field_access depth x =
match Var.Hashtbl.find candidates x with
| exception Not_found -> false
| depth' ->
if depth' = depth
then true
else (
Var.Hashtbl.remove candidates x;
false)
in
let rec traverse depth_stack max_depth depth start_pc pc =
if not (BitSet.mem visited pc)
then (
BitSet.set visited pc;
let block = Addr.Map.find pc p.blocks in
List.iter
~f:(fun i ->
match i with
| Let (x, Block (0, [| _ |], (NotArray | Unknown), Maybe_mutable)) ->
Freevars.iter_instr_free_vars discard i;
Var.Hashtbl.replace candidates x depth
| Let (_, Closure (_, (pc', _), _)) ->
traverse [] (max_depth + 1) (max_depth + 1) pc' pc'
| Let (_, Field (x, 0, Non_float)) -> ignore (check_field_access depth x)
| Offset_ref (x, _) ->
if check_field_access depth x then Var.Hashtbl.replace updated x start_pc
| Set_field (x, _, Non_float, y) ->
discard y;
if check_field_access depth x then Var.Hashtbl.replace updated x start_pc
| _ -> Freevars.iter_instr_free_vars discard i)
block.body;
Freevars.iter_last_free_var discard block.branch;
match block.branch with
| Pushtrap ((pc', _), _, (pc'', _)) ->
traverse (depth :: depth_stack) (max_depth + 1) (max_depth + 1) start_pc pc';
traverse depth_stack max_depth depth start_pc pc''
| Poptrap (pc', _) ->
traverse (List.tl depth_stack) max_depth (List.hd depth_stack) start_pc pc'
| _ ->
Code.fold_children
p.blocks
pc
(fun pc' () -> traverse depth_stack max_depth depth start_pc pc')
())
in
traverse [] 0 0 p.start p.start;
if debug ()
then
Print.program
Format.err_formatter
(fun _ i ->
match i with
| Instr (Let (x, _))
when Var.Hashtbl.mem candidates x && Var.Hashtbl.mem updated x -> "REF"
| _ -> "")
p;
Var.Hashtbl.filter_map_inplace
(fun x _depth -> try Some (Var.Hashtbl.find updated x) with Not_found -> None)
candidates;
let functions =
Var.Hashtbl.fold (fun _ pc s -> Addr.Set.add pc s) candidates Addr.Set.empty
in
let p, subst =
Addr.Set.fold
(fun pc (p, subst) -> rewrite_function p ~unboxed_refs:candidates pc subst)
functions
(p, Var.Map.empty)
in
let p =
if Var.Map.is_empty subst
then p
else Subst.Excluding_Binders.program (Subst.from_map subst) p
in
if times () then Format.eprintf " reference unboxing: %a@." Timer.print t;
if stats ()
then Format.eprintf "Stats - reference unboxing: %d@." (Var.Hashtbl.length candidates);
p