Fil-C on Nix

Filnix wiki

mbrock/filnix/nanofil

ir.pl

The nanofil IR: modules of functions and data as ground Prolog terms, the contract between the frontend, the middle end, the interpreter and the backends. This module states its grammar and checks that a module is well formed.

A function is a list of blocks; a block is phis, then instructions, then one jump. Temps are ground names; logic variables never appear in IR terms.

func(Name, Linkage, Ret, Params, Blocks)
┌───────────────────────────────┐
│ entry block: first, no phis   │
└───────────────┬───────────────┘
                ▼
┌───────────────────────────────┐
│ phis    phi(tmp(D), K, Pairs) │
│ insts   let, store, call, loc │
│ jump    goto, branch, ret ... │
└───────────────────────────────┘

Classes

Every value has a class, one letter:

ClassValue
w32-bit integer (a word)
l64-bit integer (a long)
s32-bit float (single)
d64-bit float (double)
ppointer: an address with its Fil-C capability (filc mode only)
aproven address: a pointer or named-local access that a check has cleared (checked stage only)
fproven function: a p that a function check has cleared (checked stage only)

A store's width may also be b (8 bits) or h (16 bits). A load and ext name the narrow types sb ub sh uh sw uw: signed or unsigned byte, halfword and word.

Items

TermMeans
moduleItemsItems are func/5, data/4 and optional context/2 terms
funcName, Linkage, Ret, Params, Blocksa function, fields below
dataName, Linkage, Align, Itemsa data object, fields under Data
contextName, Recordsexplanatory binding, typed-origin, value, alias and site records; see provenance.pl
Field of funcIs
Namean atom, the symbol
Linkageexport or local
Retw, l, p, s, d, void, or tupleK1, K2
Paramsa list of paramK, tmpN, K one of w l p s d
Blocksa list of blockLabel, Phis, Insts, Jump, the entry block first

A tupleK1, K2 return is two words, each Ki l or p: a struct of 9 to 16 bytes (filc mode only).

Phis and instructions

TermMeans
phitmpD, K, Pred−ValueD is Value when control came from block Pred
lettmpD, K, Op, ArgsD, of class K, is Op of Args (Ops below)
storeWidth, Value, Addrstore Value at Addr; Width one of b h w l p s d
local_allocName, Sizecreate a zeroed, exact-sized invocation-local object, without an address value
local_loadtmpD, K, T, Name, Offchecked load semantics at the fixed byte offset Off in Name
local_storeWidth, Value, Name, Offchecked store semantics at the fixed byte offset Off in Name
callRes, Callee, argK, Valuecall Callee: globalF, or (filc mode) a p temp
locFile, Linedoes nothing: the instructions after it, up to the next loc, come from line Line of File
raw_storeWidth, Off, Value, Addrchecked stage: a non-pointer store at Addr+Off that does not check (Stages, below)
raw_store_pointerOff, Value, Addr, Effectschecked stage: a pointer store whose ordered aux/barrier effects are explicit
raw_callRes, Callee, Argschecked stage: call the proven function Callee (class f); argument, result and exception handling are as for call
flighttmpD, Op, Args, Lowerchecked stage: construct a p from a 64-bit address calculation and an independently selected capability

A call's Res is one of resulttmpD, K, void, or tupleresulttmpD1, K1, resulttmpD2, K2.

Named locals are Fil-C-only. Their ground names have a separate namespace from temporaries. Each is declared once in the entry block, before its uses, and lives until this invocation exits. Functions with setjmp cannot contain them. Off is a signed 64-bit integer; an access outside [0, Size) traps, including every access to a zero-sized object. Pointer contents have ordinary capabilities, but a local's own native address is never a p value or heap lower.

flight computes its address with copy, add or sub, using unsigned 64-bit arithmetic. Each argument is an l value or addressP, the address bits of a p value. Lower is none or lowerP, exactly P's capability identity, not an integer bound. The donors may differ: constructing a pair proves nothing about access. No getter, check, memory access or safepoint occurs inside flight; both components become one rooted p result. Projections occur only here and cannot read globals or proof classes.

Jumps

JumpMeans
gotoLgo to L
branchV, L1, L2go to L1 if V is nonzero, else to L2
retVreturn V
retsV1, V2return a tuple
retreturn from a void function
unreachablestop the program (a trap instruction)

A jump's target may be pollL: a pollcheck on the edge, after the phis' copies, then L. A pollcheck is a safepoint: the thread may run signal handlers and answer the collector.

Ops

The Op of a lettmpD, K, Op, Args; op_kind/2 and valid_op/1 state these sets once, op_result_ok/3 and op_arg_classes/4 the classes.

OpResult KArgsMeans
add subany[K, K]; at p, a p and an l offset
mul divw l s d[K, K]
udiv rem urem and or xorw l[K, K]
sar shr shlw l[K, w]the count is read at w
negw l s d[K]
copyany[K]
cmpCond, K1w l; w when K1 is a float[K1, K1]Cond for K1 w or l: eq ne sle slt sge sgt ule ult uge ugt; for s or d: eq ne le lt ge gt o uo
extTl for sw uw; w or l for sh uh sb ub[w]extend the low bytes
extsd[s]single to double
truncds[d]double to single
stosi stoui / dtosi dtouiw l[s] / [d]float to signed or unsigned integer
swtof uwtof / sltof ultofs d[w] / [l]signed or unsigned integer to float
castw s l d[s] [w] [d] [l]bitwise: w and s, l and d
loadTas T: l, p, s, d; w or l for a narrow T[Addr]T one of l sw uw sh uh sb ub p s d
allocAlignthe address class[l], the sizeAlign one of 4, 8, 16
ptoil[p]filc only
itopp[l]filc only
va_argspconstNfilc only: a cursor into the entry argument snapshot; N, non-negative, is the byte count of the fixed prefix
va_argany[p]filc only: a load through a va_list address, advancing its cursor (reads and writes memory)
setjmpKindw[p, w]: Buf, SaveMaskfilc only, below
checkLo, Hi, Rights, Aligna[p]checked stage, below
check_localName, Off, Bytes, Rights, Aligna[]checked stage: prove exact bounds and alignment for the named object, preserving its identity and offset
raw_loadT, Offas loadT, except p[a]checked stage: a non-pointer load at A+Off that does not check
raw_load_pointerOff, Effectsp[a]checked stage: read aux, resolve the lower, then read the primary address
check_functionf[p]checked stage: trap unless the pointer designates a function object at its exact callable address

setjmpKind: Kind is setjmp, '_setjmp' or sigsetjmp, and SaveMask must be const0 except for sigsetjmp. It returns twice natively; the snapshot and restore contract is in docs/setjmp.md.

checkLo, Hi, Rights, Align reads [p P]. It traps unless P's capability permits Rights (read or write) on [P+Lo, P+Hi) and P+Lo is Align-aligned (Align one of 1, 2, 4, 8, 16); otherwise its result is P, proven accessible.

check_local proves [Off, Off+Bytes) within the named object's exact size and 16-aligned base. Its adjacent raw access has offset zero. Pointer load/store effects are respectively [aux_read, address_read] and [lower_write, address_write]: explicit stack aux has neither a heap header nor a lower box. Validation selects this protocol by proof provenance, not list shape.

