Skip to content

Document arrays of unboxed numbers #2672

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 11, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 61 additions & 2 deletions ocaml/jane/doc/extensions/unboxed-types/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,17 @@ Here by defining module type `S` with layout `any` and using `with` constraints,
reason about modules with similar shapes but that operate on different layouts. This removes code
duplication and can aid ppxs in supporting unboxed types.

<!-- This heading is referred to by name in a link to an HTML anchor below.
If you rename it, please also update that link.
-->
# `[@layout_poly]` attribute

The attribute enables support for **limited layout polymorphism on external
`%`-primitives**. This is possible because these primitives are always inlined at every
use site. We can thus specialize the function implementation based on the layout
information at each site.

With a `[@layout-poly]` external declaration like this:
With a `[@layout_poly]` external declaration like this:

```ocaml
external[@layout_poly] opaque_identity : ('a : any). 'a -> 'a = "%opaque"
Expand Down Expand Up @@ -270,7 +273,7 @@ let f2 : float# -> float# = magic;; (* ok *)
let f3 : float# -> int32# = magic;; (* error *)
```

This feature is conceptually similar to `[@local_opt]` for modes and would be useful for
This feature is conceptually similar to `[@local_opt]` for modes and is useful for
array access primitives.

Here's the list of primitives that currently support `[@layout_poly]`:
Expand All @@ -285,17 +288,73 @@ Here's the list of primitives that currently support `[@layout_poly]`:
* `%array_safe_set`
* `%array_unsafe_get`
* `%array_unsafe_set`
* `%array_size`

# Arrays of unboxed elements

Arrays can store elements of any layout. You can think of `array` as having been declared as:

```ocaml
type ('a : any) array = (* ... *)
```

Array elements are packed according to their width. For example, arrays of
elements whose layout is `bits32` store two elements per word.

You can use normal array syntax for constructing such an array:

```ocaml
let array = [| #2l |]
```

Array primitives must be declared with `[@layout_poly]` to be usable with arrays of unboxed elements.

```ocaml
module Array = struct
external[@layout_poly] get : ('a : any). 'a array -> int -> 'a = "%array_safe_get"
end

let first_elem () = array.(0)
```

(The above relies on the fact that array projection syntax desugars to a call to whatever `Array.get` is in scope.)

A limited set of primitives may be bound as `[@layout_poly]`;
[see the earlier section](#layout_poly-attribute) for more information.

## Runtime representation

| Array | Tag | Layout of data |
|----------------------------------|--------------------|-------------------------------------------------------------|
| `float# array` | `Double_array_tag` | 64 bits per element |
| `int64# array` | `Custom_tag` | reserved custom block word, followed by 64 bits per element |
| `float32# array`, `int32# array` | `Custom_tag` | reserved custom block word, followed by 32 bits per element |

The above table is written about concrete types like `float#` and `int64#`, but
actually holds for all types of the relevant layout.

The reserved custom block word is the standard custom block field that stores a
pointer to the record of custom operations, like polymorphic equality and
comparison. For unboxed 32-bit element types, like `int32#` and `float32#`, the
custom operations pointer is different for odd-length arrays and even-length
arrays.

Odd-length arrays of 32-bit element type have 32 bits of padding at the end.
The contents of this padding is unspecified, and it is not guaranteed that
the padding value will be preserved by the generated code or the runtime.

# Using unboxed types in structures

Unboxed types can usually be put in structures, though there are some restrictions.

These structures may contain unboxed types, but have some restrictions on field
orders:

* Records
* Constructors

Unboxed numbers can't be put in these structures:

* Constructors with inline record fields
* Exceptions
* Extensible variant constructors
Expand Down
Loading