Skip to content

Commit 3809734

Browse files
authored
flambda-backend: Add Obj functions for accessing the scannable prefix len of a mixed block (#2636)
* Add scannable prefix externals * Mark as noalloc * bytecode/native changes * Address review comment
1 parent ef58a0e commit 3809734

File tree

7 files changed

+163
-0
lines changed

7 files changed

+163
-0
lines changed

runtime/obj.c

+16
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,19 @@ struct queue_chunk {
330330
struct queue_chunk *next;
331331
value entries[ENTRIES_PER_QUEUE_CHUNK];
332332
};
333+
334+
/* Return 0 for uniform blocks and 1+n for a mixed block with scannable prefix
335+
len n.
336+
*/
337+
CAMLprim value caml_succ_scannable_prefix_len (value v) {
338+
#ifdef NATIVE_CODE
339+
return Val_long(Reserved_val(v));
340+
#else
341+
reserved_t reserved = Reserved_val(v);
342+
if (reserved == Faux_mixed_block_sentinel) {
343+
return Val_long(Scannable_wosize_val(v) + 1);
344+
} else {
345+
return Val_long(0);
346+
}
347+
#endif /* NATIVE_CODE */
348+
}

runtime4/obj.c

+16
Original file line numberDiff line numberDiff line change
@@ -365,3 +365,19 @@ struct queue_chunk {
365365
struct queue_chunk *next;
366366
value entries[ENTRIES_PER_QUEUE_CHUNK];
367367
};
368+
369+
/* Return 0 for uniform blocks and 1+n for a mixed block with scannable prefix
370+
len n.
371+
*/
372+
CAMLprim value caml_succ_scannable_prefix_len (value v) {
373+
#ifdef NATIVE_CODE
374+
return Val_long(Reserved_val(v));
375+
#else
376+
reserved_t reserved = Reserved_val(v);
377+
if (reserved == Faux_mixed_block_sentinel) {
378+
return Val_long(Scannable_wosize_val(v) + 1);
379+
} else {
380+
return Val_long(0);
381+
}
382+
#endif /* NATIVE_CODE */
383+
}

stdlib/obj.ml

+28
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,31 @@ module Ephemeron = struct
197197
external blit_data : t -> t -> unit = "caml_ephe_blit_data"
198198

199199
end
200+
201+
module Uniform_or_mixed = struct
202+
type obj_t = t
203+
204+
(* In native code, the raw reserved header bits, which is either 0 if the
205+
block is uniform or n+1 if the block has a scannable prefix of length n.
206+
In bytecode, this will be size+1 for "faux mixed blocks" representing
207+
mixed records, and otherwise 0.
208+
*)
209+
type t = int
210+
211+
external of_block : obj_t -> t = "caml_succ_scannable_prefix_len" [@@noalloc]
212+
213+
type repr =
214+
| Uniform
215+
| Mixed of { scannable_prefix_len : int }
216+
217+
let repr = function
218+
| 0 -> Uniform
219+
| n -> Mixed { scannable_prefix_len = n - 1 }
220+
221+
let is_uniform t = t = 0
222+
let is_mixed t = not (is_uniform t)
223+
let mixed_scannable_prefix_len_exn t =
224+
if is_uniform t
225+
then invalid_arg "Uniform_or_mixed.mixed_scannable_prefix_len_exn";
226+
t - 1
227+
end

stdlib/obj.mli

+36
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,39 @@ module Ephemeron: sig
172172
(** Maximum length of an ephemeron, ie the maximum number of keys an
173173
ephemeron could contain *)
174174
end
175+
176+
module Uniform_or_mixed : sig
177+
(** Blocks with a nominally scannable tag can still have a suffix of
178+
unscanned objects; such a block is "mixed". This contrasts with
179+
"uniform" blocks which are either all-scanned or all-unscanned.
180+
181+
Note that this module can return different results for the scannable
182+
prefix len of a mixed block in native code vs. bytecode. That's
183+
because more fields are scanned in bytecode.
184+
*)
185+
186+
type obj_t := t
187+
188+
type t [@@immediate]
189+
190+
type repr =
191+
| Uniform
192+
(** The block is tagged as not scannable or the block is tagged as scannable
193+
and all fields can be scanned. *)
194+
| Mixed of { scannable_prefix_len : int }
195+
(** The block is tagged as scannable but some fields can't be scanned. *)
196+
197+
val repr : t -> repr
198+
199+
external of_block : obj_t -> t = "caml_succ_scannable_prefix_len" [@@noalloc]
200+
201+
val is_uniform : t -> bool
202+
(** Equivalent to [repr] returning [Uniform]. *)
203+
204+
val is_mixed : t -> bool
205+
(** Equivalent to [repr] returning [Mixed _]. *)
206+
207+
val mixed_scannable_prefix_len_exn : t -> int
208+
(** Returns the [scannable_prefix_len] without materializing the return
209+
value of [repr]. Raises if [is_mixed] is [false]. *)
210+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
t_uniform1: uniform
2+
t_uniform2.0: uniform
3+
t_uniform2.1: uniform
4+
t_uniform3: uniform
5+
t_mixed0: mixed (scannable_prefix_len = 1)
6+
t_mixed1: mixed (scannable_prefix_len = 2)
7+
t_mixed2: mixed (scannable_prefix_len = 3)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
(* TEST
2+
flags = "-extension layouts_beta";
3+
{
4+
reference = "${test_source_directory}/uniform_or_mixed.native.reference";
5+
compiler_reference2 = "${test_source_directory}/uniform_or_mixed.compiler.reference";
6+
native;
7+
}{
8+
reference = "${test_source_directory}/uniform_or_mixed.bytecode.reference";
9+
compiler_reference2 = "${test_source_directory}/uniform_or_mixed.compiler.reference";
10+
bytecode;
11+
}
12+
*)
13+
14+
(* Bytecode and native code have slightly different outputs for the scannable prefix
15+
of mixed records. The fields of mixed records must be scanned in bytecode.
16+
(They are "faux mixed blocks".)
17+
*)
18+
19+
type t_uniform1 = { x : int }
20+
type t_uniform2 = floatarray
21+
type t_uniform3 = int -> int
22+
23+
type t_mixed0 = { x : int64# }
24+
type t_mixed1 = { x : string; y : int64# }
25+
type t_mixed2 = { x : string; y : string; z : int64# }
26+
27+
let run (type a) test_name (obj : a) =
28+
let obj = Obj.repr obj in
29+
let uniform_or_mixed = Obj.Uniform_or_mixed.of_block obj in
30+
match Obj.Uniform_or_mixed.repr uniform_or_mixed with
31+
| Uniform ->
32+
assert (Obj.Uniform_or_mixed.is_uniform uniform_or_mixed);
33+
assert (not (Obj.Uniform_or_mixed.is_mixed uniform_or_mixed));
34+
Printf.printf "%s: uniform\n" test_name
35+
| Mixed { scannable_prefix_len } ->
36+
assert (Obj.Uniform_or_mixed.is_mixed uniform_or_mixed);
37+
assert (not (Obj.Uniform_or_mixed.is_uniform uniform_or_mixed));
38+
assert
39+
(Obj.Uniform_or_mixed.mixed_scannable_prefix_len_exn uniform_or_mixed =
40+
scannable_prefix_len);
41+
Printf.printf "%s: mixed (scannable_prefix_len = %d)\n"
42+
test_name scannable_prefix_len;
43+
;;
44+
45+
let () = run "t_uniform1" ({ x = 3 } : t_uniform1)
46+
let () = run "t_uniform2.0" (Float.Array.create 0 : t_uniform2)
47+
let () = run "t_uniform2.1" (Float.Array.create 1 : t_uniform2)
48+
let () = run "t_uniform3" ((fun x -> x) : t_uniform3)
49+
50+
let () = run "t_mixed0" ({ x = #4L } : t_mixed0)
51+
let () = run "t_mixed1" ({ x = ""; y = #5L } : t_mixed1)
52+
let () = run "t_mixed2" ({ x = ""; y = ""; z = #5L }: t_mixed2)
53+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
t_uniform1: uniform
2+
t_uniform2.0: uniform
3+
t_uniform2.1: uniform
4+
t_uniform3: uniform
5+
t_mixed0: mixed (scannable_prefix_len = 0)
6+
t_mixed1: mixed (scannable_prefix_len = 1)
7+
t_mixed2: mixed (scannable_prefix_len = 2)

0 commit comments

Comments
 (0)