Values

ValueMeans
tmpNamea temporary, defined once (SSA)
constIntegera number; at s or d an unsigned IEEE bit pattern (docs/float.md)
globalNamethe address of a function or data object, by its symbol

Data

In dataName, Linkage, Align, Items, Linkage is export, local, roexport or rolocal, ro for read-only data (in .rodata), and Align is 1, 2, 4, 8 or 16. Items is a list of:

ItemMeans
itemW, constNN at width W
itemb, strCharsthe bytes themselves, one character per byte (codes 0..255); a backend escapes them when it prints assembly
iteml, symName, Offthe address of Name plus Off (plain mode)
itemp, symName, Offa pointer with the capability of Name (filc mode only)
zeroNN zero bytes

Modes

A module is in one of two modes, told apart by what it uses (module_mode/2):

ModeMeans
plainclasses w, l, s and d only. Addresses are l values, and the plain backend (amd64.pl) compiles it to ordinary x86-64.
filcanything of class p, a tuple return, read-only data, a pointer-only op, or a call to a function the module does not define. Addresses are p values (flight pointers with a capability), alloc yields p, loads and stores take a p address, p arithmetic is add or sub of an l offset, and ptoi and itop convert.

In filc mode calls may name functions outside the module, or go through a p value. A symbol globalS is read only by lettmpD, p, copy, globalS, which yields a pointer to the data or function object; S may be defined outside the module. See docs/m3.md and docs/m4.md.

Stages

A module is in one of two stages, told apart by its vocabulary:

StageMeans
uncheckedas the frontend and the middle end write it. In filc mode, loadT and store at a p address mean "Fil-C's access check, then the access".
checkedafter pizlonate.pl, what the Fil-C backend compiles: every check is explicit, and every cycle of the CFG has a poll edge.

In the checked stage a check yields a temporary of class a, a proven address, which only raw accesses read: raw_loadT, Off and raw_storeW, Off, V, A. The check that covers an access is its address operand's definition. A raw access its check does not cover is a bug of the pass, which the interpreter reports as the outcome uncheckedWhy.

Pure pointer copies, add/sub and integer-to-pointer conversion use flight in checked IR; ordinary p copy remains only for getters. The middle end runs before pizlonation. Its entry point leaves a directly supplied checked module unchanged, even with -O.

A check_function similarly yields class f, consumed only as the callee of the immediately following raw_call. It proves the target's identity, not a signature match: raw_call still selects fast or generic argument/result handling. A bad raw target reports uncheckednot_a_function; traps from that handling or from the callee remain traps. Direct calls keep call and their ABI thunks.

Pointer accesses carry the complete ordered list described by pointer_memory_effects/2. The list is executed, not an annotation. Each access is one instruction for liveness and layout, not an atomic transaction: aux_ensure may exit/re-enter or fail without returning. rooted_reload reloads the original p pair, not the ephemeral proof. The stored value also remains rooted through the whole instruction.

Neither proof class crosses an intervening safepoint (a call, a pollcheck, alloc, setjmp, va_arg or getter), since a free may make it stale. Each check is followed immediately by its one use. The pointer store's internal ensure-aux/reload sequence is the existing Fil-C exception, not permission to move another effect between the pair.

Variable names

In the predicates below: M a module, Fn a function name, K a class, V an IR value, L a label.

useslists
usesassoc
usesdcgs
usescfg
usesprovenancecode_items/2, valid_contexts/1
class?K, ?Kind, ?Bitsnondet

K is a value class of Kind (int, pointer or float), Bits wide. Every other fact about classes below follows from this table.

class
266wint32
267lint64
268ppointer64
269sfloat32
270dfloat64
272any_classK ←
classK, _, _
273int_classK ←
classK, int, _
274float_classK ←
classK, float, _
275numeric_classK ←
276numeric_classK ←
class_bits+K, -Bitssemidet

Bits is the width in bits of the numeric class K (w, l, s or d).

282class_bitsK, Bits ←
modulus+K, -Modsemidet

Mod is 2^bits(K): values of class K are the integers 0..Mod-1. A p value's address has the modulus of l.

291modulusK, Mod ←
classK, _, Bits
Mod is 1 << Bits
half_modulus+K, -Halfsemidet

Half is half the modulus of the integer class K: the values Half..Mod-1 are negative when read as signed.

300half_modulusK, Half ←
classK, int, Bits
Half is 1 << Bits−1
width_bytes+Width, -Sizesemidet

A store of Width (b, h, or a class) writes Size bytes.

308width_bytesb, 1
309width_bytesh, 2
310width_bytesK, Size ←
classK, _, Bits
Size is Bits//8
addr_class+Mode, ?Ksemidet

K is the class of an address in a module of Mode (plain or filc).

318addr_classplain, l
319addr_classfilc, p
op_arity+Op, -Nsemidet

Op takes N arguments; it fails for anything that is not an Op of the grammar.

326op_arityOp, N ←
op_kindOp, Kind
kind_arityKind, N
kind_arity
330additive2
331alu2
332shift2
333cmp2
334neg1
335copy1
336ptoi1
337itop1
338va_args1
339va_arg1
340setjmp2
341ext1
342load1
343alloc1
344float_convert1
345float_to_int1
346int_to_float1
347cast1
348check1
349check_local0
350check_function1
351ld1
352ld_pointer1
valid_op+Opsemidet

Op is an instruction of the IR: one of op_kind/2's, with operands from the sets below where it has them.

359valid_opcmpCond, K ←
!
comparisonCond, K
360valid_oploadT ←
361valid_opallocAlign ←
362valid_opcheckLo, Hi, Rights, Align ←
!
integerLo
integerHi
Lo < Hi
memberchkRights, read, write
memberchkAlign, 1, 2, 4, 8, 16
366valid_opcheck_localName, Off, Bytes, Rights, Align ←
!
groundName
integerBytes
Bytes > 0
Bytes ≤ 2147483647
memberchkRights, read, write
memberchkAlign, 1, 2, 4, 8, 16
369valid_opraw_loadT, Off ←
!
T ≢ p
integerOff
370valid_opraw_load_pointerOff, Effects ←
!
integerOff
or
pointer_memory_effectslocal_load, Effects
373valid_opOp ←
op_kindOp, _
pointer_memory_effects?Access, ?Effects

the closed protocol for an ordinary non-atomic pointer access. aux_read reads the encoded lower (or none); lower_resolve unboxes it without taking the box's address. A store ensures aux (may exit), barriers the value's lower (may call, cannot exit), reloads its destination p pair from rooted storage, then writes the ordinary lower before the primary address. No effect list may omit or reorder these obligations.

pointer_memory_effects
382loadaux_read, lower_resolve, address_read
383storeaux_ensure, lower_barrier, rooted_reload, lower_write, address_write

Proven nonescaping memory has an explicit stack aux, never a heap header or atomic box. These protocols are selected by check_local's provenance, not merely by a shorter effect list supplied by the caller.

388local_loadaux_read, address_read
389local_storelower_write, address_write
access_size_align?X, ?Size, ?Alignnondet

