Repository navigation
Literals v3
This document describes Version 3 of the bytecode for generating heap-allocated
literals in the SML/NJ compiler. It replaces the previous bytecode
and was introduced as part of the support for future improvements,
such as 64-bit support, Real32, and better Int64 and IntInf
integration.
The runtime system implements a simple stack machine for constructing a per-compilation-unit literal record. The stack contains five kinds of values: tagged integers, raw numbers (either integers or reals), heap-allocated bignums, heap-allocated vectors (both polymorphic and monomorphic), and heap-allocated records (uniform, mixed, or raw). We distinguish between uniform values (i.e., tagged ints and heap objects) and raw values. Sharing of heap-allocated objects is supported using a scratch memory.
The result of running a correct literal program should be either the tagged
integer 0 (i.e., the representation of the unit value) or a record object.
The final instruction of the program should be a RETURN operation that returns
the value on the top of the stack.
Note that construction of "wrapped" literals will be represented by the combination of a raw value followed by creation of a raw record containing the value.
The compiler extracts the literal values from the CPS IR and generates a program for building a record of literal values. Direct references to literal values in the CPS code are replaced by references to components of the literal record. The binary data representing the literal-construction program is packaged as part of the [[Binfile|New-Binfile-Format-and-Linking]] generated by the compiler.
Multiple byte quantities are represented in big-endian form (most-significant byte first).
The first four 32-bit words of the literal representation correspond to the following C struct:
struct literal_header {
uint32_t magic;
uint32_t maxstk;
uint32_t wordsz;
uint32_t maxsaved;
};where
-
magiccontains the version ID (which should be0x20260912) -
maxstkis the maximum stack depth required, and -
wordszis the size of an ML value (should be 64) -
maxsavedis the number of saved literals (used for sharing)
Note that Version 1 files have the version ID 0x19981022 and have
the first two header fields, but not the wordsz or numsaved fields.
Version 2 files have the same header structure, but the version ID
will be 0x20190921. The Version 3 format was introduced in
SML/NJ Version 2026.3.
The following is a list of the symbolic opcodes used in the interpreter and their semantics. When constructing an aggregate value (e.g., a record) from values on the stack, the topmost value will be the last element in the aggregate. The encoding of these is described below.
-
INT63(
$n$ ) push a tagged integer literal. The valuenshould be in the range$-2^{62}$ to$2^{62}-1$ when encoded as a$63$ -bit 2’s complement integer. -
INT64(
$n$ ) push raw 64-bit integer literal. The valuenshould be in the range$-2^{63}$ to$2^{63}-1$ when encoded as a$64$ -bit 2’s complement integer. -
RAWINT(
$64,n$ ) push a raw 64-bit integer literal. The valuenshould be in the range$-2^{63}$ to$2^{63}-1$ when encoded as a$w$ -bit 2’s complement integer. -
STR8(
$n$ ) construct a$n$ -character string literal from the instruction stream and pushes it on the stack. -
REAL64(
$d$ ) push a raw 64-bit floating-point literal, which is taken from the instruction stream. -
RECORD(
$n$ ) pop$n$ values, construct a record from them, and push it on the stack. The values should be uniform values. -
RAW(
$n$ ) pop$n$ values, construct a raw vector from them, and push it on the stack. The values should be raw values. -
MIXED(
$n,m$ ) pop$n+m$ values, construct a mixed record from them, and push it on the stack. The topmost$m$ values should be either raw or tagged integers (i.e., not heap objects). The next$n$ values should be uniform values. -
VEC(
$n$ ) pop$n$ values, construct a vector from them, and push it on the stack. The values should be uniform values. -
CONCAT(
$n$ ) pop$n$ records/vectors from the stack, concatenate them into a single record/vector, and push the result on the stack. This operation allows the implementation to avoid excessively large stacks when building very large record/vector literals. -
SAVE(
$i$ ) save the top of the stack in the ith save slot, which allows it to be shared by some subsequent aggregate literal. The saved value should be a heap object. Note that this operation does not pop the stack. -
LOAD(
$i$ ) push the ith saved literal on the stack. -
RETURN signals the end of the program; the stack depth should be one and that value is popped and returned as the result.
-
UNUSED represents unused opcodes.
The following opcodes are not currently generated by the compiler, but are reserved for future use.
-
RAWINT(
$k,n$ ) pushes a raw$k$ -bit integer literal, where$k$ is one of$8$ ,$16$ , or$32$ . The valuenshould be in the range$-2^{k-1}$ to$2^{k-1}-1$ when encoded as a$w$ -bit 2’s complement integer. -
BIGINT(
$sgn,n$ ) pop$n$ integer literals, and then create and push a bignum from them;$sgn$ istruefor negative numbers andfalsefor positive numbers (currently not used). -
REAL32(
$f$ ) pushes a raw 32-bit floating-point literal, which is taken from the instruction stream. -
UTF8(
$n$ ) constructs an$n$ -character wide-string literal from the instruction stream and pushes it on the stack. The string literal is encoded using the UTF-8 variable-length encoding. -
RAWVEC(
$k,n$ ) construct a raw vector of$n$ $k$ -bit elements from the instruction stream and push it on the stack.
The opcodes are organized into three classes based on the two most-significant bits (MSB):
-
00— these are small tagged integers. The remaining six bits are treated as a 2’s complement 6-bit integer. -
01— these are small 64-bit integers. The remaining six bits are treated as a 2’s complement 6-bit integer. -
10and11— all other opcodes
In the encoding below, we use the following conventions:
-
b represents a signed 8-bit integer.
-
ub represents an unsigned 8-bit integer.
-
c represents a 8-bit character.
-
h represents a signed 16-bit integer.
-
uh represents an unsigned 16-bit integer.
-
w represents a signed 32-bit integer.
-
uw represents an unsigned 32-bit integer.
-
l represents a signed 64-bit integer.
-
ul represents an unsigned 64-bit integer.
-
f represents a 32-bit floating-point literal.
-
d represents a 64-bit floating-point literal.
| Opcode (bits) | Opcode (hex) | Length | Operation | Description |
|---|---|---|---|---|
|
|
INT63(0) |
push tagged literal value |
|
|
|
INT63(1) |
push tagged literal value |
|
|
|
INT63(2) |
push tagged literal value |
|
|
|
INT63(3) |
push tagged literal value |
|
|
|
INT63(4) |
push tagged literal value |
|
|
|
INT63(5) |
push tagged literal value |
|
|
|
INT63(6) |
push tagged literal value |
|
|
|
INT63(7) |
push tagged literal value |
|
|
|
INT63(8) |
push tagged literal value |
|
|
|
INT63(9) |
push tagged literal value |
|
|
|
INT63(10) |
push tagged literal value |
|
|
|
INT63(11) |
push tagged literal value |
|
|
|
INT63(12) |
push tagged literal value |
|
|
|
INT63(13) |
push tagged literal value |
|
|
|
INT63(14) |
push tagged literal value |
|
|
|
INT63(15) |
push tagged literal value |
|
|
|
INT63(16) |
push tagged literal value |
|
|
|
INT63(17) |
push tagged literal value |
|
|
|
INT63(18) |
push tagged literal value |
|
|
|
INT63(19) |
push tagged literal value |
|
|
|
INT63(20) |
push tagged literal value |
|
|
|
INT63(21) |
push tagged literal value |
|
|
|
INT63(22) |
push tagged literal value |
|
|
|
INT63(23) |
push tagged literal value |
|
|
|
INT63(24) |
push tagged literal value |
|
|
|
INT63(25) |
push tagged literal value |
|
|
|
INT63(26) |
push tagged literal value |
|
|
|
INT63(27) |
push tagged literal value |
|
|
|
INT63(28) |
push tagged literal value |
|
|
|
INT63(29) |
push tagged literal value |
|
|
|
INT63(30) |
push tagged literal value |
|
|
|
INT63(31) |
push tagged literal value |
|
|
|
INT63(~32) |
push tagged literal value |
|
|
|
INT63(~31) |
push tagged literal value |
|
|
|
INT63(~30) |
push tagged literal value |
|
|
|
INT63(~29) |
push tagged literal value |
|
|
|
INT63(~28) |
push tagged literal value |
|
|
|
INT63(~27) |
push tagged literal value |
|
|
|
INT63(~26) |
push tagged literal value |
|
|
|
INT63(~25) |
push tagged literal value |
|
|
|
INT63(~24) |
push tagged literal value |
|
|
|
INT63(~23) |
push tagged literal value |
|
|
|
INT63(~22) |
push tagged literal value |
|
|
|
INT63(~21) |
push tagged literal value |
|
|
|
INT63(~20) |
push tagged literal value |
|
|
|
INT63(~19) |
push tagged literal value |
|
|
|
INT63(~18) |
push tagged literal value |
|
|
|
INT63(~17) |
push tagged literal value |
|
|
|
INT63(~16) |
push tagged literal value |
|
|
|
INT63(~15) |
push tagged literal value |
|
|
|
INT63(~14) |
push tagged literal value |
|
|
|
INT63(~13) |
push tagged literal value |
|
|
|
INT63(~12) |
push tagged literal value |
|
|
|
INT63(~11) |
push tagged literal value |
|
|
|
INT63(~10) |
push tagged literal value |
|
|
|
INT63(~9) |
push tagged literal value |
|
|
|
INT63(~8) |
push tagged literal value |
|
|
|
INT63(~7) |
push tagged literal value |
|
|
|
INT63(~6) |
push tagged literal value |
|
|
|
INT63(~5) |
push tagged literal value |
|
|
|
INT63(~4) |
push tagged literal value |
|
|
|
INT63(~3) |
push tagged literal value |
|
|
|
INT63(~2) |
push tagged literal value |
|
|
|
INT63(~1) |
push tagged literal value |
|
|
|
INT64(0) |
push 64-bit raw literal value |
|
|
|
INT64(1) |
push 64-bit raw literal value |
|
|
|
INT64(2) |
push 64-bit raw literal value |
|
|
|
INT64(3) |
push 64-bit raw literal value |
|
|
|
INT64(4) |
push 64-bit raw literal value |
|
|
|
INT64(5) |
push 64-bit raw literal value |
|
|
|
INT64(6) |
push 64-bit raw literal value |
|
|
|
INT64(7) |
push 64-bit raw literal value |
|
|
|
INT64(8) |
push 64-bit raw literal value |
|
|
|
INT64(9) |
push 64-bit raw literal value |
|
|
|
INT64(10) |
push 64-bit raw literal value |
|
|
|
INT64(11) |
push 64-bit raw literal value |
|
|
|
INT64(12) |
push 64-bit raw literal value |
|
|
|
INT64(13) |
push 64-bit raw literal value |
|
|
|
INT64(14) |
push 64-bit raw literal value |
|
|
|
INT64(15) |
push 64-bit raw literal value |
|
|
|
INT64(16) |
push 64-bit raw literal value |
|
|
|
INT64(17) |
push 64-bit raw literal value |
|
|
|
INT64(18) |
push 64-bit raw literal value |
|
|
|
INT64(19) |
push 64-bit raw literal value |
|
|
|
INT64(20) |
push 64-bit raw literal value |
|
|
|
INT64(21) |
push 64-bit raw literal value |
|
|
|
INT64(22) |
push 64-bit raw literal value |
|
|
|
INT64(23) |
push 64-bit raw literal value |
|
|
|
INT64(24) |
push 64-bit raw literal value |
|
|
|
INT64(25) |
push 64-bit raw literal value |
|
|
|
INT64(26) |
push 64-bit raw literal value |
|
|
|
INT64(27) |
push 64-bit raw literal value |
|
|
|
INT64(28) |
push 64-bit raw literal value |
|
|
|
INT64(29) |
push 64-bit raw literal value |
|
|
|
INT64(30) |
push 64-bit raw literal value |
|
|
|
INT64(31) |
push 64-bit raw literal value |
|
|
|
INT64(~32) |
push 64-bit raw literal value |
|
|
|
INT64(~31) |
push 64-bit raw literal value |
|
|
|
INT64(~30) |
push 64-bit raw literal value |
|
|
|
INT64(~29) |
push 64-bit raw literal value |
|
|
|
INT64(~28) |
push 64-bit raw literal value |
|
|
|
INT64(~27) |
push 64-bit raw literal value |
|
|
|
INT64(~26) |
push 64-bit raw literal value |
|
|
|
INT64(~25) |
push 64-bit raw literal value |
|
|
|
INT64(~24) |
push 64-bit raw literal value |
|
|
|
INT64(~23) |
push 64-bit raw literal value |
|
|
|
INT64(~22) |
push 64-bit raw literal value |
|
|
|
INT64(~21) |
push 64-bit raw literal value |
|
|
|
INT64(~20) |
push 64-bit raw literal value |
|
|
|
INT64(~19) |
push 64-bit raw literal value |
|
|
|
INT64(~18) |
push 64-bit raw literal value |
|
|
|
INT64(~17) |
push 64-bit raw literal value |
|
|
|
INT64(~16) |
push 64-bit raw literal value |
|
|
|
INT64(~15) |
push 64-bit raw literal value |
|
|
|
INT64(~14) |
push 64-bit raw literal value |
|
|
|
INT64(~13) |
push 64-bit raw literal value |
|
|
|
INT64(~12) |
push 64-bit raw literal value |
|
|
|
INT64(~11) |
push 64-bit raw literal value |
|
|
|
INT64(~10) |
push 64-bit raw literal value |
|
|
|
INT64(~9) |
push 64-bit raw literal value |
|
|
|
INT64(~8) |
push 64-bit raw literal value |
|
|
|
INT64(~7) |
push 64-bit raw literal value |
|
|
|
INT64(~6) |
push 64-bit raw literal value |
|
|
|
INT64(~5) |
push 64-bit raw literal value |
|
|
|
INT64(~4) |
push 64-bit raw literal value |
|
|
|
INT64(~3) |
push 64-bit raw literal value |
|
|
|
INT64(~2) |
push 64-bit raw literal value |
|
|
|
INT64(~1) |
push 64-bit raw literal value |
|
|
|
INT63( |
||
|
|
INT63( |
||
|
|
INT63( |
||
|
|
INT63( |
||
|
|
REAL32( |
||
|
|
REAL64( |
||
|
|
BIGINT( |
||
|
|
BIGINT( |
||
|
|
1 |
STR8(0) |
|
|
|
STR8( |
build an empty string |
|
|
|
STR8( |
build a string |
|
|
|
STR8( |
build a string |
|
|
|
UTF8(0) |
build a UTF8 empty wide-string |
|
|
|
UTF8( |
build a UTF8 encoded wide-string |
|
|
|
UTF8( |
build a UTF8 encoded wide-string |
|
|
|
UTF8( |
build a UTF8 encoded wide-string |
|
|
|
RAWINT(8,8) |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
RAWINT(16,8) |
||
|
|
RAWINT(16,16) |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
RAWINT(32,8) |
||
|
|
RAWINT(32,16) |
||
|
|
RAWINT(32,32) |
||
|
|
UNUSED |
||
|
|
RAWINT(64,8) |
||
|
|
RAWINT(64,16) |
||
|
|
RAWINT(64,32) |
||
|
|
RAWINT(64,64) |
||
|
|
RECORD(1) |
build a one-element record |
|
|
|
RECORD(2) |
build a two-element record |
|
|
|
RECORD(3) |
build a three-element record |
|
|
|
RECORD(4) |
build a four-element record |
|
|
|
RECORD(5) |
build a five-element record |
|
|
|
RECORD(6) |
build a six-element record |
|
|
|
RECORD(7) |
build a seven-element record |
|
|
|
RECORD(8) |
build an eight-element record |
|
|
|
RECORD(9) |
build a nine-element record |
|
|
|
RECORD(10) |
build a 10-element record |
|
|
|
RECORD(11) |
build a 11-element record |
|
|
|
RECORD(12) |
build a 12-element record |
|
|
|
RECORD(13) |
build a 13-element record |
|
|
|
RECORD(14) |
build a 14-element record |
|
|
|
RECORD( |
build a |
|
|
|
RECORD( |
build a |
|
|
|
RAW(1) |
build a one-element raw record |
|
|
|
RAW(2) |
build a two-element raw record |
|
|
|
RAW(3) |
build a three-element raw record |
|
|
|
RAW(4) |
build a four-element raw record |
|
|
|
RAW(5) |
build a five-element raw record |
|
|
|
RAW(6) |
build a six-element raw record |
|
|
|
RAW(7) |
build a seven-element raw record |
|
|
|
RAW(8) |
build an eight-element raw record |
|
|
|
RAW(9) |
build a nine-element raw record |
|
|
|
RAW(10) |
build a 10-element raw record |
|
|
|
RAW(11) |
build a 11-element raw record |
|
|
|
RAW(12) |
build a 12-element raw record |
|
|
|
RAW(13) |
build a 13-element raw record |
|
|
|
RAW(14) |
build a 14-element raw record |
|
|
|
RAW( |
build a |
|
|
|
RAW( |
build a |
|
|
|
MIXED( |
build a mixed record with |
|
|
|
MIXED( |
build a mixed record with |
|
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
VEC(0) |
build a zero-length vector |
|
|
|
VEC( |
build a |
|
|
|
VEC( |
build a |
|
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
RAWVEC( |
||
|
|
RAWVEC( |
||
|
|
RAWVEC( |
||
|
|
RAWVEC( |
||
|
|
RAWVEC( |
||
|
|
RAWVEC( |
||
|
|
RAWVEC( |
||
|
|
RAWVEC( |
||
|
|
RAWVEC( |
||
|
|
RAWVEC( |
||
|
|
RAWVEC( |
||
|
|
RAWVEC( |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
UNUSED |
||
|
|
STORE(0) |
||
|
|
STORE(1) |
||
|
|
STORE(2) |
||
|
|
STORE(3) |
||
|
|
STORE(4) |
||
|
|
STORE(5) |
||
|
|
STORE(6) |
||
|
|
STORE( |
||
|
|
LOAD(0) |
||
|
|
LOAD(1) |
||
|
|
LOAD(2) |
||
|
|
LOAD(3) |
||
|
|
LOAD(4) |
||
|
|
LOAD(5) |
||
|
|
LOAD(6) |
||
|
|
LOAD( |
||
|
|
CONCAT(2) |
pop and concatenate two records |
|
|
|
CONCAT(3) |
pop and concatenate three records |
|
|
|
CONCAT(4) |
pop and concatenate four records |
|
|
|
CONCAT(5) |
pop and concatenate five records |
|
|
|
CONCAT(6) |
pop and concatenate six records |
|
|
|
CONCAT(7) |
pop and concatenate seven records |
|
|
|
CONCAT(8) |
pop and concatenate eight records |
|
|
|
RETURN |
pop and return the top of the stack |