nfb.pl
NFB, the nanofil bytecode: a module of ir.pl written as little-endian 32-bit words in a sectioned container, and read back (docs/nfb.md). The encoding is a function of the module, and the reader accepts only its image, so the words are the module's identity.
One table, nfb_form/3, states every instruction: its opcode, its term with the parts as variables, and the fields its words hold in order. The writer reads the table with the term given, the reader with the opcode given, so the two directions cannot disagree about a layout. module_nfb/2 is the relation between a module and its words; it runs either way, and its reading checks that writing the module gives the very words it read.
The container
| Words | Hold |
|---|---|
| header | magic 0x0042464E, version, flags (stage, mode, ordered), section count |
| STRINGS (1) | symbol names and loc files, sorted bytes |
| CONSTANTS (2) | constants an operand word cannot hold, sorted, 3 words each |
| SYMBOLS (3) | defined symbols in item order, then the named-only sorted |
| DATA (4), FUNCTIONS (5) | the data objects and the function bodies, in symbol order |
| NAMES (0x100) | the names of temporaries, labels and locals, as terms |
| CONTEXT (0x101) | the context/2 items and where they stood |
Ids
A temporary's id is its place among the function's first definitions: the parameters, then each definition in stream order. A later definition of the same temporary (ir.pl's IR is not strict SSA) is preceded by a rebind word naming its id. Blocks and named locals are numbered in stream order. Without NAMES, the reader names each by its id, an integer.
The table
Code is the word that stands for Term in a field of enumeration Enum. Classes and widths share codes, so that a store's width and a load's type read like the class they hold.
The instruction, phi or jump Term is written as the header word of Opcode, then Fields in order. Each field names a part of Term:
| Field | Words |
|---|---|
defD | none: tmpD takes the next temporary id |
classK, enumE, X | one code (nfb_code/3) |
u32N, i64N | N as one word, or as two (low, high), signed |
opndV | an operand word: a temporary, constant or symbol |
labelL, targetT | a block index; for a target shifted left, bit 0 for poll |
localX, local_defX | a local's id; the definition writes none |
strA | a STRINGS id |
resultR | a call's result word, defining its temporaries |
altX, Alts | a tag word, then the fields of the alternative Tag-X-Fields |
countXs, X, Fs | the length of Xs, then Fs for each element X |
restXs, X, Fs | Fs for each element X, to the instruction's end |
Each opcode belongs to one group: 1 is the rebind prefix, 8 the phi, 0x10 to 0x7F plain instructions and 0x80 to 0x8F jumps.
phi, plain or jump.
The relation
Words is the NFB of Module, NAMES and CONTEXT included. With Module ground it writes; otherwise it reads Words, and fails unless they are exactly what writing the module they decode to gives. Throws nanofil_error(Msg) for a module that has no encoding.
Words is the NFB of Module with the side sections Sides, a subset of [names, context].
Core is the NFB Words without its side sections: the words the module's identity is taken over.
Ws with only its sections of ids below Limit.
the context items of All, each with the number of other items before it, and the rest.
Tables: strings, constants and symbols
tables(Strs, Consts, Syms, SymIds, StrIds, ConstIds), each Ids an assoc from a name or constant to its id. Syms lists sym(Name, Kind, Item): the defined symbols in item order, then the named-only by name.
each name's first symbol id.
the item refers to the symbol of global(S), uses the constant const(N) or the file file(F), as its forms say.
Data
Functions
the FUNCTIONS payload; O is true when O0 is and every body is ordered, and NameRecs the names of each function's temporaries, labels and locals.
fctx(Temps, Labels, Locals, Tables): a function's ids, assocs from each name to its id.
each temporary's id by first definition, each label's block index and each local's id, and the names in id order.
Ds without the repeats, first occurrence kept.
D is a temporary Inst defines, in order.
the stream of the blocks from index I, N0 temporaries defined before them, O whether ordered.
Words
Side sections: terms
A side section's terms are written with their own string pool: the atoms and functors they use, sorted by bytes. A term is a tag word and its parts: 0 an integer (wide/1), 1 an atom, 2 a compound (its functor, its arity, its arguments), 3 a proper list (its length, its elements).
NAMES: the pool, then per function its temporaries', labels' and locals' names, each list in id order.
CONTEXT: the pool, the count, then each context item's position among the other items and its term.
Reading
M is the module the NFB Ws encode; Ws must be exactly its encoding, with the side sections Ws has.
X is the Ith element of a table the reader built with =.., which has no arguments when it is empty.
data(Name, Linkage, Align, Items) of each kind-1 symbol, in order.
the items in symbol order: each defined symbol's function, data or alias.
Side sections.
the function of each kind-0 symbol, named from Names or by ids.
terms whose arguments are the names by id: from NAMES, or the ids themselves.
the blocks from index I; N0 temporaries and X0 locals defined before them.
at_end//: no words are left.
the next instruction of Ws, after any rebind, is of group G.
each definition's name: the next id's, or the rebound id's.
Files
Words are the little-endian words of File. Throws nanofil_error(Msg) when its length is not a multiple of four.