A load of type X or a store of width X accesses Size bytes, and Fil-C requires its address to be Align-aligned: 8 for a pointer, whose capability lives in the aux word of its 8-byte slot, else 1.

397access_size_alignp, 8, 8
398access_size_alignX, Size, 1 ←
X ≢ p
or
narrow_typeX, Size, _
cmp_condeqneslesltsgesgtuleultugeugt
407comparisonCond, K ←
408comparisonCond, K ←
float_condeqneleltgegtouo
narrow_type?T, ?Bytes, ?Signednessnondet

The extension (and load) type T takes the low Bytes bytes of an integer and extends them as Signedness says.

narrow_type
416sw4signed
416uw4unsigned
417sh2signed
417uh2unsigned
418sb1signed
418ub1unsigned
421load_typel
421load_typep
422load_typed
423load_types
424load_typeT ←
alloc_align4816
op_kind?Op, ?Kindnondet

Kind is the category of Op that its consumers dispatch on, so that each can case-split on Kind in clause heads. Deterministic in mode (+, -): one fact per op, indexed on the first argument.

additive  add sub: integer, or (filc) a p plus or minus an l offset
alu       mul div udiv rem urem and or xor: binary, integer only
shift     sar shr shl: the count is read at w
neg copy ptoi itop  each its own kind; copy may be of class p
cmp ext load alloc  compounds
check ld   the checked stage: a check and a raw load
443op_kindadd, additive
444op_kindsub, additive
445op_kindmul, alu
446op_kinddiv, alu
447op_kindudiv, alu
448op_kindrem, alu
449op_kindurem, alu
450op_kindand, alu
451op_kindor, alu
452op_kindxor, alu
453op_kindsar, shift
454op_kindshr, shift
455op_kindshl, shift
456op_kindneg, neg
457op_kindcopy, copy
458op_kindptoi, ptoi
459op_kinditop, itop
460op_kindva_args, va_args
461op_kindva_arg, va_arg
462op_kindsetjmpKind, setjmp ←
memberKind, setjmp, '_setjmp', sigsetjmp
463op_kindexts, float_convert
464op_kindtruncd, float_convert
465op_kindstosi, float_to_int
466op_kindstoui, float_to_int
467op_kinddtosi, float_to_int
468op_kinddtoui, float_to_int
469op_kindswtof, int_to_float
470op_kinduwtof, int_to_float
471op_kindsltof, int_to_float
472op_kindultof, int_to_float
473op_kindcast, cast
474op_kindcmp_, _, cmp
475op_kindextT, ext ←
476op_kindload_, load
477op_kindalloc_, alloc
478op_kindcheck_, _, _, _, check
479op_kindcheck_local_, _, _, _, _, check_local
480op_kindcheck_function, check_function
481op_kindraw_load_, _, ld
482op_kindraw_load_pointer_, _, ld_pointer
func_named+M, +Name, -Funcsemidet

Func is the func/5 term that defines Name in the module M.

488func_namedmoduleItems, Name, Func ←
Func = funcName, _, _, _, _
memberchkFunc, Items

Entry snapshot metadata is retained even if its cursor is unused.

493variadic_funcfunc_, _, _, _, Blocks ←
memberblock_, _, Insts, _, Blocks
memberlet_, p, va_args, _, Insts
!

Nonlocal returns restore compiler slots, not mutable local memory. Transformations must retain this boundary even in unreachable blocks.

499setjmp_funcfunc_, _, _, _, Blocks ←
memberblock_, _, Insts, _, Blocks
memberlet_, _, setjmp_, _, Insts
!

Module mode

module_mode+M, -Modedet

Mode is filc when anything in M has class p, uses a pointer-only op, or calls a function M does not define (which only the Fil-C ABI can reach), and plain otherwise.

The mode is inferred rather than stated: M uses a feature only filc mode has, or it does not. Every rule above is such a feature, the external call as much as class p. Stating the mode would mean a module(Mode, Items) term or a Mode argument for valid_module/1, run/5, promote and the backends, and a mode clause in each IR file, for no case the inference gets wrong: a C module is filc unless it uses none of these features (no memory, no pointers, no external calls), and then validation, promotion and the interpreter treat it alike in both modes; nanofil cc picks the Fil-C backend itself.

521module_modemoduleItems, Mode ←
memberItem, Items
→
Mode = filc
memberfunc_, _, _, _, Blocks, Items
memberblock_, _, Insts, _, Blocks
membercall_, globalCallee, _, Insts
¬memberchkfuncCallee, _, _, _, _, Items
→
Mode = filc
else
Mode = plain
pointer_item+Item

Item has class p somewhere, or is read-only data, which only filc mode models. The cuts commit to the first witness.

535pointer_itemfunc_, _, Ret, Params, Blocks ←
Ret ≡ p
or
Ret = tuple_
or
memberparamp, _, Params
or
memberblock_, Phis, Insts, _, Blocks
memberphi_, p, _, Phis
or
memberInst, Insts
!
545pointer_itemdata_, Linkage, _, Items ←
Linkage = ro_
or
memberchkitemp, _, Items
!
pointer_inst+Inst

Inst produces, stores, passes or calls through a p value, or converts one (ptoi). Several clauses may hold for one Inst.

553pointer_instlet_, p, _, _
554pointer_instlet_, _, ptoi, _
555pointer_instlet_, _, setjmp_, _
556pointer_inststorep, _, _
557pointer_instcallresult_, p, _, _
558pointer_instcalltuple_, _, _
559pointer_instcall_, _, Args ←
memberchkargp, _, Args
560pointer_instcall_, tmp_, _
561pointer_instlet_, K, _, _ ←
562pointer_instraw_store_, _, _, _
563pointer_instraw_store_pointer_, _, _, _
564pointer_instlet_, _, raw_load_pointer_, _, _
565pointer_instraw_call_, _, _
566pointer_instflight_, _, _, _
567pointer_instlocal_alloc_, _
568pointer_instlocal_load_, _, _, _, _
569pointer_instlocal_store_, _, _, _

Validation

valid_module+Msemidet

M is a well-formed module. When it is not, throws nanofil_error(Message) describing the first problem found, so callers get a diagnostic rather than a silent failure. A few terms outside the grammar (an item, instruction or jump of no known shape) make it fail instead.

Functions are checked against a context

ctx(Module, Fn, Ret, LabelSet, TempClasses, PredMap, Mode)

whose three assocs map each label to t, each temporary to its class, and each label to its sorted predecessors.

586valid_moduleM ←
M = moduleAll
code_itemsAll, Items
module_stageM, Stage
maplistvalid_itemM, Mode, Stage, Items
module_stage+M, -Stage

Stage is checked when M uses any word of the checked stage (a check, a raw access, flight or a poll edge), else unchecked.

596module_stagemoduleItems, Stage ←
memberfunc_, _, _, _, Blocks, Items
memberblock_, _, Insts, Jump, Blocks
memberInst, Insts
or
→
Stage = checked
else
Stage = unchecked
checked_instlet_, _, check_, _, _, _, _let_, _, check_local_, _, _, _, _, _let_, _, check_function, _let_, _, raw_load_, _, _let_, _, raw_load_pointer_, _, _raw_store_, _, _, _raw_store_pointer_, _, _, _raw_call_, _, _flight_, _, _, _
checked_jumpgotopoll_branch_, poll_, _branch_, _, poll_
valid_item+M, +Mode, +Stage, +Item

