You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: jane/doc/extensions/unboxed-types/index.md
+145-1
Original file line number
Diff line number
Diff line change
@@ -284,4 +284,148 @@ Here's the list of primitives that currently support `[@layout_poly]`:
284
284
*`%array_safe_get`
285
285
*`%array_safe_set`
286
286
*`%array_unsafe_get`
287
-
*`%array_unsafe_set`
287
+
*`%array_unsafe_set`
288
+
289
+
# Using unboxed types in structures
290
+
291
+
Unboxed types can usually be put in structures, though there are some restrictions.
292
+
293
+
These structures may contain unboxed types, but have some restrictions on field
294
+
orders:
295
+
* Records
296
+
* Constructors
297
+
298
+
Unboxed numbers can't be put in these structures:
299
+
* Constructors with inline record fields
300
+
* Exceptions
301
+
* Extensible variant constructors
302
+
* Top-level fields of modules
303
+
* Tuples
304
+
305
+
There aren't fundamental issues with the structures that lack support. They will
306
+
just take some work to implement.
307
+
308
+
Here's an example of a record with an unboxed field. We call such a record
309
+
a "mixed record".
310
+
311
+
```ocaml
312
+
type t =
313
+
{ str : string;
314
+
i : int;
315
+
f : float#;
316
+
}
317
+
```
318
+
319
+
## Restrictions on field ordering
320
+
321
+
The below is written about record fields but equally applies to constructor
322
+
arguments.
323
+
324
+
Suppose a record contains any unboxed field `fld` whose layout is not `value`[^or-combination-of-values]. Then, the following restriction applies: All
325
+
fields occurring after `fld` in the record must be "flat", i.e. the GC can
326
+
skip looking at them. The only options for flat fields are immediates (i.e. things
327
+
represented as ints at runtime) and other unboxed numbers.
328
+
329
+
[^or-combination-of-values]: Technically, there are some non-value layouts that don't hit this restriction, like unboxed products and unboxed sums consisting only of values.
330
+
331
+
The following definition is rejected, as the boxed field `s : string` appears
332
+
after the unboxed float field `f`:
333
+
334
+
```ocaml
335
+
type t_rejected =
336
+
{ f : float#;
337
+
s : string;
338
+
}
339
+
(* Error: Expected all flat fields after non-value field, f,
340
+
but found boxed field, s. *)
341
+
```
342
+
343
+
The only relaxation of the above restriction is for records that consist
344
+
solely of `float` and `float#` fields. Any ordering of `float` and `float#`
345
+
fields is permitted. The "flat float record optimization" applies to any
346
+
such record—all of the fields are stored flat, even the `float` ones
347
+
that will require boxing upon projection. The ordering restriction is relaxed
348
+
in this case to provide a better migration story for all-`float` records
349
+
to which the flat float record optimization currently applies.
350
+
351
+
```ocaml
352
+
type t_flat_float =
353
+
{ x1 : float;
354
+
x2 : float#;
355
+
x3 : float;
356
+
}
357
+
```
358
+
359
+
The ordering restriction has to do with the "mixed block" runtime
360
+
representation. Read on for more detail about that.
361
+
362
+
## Generic operations aren't supported
363
+
364
+
Some operations built in to the OCaml runtime aren't supported for structures
365
+
containing unboxed types.
366
+
367
+
These operations aren't supported:
368
+
* polymorphic comparison and equality
369
+
* polymorphic hash
370
+
* marshaling
371
+
372
+
These operations raise an exception at runtime, similar to how polymorphic
373
+
comparison raises when called on a function.
374
+
375
+
You should use ppx-derived versions of these operations instead.
376
+
377
+
## Runtime representation: mixed blocks
378
+
379
+
As a general principle: The compiler should not change the user-specified
380
+
field ordering when deciding the runtime representation.
381
+
382
+
Abiding by this principle allows you to write C bindings and
383
+
predict hardware cache performance.
384
+
385
+
A structure containing unboxed types is represented at runtime as a "mixed
386
+
block". A mixed block always consists of fields the GC can-or-must scan followed by
387
+
fields the GC can-or-must skip[^can-or-must]. The garbage collector must be kept
388
+
informed of which fields of the block it should scan. A portion of the header
389
+
word is reserved to track the length of the prefix of the block that should be
390
+
scanned by the garbage collector.
391
+
392
+
[^can-or-must]: "Can-or-must" is a bit of a mouthful, but it captures the right nuance. Pointer values *must* be scanned, unboxed number fields *must* be skipped, and immediate values *can* be scanned or skipped.
393
+
394
+
The ordering constraint on structure fields is a reflection of the same
395
+
ordering restriction in the runtime representation.
396
+
397
+
## C bindings for mixed blocks
398
+
399
+
The implementation of field layout in a mixed block is not finalized. For example, we'd like for int32 fields to be packed efficiently (two to a word) on 64 bit platforms. Currently that's not the case: each one takes up a word.
400
+
401
+
Users who write C bindings might want to be notified when we change this layout. To ensure that your code will need to be updated when the layout changes, use the `Assert_mixed_block_layout_v#` family of macros. For example,
402
+
403
+
```
404
+
Assert_mixed_block_layout_v1;
405
+
```
406
+
407
+
Write the above in statement context, i.e. either at the top-level of a file or
408
+
within a function.
409
+
410
+
Here's a full example. Say you're writing C bindings against this OCaml type:
411
+
412
+
```ocaml
413
+
(** foo.ml *)
414
+
type t =
415
+
{ x : int32#;
416
+
y : int32#;
417
+
}
418
+
```
419
+
420
+
Here is the recommend way to access fields:
421
+
422
+
```c
423
+
Assert_mixed_block_layout_v1;
424
+
#defineFoo_t_x(foo) (*(int32_t*)&Field(foo, 0))
425
+
#define Foo_t_y(foo) (*(int32_t*)&Field(foo, 1))
426
+
```
427
+
428
+
We would bump the version number in either of these cases, which would prompt you to think about the code:
0 commit comments