Source file eliom_wrap.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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# 1 "src/lib/eliom_wrap.server.ml"
let section = Lwt_log.Section.make "eliom:wrap"
type poly
external to_poly : 'a -> poly = "%identity"
type 'a wrapped_value = poly * 'a
let with_no_heap_compaction f v =
let gc_control = Gc.get () in
Gc.set {gc_control with Gc.max_overhead = max_int};
match f v with
| v ->
Gc.set gc_control; v
| exception e ->
Gc.set gc_control; raise e
module Mark : sig
type t
val wrap_mark : t
val do_nothing_mark : t
val unwrap_mark : t
end = struct
type t = string
let wrap_mark = "wrap_mark"
let do_nothing_mark = "do_nothing_mark"
let unwrap_mark = "unwrap_mark"
end
type marked_value = {mark : Mark.t; f : (Obj.t -> Obj.t) option}
[@@warning "-69"]
let make_mark f mark = {mark; f}
let is_marked o =
let is_mark o =
if Obj.tag o = 0
&& Obj.size o = 2
&& Obj.field o 0 == Obj.repr Mark.wrap_mark
then (
let f = Obj.field o 1 in
assert (Obj.tag f = 0);
assert (Obj.size f = 1);
assert (
let tag = Obj.tag (Obj.field f 0) in
tag = Obj.infix_tag || tag = Obj.closure_tag);
true)
else false
in
if Obj.tag o = 0 && Obj.size o >= 2
then
let potential_mark = Obj.field o (Obj.size o - 1) in
is_mark potential_mark
else false
let wrap_locally o =
let mark : marked_value = Obj.obj (Obj.field o (Obj.size o - 1)) in
match mark.f with Some f -> f o | None -> assert false
let bits = 8
let none = Obj.repr 0
module DynArray = struct
let rec check_size a i =
let len = Array.length !a in
if i > len
then (
let old_a = !a in
a := Array.make (2 * len) none;
Array.blit old_a 0 !a 0 len;
check_size a i)
let make () = ref (Array.make (1 lsl (bits - 1)) none)
let get a i = !a.(i)
let set a i v = !a.(i) <- v
end
let resize_count = ref 0
let rehash_count = ref 0
module Tbl = struct
type t =
{ mutable size : int
;
mutable shift : int
;
mutable occupancy : int
;
mutable obj : Obj.t array
;
mutable idx : int array
;
mutable gc : int
;
on_resize : (int -> unit) list }
let cst =
Int64.to_int (Int64.shift_right 0x4F1BBCDCBFA53E09L (63 - Sys.int_size))
let hash tbl x = (Obj.magic x * cst) lsr tbl.shift
let gc_count () = Gc.((quick_stat ()).minor_collections)
let reallocate resize tbl =
let old_size = tbl.size in
let old_obj = tbl.obj in
let old_idx = tbl.idx in
if resize
then (
tbl.size <- 2 * old_size;
tbl.shift <- tbl.shift - 1;
List.iter (fun f -> f (tbl.size lsr 1)) tbl.on_resize);
tbl.obj <- Array.make tbl.size none;
tbl.idx <- Array.make tbl.size (-1);
tbl.gc <- gc_count ();
let rec insert tbl h x idx =
let y = tbl.obj.(h) in
if y == none
then (
tbl.obj.(h) <- x;
tbl.idx.(h) <- idx)
else if y == x
then tbl.idx.(h) <- max idx tbl.idx.(h)
else insert tbl ((h + 1) land (tbl.size - 1)) x idx
in
for i = 0 to old_size - 1 do
let x = old_obj.(i) in
if x != none then insert tbl (hash tbl x) x old_idx.(i)
done
let resize tbl = incr resize_count; reallocate true tbl
let rehash tbl = incr rehash_count; reallocate false tbl
let make tbls =
let size = 1 lsl bits in
let obj = Array.make size none in
let idx = Array.make size (-1) in
let on_resize = List.map DynArray.check_size tbls in
let gc = gc_count () in
{size; shift = Sys.int_size - bits; occupancy = 0; obj; idx; gc; on_resize}
let rec allocate_rec tbl x i =
if tbl.obj.(i) == x
then tbl.idx.(i)
else if tbl.obj.(i) == none
then (
tbl.obj.(i) <- x;
let idx = tbl.occupancy in
tbl.idx.(i) <- idx;
tbl.occupancy <- idx + 1;
if tbl.occupancy * 2 >= tbl.size then resize tbl;
idx)
else allocate_rec tbl x ((i + 1) land (tbl.size - 1))
let allocate_index tbl x = allocate_rec tbl x (hash tbl x)
let rec get_rec tbl x i =
let y = tbl.obj.(i) in
if y == x
then tbl.idx.(i)
else if y == none
then -1
else get_rec tbl x ((i + 1) land (tbl.size - 1))
let get_index_no_retry tbl x = get_rec tbl x (hash tbl x)
let get_index tbl x =
let idx = get_index_no_retry tbl x in
if idx <> -1
then idx
else (
rehash tbl;
let idx = get_index_no_retry tbl x in
if idx = -1
then (
for i = 0 to Array.length tbl.obj - 1 do
assert (tbl.obj.(i) != x)
done;
Format.eprintf "%b@." (is_marked x));
assert (idx <> -1);
idx)
let was_up_to_date tbl = tbl.gc = gc_count ()
end
let obj_kind v =
if not (Obj.is_block v)
then `Opaque
else
let tag = Obj.tag v in
if tag >= Obj.no_scan_tag
then `Opaque
else if tag <= Obj.last_non_constant_constructor_tag
then `Scannable
else if tag = Obj.forward_tag
then
let tag' = Obj.tag (Obj.field v 0) in
if tag' = Obj.forward_tag || tag' = Obj.double_tag
then `Scannable
else
`Forward
else (
if tag = Obj.lazy_tag
then failwith "lazy values must be forced before wrapping";
if tag = Obj.object_tag then failwith "cannot wrap object values";
if tag = Obj.closure_tag then failwith "cannot wrap functional values";
if tag = Obj.infix_tag
then failwith "cannot wrap functional values: infix tag";
failwith (Printf.sprintf "cannot wrap value (unexpected tag %d)" tag))
let unchanged =
Obj.repr 1
let modified =
Obj.repr 2
let iteration_count = ref 0
let wrap_count = ref 0
let rec find_substs tbl subst_tbl v =
incr iteration_count;
match obj_kind v with
| `Opaque ->
unchanged
| `Forward ->
find_substs tbl subst_tbl (Obj.field v 0)
| `Scannable ->
let idx = Tbl.allocate_index tbl v in
let v' = DynArray.get subst_tbl idx in
if v' == none
then
if is_marked v
then
if not (Tbl.was_up_to_date tbl)
then (
Tbl.rehash tbl;
find_substs tbl subst_tbl v)
else (
incr wrap_count;
let v' = wrap_locally v in
DynArray.set subst_tbl idx v';
ignore (find_substs tbl subst_tbl v');
modified)
else (
DynArray.set subst_tbl idx modified;
let size = Obj.size v in
let is_unchanged = ref true in
for i = 0 to size - 1 do
let status = find_substs tbl subst_tbl (Obj.field v i) in
is_unchanged := !is_unchanged && status == unchanged
done;
let res = if !is_unchanged then unchanged else modified in
DynArray.set subst_tbl idx res;
res)
else v'
let copy_count = ref 0
let rec duplicate tbl subst_tbl copy_tbl orig =
match obj_kind orig with
| `Opaque ->
orig
| `Forward ->
duplicate tbl subst_tbl copy_tbl (Obj.field orig 0)
| `Scannable ->
let idx = Tbl.get_index tbl orig in
let subst = DynArray.get subst_tbl idx in
if subst == unchanged
then
orig
else if subst != modified
then
duplicate tbl subst_tbl copy_tbl subst
else
let copy = DynArray.get copy_tbl idx in
if copy != none
then
copy
else (
incr copy_count;
let copy = Obj.dup orig in
DynArray.set copy_tbl idx copy;
let size = Obj.size orig in
for i = 0 to size - 1 do
let child = Obj.field orig i in
let child_copy = duplicate tbl subst_tbl copy_tbl child in
if child_copy != child then Obj.set_field copy i child_copy
done;
copy)
let perform_wrap =
with_no_heap_compaction @@ fun v ->
iteration_count := 0;
copy_count := 0;
wrap_count := 0;
resize_count := 0;
rehash_count := 0;
let subst_tbl = DynArray.make () in
let copy_tbl = DynArray.make () in
let tbl = Tbl.make [subst_tbl; copy_tbl] in
ignore (find_substs tbl subst_tbl v);
let w = duplicate tbl subst_tbl copy_tbl v in
Lwt_log.ign_debug_f ~section
"Wrap stats: %d visited (%d blocks), %d wrapped, %d copied, %d resizes, %d rehashes"
!iteration_count tbl.occupancy !wrap_count !copy_count !resize_count
!rehash_count;
w
type +'a wrapper = marked_value
let create_wrapper (f : 'a -> 'b) : 'a wrapper =
make_mark (Some (fun x -> Obj.repr (f (Obj.obj x)))) Mark.wrap_mark
let empty_wrapper : 'a wrapper = make_mark None Mark.do_nothing_mark
type unwrap_id = int
let id_of_int x = x
type unwrapper =
{
id : unwrap_id
; umark : Mark.t }
[@@warning "-69"]
let create_unwrapper id = {id; umark = Mark.unwrap_mark}
let empty_unwrapper = {id = -1; umark = Mark.do_nothing_mark}
let wrap v = to_poly Mark.unwrap_mark, Obj.obj (perform_wrap (Obj.repr v))