Item, an item of M, is well formed.

622valid_itemM, Mode, Stage, funcName, _, Ret, Params, Blocks ←
Blocks ≡ []
→
fn_errorName, 'function has no blocks', []
else
true
or
Ret ≡ void
or
Ret = tuple_
→
true
else
fn_errorName, 'invalid return class ~w', Ret
Ret = tupleKs
¬
Ks = _, _
forallmemberK, Ks, memberchkK, l, p
→
fn_errorName, 'a tuple return is two words of class l or p, not ~w', Ks
else
true
findallLabel, memberblockLabel, _, _, _, Blocks, Labels
msortLabels, Sorted
sortLabels, Sorted
→
true
else
fn_errorName, 'duplicate block label', []
temp_defsParams, Blocks, Defs0
sortDefs0, Defs
Labels, definitions and predecessors are indexed once, so checking
a function stays near linear in its size.
findallLabel−t, memberLabel, Labels, LabelPairs
list_to_assocLabelPairs, LabelSet
list_to_assocDefs, TempClasses
predecessorsBlocks, PredMap
Ctx = ctxM, Name, Ret, LabelSet, TempClasses, PredMap, Mode
maplistvalid_blockCtx, Blocks
Blocks = blockEntry, _, _, _|_
memberblock_, _, _, Jump, Blocks
jump_targetJump, Entry
→
fn_errorName, 'jump to the entry block ~w', Entry
else
true
proof_class_defsName, Params, Blocks
definitions_dominateName, Params, Blocks
valid_localsName, Params, Blocks
stage_funcStage, Name, Mode, Blocks
657valid_item_, Mode, _, dataName, _, Align, Items ←
memberchkAlign, 1, 2, 4, 8, 16
→
true
else
fn_errorName, 'unsupported data alignment ~w', Align
forall
memberitemK, V, Items
,
V = constN
integerN
modulusK, Mod
N ≥ 0
N < Mod
→
true
else
fn_errorName, 'invalid float data ~w', V
Mode ≡ plain
memberchkitemp, _, Items
→
fn_errorName, 'pointer data items need a Fil-C module', []
Mode ≡ filc
memberchkiteml, symSym, _, Items
→
fn_errorName, 'a pointer in a Fil-C module is item(p, sym(~w, _))', Sym
else
true
forallmemberitem_, strCs, Items,
maplistbyte_char, Cs
→
true
else
fn_errorName, 'str/1 holds characters of codes 0..255', []
byte_char+C

C is a character that stands for one byte.

676byte_charC ←
atomC
atom_lengthC, 1
char_codeC, N
N ≤ 255
local_objects+Func, -Objects

declarations, in instruction order. Names belong to a separate namespace: they are never ordinary values.

680local_objectsfunc_, _, _, _, Blocks, Objects ←
findallName−Size,
memberblock_, _, Is, _, Blocks
memberlocal_allocName, Size, Is
,
Objects
685local_offsetOff ←
integerOff
Off ≥ -9223372036854775808
Off < 9223372036854775808
687valid_localsFn, Params, Blocks ←
F = funcFn, local, void, Params, Blocks
local_objectsF, Objects
findallN, memberN−_, Objects, Names
sortNames, Unique
lengthNames, Count
lengthUnique, Count
→
true
else
fn_errorFn, 'duplicate local allocation', []
Objects ≢ []
→
fn_errorFn, 'local storage in a returns-twice function', []
else
true
Blocks = blockEntry, _, _, _|_
findallN−I,
memberblockEntry, _, Is, _, Blocks
nth0I, Is, local_allocN, _
,
Declared
lengthDeclared, Count
→
true
else
fn_errorFn, 'local allocation outside the entry block', []
forall
memberblockL, _, Is, _, Blocks
nth0I, Is, Ins
,
memberchkN−At, Declared
L ≢ Entry
or
At < I
→
true
else
fn_errorFn, 'local ~w is used before its declaration', N
local_reference
705local_load_, _, _, N, _N
706local_store_, _, N, _N
707let_, _, check_localN, _, _, _, _, _N
temp_defs+Params, +Blocks, -Defs

Name-Class for every definition of a temporary, in parameter and then block order.

711temp_defsParams, Blocks, Defs ←
findallN−K, memberparamK, tmpN, Params, ParamDefs
findallN−K,
memberblock_, Phis, Insts, _, Blocks
memberphitmpN, K, _, Phis
or
memberlettmpN, K, _, _, Insts
or
memberlocal_loadtmpN, K, _, _, _, Insts
or
memberflighttmpN, _, _, _, Insts
K = p
or
memberI, Insts
Res = resulttmpN, K
or
Res = tupleTos
memberresulttmpN, K, Tos
,
BlockDefs
appendParamDefs, BlockDefs, Defs

Both call forms have the same result and argument vocabulary; only the callee's proof obligation differs.

728call_result_argscallRes, _, Args, Res, Args
729call_result_argsraw_callRes, _, Args, Res, Args

Defs is sorted, so two classes for one temporary are adjacent.

732check_temp_classesFn, Defs ←
forallmember_−K, Defs,
→
true
else
fn_errorFn, 'invalid class ~w', K
append_, N−K1, N−K2|_, Defs
K1 ≢ K2
→
fn_errorFn, 'tmp(~w) has classes ~w and ~w', N, K1, K2
else
true
predecessors+Blocks, -PredMap

each label's sorted predecessors.

741predecessorsBlocks, PredMap ←
findallTarget−Pred,
memberblockPred, _, _, Jump, Blocks
jump_targetJump, Target
,
Edges0
sortEdges0, Edges
empty_assocPredMap0
foldladd_predecessor, Edges, PredMap0, PredMap
751add_predecessorTarget−Pred, PredMap0, PredMap ←
get_assocTarget, PredMap0, Preds0
→
true
else
Preds0 = []
appendPreds0, Pred, Preds
put_assocTarget, PredMap0, Preds, PredMap
valid_block+Ctx, +Block

Block's phis cover exactly its predecessors, and its instructions and jump are well formed.

758valid_blockCtx, blockLabel, Phis, Insts, Jump ←
Ctx = ctx_, Fn, _, _, _, PredMap, _
get_assocLabel, PredMap, Preds
→
true
else
Preds = []
maplistvalid_phiCtx, Label, Preds, Phis
maplistvalid_instCtx, Insts
valid_jumpCtx, Jump
Phis ≢ []
Preds ≡ []
→
fn_errorFn, 'phi in block ~w, which has no predecessors', Label
else
true
jump_target+Jump, -Lnondet

Jump can branch to the label L, with or without a pollcheck.

772jump_targetgotoT, L ←
773jump_targetbranch_, T, _, L ←
774jump_targetbranch_, _, T, L ←
temp_class?K

a temporary may have a value class or a proof class. Proofs live in no memory and are not values of C.

778temp_classK ←
779temp_classK ←
781proof_classa
782proof_classf
proof_class_defs+Fn, +Params, +Blocks

