mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
143959b94b
Co-authored-by: Claude <noreply@anthropic.com>
97 lines
5.2 KiB
Markdown
97 lines
5.2 KiB
Markdown
<!-- summary: About Value Boxing -->
|
|
|
|
# Boxing
|
|
|
|
The mruby objects and data are represented by C data type `mrb_value`. There are three options how to pack the data values in the `mrb_value`.
|
|
|
|
- Word Boxing
|
|
- NaN Boxing
|
|
- No Boxing
|
|
|
|
## Word Boxing
|
|
|
|
Word boxing packs the Ruby data in a word, which is a natural integer size that equals to the size of pointers (`intptr_t`). Word boxing can be specified by `MRB_WORD_BOXING`, and it's default configuration for most platforms.
|
|
|
|
Some values (called immediate values, e.g. integers, booleans, symbols, etc.) are directly packed in the word. The other data types are represented by pointers to the heap allocated structures.
|
|
|
|
The Word boxing packing bit patterns are like following:
|
|
|
|
| Types | Bit Pattern |
|
|
| ------ | ------------------------------------- |
|
|
| object | `xxxxxxxx xxxxxxxx xxxxxxxx xxxxx000` |
|
|
| fixnum | `xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxx1` |
|
|
| nil | `00000000 00000000 00000000 00000000` |
|
|
| true | `00000000 00000000 00000000 00001100` |
|
|
| false | `00000000 00000000 00000000 00000100` |
|
|
| undef | `00000000 00000000 00000000 00010100` |
|
|
| symbol | `xxxxxxxx xxxxxxxx xxxxxxxx xxxxxx10` |
|
|
|
|
### Inline Float (64-bit)
|
|
|
|
On 64-bit platforms, `double` values are packed into the word using
|
|
rotation encoding. The IEEE 754 exponent field is rotated so that
|
|
common exponent values (those not colliding with the pointer/tag
|
|
patterns above) fit directly in a word. This encoding is lossless
|
|
for most float values; only a small set of exotic exponents require
|
|
heap allocation as `RFloat`.
|
|
|
|
To disable inline float and heap-allocate all floats, define
|
|
`MRB_WORDBOX_NO_INLINE_FLOAT`.
|
|
|
|
### 32-bit Considerations
|
|
|
|
On 32-bit platforms with 64-bit `double` (the common case),
|
|
`MRB_WORDBOX_NO_INLINE_FLOAT` is automatically defined because a
|
|
64-bit double cannot fit in a 32-bit word. All floats are
|
|
heap-allocated as `RFloat` objects.
|
|
|
|
The `RFloat` struct uses a `char[]` buffer instead of a `double`
|
|
field to avoid alignment issues, since GC heap slots (RVALUE) on
|
|
32-bit have only 4-byte alignment but `double` requires 8-byte.
|
|
Accessor functions `mrb_rfloat_value()` and `mrb_rfloat_set()` use
|
|
`memcpy` for safe access.
|
|
|
|
## NaN Boxing
|
|
|
|
NaN boxing packs the Ruby data in a floating-point numbers, which represent NaN (Not a Number) values. Under IEEE753 definitions every value that exponent is all set are considered as NaN. That means NaN can represent `2^51` values. NaN boxing is a teaching to pack the values in those NaN representation. In theory, 64-bit pointers are too big to fit in NaN, but practically most OS use only 48 bits at most for pointers (except for some OS e.g. Solaris).
|
|
|
|
The NaN boxing packing bit patterns are like following:
|
|
|
|
| Types | Bit Pattern |
|
|
| ------ | ------------------------------------------------------------------------- |
|
|
| float | `SEEEEEEE EEEEFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF` |
|
|
| +/-inf | `S1111111 11110000 00000000 00000000 00000000 00000000 00000000 00000000` |
|
|
| nan | `01111111 11111000 00000000 00000000 00000000 00000000 00000000 00000000` |
|
|
| fixnum | `01111111 11111001 00000000 00000000 IIIIIIII IIIIIIII IIIIIIII IIIIIIII` |
|
|
| symbol | `01111111 11111110 00000000 00000000 SSSSSSSS SSSSSSSS SSSSSSSS SSSSSSSS` |
|
|
| misc | `01111111 11111111 00000000 00000000 00000000 00000000 00TTTTTT 0000MMMM` |
|
|
| object | `01111111 11111100 PPPPPPPP PPPPPPPP PPPPPPPP PPPPPPPP PPPPPPPP PPPPPP00` |
|
|
| ptr | `01111111 11111100 PPPPPPPP PPPPPPPP PPPPPPPP PPPPPPPP PPPPPPPP PPPPPP01` |
|
|
| nil | `00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000` |
|
|
|
|
The object values appear far more frequently than floating-point numbers, so we offset the value so that object pointers are unchanged. This technique is called "favor pointer".
|
|
|
|
## No Boxing
|
|
|
|
No boxing represents `mrb_value` by the C struct with `type` and the value union. This is the most portable (but inefficient) representation. No boxing can be specified by `MRB_NO_BOXING`, and it's default for debugging configuration (e.g. `host-debug`).
|
|
|
|
## Comparison
|
|
|
|
| Property | Word Boxing | NaN Boxing | No Boxing |
|
|
| ---------------------- | ----------------- | ---------------- | -------------------- |
|
|
| `mrb_value` size | 1 word (4/8 byte) | 8 bytes | 2 words (8/16 bytes) |
|
|
| Default on | most platforms | (manual opt-in) | `host-debug` |
|
|
| Macro | `MRB_WORD_BOXING` | `MRB_NAN_BOXING` | `MRB_NO_BOXING` |
|
|
| Inline integers | yes (31/63 bit) | yes (32 bit) | yes (full width) |
|
|
| Inline floats (64-bit) | yes (rotation) | yes (native) | yes (struct field) |
|
|
| Inline floats (32-bit) | no (heap RFloat) | yes (native) | yes (struct field) |
|
|
| Pointer size limit | none | 48 bits | none |
|
|
| Debugger friendly | no | no | yes |
|
|
|
|
## ABI Compatibility
|
|
|
|
The boxing mode changes the layout of `mrb_value`. Code compiled with
|
|
one boxing mode **cannot** be linked against a library built with a
|
|
different mode. Always use `mruby-config --cflags` to get the correct
|
|
compiler flags.
|