only a check defines a proof temporary, and nothing passes one as a call argument. Phis, parameters and call results would carry a proof across an edge or a safepoint. (A check's own class is op_result_ok/3's.)

788proof_class_defsFn, Params, Blocks ←
memberparamK, _, Params
or
memberblock_, Phis, Insts, _, Blocks
memberphi_, K, _, Phis
or
memberI, Insts
call_result_argsI, Res, Args
Res = result_, K
or
Res = tupleTos
memberresult_, K, Tos
or
memberargK, _, Args
→
fn_errorFn, 'class ~w is only a check\'s result', K
else
true

definitions_dominate(+Fn, +Params, +Blocks): in every reachable block, each use of a temporary is dominated by one of its definitions: a parameter, an earlier instruction or phi of the same block, or one in a block that strictly dominates it. A phi reads its value at the end of the predecessor, if that is reachable. A temporary may be defined more than once (tests/fixtures/memory.ir); one dominating definition is enough. Unreachable blocks are not checked.

810definitions_dominateFn, Params, Blocks ←
func_cfgBlocks, Cfg
cfg_idomsCfg, Idom
dom_intervalsCfg, Idom, Intervals
findallN−param, memberparam_, tmpN, Params, ParamSites
block_sitesBlocks, Pairs0, []
appendParamSites, Pairs0, Pairs
keysortPairs, Sorted
group_sitesSorted, Grouped
list_to_assocGrouped, Sites
findallL−End,
memberblockL, _, Insts, _, Blocks
lengthInsts, End
,
Ends0
list_to_assocEnds0, Ends
dominated_blocksBlocks, domFn, Sites, Intervals, Ends
block_sites+Blocks//

N-at(L, I) for each definition of tmp(N), at index I of block L, a phi's at -1.

827block_sites[] ⟶
[]
828block_sitesblockL, Phis, Insts, _|Bs ⟶
phi_sitesPhis, L
inst_sitesInsts, L, 0
833phi_sites[], _ ⟶
[]
834phi_sitesphitmpN, _, _|Phis, L ⟶
N−atL, -1
phi_sitesPhis, L
836inst_sites[], _, _ ⟶
[]
837inst_sitesInst|Insts, L, I ⟶
findallN−atL, I, inst_defInst, N, Defs
I1 is I + 1
seqDefs
inst_sitesInsts, L, I1
group_sites+Pairs, -Groups

Pairs N-Site, sorted by N, grouped as N-Sites.

845group_sites[], []
846group_sitesN−S|Ps, N−S|Ss|Gs ←
same_keyN, Ps, Ss, Rest
group_sitesRest, Gs
850same_keyN, N1−S|Ps, S|Ss, Rest ←
N1 ≡ N
!
same_keyN, Ps, Ss, Rest
851same_key_, Ps, [], Ps
dominated_blocks+Blocks, +D

every use in the reachable blocks of Blocks (those with a dominator-tree interval) is dominated: an instruction's at its index, the jump's at the end of the block, and a phi's operand at the end of its predecessor, if that is reachable. D is dom(Fn, Sites, Intervals, Ends), Ends the number of instructions of each block. Plain recursion, not maplist/3 over closures, which Trealla copies with D at every call.

860dominated_blocks[], _
861dominated_blocksblockL, Phis, Insts, Jump|Bs, D ←
D = dom_, _, Intervals, _
get_assocL, Intervals, _
→
dominated_instsInsts, 0, End, L, D
jump_usesJump, Us
dominated_usesUs, L, End, D
else
true
872dominated_insts[], I, I, _, _
873dominated_instsInst|Insts, I, End, L, D ←
inst_usesInst, Us
dominated_usesUs, L, I, D
I1 is I + 1
dominated_instsInsts, I1, End, L, D
879dominated_uses[], _, _, _
880dominated_usesN|Ns, L, I, D ←
dominated_useD, L, I, N
dominated_usesNs, L, I, D
884dominated_phis[], _
885dominated_phisphi_, _, PVs|Phis, D ←
889dominated_phi_operands[], _
890dominated_phi_operandsP−V|PVs, D ←
D = dom_, _, Intervals, Ends
V = tmpN
get_assocP, Intervals, _
→
get_assocP, Ends, End
dominated_useD, P, End, N
else
true
dominated_use+D, +L, +I, +N

some definition of tmp(N) dominates position I of block L.

901dominated_usedomFn, Sites, Intervals, _, L, I, N ←
get_assocN, Sites, Defs
memberDef, Defs
site_dominatesDef, L, I, Intervals
→
true
else
fn_errorFn, 'tmp(~w) is used in block ~w, which no definition dominates', N, L
site_dominates+Def, +L, +I, +Intervals

the definition Def comes before position I of block L on every path from the entry.

912site_dominatesparam, _, _, _
913site_dominatesatL, J, L, I, _ ←
J < I
914site_dominatesatD, _, L, _, Intervals ←
D ≢ L
dominates_by_intervalIntervals, D, L
stage_func+Stage, +Fn, +Mode, +Blocks

Fn's blocks are well formed for the module's Stage. A checked function accesses memory only by raw accesses, each covered by the check right before it, and polls on every cycle.

922stage_funcunchecked, _, _, _
923stage_funcchecked, Fn, Mode, Blocks ←
forallmemberblock_, _, Insts, _, Blocks, checked_instsInsts, Fn, Mode
→
true
else
fn_errorFn, 'a cycle without a poll edge in checked IR', []
checked_insts+Insts, +Fn, +Mode

each check in Insts is followed by the one access it covers, which is its only use, so no safepoint can come between them; no other instruction accesses memory unchecked.

932checked_insts[], _, _
933checked_instsInst|Insts, Fn, Mode ←
checked_inst_restInst, Insts, Fn, Mode, Rest
checked_instsRest, Fn, Mode
937checked_inst_restlettmpQ, a, checkLo, Hi, Rights, Align, _, Insts, Fn, _, Rest ←
!
Insts = Access|Rest
covered_accessAccess, Q, Lo, Hi, Rights, Align
→
true
else
fn_errorFn, 'check tmp(~w) is not followed by an access it covers', Q
945checked_inst_restlettmpQ, a, check_local_, _, Bytes, Rights, Align, [], Insts, Fn, _, Rest ←
!
Insts = Access|Rest
raw_accessAccess, Q, _, 0, _
covered_accessAccess, Q, 0, Bytes, Rights, Align
→
true
else
fn_errorFn, 'local check tmp(~w) is not followed by an access it covers', Q
954checked_inst_restlettmpQ, f, check_function, _, Insts, Fn, _, Rest ←
!
Insts = raw_call_, tmpQ, _|Rest
→
true
else
fn_errorFn, 'function check tmp(~w) is not followed by its raw call', Q
958checked_inst_restInst, Insts, Fn, Mode, Insts ←
raw_accessInst, _, _, _, _
→
fn_errorFn, 'a raw access whose address is not checked just before it', []
Inst = raw_call_, _, _
→
fn_errorFn, 'a raw call whose target is not checked just before it', []
Mode ≡ filc
→
fn_errorFn, '~w in checked IR, where every access is checked', What
→
fn_errorFn, 'implicit pointer operation in checked IR; use flight', []
else
true
implicit_flight+Inst

a pure pointer operation whose address/lower policy must be made explicit before either validation or emission of checked IR. Getter copies are effects, not members of this set.

973implicit_flightlet_, p, Op, _ ←
memberchkOp, add, sub, itop
974implicit_flightlet_, p, copy, tmp_
975implicit_flightlet_, p, copy, const_
raw_access?Inst, ?Q, ?X, ?Off, ?Rights

Inst is a raw load of type X or store of width X at tmp(Q)+Off, needing Rights.

raw_access
979let_, _, raw_loadT, Off, tmpQQTOffread
980raw_storeW, Off, _, tmpQQWOffwrite
981let_, _, raw_load_pointerOff, _, tmpQQpOffread
982raw_store_pointerOff, _, tmpQ, _QpOffwrite
unchecked_access
984let_, _, loadT, _loadT
985storeW, _, _storeW
986local_load_, _, T, _, _local_loadT
987local_storeW, _, _, _local_storeW
988call_, tmp_, _'indirect call'
990access_storage_effectslet_, _, raw_load_, _, _, _
991access_storage_effectsraw_store_, _, _, _, _
992access_storage_effectslet_, p, raw_load_pointer_, Es, _, Storage ←
994access_storage_effectsraw_store_pointer_, _, _, Es, Storage ←
storage_effect_kindStorage, store, Kind
storage_effect_kind
997heaploadload
998heapstorestore
999localloadlocal_load
1000localstorelocal_store
covered_access+Access, +Q, +Lo, +Hi, +Rights, +Align

Access is a raw access at tmp(Q) within [Q+Lo, Q+Hi), as aligned as it must be, and Rights allow it (write allows reading too).

1005covered_accessAccess, Q, Lo, Hi, Rights, Align ←
raw_accessAccess, Q, X, Off, Need
access_size_alignX, Size, AccessAlign
!
Lo ≤ Off
Off + Size ≤ Hi
Align mod AccessAlign =:= 0
Off−Lo mod AccessAlign =:= 0
rights_allowRights, Need
1013rights_allowwrite, _
1014rights_allowread, read
polls_every_cycle+Blockssemidet

Every cycle of the control-flow graph of Blocks, unreachable blocks included, has a poll edge: without them the graph is acyclic. A call does not count, since the callee need not poll. Depth-first search, with each label gray while on the path and done after.

1022polls_every_cycleBlocks ←
findallL−Ss,
memberblockL, _, _, Jump, Blocks
findallS, unpolled_edgeJump, S, Ss
,
Pairs
list_to_assocPairs, Graph
empty_assocColor0
findallL, memberL−_, Pairs, Labels
acyclic_from_allLabels, Graph, Color0, _
1032unpolled_edgegotoT, T ←
T ≠ poll_
1033unpolled_edgebranch_, T, _, T ←
T ≠ poll_
1034unpolled_edgebranch_, _, T, T ←
T ≠ poll_
acyclic_from_all+Ls, +Graph, +Color0, -Color

no cycle of Graph is reachable from Ls; a gray successor closes one. Plain recursion, since Trealla copies a closure's arguments, Graph included, at every call.

1039acyclic_from_all[], _, Color, Color
1040acyclic_from_allL|Ls, Graph, Color0, Color ←
get_assocL, Color0, C
→
C ≡ done
Color1 = Color0
else
put_assocL, Color0, gray, ColorG
get_assocL, Graph, Ss
acyclic_from_allSs, Graph, ColorG, ColorS
put_assocL, ColorS, done, Color1
acyclic_from_allLs, Graph, Color1, Color
1050valid_phiCtx, Label, Preds, phi_, K, Pairs ←
Ctx = ctx_, Fn, _, _, _, _, _
split_pairsPairs, PhiPreds0, Values
sortPhiPreds0, PhiPreds
PhiPreds ≡ Preds
→
true
else
fn_errorFn, 'phi in block ~w covers ~w but predecessors are ~w', Label, PhiPreds, Preds
maplistvalid_useCtx, K, Values

pairs_keys_values/3, without depending on library(pairs).

1061split_pairs[], [], []
1062split_pairsK−V|Ps, K|Ks, V|Vs ←
split_pairsPs, Ks, Vs
valid_inst+Ctx, +Inst

Inst is well formed and reads each operand at a class its definition allows. In filc mode a symbol is an operand only of copy, which yields its pointer; the cut commits to that case before the general clause for ins/4.

1068valid_instctx_, Fn, _, _, _, _, _, locFile, Line ←
!
atomFile
integerLine
→
true
else
fn_errorFn, 'bad source location loc(~q, ~q)', File, Line
1072valid_instctx_, Fn, _, _, _, _, filc, local_allocName, Size ←
groundName
integerSize
Size ≥ 0
Size ≤ 2147483647
→
true
else
fn_errorFn, 'invalid local allocation ~w, ~w', Name, Size
1075valid_instctx_, Fn, _, _, _, _, filc, local_loadtmp_, K, T, _, Off ←
else
fn_errorFn, 'invalid local load ~w, ~w, ~w', K, T, Off
1078valid_instCtx, local_storeW, V, _, Off ←
Ctx = ctx_, Fn, _, _, _, _, filc
else
fn_errorFn, 'invalid local store ~w, ~w', W, Off
valid_useCtx, K, V
1083valid_instCtx, flighttmp_, Op, Args, Lower ←
Ctx = ctx_, Fn, _, _, _, _, filc
memberchkOp, copy, add, sub
lengthArgs, N
→
true
else
fn_errorFn, 'bad flight calculation ~w with ~w', Op, Args
maplistvalid_flight_argCtx, Args
1091valid_instctx_, _, _, _, _, _, filc, let_, p, copy, global_ ←
!
1092valid_instCtx, let_, K, Op, Args ←
Ctx = ctx_, Fn, _, _, _, _, Mode
→
true
else
fn_errorFn, 'unknown instruction ~w', Op
lengthArgs, N
→
true
else
fn_errorFn, 'bad instruction ~w with ~w', Op, Args
op_result_okMode, Op, K
→
true
else
fn_errorFn, 'instruction ~w cannot have class ~w', Op, K
Op ≡ va_args
¬
Args = constOff
integerOff
Off ≥ 0
→
fn_errorFn, 'va_args needs a non-negative constant', []
else
true
Op = setjmpKind
Kind ≢ sigsetjmp
Args ≠ _, const0
→
fn_errorFn, 'setjmp and _setjmp need a zero SaveMask', []
else
true
op_arg_classesOp, K, Mode, ArgClasses
maplistvalid_useCtx, ArgClasses, Args
1114valid_instCtx, storeWidth, Value, Addr ←
Ctx = ctx_, Fn, _, _, _, _, Mode
Width ≡ p
Mode ≡ plain
→
fn_errorFn, 'store(p, ...) needs a Fil-C module', []
else
true
store_value_classWidth, ValueClass
valid_useCtx, ValueClass, Value
addr_classMode, AddrClass
valid_useCtx, AddrClass, Addr
1124valid_instCtx, raw_storeWidth, Off, Value, Addr ←
Ctx = ctx_, Fn, _, _, _, _, _
Width ≢ p
store_value_classWidth, ValueClass
integerOff
→
true
else
fn_errorFn, 'bad raw store raw_store(~w, ~w, ...)', Width, Off
valid_useCtx, ValueClass, Value
valid_useCtx, a, Addr
1131valid_instCtx, raw_store_pointerOff, Value, Addr, Effects ←
Ctx = ctx_, Fn, _, _, _, _, filc
integerOff
or
pointer_memory_effectslocal_store, Effects
→
true
else
fn_errorFn, 'bad pointer store offset/effects ~w, ~w', Off, Effects
valid_useCtx, p, Value
valid_useCtx, a, Addr
1140valid_instCtx, callRes, globalCallee, Args ←
Ctx = ctxM, Fn, _, _, _, _, Mode
findallK, memberargK, _, Args, ArgClasses
func_namedM, Callee, CalleeFunc
CalleeFunc = func_, _, CalleeRet, Params, _
→
findallK, memberparamK, _, Params, ParamClasses
ParamClasses ≡ ArgClasses
or
variadic_funcCalleeFunc
appendParamClasses, _, ArgClasses
→
true
else
fn_errorFn, 'call to global(~w) passes ~w, expects ~w', Callee, ArgClasses, ParamClasses
call_result_okFn, Callee, Res, CalleeRet
Mode ≡ filc
→
true
else
fn_errorFn, 'call to undefined function global(~w)', Callee
forallmemberargK, V, Args, valid_useCtx, K, V
1157valid_instCtx, call_, Callee, Args ←
Ctx = ctx_, Fn, _, _, _, _, Mode
Mode ≡ filc
Callee = tmp_
→
valid_useCtx, p, Callee
else
fn_errorFn, 'bad callee ~w', Callee
forallmemberargK, V, Args, valid_useCtx, K, V
1163valid_instCtx, raw_call_, Callee, Args ←
valid_useCtx, f, Callee
forallmemberargK, V, Args, valid_useCtx, K, V
store_value_class?Width, ?K

a store of Width reads its value at K.

1168store_value_classb, w
1169store_value_classh, w
1170store_value_classK, K ←
call_result_ok+Fn, +Callee, +Res, +CalleeRet

the call site reads the result the way the callee returns it.

1174call_result_ok_, _, void, _
1175call_result_okFn, Callee, tupleTos, Ret ←
findallK, memberresult_, K, Tos, Ks
Ret ≡ tupleKs
→
true
else
fn_errorFn, 'reads the ~w result of global(~w) as tuple(~w)', Ret, Callee, Ks
1181call_result_okFn, Callee, result_, K, Ret ←
to_result_okRet, K, Fn, Callee
to_result_ok+Ret, +K, +Fn, +Callee

a call site may read at class K the result of Callee, which returns Ret.

1186to_result_okvoid, _, Fn, Callee ←
fn_errorFn, 'uses the result of void global(~w)', Callee
1188to_result_oktuple_, K, Fn, Callee ←
fn_errorFn, 'reads the tuple result of global(~w) as ~w', Callee, K
1190to_result_okRet, K, Fn, Callee ←
class_result_okRet, K, Fn, Callee
class_result_ok+Ret, +K, +Fn, +Callee

as to_result_ok/4, for a result of class Ret. A long may be read as a word, nothing else changes class.

1197class_result_okRet, K, Fn, Callee ←
Ret ≢ K
→
fn_errorFn, 'reads the ~w result of global(~w) as ~w', Ret, Callee, K
K ≡ l
Ret ≡ w
→
fn_errorFn, 'reads the w result of global(~w) as l', Callee
K ≡ p
Ret ≢ p
or
K ≢ p
Ret ≡ p
→
fn_errorFn, 'reads the ~w result of global(~w) as ~w', Ret, Callee, K
else
true
valid_jump+Ctx, +Jump

Jump names existing labels and returns what the function's return class requires.

1209valid_jumpCtx, gotoT ←
1210valid_jumpCtx, branchV, T1, T2 ←
1214valid_jumpCtx, retV ←
Ctx = ctx_, _, Ret, _, _, _, _
ret_value_okRet, Ctx, V
1217valid_jumpCtx, retsVs ←
Ctx = ctx_, Fn, Ret, _, _, _, _
Ret = tupleKs
lengthKs, N
lengthVs, N
→
maplistvalid_useCtx, Ks, Vs
else
fn_errorFn, 'ret of a tuple in a ~w function', Ret
1223valid_jumpCtx, ret ←
Ctx = ctx_, Fn, Ret, _, _, _, _
1226valid_jump_, unreachable
ret_value_ok+Ret, +Ctx, +V

a function that returns Ret may return the one value V.

1230ret_value_okvoid, ctx_, Fn, _, _, _, _, _, _ ←
fn_errorFn, 'ret with a value in a void function', []
1232ret_value_oktuple_, ctx_, Fn, _, _, _, _, _, _ ←
fn_errorFn, 'ret of one value in a tuple function', []
1234ret_value_okRet, Ctx, V ←
valid_useCtx, Ret, V
ret_void_ok+Ret, +Fn

a function that returns Ret may return nothing.

1240ret_void_okvoid, _
1241ret_void_oktuple_, Fn ←
fn_errorFn, 'ret without a value', []
1242ret_void_okRet, Fn ←
fn_errorFn, 'ret without a value', []
target_exists+Ctx, +T

the jump target T, a label or poll(Label), names a block.

1248target_existsCtx, T ←
1252label_existsctx_, Fn, _, LabelSet, _, _, _, L ←
get_assocL, LabelSet, _
→
true
else
fn_errorFn, 'undefined block label ~w', L

Projections are local to flight, not new ordinary operands. A lower is copied only from a p value; no integer can manufacture one.

1259valid_flight_argCtx, addressP ←
!
valid_useCtx, p, P
1260valid_flight_argCtx, V ←
valid_useCtx, l, V
1262valid_flight_lowernone, _
1263valid_flight_lowerlowerP, Ctx ←
valid_useCtx, p, P
valid_use+Ctx, +K, +V

V may be read at class K. Arguments are in the order maplist needs; valid_use_/3 indexes on the value.

1267valid_useCtx, K, V ←
Ctx = ctx_, Fn, _, _, _, _, _
→
true
else
fn_errorFn, 'invalid use class ~w', K
valid_use_V, Ctx, K

A p temporary is used only at p and vice versa; ptoi and itop are the conversions. An integer constant at p is a pointer with no capability. Only a check makes a proof (class a or f): no constant is one.

1275valid_use_tmpN, ctx_, Fn, _, _, TempClasses, _, _, K ←
get_assocN, TempClasses, K0
→
→
fn_errorFn, 'tmp(~w) has class ~w but is used as ~w', N, K0, K
→
fn_errorFn, 'tmp(~w) has class ~w but is used as ~w', N, K0, K
K ≡ l
K0 ≡ w
→
fn_errorFn, 'tmp(~w) has class w but is used as l', N
K0 ≡ p
K ≢ p
→
fn_errorFn, 'tmp(~w) has class p but is used as ~w (use ptoi)', N, K
K ≡ p
K0 ≢ p
→
fn_errorFn, 'tmp(~w) has class ~w but is used as p (use itop)', N, K0
else
true
else
fn_errorFn, 'undefined temporary tmp(~w)', N
1293valid_use_constN, ctx_, Fn, _, _, _, _, _, K ←
integerN
N < 0
or
N ≥ M
→
fn_errorFn, 'float bits out of range: ~w', N
→
fn_errorFn, 'const(~w) used as a proof (class ~w)', N, K
else
true
1300valid_use_globalG, Ctx, K ←
Ctx = ctx_, _, _, _, _, _, Mode
symbol_use_okMode, G, Ctx, K
symbol_use_ok+Mode, +G, +Ctx, +K

the symbol global(G) may be read at class K in a module of Mode: in plain mode as an address of data, in filc mode never (copy, which yields its pointer, is checked apart).

1307symbol_use_okfilc, G, ctx_, Fn, _, _, _, _, _, _ ←
fn_errorFn, 'in a Fil-C module only copy at p reads global(~w)', G
1309symbol_use_okplain, G, ctxM, Fn, _, _, _, _, _, K ←
addr_classplain, AddrClass
K ≡ AddrClass
→
true
else
fn_errorFn, 'global(~w) used as ~w', G, K
M = moduleItems
memberchkdataG, _, _, _, Items
→
true
else
fn_errorFn, 'global(~w) is not a data definition', G

Operand and result classes

op_result_ok+Mode, ?Op, ?Knondet

Op may produce a result of class K in a module of Mode. With Op partial (cmp(_, _), ext(_), load(_)) it enumerates the operands.

1325op_result_okMode, Op, K ←
op_kindOp, Kind
kind_result_okKind, Op, Mode, K
kind_result_ok+Kind, +Op, +Mode, +K

as op_result_ok/3, for Op of Kind.

1331kind_result_okadditive, _, _, K ←
1332kind_result_okalu, Op, _, K ←
or
memberchkOp, mul, div
1334kind_result_okshift, _, _, K ←
1335kind_result_okneg, _, _, K ←
1336kind_result_okcopy, _, _, K ←
1337kind_result_okptoi, _, filc, l
1338kind_result_okitop, _, filc, p
1339kind_result_okva_args, _, filc, p
1340kind_result_okva_arg, _, filc, K ←
1341kind_result_oksetjmp, _, filc, w
1342kind_result_okcmp, cmpC, CK, _, K ←
→
K = w
1344kind_result_okfloat_convert, exts, _, d
1345kind_result_okfloat_convert, truncd, _, s
1346kind_result_okfloat_to_int, _, _, K ←
1347kind_result_okint_to_float, _, _, K ←
1348kind_result_okcast, _, _, K ←
1349kind_result_okext, extT, _, K ←
1350kind_result_okload, loadT, Mode, K ←
load_result_okT, Mode, K
1351kind_result_okalloc, _, Mode, K ←
addr_classMode, K
1352kind_result_okcheck, _, filc, a
1353kind_result_okcheck_local, _, filc, a
1354kind_result_okcheck_function, _, filc, f
1355kind_result_okld, raw_loadT, _, filc, K ←
load_result_okT, filc, K
1356kind_result_okld_pointer, _, filc, p
ext_result_ok+T, +K

an extension of type T may have class K; one from a word is a long.

1360ext_result_oksw, l
1361ext_result_okuw, l
1362ext_result_oksh, K ←
1363ext_result_okuh, K ←
1364ext_result_oksb, K ←
1365ext_result_okub, K ←
load_result_ok+T, +Mode, +K

a load of type T may have class K in a module of Mode.

1369load_result_okl, _, l
1370load_result_okp, filc, p
1371load_result_oks, _, s
1372load_result_okd, _, d
1373load_result_oksw, _, K ←
1374load_result_okuw, _, K ←
1375load_result_oksh, _, K ←
1376load_result_okuh, _, K ←
1377load_result_oksb, _, K ←
1378load_result_okub, _, K ←
op_arg_classes+Op, +K, +Mode, ?Classessemidet

Classes is the list of classes at which the arguments of Op are read when its result has class K in a module of Mode. Fails for an Op outside the grammar.

1386op_arg_classesOp, K, Mode, Classes ←
op_kindOp, Kind
kind_arg_classesKind, Op, K, Mode, Classes
kind_arg_classes+Kind, +Op, +K, +Mode, ?Classes

as op_arg_classes/4, for Op of Kind.

1392kind_arg_classesadditive, _, K, _, Classes ←
1394kind_arg_classesalu, _, K, _, K, K
1395kind_arg_classesshift, _, K, _, K, w
1396kind_arg_classesneg, _, K, _, K
1397kind_arg_classescopy, _, K, _, K
1398kind_arg_classesptoi, _, _, _, p
1399kind_arg_classesitop, _, _, _, l
1400kind_arg_classesva_args, _, p, filc, l
1401kind_arg_classesva_arg, _, _, filc, p
1402kind_arg_classessetjmp, _, w, filc, p, w
1403kind_arg_classescmp, cmp_, CK, _, _, CK, CK
1404kind_arg_classesfloat_convert, exts, _, _, s
1405kind_arg_classesfloat_convert, truncd, _, _, d
1406kind_arg_classesfloat_to_int, Op, _, _, K ←
memberchkOp, stosi, stoui
→
K = s
else
K = d
1408kind_arg_classesint_to_float, Op, _, _, K ←
memberchkOp, swtof, uwtof
→
K = w
else
K = l
1410kind_arg_classescast, _, K, _, From ←
1411kind_arg_classesext, _, _, _, w
1412kind_arg_classesload, _, _, Mode, A ←
addr_classMode, A
1413kind_arg_classesalloc, _, _, _, l
1414kind_arg_classescheck, _, _, filc, p
1415kind_arg_classescheck_local, _, _, filc, []
1416kind_arg_classescheck_function, _, _, filc, p
1417kind_arg_classesld, _, _, filc, a
1418kind_arg_classesld_pointer, _, _, filc, a
additive_arg_classes+K, ?Classes

add and sub of class K read Classes; at p they offset a pointer by an l.

additive_arg_classes
1422ww, w
1423ll, l
1424pp, l
1425ss, s
1426dd, d
cast_classes
1428ws
1429sw
1430ld
1431dl
fn_error+Fn, +Fmt, +Args

throw nanofil_error with the message Fmt/Args, attributed to the function Fn.

1435fn_errorFn, Fmt, Args ←
formatatomMsg, Fmt, Args
formatatomFull, 'in ~w: ~w', Fn, Msg
thrownanofil_errorFull
without_locs+Module0, -Moduledet

Erase explanatory metadata at execution/code-generation boundaries. Never use erasure to relax checked-IR validation's adjacency rule.

1444without_locsmoduleAll, moduleItems ←
code_itemsAll, Items0
maplistitem_without_locs, Items0, Items
1448item_without_locsfuncN, L, R, Ps, Bs0, funcN, L, R, Ps, Bs ←
!
maplistblock_without_locs, Bs0, Bs
1450item_without_locsItem, Item
1452block_without_locsblockL, Phis, Insts0, J, blockL, Phis, Insts, J ←
excludeis_loc, Insts0, Insts
1455is_locloc_, _