Fil-C on Nix

Filnix wiki

mbrock/filnix/nanofil

c_lower.pl

Lowering from C to IR (milestone 4b), in the style of cproc: it turns the parsed translation unit into an IR module for the Fil-C backend.

Every local variable and parameter lives in an alloc'd object, and the allocs are hoisted into the entry block; with -O, promote turns them into temporaries. Pointers are class p, so the output is a Fil-C module.

Expressions are lowered from c_types' typed trees (elaborate/3). The tree says what each expression is and what its operands are converted to, and the elaborator has checked C's constraints, so lowering only translates: lower//3 for values, place//3 for objects and functions.

A narrow integer (char, short, bool) is always held sign- or zero-extended to 32 bits in a w, so conversions only extend when the source type has values the target cannot represent.

Values

ValueIs
vValue, Typean rvalue: Value is an IR value, Type a C type
lvAddr, Typean object at Addr, a p value: an lvalue, or a member of a struct rvalue
fdCallee, Typea function designator; Callee is an IR value, globalSymbol for a named function

State

The lowering nonterminals describe a one-element list holding the state, accessed through the semicontext nonterminals state//1 and state//2 and called with phrase/3 at the entry points. The state is stEnv, Cur, Insts, Blocks, N, Labels, Cases, Allocs:

FieldIs
Envthe c_types environment eOrds, Tags, Defs, Next
CurcurLabel, Phis, the open block, or none after a jump; dead code then opens an unreachable block
Insts, Blocksreversed instructions and blocks, temporarily paired with origin IDs
AllocsbuildRevAllocs, ActiveOrigins, OriginCounter, RevRecords, Indexes; independent of executable names
Nthe counter for fresh temporaries and labels
Labelsan assoc from C goto labels to IR labels
Casesthe enclosing switch's caseValue, Label and defaultLabel

The context argument Ctx is ctxFunction, ReturnType, Break, Continue, Loc, where Break and Continue are someLabel, or none outside a loop or switch.

The environment

Besides what c_types keeps there, the environment's Defs hold:

  • gdefSymbol: a global definition;
  • litSymbol: a string literal;
  • deferredSymbol: a static inline function, lowered only if something refers to it.

Next numbers string and compound literals as well as struct ids.

Variable names

NameIs
State0, Statest/8 terms
Env0 ... Envthe environment
Ctxthe context
Loca source location
Ra v, lv or fd value term; Value0 is one before conversion
Van IR value
L-prefixed namesIR labels
Ebseightbytes, ebOffset, Bytes, Class
S0 ... SsEnv, Chunks, End in the static initializer predicates
modulec_lower
exportsc_module/2
useslists
usesassoc
usesdcgs
usesirop_kind/2, narrow_type/3, width_bytes/2
usesc_parsecompound_assignment/2
usescfginst_def/2
c_module+Items, -Module

Module is the IR module of the top-level Items from c_parse: module(Funcs) with the functions followed by the data of every global and string literal. Throws a semantic error for C that nanofil cannot lower.

89c_moduleItems, moduleFuncs ←
findallName, external_inline_declarationItems, Name, Names0
sortNames0, ExternalNames
foldltop_itemExternalNames, Items, Funcss, Env0, Env
appendFuncss, Funcs0
with_used_inlinesFuncs0, Env, Funcs

C99 6.7.4: an inline body is an external definition unless ALL file-scope declarations are inline without extern. Inspect the whole translation unit: even a declaration after the body changes emission.

100external_inline_declarationItems, Name ←
memberItem, Items
Item = decl_, Specs, Inits
memberinitdclsomeName, _, _, _, Inits
or
Item = fundef_, Specs, dclsomeName, _, _, _
¬memberchkstoragetypedef, Specs
memberchkstorageextern, Specs
or
¬memberchkfspecinline, Specs

GNU inline reverses the C99 rule: an extern inline body is never the external definition, while a plain inline body is. The parser retains both spellings in the balanced tokens of specifier and suffix attrs.

112external_inline_bodySpecs, Storage, Name, ExternalNames ←
memberattrgroupAttrs, Specs
memberidAttr, Attrs
memberchkAttr, gnu_inline, '__gnu_inline__'
→
Storage ≢ extern
else
memberchkName, ExternalNames

with_used_inlines(+Funcs0, +Env0, -Items): Items is Funcs0, then every deferred static inline function they refer to, directly or through one another, then the global data. A static inline function is lowered only if something refers to it, as clang emits it only then; headers are full of ones nanofil cannot lower. Lowering one can refer to more, so this iterates until no new symbol turns up.

126with_used_inlinesFuncs0, Env0, Items ←
global_dataEnv0, Data
appendFuncs0, Data, Items0
env_defsEnv0, Defs
findallSym,
item_refItems0, Sym
get_assocdeferredSym, Defs, _
¬memberchkfuncSym, _, _, _, _, Funcs0
,
Syms0
sortSyms0, Syms
Syms ≡ []
→
Items = Items0
else
foldllower_inline, Syms, Newss, Env0, Env1
appendNewss, New
appendFuncs0, New, Funcs1
with_used_inlinesFuncs1, Env1, Items
item_ref+Items, -Sym

some function or data item of Items refers to symbol Sym (nondet, with repeats).

147item_refItems, Sym ←
memberItem, Items
Item = func_, _, _, _, Blocks
memberblock_, _, Insts, _, Blocks
memberInst, Insts
Inst = let_, _, _, Args
memberglobalSym, Args
or
Inst = call_, globalSym, _
or
Item = data_, _, _, DataItems
memberitemp, symSym, _, DataItems
lower_inline+Sym, -Func, +Env0, -Env

Func is the deferred inline function Sym, lowered in the scope it was defined in.

161lower_inlineSym, Func, Env0, Env ←
env_defsEnv0, Defs
get_assocdeferredSym, Defs, dfLoc, Name, Type, DclType, Body, Scope
env_restoreScope, Env0, Env1
functionLoc, Name, Type, DclType, Body, Env1, Env2, Func
env_scopeEnv0, Scope0, _
env_restoreScope0, Env2, Env

Environment

171env_defse_, _, Defs, _, Defs
put_def+Key, +Value, +Env0, -Env

Env is Env0 with Key defined as Value.

175put_defKey, Value, eOrds, Tags, Defs0, Next, eOrds, Tags, Defs, Next ←
put_assocKey, Defs0, Value, Defs
next_id-Id, +Env0, -Env

Id is Env0's next number, which Env has moved past.

180next_idId, eOrds, Tags, Defs, Id, eOrds, Tags, Defs, Next ←
Next is Id + 1

Top level

top_item+ExternalNames, +Item, -Funcs, +Env0, -Env

Funcs are the IR functions a top-level item defines now (a static inline is deferred).

187top_item_, static_assertLoc, Expr, _, [], Env, Env ←
!
static_assertLoc, Expr, Env
189top_item_, declLoc, Specs, Inits, [], Env0, Env ←
!
catchdecl_typesSpecs, Inits, Decls, Env0, Env1, not_constant, sema_errorLoc, 'array size is not a constant', []
foldlglobal_declLoc, Decls, Env1, Env
193top_itemExternalNames, fundefLoc, Specs, dclsomeName, DclType, Asm, Body, Funcs, Env0, Env ←
decl_typesSpecs, initdclsomeName, DclType, Asm, none, d_, Type, Storage, _, _, Env0, Env1
global_declLoc, dName, Type, Storage, Asm, none, Env1, Env2
memberchkfspecinline, Specs
Storage ≡ static
or
¬external_inline_bodySpecs, Storage, Name, ExternalNames
→
Funcs = []
Storage ≡ static
→
lookup_ordinaryEnv2, Name, fn_, Sym, _
env_scopeEnv2, Scope, _
put_defdeferredSym, dfLoc, Name, Type, DclType, Body, Scope, Env2, Env
else
Env = Env2GNU extern inline or a C99 inline-only definition
else
functionLoc, Name, Type, DclType, Body, Env2, Env, Funcs
static_assert+Loc, +Expr, +Env

throw a semantic error unless Expr is a nonzero constant.

215static_assertLoc, Expr, Env ←
catchconst_valueExpr, Env, Value, _, not_constant, fail
→
true
else
sema_errorLoc, 'static assertion is not a constant', []
Value ≠ 0
→
true
else
sema_errorLoc, 'static assertion failed', []
global_decl+Loc, +Decl, +Env0, -Env

Env binds the d/5 declaration Decl of a function or of an object with static storage, and defines the object unless it is an extern without an initializer.

228global_declLoc, dName, Type0, Storage, Asm, Init, Env0, Env ←
Asm = asmSym
→
true
else
Sym = Name
Type0 = func_, _, _
→
Storage ≡ static
→
Linkage = local
else
Linkage = export
Init ≢ none
→
sema_errorLoc, 'function ~w has an initializer', Name
else
true
bind_ordinaryName, fnType0, Sym, Linkage, Env0, Env
else
vm_checkType0, Loc
complete_arrayType0, Init, Type, Loc, Env0, none
bind_ordinaryName, gvarType, Sym, Storage, Init, Env0, Env1
Storage ≡ extern
Init ≡ none
→
Env = Env1
else
Storage ≡ static
→
Linkage = local
else
Linkage = export
define_globalSym, Type, Linkage, Init, Loc, Env1, Env

Definitions of globals live in the environment's Defs under gdef(Symbol), string literals under lit(Symbol); both survive block scopes. A tentative definition (no initializer) does not replace one with an initializer.

251define_globalSym, Type, Linkage, Init, Loc, Env0, Env ←
env_defsEnv0, Defs
get_assocgdefSym, Defs, g_, _, Init0, _
Init0 ≢ none
→
Init ≡ none
→
Env = Env0
else
sema_errorLoc, '~w is initialized twice', Sym
else
put_defgdefSym, gType, Linkage, Init, Loc, Env0, Env
new_literal+Lit, -Sym, +Env0, -Env

Sym is a fresh symbol that Env defines as literal Lit, a list of bytes (a char array) or w(ElemType, Values).

264new_literalLit, Sym, Env0, Env ←
next_idN, Env0, Env1
formatatomSym, 'str.~w', N
put_deflitSym, Lit, Env1, Env
str_elems+Str, +ElemType, -Elems

string literal term Str can initialize an array of ElemType, and Elems are its elements. Str is str(Bytes) for a char array, or wstr(WideType, Values) for an integer array of its element's size.

273str_elemsstrBytes, ElemType, Bytes ←
is_char_typeElemType
275str_elemswstrWideType, Values, ElemType, Values ←
int_typeElemType
int_infoElemType, Size, _
int_infoWideType, Size, _
279str_elemslistentry[], Str, ElemType, Values ←
Str ≠ list_
str_elemsStr, ElemType, Values
new_complit+Type0, +Init, +Loc, -Sym, +Env0, -Env

Sym is a fresh symbol that Env defines as a file-scope compound literal, an unnamed static object.

285new_complitType0, Init, Loc, Sym, Env0, Env ←
next_idN, Env0, Env1
formatatomSym, 'complit.~w', N
complete_arrayType0, Init, Type, Loc, Env0, none
define_globalSym, Type, local, Init, Loc, Env1, Env
global_data+Env0, -Data

Data are the data items of every global definition and string literal.

293global_dataEnv0, Data ←
global_itemsEnv0, [], GlobalData, Env
env_defsEnv, Defs
assoc_to_listDefs, Pairs
findalldataSym, rolocal, Align, Items,
memberlitSym−Lit, Pairs
Lit = wWideType, Values0
→
int_infoWideType, Align, _
memberchkAlign−Width, 2−h, 4−w
else
Values0 = Lit
Align = 1
Width = b
appendValues0, 0, Values
findallitemWidth, constV, memberV, Values, Items
,
LiteralData
appendGlobalData, LiteralData, Data
global_items+Env0, +Done, -Data, -Env

Data are the data items of the global definitions whose symbols are not in Done. Initializers can define more globals (file-scope compound literals), so this repeats until there are no new ones; Done lists the symbols already emitted.

315global_itemsEnv0, Done, Data, Env ←
env_defsEnv0, Defs
assoc_to_listDefs, Pairs
findallSym−G,
membergdefSym−G, Pairs
¬memberchkSym, Done
,
New
New ≡ []
→
Data = []
Env = Env0
else
foldlglobal_item, New, Data0, Env0, Env1
findallNewSym, memberNewSym−_, New, NewSyms
appendDone, NewSyms, Done1
global_itemsEnv1, Done1, Data1, Env
appendData0, Data1, Data
global_item+Sym−G, -Data, +Env0, -Env

Data is the data item of global definition G.

332global_itemSym−gType, Linkage, Init, Loc, dataSym, Linkage, Align, Items, Env0, Env ←
catchtype_size_alignType, Env0, _, Align0, badWhy, sema_errorLoc, '~w has an unsupported type (~w)', Sym, Why
Align is minAlign0, 16
env_defsEnv0, Defs
get_associnitializedSym, Defs, Items
→
Env = Env0
else
static_itemsType, Init, Loc, Env0, Env, Items

Functions

function+Loc, +Name, +Type, +DclType, +Body, +Env0, -Env, -Func

Func is the IR function for the definition of Name with body Body.

347functionLoc, Name, funcRetType, ParamTypes0, Variadic, DclType, Body, Env0, Env, funcSym, Linkage, RetClass, Params, Blocks, contextSym, Records ←
lookup_ordinaryEnv0, Name, fn_, Sym, Linkage
ParamTypes0 ≡ unspecified
→
ParamTypes = []
else
ParamTypes = ParamTypes0
ret_classLoc, RetType, Env0, RetClass
param_namesDclType, Names
reflect_paramsDclType, ParamTypes, Env0, ParamEnv
source_locationLoc, SourceLoc
empty_assocEmpty
put_assoc0, Empty, SourceLoc, OriginLocs
State0 = stParamEnv, curstart, [], [], [], 0, Labels, [], build[], 0, 1, origin0, SourceLoc, statementfunction, [], indexEmpty, Empty, OriginLocs
empty_assocLabels
Ctx = ctxName, RetType, none, none, Loc
phrase
sret_paramRetType, SretParams
paramsNames, ParamTypes, Ctx, Params0
variadic_entryVariadic, SretParams, Params0
stmtBody, Ctx
fall_offRetType
,
State0, State
appendSretParams, Params0, Params
State = stEnvEnd, none, [], RevBlocks, _, _, _, buildRevAllocs, _, _, RevRecords, _
env_scopeEnv0, Scope, _
env_restoreScope, EnvEnd, Env
reverseRevBlocks, blockstart, [], Insts0, Jump|Rest
reverseRevAllocs, Allocs
appendAllocs, Insts0, Insts
maplistcontext_block, blockstart, [], Insts, Jump|Rest, Blocks, Sites0
appendSites0, Sites
reverseRevRecords, RawRecords
excludeis_hint, RawRecords, Origins
findallT−H, memberhintT, H, RawRecords, HintPairs
list_to_assocHintPairs, Hints
findallS−Os, membersiteS, Os, Sites, SitePairs
list_to_assocSitePairs, SiteMap
findallvalueT, Hint, Os,
memberparam_, tmpT, Params
Os = 0
or
memberblockL, Phis, Is, _, Blocks
memberphitmpT, _, _, Phis
get_assocphiT, SiteMap, Os
or
nth0I, Is, Inst
inst_defInst, T
get_associnstL, I, SiteMap, Os
get_assocT, Hints, H
→
Hint = H
else
Hint = none
,
Values
appendOrigins, Values, Sites, Records
393is_hinthint_, _
395context_blockblockL, Ps0, Is0, fromOs, J, blockL, Ps, Is, J, Rs ←
maplistcontext_phi, Ps0, Ps, PRs
context_instsIs0, L, 0, Is, IRs
appendPRs, IRs, sitejumpL, Os, Rs
399context_phifromOs, P, P, sitephiT, Os ←
P = phitmpT, _, _
400context_insts[], _, _, [], []
401context_instsfromOs, I|Is0, L, N, I|Is, siteinstL, N, Os|Rs ←
N1 is N + 1
context_instsIs0, L, N1, Is, Rs
404variadic_entryfixed, _, _ ⟶
[]
405variadic_entryvariadic, Sret, Params ⟶
appendSret, Params, All
lengthAll, N
Off is 8 × N
bindbuiltinva_args, tmpva_args
emitlettmpva_args, p, va_args, constOff
ret_class+Loc, +RetType, +Env, -Class

Class is the IR return class of a function returning RetType.

412ret_class_, void, _, void ←
!
413ret_class_, Type, _, Class ←
type_classType, Class
!
415ret_classLoc, Type, Env, Class ←
suType
!
ret_shapeType, Env, Loc, Shape
Shape = wordseb_, _, K0
→
Class = K0
Shape = wordseb_, _, K1, eb_, _, K2
→
Class = tupleK1, K2
else
Class = void
423ret_classLoc, Type, _, _ ←
Type ≡ ldouble
!
sema_errorLoc, 'long double is not supported', []
427ret_classLoc, Type, _, _ ←
sema_errorLoc, 'returning ~w is not supported yet', Type
sret_param+RetType, -Params//

Params are the hidden parameters of a function returning RetType. A struct over 16 bytes is returned through a pointer the caller passes first, as clang does; the function itself returns nothing.

434sret_paramRetType, Params ⟶
envEnv
suRetType
ret_shapeRetType, Env, unknown, sret
→
bind'%sret', sretP
Params = paramp, P
else
Params = []
param_names+DclType, -Names

Names are the parameter names, each some(Name) or none, from the function's own declarator: the outermost func, since declarator types are built outermost first.

446param_namesfunc_, unspecified, [] ←
!
447param_namesfunc_, paramsParams, _, Names ←
findallName, memberparam_, dclName, _, _, Params, Names
params+Names, +Types, +Ctx, -Params//

Params are the IR parameters, and each named one is stored into a local. A struct or union comes in one word per eightbyte.

453params[], [], _, [] ⟶
[]
454paramsName|Names, Type|Types, Ctx, Params ⟶
suType
!
envEnv
ctx_locCtx, Loc
arg_wordsType, Env, Loc, Ebs
fresh_paramsEbs, Params0, Values
su_param_localName, Type, Ebs, Values
paramsNames, Types, Ctx, Params1
appendParams0, Params1, Params
463paramsName|Names, Type|Types, Ctx, paramClass, P|Params ⟶
ctx_locCtx, Loc
vm_checkType, Loc
type_classType, Class
→
true
else
sema_errorLoc, 'parameter of type ~w is not supported yet', Type
param_localName, Type, P
paramsNames, Types, Ctx, Params
su_param_local+Name, +Type, +Ebs, +Values//

and param_local(+Name, +Type, +P)//: a named parameter of Type, which came in as the words Values (eightbytes Ebs) or the temporary P, is stored into a local it is bound to; an unnamed one is dropped.

477su_param_localnone, _, _, _ ⟶
[]
478su_param_localsomeName, Type, Ebs, Values ⟶
local_slotType, Addr
bindName, varAddr, Type
name_valuesValues, Addr, piece
store_wordsEbs, Values, Addr
484param_localnone, _, _ ⟶
[]
485param_localsomeName, Type, P ⟶
local_slotType, Addr
bindName, varAddr, Type
name_valueP, Addr, value
store_typedType, P, Addr
fresh_params+Ebs, -Params, -Values//

one fresh parameter per eightbyte; Values are their temporaries.

493fresh_params[], [], [] ⟶
[]
494fresh_paramseb_, _, Class|Ebs, paramClass, P|Params, P|Values ⟶
fresh_paramsEbs, Params, Values
fall_off+RetType//

end the body; falling off the end returns as a bare return would.

500fall_offRetType ⟶
curCur
cur_fall_offCur, RetType
cur_fall_off+Cur, +RetType//

the end of the body when Cur is the current block: nothing after a jump, else a bare return.

506cur_fall_offnone, _ ⟶
[]
507cur_fall_offcur_, _, RetType ⟶
zero_retRetType
zero_ret+RetType//

return without a value (zero, if the function has a result).

511zero_retRetType ⟶
envEnv
RetType ≡ void
→
termret
suRetType
ret_shapeRetType, Env, unknown, Shape
→
Shape = words_, _
→
termretsconst0, const0
Shape = words_
→
termretconst0
else
termret
else
termretconst0

State primitives

state-S//

S is the state. state(-S0, +S)//: the state is S0 and becomes S. The semicontext of the one-element state list.

526stateS, S ⟶
S
528stateS0, S, S ⟶
S0

Metadata has its own counter; it never consumes a temporary or label.

531build_stateB0, B ⟶
statestE, C, Is, Bs, N, Ls, Cs, B0, stE, C, Is, Bs, N, Ls, Cs, B
533active_originsOs ⟶
build_statebuildA, Os, N, R, X, buildA, Os, N, R, X
534metadataIndex ⟶
build_statebuildA, Os, N, R, Index, buildA, Os, N, R, Index
535recordR ⟶
build_statebuildA, Os, N, Rs, X0, buildA, Os, N, R|Rs, X
539indexed_recordbindingId, Name, T, Loc, indexB0, H, O, indexB, H, O ←
!
put_assocId, B0, bindingId, Name, T, Loc, B
541indexed_recordhintT, Hint, indexB, H0, O, indexB, H, O ←
!
put_assocT, H0, Hint, H
543indexed_recordoriginId, Loc, _, _, indexB, H, O0, indexB, H, O ←
put_assocId, O0, Loc, O
546enter_originLoc0, Description, Parent ⟶
build_statebuildA, Parent, N, Rs, X, buildA, N, Next, Rs, X
Next is N + 1
recordoriginN, Loc, Description, Parent
550restore_originOs ⟶
build_statebuildA, _, N, Rs, X, buildA, Os, N, Rs, X
551source_locationF:L, atF, L ←
!
552source_location_, none
554binding_contextName, vartmpB, _ ⟶
!
envEnv
metadataindex_, _, Origins
expression_typeidName, Env, T
Os = O|_
get_assocO, Origins, At
→
Loc = At
else
Loc = none
recordbindingB, Name, T, Loc
name_valuetmpB, tmpB, address
559binding_contextName, gvar_, Sym, _, _ ⟶
!
envEnv
metadataindexBindings, _, _
get_assocglobalSym, Bindings, _
→
[]
else
expression_typeidName, Env, T
recordbindingglobalSym, Name, T, none
564binding_context_, _ ⟶
[]
566name_values[], _, _ ⟶
[]
567name_valuesV|Vs, B, Role ⟶
name_valueV, B, Role
name_valuesVs, B, Role
568name_valuetmpT, tmpB, Role ⟶
!
metadataindexBindings, Hints, _
address_bindingB, Bindings, Hints, Id
¬get_assocT, Hints, _
→
recordhintT, namedId, Role
else
[]
573name_value_, _, _ ⟶
[]
575address_bindingB, Bindings, Hints, Id ←
get_assocB, Bindings, _
→
Id = B
else
get_assocB, Hints, namedId, address
578stored_namestoreW, V, Addr ⟶
!
access_namestoreW, V, Addr
579stored_nameletV, K, loadOp, Addr ⟶
!
access_nameloadK, Op, V, Addr
580stored_name_ ⟶
[]

A zero-offset field or partial access is not the whole C object.

583access_nameAccess, V, tmpA ⟶
!
metadataindexBs, Hs, _
address_bindingA, Bs, Hs, B
get_assocB, Bs, binding_, _, T, _
scalar_accessAccess, U
→
name_valueV, tmpA, value
else
[]
589access_name_, _, _ ⟶
[]
590scalar_accessstoreW, T ←
591scalar_accessloadK, Op, T ←
load_opT, K, Op
593typed_originX, Ctx, Parent ⟶
envEnv
X =.. Category, Type, Node
ctx_locCtx, Loc
shallow_nodeNode, Env, Form
groundType
→
Description = typedCategory, Type, Form
else
Description = generatedlower, Form
enter_originLoc, Description, Parent

Keep the operation and resolved binding, not recursive typed trees.

602shallow_nodeidName, Env, bindingB ←
lookup_ordinaryEnv, Name, vartmpB, _
!
604shallow_nodeidName, Env, bindingglobalSym ←
lookup_ordinaryEnv, Name, gvar_, Sym, _, _
!
606shallow_nodeNode, _, Form ←
Node =.. Op|Args
includeatomic, Args, Atoms
Form =.. Op|Atoms
609result_nameval_, loadobj_, idName, vV, _ ⟶
!
envEnv
lookup_ordinaryEnv, Name, varAddr, _
→
name_valueV, Addr, value
else
[]
612result_name_, _ ⟶
[]

Readers (cur//1, env//1, cases//1) leave the state as it is; set_*//1 replace one field; fresh//1 and label//1 take the next number.

617curCur ⟶
statest_, Cur, _, _, _, _, _, _
619envEnv ⟶
statestEnv, _, _, _, _, _, _, _
621set_envEnv ⟶
statest_, Cur, Insts, Blocks, N, Labels, Cases, Allocs, stEnv, Cur, Insts, Blocks, N, Labels, Cases, Allocs
625bindName, Value ⟶
envEnv0
bind_ordinaryName, Value, Env0, Env
binding_contextName, Value
631freshtmpTemp ⟶
statestEnv, Cur, Insts, Blocks, N0, Labels, Cases, Allocs, stEnv, Cur, Insts, Blocks, N, Labels, Cases, Allocs
N is N0 + 1
formatatomTemp, 'v~w', N0
637labelLabel ⟶
statestEnv, Cur, Insts, Blocks, N0, Labels, Cases, Allocs, stEnv, Cur, Insts, Blocks, N, Labels, Cases, Allocs
N is N0 + 1
formatatomLabel, 'L~w', N0

ensure_live//: make sure there is a current block, opening an unreachable one after a jump.

645ensure_live ⟶
cur_live+Cur//

the current block is Cur, or a new one if Cur is none.

651cur_livenone ⟶
labelLabel
set_curcurLabel, []
652cur_livecur_, _ ⟶
[]
654set_curCur ⟶
statestEnv, _, Insts, Blocks, N, Labels, Cases, Allocs, stEnv, Cur, Insts, Blocks, N, Labels, Cases, Allocs
emit+Inst//

append Inst with its active typed origin. These private wrappers disappear before c_module/2 returns; they are never IR.

662push_instInst ⟶
statestEnv, Cur, Insts, Blocks, N, Labels, Cases, Allocs, stEnv, Cur, Inst|Insts, Blocks, N, Labels, Cases, Allocs
term+Jump//

end the current block with Jump.

667termJump ⟶
669close_blockJump ⟶
statestEnv, curLabel, Phis, Insts, Blocks, N, Labels, Cases, Allocs, stEnv, none, [], blockLabel, Phis, BlockInsts, fromOs, Jump|Blocks, N, Labels, Cases, Allocs
reverseInsts, BlockInsts
start+Label//

begin block Label, falling through from a live current block.

678startLabel ⟶
curCur
cur_fallthroughCur, Label
set_curcurLabel, []
cur_fallthrough+Cur, +Label//

a live current block Cur ends with a jump to Label; after a jump (none) there is nothing to end.

685cur_fallthroughnone, _ ⟶
[]
686cur_fallthroughcur_, _, Label ⟶
termgotoLabel
start_phi+Label, +Phis//

begin block Label, reached only by jumps.

689start_phiLabel, Phis ⟶
maplistfrom_originsOs, Phis, Located
set_curcurLabel, Located
693from_originsOs, I, fromOs, I
here-Label//

Label is the current block's label.

696hereLabel ⟶
curcurLabel, _
698add_allocInst ⟶
build_statebuildAs, Os, N, Rs, X, buildfromOs, Inst|As, Os, N, Rs, X
701casesCases ⟶
statest_, _, _, _, _, _, Cases, _
703set_casesCases ⟶
statestEnv, Cur, Insts, Blocks, N, Labels, _, Allocs, stEnv, Cur, Insts, Blocks, N, Labels, Cases, Allocs
goto_label+Name, -Label//

Label is the IR label of C label Name, made on first use.

709goto_labelName, Label ⟶
statestEnv, Cur, Insts, Blocks, N0, Labels0, Cases, Allocs, stEnv, Cur, Insts, Blocks, N, Labels, Cases, Allocs
get_assocName, Labels0, Label
→
N = N0
Labels = Labels0
else
N is N0 + 1
formatatomLabel, 'L~w', N0
put_assocName, Labels0, Label, Labels
let+Class, +Op, +Args, -Dest//

emit an instruction into Dest, a fresh temporary.

721letClass, Op, Args, Dest ⟶
freshDest
emitletDest, Class, Op, Args
local_slot+Type, -Addr//

Addr points at a new local object of Type, an alloc in the entry block.

727local_slotType, Addr ⟶
envEnv
catchtype_size_alignType, Env, Size0, Align0, badWhy, thrownanofil_errorWhy
Align0 ≤ 4
→
Align = 4
else
Align = Align0
Size is maxSize0, 1
freshAddr
add_allocletAddr, p, allocAlign, constSize

Context

738ctx_locctx_, _, _, _, Loc, Loc
740ctx_atctxFunc, Ret, Break, Cont, _, Loc, ctxFunc, Ret, Break, Cont, Loc
loop_ctx+Ctx0, +LBreak, +LCont, -Ctx

, switch_ctx(+Ctx0, +LBreak, -Ctx): Ctx is Ctx0 inside a loop or switch whose break jumps to LBreak and continue to LCont.

745loop_ctxctxFunc, Ret, _, _, Loc, LBreak, LCont, ctxFunc, Ret, someLBreak, someLCont, Loc
748switch_ctxctxFunc, Ret, _, Cont, Loc, LBreak, ctxFunc, Ret, someLBreak, Cont, Loc

Statements

A lexical prepass gives each statement-expression boundary its AST path. Gotos may stay inside or leave boundaries, never enter one (including a sibling). Case dispatch must originate inside the same boundary. Keeping this separate from emission also checks forward gotos and unevaluated code.

757statement_expression_jumpsBody, Loc ←
phrasejump_regionsBody, [], [], [], Loc, Events
maplistcheck_jump_regionEvents, Events
761check_jump_regionEvents, jumpName, From, Loc ←
!
memberlabel_regionName, To, Events
¬append_, To, From
→
sema_errorLoc, 'goto into a statement expression is not allowed', []
else
true
767check_jump_region_, _

Visit leaves before matching compound shapes: on Trealla an atom built by atom_chars/2 (e.g. the field name "label") can unify with label/2. Recursing into its unbound "body" would then grow regions indefinitely.

772jump_regionsTerm, _, _, _, _ ⟶
atomicTerm
!
773jump_regionsatLoc, Term, Path, Regions, Switch, _ ⟶
!
jump_regionsTerm, Path, Regions, Switch, Loc
775jump_regionsstmtexprBlock, Path, Regions, Switch, Loc ⟶
!
jump_regionsBlock, 0|Path, Path|Regions, Switch, Loc
777jump_regionslabelName, Body, Path, Regions, Switch, Loc ⟶
!
label_regionName, Regions
jump_regionsBody, 0|Path, Regions, Switch, Loc
780jump_regionsgotoName, _, Regions, _, Loc ⟶
!
jumpName, Regions, Loc
782jump_regionsswitchExpr, Body, Path, Regions, Switch, Loc ⟶
!
jump_regionsExpr, 0|Path, Regions, Switch, Loc
jump_regionsBody, 1|Path, Regions, Regions, Loc
785jump_regionscaseExpr, Body, Path, Regions, Switch, Loc ⟶
!
check_case_regionRegions, Switch, Loc
jump_regionsExpr, 0|Path, Regions, Switch, Loc
jump_regionsBody, 1|Path, Regions, Switch, Loc
789jump_regionsdefaultBody, Path, Regions, Switch, Loc ⟶
!
check_case_regionRegions, Switch, Loc
jump_regionsBody, 0|Path, Regions, Switch, Loc
792jump_regionsTerm, Path, Regions, Switch, Loc ⟶
compoundTerm
Term =.. _|Args
→
jump_region_argsArgs, 0, Path, Regions, Switch, Loc
else
[]
798jump_region_args[], _, _, _, _, _ ⟶
[]
799jump_region_argsArg|Args, I, Path, Regions, Switch, Loc ⟶
jump_regionsArg, I|Path, Regions, Switch, Loc
J is I + 1
jump_region_argsArgs, J, Path, Regions, Switch, Loc
804check_case_regionRegions, Switch, Loc ←
Regions ≡ Switch
→
true
else
sema_errorLoc, 'switch dispatch into a statement expression is not allowed', []
stmt+Stmt, +Ctx//

lower statement Stmt, an at(Loc, S) term; stmt_//2 lowers S itself.

811stmtatLoc, Stmt, Ctx0 ⟶
ctx_atCtx0, Loc, Ctx
functorStmt, Kind, _
enter_originLoc, statementKind, Parent
stmt_Stmt, Ctx

Declaration and loop helpers update the enclosing source context.

817locatedKind, Loc ⟶
enter_originLoc, statementKind, _
loop_located+Ctx//

a loop's condition or step, in a block of its own after the body, is at the loop statement's location.

821loop_locatedCtx ⟶
ctx_locCtx, Loc
locatedloop, Loc
823stmt_blockItems, Ctx ⟶
itemsItems, Ctx
827stmt_null, _ ⟶
[]
828stmt_exprExpr, Ctx ⟶
judgedExpr, Ctx
rvalExpr, Ctx, _
829stmt_ifCond, Then, Else, Ctx ⟶
judgedCond, Ctx
labelLThen
labelLElse
labelLJoin
cond_jumpCond, Ctx, LThen, LElse
startLThen
stmtThen, Ctx
termgotoLJoin
startLElse
Else ≡ none
→
[]
else
stmtElse, Ctx
startLJoin
836stmt_whileCond, Body, Ctx ⟶
labelLCond
labelLBody
labelLEnd
startLCond
judgedCond, Ctx
cond_jumpCond, Ctx, LBody, LEnd
startLBody
loop_ctxCtx, LEnd, LCond, BodyCtx
stmtBody, BodyCtx
termgotoLCond
startLEnd
845stmt_doBody, Cond, Ctx ⟶
labelLBody
labelLCond
labelLEnd
startLBody
loop_ctxCtx, LEnd, LCond, BodyCtx
stmtBody, BodyCtx
startLCond
judgedCond, Ctx
cond_jumpCond, Ctx, LBody, LEnd
startLEnd
853stmt_forInit, Cond, Step, Body, Ctx ⟶
Init ≡ none
→
[]
Init = exprInitExpr
→
judgedInitExpr, Ctx
rvalInitExpr, Ctx, _
else
itemInit, Ctx
labelLCond
labelLBody
labelLStep
labelLEnd
startLCond
Cond ≡ none
→
termgotoLBody
else
judgedCond, Ctx
cond_jumpCond, Ctx, LBody, LEnd
startLBody
loop_ctxCtx, LEnd, LStep, BodyCtx
stmtBody, BodyCtx
startLStep
Step ≡ none
→
[]
else
judgedStep, Ctx
rvalStep, Ctx, _
termgotoLCond
startLEnd
872stmt_switchExpr, Body, Ctx ⟶
judgedExpr, Ctx
rvalExpr, Ctx, Value0
Value0 = v_, Type0
ctx_locCtx, Loc
→
else
sema_errorLoc, 'switch on a non-integer', []
convertValue0, Type, vValue, _
labelLDispatch
labelLEnd
termgotoLDispatch
casesOuter
switch_ctxCtx, LEnd, BodyCtx
stmtBody, BodyCtx
termgotoLEnd
casesCases0
reverseCases0, Cases
startLDispatch
dispatchCases, Value, Type, LEnd
startLEnd
890stmt_caseExpr, Body, Ctx ⟶
envEnv
ctx_locCtx, Loc
catchconst_valueExpr, Env, Value, _, not_constant, fail
→
true
else
sema_errorLoc, 'case label is not a constant', []
labelLabel
startLabel
casesCases
set_casescaseValue, Label|Cases
stmtBody, Ctx
899stmt_defaultBody, Ctx ⟶
labelLabel
startLabel
casesCases
set_casesdefaultLabel|Cases
stmtBody, Ctx
903stmt_break, ctx_, _, Break, _, Loc ⟶
target_jumpBreak, Loc, 'break outside a loop or switch'
905stmt_continue, ctx_, _, _, Cont, Loc ⟶
target_jumpCont, Loc, 'continue outside a loop'
907stmt_returnnone, Ctx ⟶
Ctx = ctx_, RetType, _, _, _
zero_retRetType
910stmt_returnExpr, Ctx ⟶
Expr ≢ none
Ctx = ctx_, RetType, _, _, Loc
judgedExpr, Ctx
rvalExpr, Ctx, Value0
RetType ≡ void
→
termret
suRetType
→
assign_convertExpr, Value0, RetType, Loc, vAddr, _
return_structRetType, Addr, Loc
else
assign_convertExpr, Value0, RetType, Loc, vValue, _
termretValue
921stmt_gotoName, _ ⟶
goto_labelName, Label
termgotoLabel
924stmt_labelName, Body, Ctx ⟶
goto_labelName, Label
startLabel
stmtBody, Ctx
928stmt_declLoc, Specs, Inits, Ctx ⟶
itemdeclLoc, Specs, Inits, Ctx
target_jump+Target, +Loc, +Error//

a jump to the break or continue Target, some(Label); none is semantic error Error at Loc.

933target_jumpsomeLabel, _, _ ⟶
termgotoLabel
934target_jumpnone, Loc, Error ⟶
sema_errorLoc, Error, []

A block scope: save_scope(Scope) on entry, restore_scope(Scope) on exit.

937save_scopeScope ⟶
envEnv
env_scopeEnv, Scope, _
941restore_scopeScope ⟶
envEnv0
env_restoreScope, Env0, Env
items+Items, +Ctx//

, item(+Item, +Ctx)//: lower the items of a block, statements and declarations.

948items[], _ ⟶
[]
949itemsItem|Items, Ctx ⟶
itemItem, Ctx
itemsItems, Ctx
951itematLoc, Stmt, Ctx ⟶
!
stmtatLoc, Stmt, Ctx
952itemstatic_assertLoc, Expr, _, _ ⟶
!
envEnv
static_assertLoc, Expr, Env
955itemdeclLoc, Specs, Inits, Ctx0 ⟶
envEnv0
catchdecl_typesSpecs, Inits, Decls, Env0, Env, not_constant, sema_errorLoc, 'array size is not a constant', []
ctx_atCtx0, Loc, Ctx
enter_originLoc, statementdecl, Parent
local_declsDecls, Ctx
local_decls+Decls, +Ctx//

, local_decl(+Decl, +Ctx)//: lower block-scope declarations. Functions and extern objects are global, a static object is a global with a function-qualified name, anything else is a local slot.

968local_decls[], _ ⟶
[]
969local_declsDecl|Decls, Ctx ⟶
local_declDecl, Ctx
local_declsDecls, Ctx
973local_decldName, Type0, Storage, Asm, Init, Ctx ⟶
ctx_locCtx, Loc
Type0 = func_, _, _
or
Storage ≡ extern
→
envEnv0
global_declLoc, dName, Type0, Storage, Asm, Init, Env0, Env
Storage ≡ static
→
envEnv0
Ctx = ctxFunc, _, _, _, _
next_idK, Env0, Env1
formatatomSym, '~w.~w.~w', Func, Name, K
complete_arrayType0, Init, Type, Loc, Env0, none
bind_ordinaryName, gvarType, Sym, static, Init, Env1, Env2
define_globalSym, Type, local, Init, Loc, Env2, Env3
Resolve block-scope names before the scope is restored.
static_itemsType, Init, Loc, Env3, Env4, Items
put_definitializedSym, Items, Env4, Env
else
envEnv
complete_arrayType0, Init, Type, Loc, Env, whole
check_local_typeType, Env, Loc, Name
local_slotType, Addr
bindName, varAddr, Type
Init ≡ none
→
[]
else
initAddr, Type, Init, Ctx
check_local_type+Type, +Env, +Loc, +Name

throw a semantic error unless Type is a type a local object Name can have.

1003check_local_typeType, Env, Loc, Name ←
vm_checkType, Loc
catch
type_size_alignType, Env, _, _
→
true
else
true
,
badWhy, sema_errorLoc, '~w has a type nanofil does not support (~w)', Name, Why

complete_array(+Type0, +Init, -Type, +Loc, +Env, +Whole): Type is Type0 with the size of an array of unknown size taken from its initializer Init, counting the entries each element takes with brace elision: {1, 2, 3, 4} is two elements of a struct {int a, b}. Whole is whole when an entry may be a struct expression that initializes a struct element whole (locals), none otherwise.

1015complete_arrayarrayElemType, none, none, arrayElemType, none, _, _, _ ←
!
1017complete_arrayarrayElemType, none, Init, arrayElemType, N, Loc, Env, Whole ←
!
str_elemsInit, ElemType, Values
→
lengthValues, N0
N is N0 + 1
Init = listEntries
→
phraseslotsarrayElemType, none, 0, 0, Entries, _, 0, N, cEnv, Whole, Loc, _
else
sema_errorLoc, 'array of unknown size', []
1028complete_arrayType, _, Type, _, _, _
single_value+Type, +V, +Env, +Whole

entry value V initializes a slot of type Type by itself, so no braces were elided. The final cut makes this semidet: the alternatives can overlap.

1033single_valueType, V, Env, Whole ←
V = list_
or
¬aggregateType
or
Type = arrayCharType, _
str_elemsV, CharType, _
or
Whole ≡ whole
suType
catchexpression_typeV, Env, T0, nanofil_error_, fail
unqualifiedT1, Type1
Type1 ≡ Type
!
dispatch+Cases, +Value, +Type, +LEnd//

the switch dispatch, one comparison per case, then a jump to the default or LEnd.

1048dispatch[], _, _, LEnd ⟶
termgotoLEnd
1049dispatchCases, Value, Type, LEnd ⟶
findallCaseValue−Label, membercaseCaseValue, Label, Cases, Pairs
memberchkdefaultLDefault, Cases
→
true
else
LDefault = LEnd
type_classType, Class
dispatch_casesPairs, Value, Type, Class, LDefault
dispatch_cases+Pairs, +Value, +Type, +Class, +LDefault//

compare Value with each case value of Pairs in turn, then jump to LDefault.

1057dispatch_cases[], _, _, _, LDefault ⟶
termgotoLDefault
1058dispatch_casesCaseValue−Label|Pairs, Value, Type, Class, LDefault ⟶
convert_intCaseValue, Type, Wrapped
letw, cmpeq, Class, Value, constWrapped, Test
labelNext
termbranchTest, Label, Next
startNext
dispatch_casesPairs, Value, Type, Class, LDefault
judged+Expr, +Ctx//

full expression Expr is typed, or c_types says why not; where it cannot say, lowering reports the fault.

1068judgedExpr, Ctx ⟶
envEnv
ctx_locCtx, Loc
elaborateExpr, Env, Loc, _
→
true
else
true
cond_jump+Expr, +Ctx, +LTrue, +LFalse//

branch on the truth of scalar Expr.

1074cond_jumpExpr, Ctx, LTrue, LFalse ⟶
rvalExpr, Ctx, Value
truthValue, Ctx, Test
termbranchTest, LTrue, LFalse
truth+Value, +Ctx, -W//

W is a w value that is nonzero exactly when scalar Value is.

1081truthvV, Type, Ctx, W ⟶
type_classType, w
→
W = V
type_classType, l
→
letw, cmpne, l, V, const0, W
→
letw, cmpne, K, V, const0, W
Type = ptr_
→
letl, ptoi, V, Int
letw, cmpne, l, Int, const0, W
else
ctx_locCtx, Loc
sema_errorLoc, 'a ~w value is not a scalar', Type

Initializers

An initializer means its leaves: the assignments it makes to parts of an object, in the order C performs them, a later one overriding what an earlier one wrote.

zero(Off, Type)        clear the object of Type at Off; a braced
                       list or a string literal first clears what
                       it initializes
val(Off, Type, N)      the integer N, an element of a string literal
expr(Off, Type, Expr)  the value of the expression Expr, converted
                       to Type: a scalar, or (locals) a whole struct

Offsets are from the start of the object. A local object stores its leaves; a static one turns them into data chunks. The walk over the entries also gives an array of unknown size its length (complete_array/6).

init_leaves+Type, +Init, +C, -Leaves

Leaves are what Init assigns to an object of Type. C = c(Env, Whole, Loc), with Whole as for complete_array/6.

1112init_leavesType, Init, C, Leaves ←
phraseobject_initType, 0, Init, C, Leaves
object_init+Type, +Off, +Init, +C//

the leaves of Init for the object of Type at Off. A scalar may be braced.

1117object_initbitsT, Pos, W, Off, listentry_, V|Rest, C ⟶
!
Rest ≡ []
→
true
else
init_errorC, 'excess initializers', []
object_initbitsT, Pos, W, Off, V, C
1120object_initbitsT, Pos, W, Off, Expr, _ ⟶
!
bitsOff, T, Pos, W, Expr
1121object_initarrayT, N, Off, listentry[], Str, C ⟶
str_elemsStr, T, _
!
object_initarrayT, N, Off, Str, C
1124object_initType, Off, listEntries, C ⟶
!
zeroOff, Type
→
slotsType, 0, Off, Entries, Rest, 0, _, C
Entries = entry_, V|Rest
→
object_initType, Off, V, C
else
Rest = []
Rest ≡ []
→
true
else
init_errorC, 'excess initializers', []
1133object_initarrayElemType, N, Off, Init, C ⟶
str_elemsInit, ElemType, Values
!
lengthValues, NValues
integerN
NValues > N
→
init_errorC, 'string initializer is too long', []
else
true
C = cEnv, _, _
type_size_alignElemType, Env, Size, _
zeroOff, arrayElemType, N
elem_leavesValues, ElemType, Off, Size
1144object_initarray_, _, _, _, C ⟶
!
init_errorC, 'array initializer must be a list', []
1146object_initType, Off, Expr, _ ⟶
exprOff, Type, Expr
elem_leaves+Values, +Type, +Off, +Size//

a val leaf for each of Values, consecutive elements of Type and Size from Off.

1150elem_leaves[], _, _, _ ⟶
[]
1151elem_leavesV|Vs, Type, Off, Size ⟶
valOff, Type, V
Off1 is Off + Size
elem_leavesVs, Type, Off1, Size

slots(+Type, +I, +Off, +Entries0, -Entries, +Max0, -Max, +C)//: the leaves of aggregate Type at Off from Entries0, the next undesignated entry going to slot I; a designator moves to its slot. Entries is what is left when the slots run out. Max is the larger of Max0 and one past the last slot initialized.

1161slots_, _, _, [], [], Max, Max, _ ⟶
!
1162slotsType, _, Off, entryD|Ds, V|Entries0, Entries, Max0, Max, C ⟶
!
designated_slotD, Type, C, I, SlotOff, SlotType
Off1 is Off + SlotOff
Ds ≡ []
→
slot_initSlotType, Off1, V, Entries0, Entries1, C
else
slotsSlotType, 0, Off1, entryDs, V, _, 0, _, C
Entries1 = Entries0
I1 is I + 1
Max1 is maxMax0, I1
slotsType, I1, Off, Entries1, Entries, Max1, Max, C
1173slotsType, I, Off, Entries0, Entries, Max0, Max, C ⟶
slotType, C, I, SlotOff, SlotType
→
Entries0 = entry[], V|Entries1
Off1 is Off + SlotOff
slot_initSlotType, Off1, V, Entries1, Entries2, C
I1 is I + 1
Max1 is maxMax0, I1
slotsType, I1, Off, Entries2, Entries, Max1, Max, C
else
Entries = Entries0
Max = Max0
slot_init+Type, +Off, +V, +Entries0, -Entries, +C//

the leaves of entry value V for the slot of Type at Off. Either V initializes the slot by itself, or the slot's braces were elided and it takes the entries it needs from [V|Entries0], up to the next designator, which belongs to the enclosing list.

1190slot_initType, Off, V, Entries, Entries, C ⟶
C = cEnv, Whole, _
single_valueType, V, Env, Whole
!
object_initType, Off, V, C
1195slot_initType, Off, V, Entries0, Entries, C ⟶
undesignatedEntries0, Pre, Post
slotsType, 0, Off, entry[], V|Pre, Rest, 0, _, C
appendRest, Post, Entries
slot+Type, +C, +I, -Off, -SlotType

the undesignated entry for slot I of aggregate Type initializes the SlotType at Off. An array of unknown size has every slot; a union has only its first member.

1203slotarrayElemType, N, cEnv, _, _, I, Off, ElemType ←
N ≡ none
→
true
else
I < N
type_size_alignElemType, Env, Size, _
Off is I × Size
1207slotstructId, cEnv, _, _, I, Off, Type ←
su_membersstructId, Env, Members
nth0I, Members, M
member_slotM, _, Off, Type
1211slotunionId, cEnv, _, _, 0, Off, Type ←
su_membersunionId, Env, M|_
member_slotM, _, Off, Type
member_slot+M, -Name, -Off, -SlotType

member M, named Name, is the slot of SlotType at Off; a bit-field's is bits(Type, Pos, Width), of the Type at Off.

1218member_slotmName, Type, Off, Name, Off, Type
1219member_slotmName, Type, Off, bitsPos, W, Name, Off, bitsType, Pos, W
designated_slot+Desig, +Type, +C, -I, -Off, -SlotType

designator Desig of aggregate Type names its slot I, the SlotType at Off; any member of a union.

1224designated_slotindexExpr, arrayElemType, N, C, I, Off, ElemType ←
!
C = cEnv, _, _
catchconst_valueExpr, Env, I, _, not_constant, fail
I ≥ 0
slotarrayElemType, N, C, I, Off, _
→
true
else
init_errorC, 'bad array designator', []
1232designated_slotfieldField, SU, C, I, Off, Type ←
suSU
!
C = cEnv, _, _
su_membersSU, Env, Members
nth0I, Members, M
member_slotM, someField, Off, Type
→
true
else
init_errorC, 'no member ~w', Field
1241designated_slot_, Type, C, _, _, _ ←
init_errorC, 'bad designator for a ~w', Type
su_members+SU, +Env, -Members

Members are the m(Name, Type, Offset) of the complete struct or union SU.

1246su_membersSU, Env, Members ←
SU =.. Kind, Id
env_defsEnv, Defs
get_assocId, Defs, defKind, Members, _, _
1251init_errorc_, _, Loc, Format, Args ←
sema_errorLoc, Format, Args
aggregatearray_, _struct_union_
undesignated+Entries, -Pre, -Post

Pre is the longest prefix of Entries without a designator, Post the rest. The final cut commits to the first split found, which ends Pre just before the first designator (or at the end).

1262undesignatedEntries, Pre, Post ←
appendPre, Post, Entries
¬memberentry_|_, _, Pre
Post = []
or
Post = entry_|_, _|_
!
1268is_char_typeType ←
memberchkType, char, schar, uchar
init+Addr, +Type, +Init, +Ctx//

initialize the local object of Type at Addr from Init, storing its leaves.

1272initAddr, Type, Init, Ctx ⟶
envEnv
ctx_locCtx, Loc
init_leavesType, Init, cEnv, whole, Loc, Leaves
store_leavesLeaves, Addr, Ctx
1278store_leaves[], _, _ ⟶
[]
1279store_leavesLeaf|Leaves, Addr, Ctx ⟶
store_leafLeaf, Addr, Ctx
store_leavesLeaves, Addr, Ctx
1283store_leafzeroOff, Type, Addr, _ ⟶
offsetAddr, Off, P
1286store_leafvalOff, Type, N, Addr, _ ⟶
offsetAddr, Off, P
store_widthType, Width
emitstoreWidth, constN, P
1290store_leafexprOff, Type, Expr, Addr, Ctx ⟶
offsetAddr, Off, P
judgedExpr, Ctx
rvalExpr, Ctx, Value0
ctx_locCtx, Loc
assign_convertExpr, Value0, Type, Loc, vValue, _
store_typedType, Value, P
1297store_leafbitsOff, Type, Pos, W, Expr, Addr, Ctx ⟶
offsetAddr, Off, P
judgedExpr, Ctx
rvalExpr, Ctx, Value0
ctx_locCtx, Loc
assign_convertExpr, Value0, Type, Loc, Value
place_writebitsP, Type, Pos, W, Value, Ctx, _
zero_object+Addr, +Type//

clear the whole object, since allocs are hoisted and a local inside a loop must be reinitialized on every pass.

1307zero_objectAddr, Type ⟶
envEnv
type_size_alignType, Env, Size, Align
zero_bytesAddr, 0, Size, Align
zero_bytes+Addr, +Off, +Size, +Align//

zero Off..Size with the widest stores the alignment allows; bytes below Off are done. The cut in the second clause commits to the widest store that fits.

1315zero_bytes_, Off, Size, _ ⟶
Off ≥ Size
!
1316zero_bytesAddr, Off, Size, Align ⟶
Left is Size−Off
memberWidth−N, l−8, w−4, h−2, b−1
N ≤ Left
N ≤ Align
Off mod N =:= 0
!
offsetAddr, Off, P
emitstoreWidth, const0, P
Off1 is Off + N
zero_bytesAddr, Off1, Size, Align

Expressions

GNU statement expressions load/decay their last expression before the block scope closes. A label on that statement does not hide its value.

1330statement_expression_items[], _, vconst0, void ⟶
[]
1331statement_expression_itemsItem, Ctx, R ⟶
1333statement_expression_itemsItem|Items, Ctx, R ⟶
itemItem, Ctx
1336statement_expression_lastatLoc, exprExpr, Ctx0, R ⟶
!
ctx_atCtx0, Loc, Ctx
judgedExpr, Ctx
rvalExpr, Ctx, R
1338statement_expression_lastat_, labelName, Body, Ctx, R ⟶
!
goto_labelName, Label
startLabel
1341statement_expression_lastItem, Ctx, vconst0, void ⟶
itemItem, Ctx
binding_obj+Binding, -R//

R is the object or function an identifier bound to Binding designates. A global's address is copied into a temporary.

1346binding_objvarAddr, Type, lvAddr, Type ⟶
[]
1347binding_objfnType, Sym, _, fdglobalSym, Type ⟶
[]
1348binding_objgvarType, Sym, _, _, lvAddr, Type ⟶
letp, copy, globalSym, Addr
callee_temp+V, -Callee//

Callee is V in a temporary, as a callee must be.

1353callee_temptmpT, tmpT ⟶
!
1354callee_tempV, Callee ⟶
letp, copy, V, Callee
member_lv+Addr, +SUType, +Field, +Ctx, -P, -Type//

P points at member Field, of Type, of the struct or union at Addr.

1358member_lvAddr, SUType, Field, Ctx, P, Type ⟶
envEnv
ctx_locCtx, Loc
SUType = struct_
or
SUType = union_
member_type_offsetSUType, Field, Env, Type, Off
→
offsetAddr, Off, P
else
sema_errorLoc, 'no member named ~w', Field
rval+Expr, +Ctx, -R//

R is v(V, Type), the value of Expr, arrays decayed, lvalues loaded, lowered from its typed tree (c_types' elaborate/3). Full expressions are judged first (judged//2), which diagnoses one that cannot be typed.

1371rvalExpr, Ctx, R ⟶
envEnv
elaborateExpr, Env, X
→
valueX, XV
lowerXV, Ctx, R
else
ctx_locCtx, Loc
sema_errorLoc, 'cannot type ~w', Expr

lower(+XV, +Ctx, -R)//: R is v(V, Type), the value whose typed tree is XV. Types are unqualified, as code generation has them; a type name the source writes (a cast's, va_arg's, sizeof's) is read again by type_name/4, which refuses one nanofil does not support.

1381lowerX, Ctx, R ⟶
typed_originX, Ctx, Parent
lower_nodeX, Ctx, R
1385lower_nodevalT, intV, _, vconstV, U ⟶
1386lower_nodeval_, floatCs, _, vconstV, Type ⟶
!
float_literalCs, V, Type
1388lower_nodevalT, loadX, Ctx, R ⟶
!6.3.2.1p2
placeX, Ctx, P
place_loadedP, U, Ctx, R
1392lower_nodevalT, decayX, Ctx, R ⟶
!6.3.2.1p3-4
placeX, Ctx, P
decayedP, T, Ctx, R
1395lower_nodevalT, addrX, Ctx, R ⟶
!6.5.3.2p3
placeX, Ctx, P
decayedP, T, Ctx, R
1398lower_nodeval_, memberXS, F, Ctx, R ⟶
!6.5.2.3p3
lowerXS, Ctx, vAddr, SU
member_lvAddr, SU, F, Ctx, P, Type
Type = arrayE, _
→
R = vP, ptrE
else
loadedType, P, Ctx, R
1404lower_nodeval_, commaXA, XB, Ctx, R ⟶
!6.5.17
lowerXA, Ctx, _
lowerXB, Ctx, R
1407lower_nodeval_, unOp, XV, Ctx, R ⟶
!
lowerXV, Ctx, V
unaryOp, V, Ctx, R
1410lower_nodeval_, binOp, XA, XB, Ctx, R ⟶
6.5.13-14
memberchkOp, land, lor
!
logicalOp, XA, XB, Ctx, R
1413lower_nodeval_, binOp, XA, XB, Ctx, R ⟶
!
lowerXA, Ctx, VA
lowerXB, Ctx, VB
binaryOp, VA, VB, Ctx, R
1417lower_nodevalT, condXC, XA, XB, Ctx, R ⟶
!6.5.15
labelLJoin
lowerXC, Ctx, VC
truthVC, Ctx, W
termbranchW, LA, LB
lowerXA, Ctx, VA
convertedVA, U, vVA1, _
hereLA1
termgotoLJoin
lowerXB, Ctx, VB
convertedVB, U, vVB1, _
hereLB1
termgotoLJoin
join_armsU, LJoin, LA1−VA1, LB1−VB1, R
1426lower_nodevalT, gcondXC, XB, Ctx, R ⟶
!GNU c ?: b
lowerXC, Ctx, VC
truthVC, Ctx, W
labelLJoin
termbranchW, LA, LB
convertedVC, U, vVA1, _
hereLA1
termgotoLJoin
lowerXB, Ctx, VB
convertedVB, U, vVB1, _
hereLB1
termgotoLJoin
join_armsU, LJoin, LA1−VA1, LB1−VB1, R
1435lower_nodeval_, assignOp, X, XB, Ctx, R ⟶
!6.5.16
placeX, Ctx, P
Op ≡ (=)
→
lowerXB, Ctx, Value0
else
place_readP, Ctx, Cur
lowerXB, Ctx, VB
binaryBinOp, Cur, VB, Ctx, Value0
place_writeP, Value0, Ctx, R
1444lower_nodeval_, incdecOp, X, Ctx, R ⟶
!6.5.2.4, 6.5.3.1
placeX, Ctx, P
place_readP, Ctx, Cur
memberchkOp, preinc, postinc
→
BinOp = add
else
BinOp = sub
binaryBinOp, Cur, vconst1, int, Ctx, Value0
place_writeP, Value0, Ctx, New
memberchkOp, preinc, predec
→
R = New
else
R = Cur
1451lower_nodeval_, callXF, XArgs, Ctx, R ⟶
!6.5.2.2
calleeXF, Ctx, Callee, funcRetType, Ps, _
Ps ≡ unspecified
→
ParamTypes = []
else
ParamTypes = Ps
call_argsXArgs, ParamTypes, Ctx, IRArgs
call_resultCallee, RetType, IRArgs, Ctx, R
1456lower_nodeval_, builtinName, Args, Ctx, R ⟶
!
builtinName, Args, Ctx, R
1458lower_nodeval_, castTN, XV, Ctx, R ⟶
!6.5.4
envEnv
ctx_locCtx, Loc
type_nameTN, Type, Env, Loc
lowerXV, Ctx, Value0
convertedValue0, Type, R
1463lower_nodeval_, va_argTN, XA, Ctx, vV, Type ⟶
!7.16.1.1
envEnv
ctx_locCtx, Loc
type_nameTN, Type, Env, Loc
type_classType, K
memberchkType, int, uint, long, ulong, llong, ullong, double, ptr_
→
true
else
sema_errorLoc, 'unsupported va_arg type ~w', Type
lowerXA, Ctx, vP, _
letK, va_arg, P, V
1471lower_nodeval_, sizeofTN, Ctx, vconstSize, ulong ⟶
6.5.3.4
TN = type_, _
!
envEnv
type_sizeTN, Env, Ctx, Size
1475lower_nodeval_, sizeofX, Ctx, vconstSize, ulong ⟶
!
envEnv
tree_typeX, Type
ctx_locCtx, Loc
catchtype_size_alignType, Env, Size, _, badWhy, sema_errorLoc, 'sizeof: ~w', Why
1480lower_nodeval_, alignofTN, Ctx, vconstAlign, ulong ⟶
!
envEnv
ctx_locCtx, Loc
type_nameTN, Type, Env, Loc
catchtype_size_alignType, Env, _, Align, badWhy, sema_errorLoc, 'alignof: ~w', Why
1486lower_nodeval_, offsetofTN, Path, Ctx, vconstV, ulong ⟶
!
envEnv
ctx_locCtx, Loc
catchconst_valueoffsetofTN, Path, Env, V, _, not_constant, fail
→
true
else
sema_errorLoc, 'bad offsetof', []
1494lower_nodeval_, stmtexprat_, blockItems, Ctx, R ⟶
GNU ({ ... })
converted+Value0, +Type, -R//

R is Value0 converted to Type, as assignment and casts convert, the elaborator having judged that they may: to void it is nothing, a struct or union stays its address.

1502converted_, void, vconst0, void ⟶
!
1503convertedvAddr, _, Type, vAddr, Type ⟶
suType
!
1504convertedValue0, Type, R ⟶
convertValue0, Type, R
loaded+U, +Addr, +Ctx, -R//

R is the value of type U stored at Addr: a struct or union stands for its address.

1508loadedU, Addr, _, vAddr, U ⟶
!
1509loadedU, Addr, Ctx, vV, U ⟶
load_typedU, Addr, Ctx, V
decayed+P, +T, +Ctx, -R//

R, of pointer type T, points at the object or function P. A function's address is copied into a temp; setjmp's has none, since it is an intrinsic.

1514decayedlvAddr, _, T, _, vAddr, U ⟶
1515decayedfdglobalSym, _, _, Ctx, _ ⟶
op_kindsetjmpSym, setjmp
!
ctx_locCtx, Loc
sema_errorLoc, 'setjmp requires a direct call, not a function pointer', []
1519decayedfdglobalSym, Type, _, _, vAddr, ptrType ⟶
!
letp, copy, globalSym, Addr
1521decayedfdCallee, Type, _, _, vCallee, ptrType ⟶
[]

place(+X, +Ctx, -P)//: P is lv(Addr, Type), the object that typed tree X designates, or fd(Callee, Type) for a function.

1525placeX, Ctx, P ⟶
typed_originX, Ctx, Parent
place_nodeX, Ctx, P
1528place_nodeobj_, idName, _, P ⟶
!
envEnv
lookup_ordinaryEnv, Name, Binding
binding_objBinding, P
Binding = gvar_, Sym, _, _
P = lvtmpT, _
→
binding_contextName, Binding
recordhintT, namedglobalSym, address
else
[]
1534place_nodefun_, idName, _, P ⟶
!
envEnv
lookup_ordinaryEnv, Name, Binding
binding_objBinding, P
1537place_nodeobj_, predefined_, Ctx, P ⟶
!6.4.2.2
Ctx = ctxFunc, _, _, _, _
atom_codesFunc, Bytes
placeobj_, strBytes, Ctx, P
1540place_nodeobj_, strBytes, _, lvAddr, arraychar, N ⟶
!
envEnv0
new_literalBytes, Sym, Env0, Env
lengthBytes, N0
N is N0 + 1
letp, copy, globalSym, Addr
1547place_nodeobj_, wstrWideType, Values, _, lvAddr, arrayWideType, N ⟶
!
envEnv0
new_literalwWideType, Values, Sym, Env0, Env
lengthValues, N0
N is N0 + 1
letp, copy, globalSym, Addr

A compound literal is an unnamed local, initialized where it appears.

1555place_nodeobj_, complitTN, Init, Ctx, lvAddr, Type ⟶
!6.5.2.5
envEnv
ctx_locCtx, Loc
type_nameTN, Type0, Env, Loc
complete_arrayType0, Init, Type, Loc, Env, whole
check_local_typeType, Env, Loc, 'compound literal'
local_slotType, Addr
initAddr, Type, Init, Ctx
1563place_nodeobjT, derefXV, Ctx, lvV, U ⟶
!6.5.3.2p4
lowerXV, Ctx, vV, _
1566place_nodefunT, derefXV, Ctx, fdCallee, U ⟶
!
lowerXV, Ctx, vV, _
callee_tempV, Callee
1570place_nodeobjT, memberXS, F, Ctx, lvP, U ⟶
6.5.2.3p3
placeXS, Ctx, lvAddr, SU
member_lvAddr, SU, F, Ctx, P, _
1574place_nodeobj_, bitfieldmemberXS, F, W, Ctx, bitsP, T, Pos, W ⟶
6.7.2.1p10
placeXS, Ctx, lvAddr, SU
envEnv
member_layoutSU, F, Env, T, Off, bitsPos, W
offsetAddr, Off, P

An object is read and written through its place: lv(Addr, Type), or bits(Addr, Type, Pos, W), a bit-field of W bits from bit Pos of the Type at Addr. A bit-field is read by shifting it to the bottom of its unit, extended by its type's sign, and written by merging it into the unit; its value has its promoted type (bitfield_promotion/3).

place_loaded+P, +U, +Ctx, -R//

R is the value, of type U, of the object at place P.

1588place_loadedlvAddr, _, U, Ctx, R ⟶
loadedU, Addr, Ctx, R
1589place_loadedbitsAddr, T, Pos, W, _, Ctx, R ⟶
place_readbitsAddr, T, Pos, W, Ctx, R
place_read+P, +Ctx, -R//

R is the scalar value at place P.

1593place_readlvP, U, Ctx, vCur, U ⟶
load_typedU, P, Ctx, Cur
1594place_readbitsAddr, T, Pos, W, Ctx, R ⟶
load_typedT, Addr, Ctx, Unit
Up is KB−Pos−W
shiftedK, shl, Unit, Up, V1
bits_extendedT, W, V1, R
place_write+P, +Value0, +Ctx, -R//

store Value0, converted, at place P; R is the value stored.

1602place_writelvP, U, Value0, _, R ⟶
convertedValue0, U, R
R = vV, _
1606place_writebitsAddr, T, Pos, W, Value0, Ctx, R ⟶
convertValue0, T, vV, _
load_typedT, Addr, Ctx, Unit
Mask is 1 << W−1 << Pos
Keep is \Mask /\ 1 << KB−1
letK, and, Unit, constKeep, Kept
shiftedK, shl, V, Pos, V1
letK, and, V1, constMask, Field
letK, or, Kept, Field, New
store_typedT, New, Addr
Up is KB−W
shiftedK, shl, V, Up, V2
bits_extendedT, W, V2, R
bits_extended+T, +W, +V, -R//

R is the W-bit field at the top of V, of T's class, moved down and extended as T's sign says, of the bit-field's promoted type.

1624bits_extendedT, W, V, R ⟶
Down is KB−W
int_infoT, _, Sign
Sign ≡ signed
→
Op = sar
else
Op = shr
shiftedK, Op, V, Down, V1
convertvV1, T, P, R
shifted+K, +Op, +V, +N, -R//

R is V shifted by N bits.

1632shifted_, _, V, 0, V ⟶
!
1633shiftedK, Op, V, N, R ⟶
letK, Op, V, constN, R
1635class_bitsw, 32
1636class_bitsl, 64
load_typed+Type, +Addr, +Ctx, -V//

V is the scalar of Type loaded from Addr.

1640load_typedType, Addr, Ctx, V ⟶
load_opType, Class, Op
→
letClass, loadOp, Addr, V
else
ctx_locCtx, Loc
sema_errorLoc, 'loading a ~w value is not supported yet', Type
load_op+Type, -Class, -Op

Class and load Op hold a scalar of Type: an integer narrower than 8 bytes is loaded extended into a w.

1648load_opType, Class, Op ←
int_infoType, Size, Sign
!
type_classType, Class
Size =:= 8
→
Op = l
else
narrow_typeOp, Size, Sign
1652load_opType, Class, Class ←
type_classType, Class
store_typed+Type, +V, +Addr//

store value V of Type at Addr; a struct or union V is the address of the value, copied.

1656store_typedType, V, Addr ⟶
suType
!
copy_objectType, V, Addr
1657store_typedType, V, Addr ⟶
store_widthType, Width
emitstoreWidth, V, Addr
store_width+Type, -Width

Width is the store of a scalar of Type.

1662store_widthptr_, p ←
!
1663store_widthfloat, s ←
!
1664store_widthdouble, d ←
!
1665store_widthType, Width ←
int_infoType, Size, _
width_bytesWidth, Size
memberchkWidth, b, h, w, l
!
offset+Addr, +Off, -P//

P is the pointer Addr + Off (Addr itself for 0, with no instruction).

1671offsetAddr, 0, Addr ⟶
!
1672offsetAddr, Off, P ⟶
letp, add, Addr, constOff, P
builtin+Name, +Args, +Ctx, -R//

R is the value of a call of builtin Name, which no declaration shadows, on source arguments Args.

1676builtin'__builtin_va_start', A, _, Ctx, vconst0, void ⟶
!
envEnv
ctx_locCtx, Loc
lookup_ordinaryEnv, builtinva_args, Cursor
→
true
else
sema_errorLoc, 'va_start outside a variadic function', []
rvalA, Ctx, vP, _
emitstorep, Cursor, P
1683builtin'__builtin_va_copy', A, B, Ctx, vconst0, void ⟶
!
rvalA, Ctx, vP, _
rvalB, Ctx, vQ, _
letp, loadp, Q, V
emitstorep, V, P
1686builtin'__builtin_va_end', A, Ctx, vconst0, void ⟶
!
rvalA, Ctx, _
1688builtinName, [], _, vconstV, Type ⟶
float_builtinName, Type, V
!
1690builtin'__builtin_constant_p', Expr, _, vconstV, int ⟶
!
envEnv
catchconstant_valueExpr, Env, _, _, not_constant, fail
→
V = 1
else
V = 0
1696builtin'__builtin_unreachable', [], _, vconst0, void ⟶
!
termunreachable
1698builtin'__builtin_alloca', Size, Ctx, vAddr, ptrvoid ⟶
!
rvalSize, Ctx, V0
ctx_locCtx, Loc
assign_convertSize, V0, ulong, Loc, vN, _
letp, alloc16, N, Addr
1703builtin'__builtin_expect', A, B, Ctx, R ⟶
!
rvalA, Ctx, VA
rvalB, Ctx, _
ctx_locCtx, Loc
assign_convertA, VA, long, Loc, R
1708builtinName, Args, Ctx, R ⟶
overflow_builtinName, Op, Fixed
!
checked_arithmeticArgs, Op, Fixed, Ctx, R
1711builtinName, _, Ctx, _ ⟶
ctx_locCtx, Loc
sema_errorLoc, 'wrong arguments to ~w', Name

The generic builtins do NOT use the usual arithmetic conversions. Keep the exact result in two l limbs, then compare it with the stored value extended back to two limbs. 128 bits suffice for all supported integer operands (at most 64 bits), including unsigned 64-bit products.

1718checked_arithmeticArgs, Op, Fixed, Ctx, vFlag, bool ⟶
ctx_locCtx, Loc
Args = A, B, P
→
true
else
sema_errorLoc, 'checked arithmetic requires three arguments', []
rvalA, Ctx, VA0
rvalB, Ctx, VB0
rvalP, Ctx, VP
VA0 = v_, TA
VB0 = v_, TB
VP = vAddr, ptrType
Type ≢ bool
Fixed ≡ generic
or
Fixed ≡ Type
→
true
else
sema_errorLoc, 'invalid checked arithmetic operand types', []
overflow_operandFixed, VA0, VA
overflow_operandFixed, VB0, VB
overflow_exactOp, X, XH, Y, YH, Low, High
convertvLow, ulong, Type, vStored, Type
store_typedType, Stored, Addr
overflow_widenvStored, Type, Back, BackH
letw, cmpne, l, Low, Back, BadLow
letw, cmpne, l, High, BackH, BadHigh
letw, or, BadLow, BadHigh, Flag
1739overflow_operandgeneric, V, V ⟶
!
1740overflow_operandType, V, R ⟶
convertV, Type, R

Extension to l must explicitly truncate a w value first: int_convert can reuse an l temp after a cast to int, as w consumers ignore its top.

1744overflow_widenvV, Type, Low, High ⟶
int_infoType, Size, Sign
Size < 8
→
Sign ≡ signed
→
Ext = sw
else
Ext = uw
letl, extExt, V, Low
else
Low = V
Sign ≡ signed
→
letl, sar, Low, const63, High
else
High = const0
1756overflow_exactadd, X, XH, Y, YH, Low, High ⟶
letl, add, X, Y, Low
letw, cmpult, l, Low, X, Carry
letl, extuw, Carry, C
letl, add, XH, YH, H
letl, add, H, C, High
1761overflow_exactsub, X, XH, Y, YH, Low, High ⟶
letl, sub, X, Y, Low
letw, cmpult, l, X, Y, Borrow
letl, extuw, Borrow, C
letl, sub, XH, YH, H
letl, sub, H, C, High
1766overflow_exactmul, X, XH, Y, YH, Low, High ⟶
letl, mul, X, Y, Low
letl, and, X, const4294967295, XL
letl, shr, X, const32, XU
letl, and, Y, const4294967295, YL
letl, shr, Y, const32, YU
letl, mul, XL, YL, LL
letl, mul, XU, YL, UL
letl, mul, XL, YU, LU
letl, mul, XU, YU, UU
letl, shr, LL, const32, LLH
letl, and, UL, const4294967295, ULL
letl, and, LU, const4294967295, LUL
letl, add, LLH, ULL, Mid0
letl, add, Mid0, LUL, Mid
letl, shr, Mid, const32, Carry
letl, shr, UL, const32, ULH
letl, shr, LU, const32, LUH
letl, add, UU, ULH, H0
letl, add, H0, LUH, H1
letl, add, H1, Carry, H2
Signed inputs contribute -Y or -X to the unsigned product's high limb.
letl, and, XH, Y, CX
letl, and, YH, X, CY
letl, sub, H2, CX, H3
letl, sub, H3, CY, High
type_size+TypeName, +Env, +Ctx, -Size

Size is sizeof(TypeName).

1787type_sizeTypeName, Env, Ctx, Size ←
ctx_locCtx, Loc
type_nameTypeName, Type, Env, Loc
catchtype_size_alignType, Env, Size, _, badWhy, sema_errorLoc, 'sizeof: ~w', Why
type_name+TypeName, -Type, +Env, +Loc

Type is the type TypeName names, as lowering uses it: a variably modified one (an array of non-constant size anywhere inside) is rejected, since its size expressions would have to be evaluated, side effects and all.

1797type_nameTypeName, Type, Env, Loc ←
type_name_typeTypeName, Type, Env, _
vm_checkType, Loc
vm_check+Type, +Loc

throw a semantic error if nanofil does not support Type (c_types' unsupported_type/3).

1803vm_checkType, Loc ←
unsupported_typeType, F, As
→
sema_errorLoc, F, As
else
true

Conversions

convert+Value0, +To, -R//

R is v(V1, To), scalar Value0 = v(V, From) converted to scalar type To (or void). Callers check that the types are scalar first.

1811convertvV, Type, Type, vV, Type ⟶
!
1812convertvV, _, void, vV, void ⟶
!
1813convertvV, From, bool, vB, bool ⟶
!
From ≢ void
truthvV, From, ctx_, _, _, _, unknown, W
type_classFrom, w
¬From ≡ bool
→
letw, cmpne, w, W, const0, B
else
B = W
1820convertvconstN, From, To, vconstW, To ⟶
1824convertvV, float, double, vR, double ⟶
!
letd, exts, V, R
1826convertvV, double, float, vR, float ⟶
!
lets, truncd, V, R
1828convertvV, From, To, vR, To ⟶
type_classFrom, IK
int_infoFrom, _, Sign
int_float_opIK, Sign, Op
!
letK, Op, V, R
1833convertvV, From, To, vR, To ⟶
type_classFrom, K
int_infoTo, _, Sign
float_int_opK, Sign, Op
conversion_int_typeIK, Sign, Base
!
letIK, Op, V, I
int_convertBase, To, I, R
1840convertvconstN, From, To, vconstW, To ⟶
1844convertvV, From, To, vV1, To ⟶
!
int_convertFrom, To, V, V1
1848convertvconstN, From, ptrP, vconstW, ptrP ⟶
!
convert_intN, ulong, W
1852convertvV, From, ptrP, vV1, ptrP ⟶
!
int_convertFrom, ulong, V, V2
letp, itop, V2, V1
1857convertvV, ptr_, To, R ⟶
!
letl, ptoi, V, Int
convertvInt, ulong, To, R
1862convertvV, ptr_, ptrP, vV, ptrP ⟶
[]
int_float_op
1864wsignedswtof
1865wunsigneduwtof
1866lsignedsltof
1867lunsignedultof
float_int_op
1868ssignedstosi
1869sunsignedstoui
1870dsigneddtosi
1871dunsigneddtoui

Float-to-integer instructions produce a full w/l; normalize narrow C destinations afterward, without ever routing an unsigned result via signed.

1875conversion_int_typeClass, Sign, Base ←
integer_typeBase, _, Sign, Rank
Rank ≥ 3
type_classBase, Class
!
int_convert+From, +To, +V, -V1//

V1 is integer V of type From converted to type To, extended where the representation changes.

1880int_convertFrom, To, V, V1 ⟶
int_infoFrom, SizeFrom, SignFrom
int_infoTo, SizeTo, SignTo
SizeTo =:= 8
SizeFrom < 8
→
narrow_typeOp, 4, SignFrom
letl, extOp, V, V1
SizeTo < 4
¬representableSizeFrom, SignFrom, SizeTo, SignTo
→
narrow_typeOp, SizeTo, SignTo
letw, extOp, V, V1
else
V1 = V
representable+SizeFrom, +SignFrom, +SizeTo, +SignTo

every value of the source type is a value of the target type.

1893representableSizeFrom, SignFrom, SizeTo, SignTo ←
SizeTo > SizeFrom
SignTo ≡ signed
or
SignFrom ≡ unsigned
!
1897representableSize, Sign, Size, Sign
assign_convert+Expr, +Value0, +To, +Loc, -R//

R is Value0, the value of Expr, converted to To as by assignment: a return value, an initializer or a builtin's argument. c_types' assigned/4 judges that it may be.

1903assign_convertExpr, Value0, To, Loc, R ⟶
envEnv
assignedExpr, Env, To, Loc
convertedValue0, To, R

Operators

unary+Op, +Value0, +Ctx, -R//

R is unary operator Op applied to Value0, converted as unary_operand/3 says.

1912unaryOp, Value0, Ctx, R ⟶
Value0 = v_, From
ctx_locCtx, Loc
unary_operandOp, From, Type
→
true
else
sema_errorLoc, 'invalid operand to ~w: ~w', Op, From
unary_valueOp, Value0, Type, Ctx, R
1919unary_valuenot, Value0, _, Ctx, vV, int ⟶
!
truthValue0, Ctx, W
letw, cmpeq, w, W, const0, V
1922unary_valueOp, Value0, Type, _, vV, Type ⟶
convertValue0, Type, vV1, _
type_classType, K
unary_insOp, K, V1, V
1927unary_insplus, _, V, V ⟶
[]
1928unary_insneg, K, V1, V ⟶
letK, neg, V1, V
1929unary_insbnot, K, V1, V ⟶
letK, xor, V1, const-1, V
binary+Op, +VA, +VB, +Ctx, -R//

R is binary operator Op applied to the values VA and VB, converted as binary_operands/6 says.

1933binaryOp, VA, VB, Ctx, R ⟶
VA = v_, TA
VB = v_, TB
ctx_locCtx, Loc
binary_operandsOp, TA, TB, CA, CB, T
→
true
else
sema_errorLoc, 'invalid operands to ~w: ~w and ~w', Op, TA, TB
binary_valueOp, CA, CB, T, VA, VB, Ctx, R
binary_value+Op, +CA, +CB, +T, +VA, +VB, +Ctx, -R//

R, of type T, is Op applied to VA converted to CA and VB converted to CB. Pointer arithmetic scales by the element size. Direct ordered comparisons are essential: !(a < b) is not a >= b when either operand is NaN.

1944binary_valueOp, ptrE, long, _, VA, VB, Ctx, R ⟶
!
ptr_offsetOp, VA, E, VB, Ctx, R
1946binary_valueadd, long, ptrE, _, VA, VB, Ctx, R ⟶
!
ptr_offsetadd, VB, E, VA, Ctx, R
1948binary_valuesub, ptrE, ptr_, _, VA, VB, Ctx, R ⟶
!
ptr_diffVA, VB, E, Ctx, R
1950binary_valueOp, CA, CB, T, VA, VB, _, vV, T ⟶
convertVA, CA, vA, _
convertVB, CB, vB, _
→
Sign = signed
else
int_infoCA, _, Sign
cmp_opOp, SignedCmp, UnsignedCmp
→
Class = w
→
Cmp = Op
Sign ≡ signed
→
Cmp = SignedCmp
else
Cmp = UnsignedCmp
IOp = cmpCmp, K
else
Class = K
arith_opOp, Sign, IOp
letClass, IOp, A, B, V
cmp_op+Op, -SignedCmp, -UnsignedCmp

C comparison Op is IR comparison SignedCmp on signed and UnsignedCmp on unsigned operands.

cmp_op
1968eqeqeq
1969nenene
1970ltsltult
1971gtsgtugt
1972lesleule
1973gesgeuge
arith_op+Op, +Sign, -IROp

C arithmetic Op on operands of Sign is IR operation IROp.

arith_op
1977add_add
1978sub_sub
1979mul_mul
1980divsigneddiv
1981divunsignedudiv
1982modsignedrem
1983modunsignedurem
1984and_and
1985or_or
1986xor_xor
1987shl_shl
1988shrsignedsar
1989shrunsignedshr
ptr_offset+Op, +VP, +ElemType, +VI, +Ctx, -R//

R is P + I or P - I, I scaled by the element size.

1993ptr_offsetOp, vP, PtrType, ElemType, VI, Ctx, vR, PtrType ⟶
PtrType = ptrElemType
envEnv
ctx_locCtx, Loc
catchtype_size_alignElemType, Env, Size, _, badWhy, sema_errorLoc, 'pointer arithmetic on ~w (~w)', ElemType, Why
convertVI, long, vI, _
I = constN
→
Off is N × Size
OffV = constOff
Size =:= 1
→
OffV = I
else
letl, mul, I, constSize, OffV
letp, Op, P, OffV, R
ptr_diff+VA, +VB, +ElemType, +Ctx, -R//

R is A - B in elements.

2008ptr_diffvA, _, vB, _, ElemType, Ctx, vV, long ⟶
envEnv
ctx_locCtx, Loc
catchtype_size_alignElemType, Env, Size, _, badWhy, sema_errorLoc, 'pointer subtraction on ~w (~w)', ElemType, Why
letl, ptoi, A, IA
letl, ptoi, B, IB
letl, sub, IA, IB, Diff
Size =:= 1
→
V = Diff
else
letl, div, Diff, constSize, V
logical+Op, +XA, +XB, +Ctx, -R//

R is XA && XB (land) or XA || XB (lor), a phi of 0/1 over the short-circuit edges.

2023logicalOp, XA, XB, Ctx, vV, int ⟶
lowerXA, Ctx, VA
truthVA, Ctx, WA
labelLRight
labelLJoin
Op ≡ land
→
Jump = branchWA, LRight, LJoin
Short = 0
else
Jump = branchWA, LJoin, LRight
Short = 1
termJump
startLRight
lowerXB, Ctx, VB
truthVB, Ctx, WB
letw, cmpne, w, WB, const0, NB
termgotoLJoin
start_phiLJoin, phiV, w, LA−constShort, LB−NB
join_arms+Type, +LJoin, +LA−VA, +LB−VB, -R//

start block LJoin; R is the ?: result there, a phi of VA from LA and VB from LB.

2038join_armsType, LJoin, LA−VA, LB−VB, R ⟶
Type ≡ void
→
startLJoin
R = vconst0, void
else
suType
→
Class = p
else
type_classType, Class
start_phiLJoin, phiV, Class, LA−VA, LB−VB
R = vV, Type

Calls

A call (6.5.2.2) lowers from its tree, whose callee and arguments the elaborator has checked: the callee is a function, the arguments fit its arity and each converts to its parameter as by assignment.

callee+XF, +Ctx, -Callee, -FuncType//

Callee is the IR callee of function pointer XF: the function itself when XF is a function designator decayed, else a temp holding the pointer.

2055calleeval_, decayX, Ctx, Callee, FuncType ⟶
X = fun_, _
!
placeX, Ctx, fdCallee, FuncType
2058calleeXF, Ctx, Callee, FuncType ⟶
lowerXF, Ctx, vV, ptrFuncType
callee_tempV, Callee
call_result+Callee, +RetType, +IRArgs, +Ctx, -R//

R is the result of calling Callee with IRArgs; setjmp and its kin are intrinsics.

2064call_resultglobalKind, RetType, IRArgs, Ctx, R ⟶
op_kindsetjmpKind, setjmp
!
RetType ≡ int
setjmp_argsKind, IRArgs, Operands
→
true
else
ctx_locCtx, Loc
sema_errorLoc, 'invalid ~w signature', Kind
letw, setjmpKind, Operands, V
R = vV, int
2070call_resultCallee, RetType, IRArgs, Ctx, R ⟶
ctx_locCtx, Loc
suRetType
→
struct_callRetType, Callee, IRArgs, Loc, R
RetType ≡ void
→
emitcallvoid, Callee, IRArgs
R = vconst0, void
type_classRetType, Class
→
emitcallresultD, Class, Callee, IRArgs
normalizeRetType, D, V
R = vV, RetType
else
sema_errorLoc, 'calls returning ~w are not supported yet', RetType
setjmp_args
2085sigsetjmpargp, Buf, argw, MaskBuf, Mask
2086setjmpargp, BufBuf, const0
2087'_setjmp'argp, BufBuf, const0
call_args+XArgs, +ParamTypes, +Ctx, -IRArgs//

IRArgs pass the arguments whose trees are XArgs. A struct argument goes as one word per eightbyte; arguments past the prototype get the default promotions.

2093call_args[], _, _, [] ⟶
[]
2094call_argsXA|XAs, ParamType|ParamTypes, Ctx, IRArgs ⟶
suParamType
!
lowerXA, Ctx, Value0
convertedValue0, ParamType, vSrc, _
envEnv
ctx_locCtx, Loc
arg_wordsParamType, Env, Loc, Ebs
load_wordsEbs, Src, Words
findallargClass, W,
nth0I, Ebs, eb_, _, Class
nth0I, Words, W
,
IRArgs0
call_argsXAs, ParamTypes, Ctx, IRArgs1
appendIRArgs0, IRArgs1, IRArgs
2106call_argsXA|XAs, ParamTypes0, Ctx, argClass, V|IRArgs ⟶
lowerXA, Ctx, Value0
ParamTypes0 = ParamType|ParamTypes
→
convertedValue0, ParamType, vV, Type
else
ParamTypes = []
Value0 = v_, Type0
→
convertValue0, Type, vV, _
Type0 = ptr_
→
Value0 = vV, Type
else
ctx_locCtx, Loc
sema_errorLoc, 'passing ~w to a variadic or unprototyped function is not supported', Type0
type_classType, Class
call_argsXAs, ParamTypes, Ctx, IRArgs
struct_call+RetType, +Callee, +Args, +Loc, -R//

R is the result of a call returning a struct: its words land in a fresh local, or the callee writes the local through the sret pointer.

2127struct_callRetType, Callee, Args, Loc, vTmp, RetType ⟶
envEnv
ret_shapeRetType, Env, Loc, Shape
local_slotRetType, Tmp
Shape ≡ sret
→
emitcallvoid, Callee, argp, Tmp|Args
Shape = wordseb_, _, Class
→
emitcallresultD, Class, Callee, Args
Shape = wordsEbs
store_wordsEbs, D, Tmp
else
Shape = wordsEbs
Ebs = eb_, _, Class1, eb_, _, Class2
emitcalltupleresultD1, Class1, resultD2, Class2, Callee, Args
store_wordsEbs, D1, D2, Tmp
return_struct+RetType, +Addr, +Loc//

return the struct at Addr, in words or through the sret pointer.

2147return_structRetType, Addr, Loc ⟶
envEnv
ret_shapeRetType, Env, Loc, Shape
Shape ≡ sret
→
envEnv1
lookup_ordinaryEnv1, '%sret', sretP
copy_objectRetType, Addr, P
termret
else
Shape = wordsEbs
load_wordsEbs, Addr, Words
Words = W
→
termretW
else
termretsWords
normalize+Type, +V0, -V//

V is call result V0 of Type, a narrow one re-extended: nothing guarantees the callee did.

2164normalizeType, V0, V ⟶
int_infoType, Size, Sign
Size < 4
→
narrow_typeOp, Size, Sign
letw, extOp, V0, V
Type ≡ bool
→
letw, extub, V0, V
else
V = V0

Struct values

A struct or union rvalue is v(Addr, T): Addr points at storage holding the value, a variable's or a temporary's. Copies, arguments and returns go an eightbyte at a time (eb(Offset, Bytes, Class)): class p if a pointer starts there, so its capability is copied with load(p) and store(p, ...), else l, with a short last eightbyte moved in 4-, 2- and 1-byte pieces. A value of at most 16 bytes is passed as one word per eightbyte, as clang does; a larger return goes through an sret pointer.

2182sustruct_
2183suunion_
eightbytes+Type, +Env, +Loc, -Ebs

Ebs are the eightbytes of Type.

2186eightbytesType, Env, Loc, Ebs ←
catchtype_size_alignType, Env, Size, _, badWhy, sema_errorLoc, '~w has an unsupported type (~w)', Type, Why
ptr_offsetsType, Env, 0, PtrOffs0, []
sortPtrOffs0, PtrOffs
Last is Size + 7//8−1
findallebOff, N, Class,
between0, Last, I
Off is 8 × I
N is min8, Size−Off
memberchkOff, PtrOffs
→
Class = p
else
Class = l
,
Ebs
ptr_offsets+Type, +Env, +Base, -Offs0, ?Offs

Offs0-Offs is the difference list of the offsets, from Base, where Type holds pointers.

2202ptr_offsetsptr_, _, Base, Base|Offs, Offs ←
!
2203ptr_offsetsarrayElemType, N, Env, Base, Offs0, Offs ←
integerN
!
ptr_offsetsElemType, Env, 0, ElemOffs, []
ElemOffs ≡ []
→
Offs0 = Offs
else
type_size_alignElemType, Env, Size, _
Last is N−1
findallI, between0, Last, I, Indices
foldlelem_ptrsElemType, Env, Base, Size, Indices, Offs0, Offs
2213ptr_offsetsSU, Env, Base, Offs0, Offs ←
suSU
!
SU =.. _, Id
env_defsEnv, Defs
get_assocId, Defs, def_, Members, _, _
foldlmember_ptrsEnv, Base, Members, Offs0, Offs
2220ptr_offsets_, _, _, Offs, Offs
2222elem_ptrsElemType, Env, Base, Size, I, Offs0, Offs ←
Base1 is Base + I × Size
ptr_offsetsElemType, Env, Base1, Offs0, Offs
2226member_ptrsEnv, Base, m_, Type, Off, Offs0, Offs ←
Base1 is Base + Off
ptr_offsetsType, Env, Base1, Offs0, Offs
2229member_ptrs_, _, m_, _, _, bits_, _, Offs, Offs
arg_words+Type, +Env, +Loc, -Ebs

Ebs are the eightbytes in which a struct or union argument of Type is passed.

2233arg_wordsType, Env, Loc, Ebs ←
by_value_checkType, Env, Loc
type_size_alignType, Env, Size, _
Size > 16
→
sema_errorLoc, 'passing a ~w of ~w bytes by value is not supported yet', Type, Size
else
eightbytesType, Env, Loc, Ebs
ret_shape+Type, +Env, +Loc, -Shape

Shape is how a struct or union of Type is returned: sret, or words(Eightbytes).

2244ret_shapeType, Env, Loc, Shape ←
by_value_checkType, Env, Loc
type_size_alignType, Env, Size, _
Size > 16
→
Shape = sret
else
eightbytesType, Env, Loc, Ebs
Shape = wordsEbs
2252by_value_checkType, Env, Loc ←
→
sema_errorLoc, 'float-containing struct or union by value is not supported', []
else
true
2258contains_floatType, _ ←
2259contains_floatldouble, _
2260contains_floatarrayT, _, Env ←
2261contains_floatType, Env ←
suType
Type =.. _, Id
env_defsEnv, Defs
get_assocId, Defs, def_, Members, _, _
memberm_, T, _, Members
copy_object+Type, +Src, +Dst//

copy the object of Type at Src to Dst, an eightbyte at a time.

2268copy_objectType, Src, Dst ⟶
envEnv
eightbytesType, Env, unknown, Ebs
copy_ebsEbs, Src, Dst
2273copy_ebs[], _, _ ⟶
[]
2274copy_ebsebOff, N, Class|Ebs, Src, Dst ⟶
offsetSrc, Off, S
offsetDst, Off, D
Class ≡ p
→
letp, loadp, S, V
emitstorep, V, D
else
piecesN, 0, Pieces
copy_piecesPieces, S, D
copy_ebsEbs, Src, Dst
pieces+N, +Off, -Pieces

Pieces are N bytes from offset Off as p(Offset, Bytes, Load, Store), widest first. The cut after the member commits to the widest piece that fits.

2288pieces0, _, [] ←
!
2289piecesN, Off, pOff, Bytes, Load, Store|Pieces ←
memberBytes−Load−Store, 8−l−l, 4−uw−w, 2−uh−h, 1−ub−b
Bytes ≤ N
!
N1 is N−Bytes
Off1 is Off + Bytes
piecesN1, Off1, Pieces
2297copy_pieces[], _, _ ⟶
[]
2298copy_piecespOff, _, Load, Store|Pieces, Src, Dst ⟶
offsetSrc, Off, S
offsetDst, Off, D
letl, loadLoad, S, V
emitstoreStore, V, D
copy_piecesPieces, Src, Dst
load_words+Ebs, +Addr, -Words//

Words are the words of the value at Addr, one per eightbyte.

2307load_words[], _, [] ⟶
[]
2308load_wordsebOff, N, Class|Ebs, Addr, W|Words ⟶
offsetAddr, Off, P
Class ≡ p
→
letp, loadp, P, W
else
piecesN, 0, Pieces
load_piecesPieces, P, none, W
load_wordsEbs, Addr, Words
load_pieces+Pieces, +P, +Acc0, -W//

W is Acc0 (none at first) or'd with each of Pieces loaded from P and shifted into place.

2318load_pieces[], _, W, W ⟶
[]
2319load_piecespOff, _, Load, _|Pieces, P, Acc0, W ⟶
offsetP, Off, P1
letl, loadLoad, P1, Piece0
Off =:= 0
→
Piece = Piece0
else
Shift is 8 × Off
letl, shl, Piece0, constShift, Piece
Acc0 ≡ none
→
Acc = Piece
else
letl, or, Acc0, Piece, Acc
load_piecesPieces, P, Acc, W
store_words+Ebs, +Words, +Addr//

store Words, one per eightbyte, into the object at Addr.

2333store_words[], [], _ ⟶
[]
2334store_wordsebOff, N, Class|Ebs, W|Words, Addr ⟶
offsetAddr, Off, P
Class ≡ p
→
emitstorep, W, P
else
piecesN, 0, Pieces
store_piecesPieces, W, P
store_wordsEbs, Words, Addr
store_pieces+Pieces, +W, +P//

store each of Pieces of word W, shifted down, at P.

2344store_pieces[], _, _ ⟶
[]
2345store_piecespOff, _, _, Store|Pieces, W, P ⟶
offsetP, Off, P1
Off =:= 0
→
Piece = W
else
Shift is 8 × Off
letl, shr, W, constShift, Piece
emitstoreStore, Piece, P1
store_piecesPieces, W, P

Static initializers

static_items(+Type, +Init, +Loc, +Env0, -Env, -Items): the data items of a global of type Type. Each leaf of Init becomes a chunk chunk(Offset, Bytes, Item), a later one replacing an earlier one at the same offset, then the gaps are zero-filled. A pointer is item(p, sym(S, Off)); string literals in initializers become data of their own.

2362static_itemsType, Init, Loc, Env0, Env, Items ←
type_size_alignType, Env0, Size, _
Init ≡ none
→
Leaves = []
else
init_leavesType, Init, cEnv0, none, Loc, Leaves
foldlstatic_leafLoc, Leaves, sEnv0, [], 0, sEnv, Chunks0, _
reverseChunks0, Chunks
empty_assocByOffset0
foldlput_chunk, Chunks, ByOffset0, ByOffset
assoc_to_valuesByOffset, Sorted
fill_itemsSorted, 0, Size, Loc, Items
put_chunk+Chunk, +ByOffset0, -ByOffset

ByOffset maps Chunk's offset to Chunk, replacing any earlier one there.

2376put_chunkchunkOff, N, Item, ByOffset0, ByOffset ←
put_assocOff, ByOffset0, chunkOff, N, Item, ByOffset
fill_items+Chunks, +Pos, +Size, +Loc, -Items

Items are the items of Chunks, sorted by offset, with zero(N) filling the gaps from Pos to Size.

2382fill_items[], Pos, Size, _, Zeros ←
Pos < Size
→
N is Size−Pos
Zeros = zeroN
else
Zeros = []
2386fill_itemschunkOff, N, Item|Chunks, Pos, Size, Loc, Items ←
Off < Pos
→
sema_errorLoc, 'overlapping initializers', []
else
true
Off > Pos
→
Gap is Off−Pos
Items = zeroGap, Item|Items1
else
Items = Item|Items1
Pos1 is Off + N
fill_itemsChunks, Pos1, Size, Loc, Items1
int_chunk+Off, +Type, +V, -Chunk

Chunk holds integer V of Type at Off.

2398int_chunkOff, Type, V, chunkOff, N, itemWidth, constU ←
int_infoType, N, _
memberchkN−Width, 1−b, 2−h, 4−w, 8−l
convert_intV, Type, U

static_leaf(+Loc, +Leaf, +S0, -S): S = s(Env, Chunks, End) adds the chunk of Leaf to the reversed Chunks of S0; End is where the chunks end so far. A zero leaf drops the chunks it covers, which only a designator that goes back can have made.

2407static_leaf_, zeroOff, Type, sEnv, Chunks0, End, sEnv, Chunks, End ←
Off ≥ End
→
Chunks = Chunks0
else
type_size_alignType, Env, Size, _
Hi is Off + Size
excludechunk_withinOff, Hi, Chunks0, Chunks
2414static_leaf_, valOff, Type, V, sEnv, Chunks, End0, sEnv, Chunk|Chunks, End ←
int_chunkOff, Type, V, Chunk
chunk_endChunk, End0, End
2418static_leafLoc, exprOff, Type, Expr, sEnv0, Chunks, End0, sEnv, Chunk|Chunks, End ←
expr_chunkType, Off, Expr, Loc, Env0, Env, Chunk
chunk_endChunk, End0, End
2423static_leafLoc, bitsOff, Type, Pos, W, Expr, sEnv, Chunks0, End0, sEnv, Chunks, End ←
catchconstant_valueExpr, Env, V0, From, not_constant, fail
const_convertV0, From, Type, V
integerV
→
true
else
sema_errorLoc, 'initializer is not a constant', []
Start is 8 × Off + Pos
First is Start//8
Last is Start + W−1//8
numlistFirst, Last, Bytes
foldlbitfield_byteStart, W, V, Bytes, Chunks0, Chunks
End is maxEnd0, Last + 1
bitfield_byte+Start, +W, +V, +Byte, +Chunks0, -Chunks

Chunks adds to Chunks0 the byte at offset Byte with its bits of the W-bit field V, starting at bit Start, merged into the bits an earlier bit-field put there.

2440bitfield_byteStart, W, V, Byte, Chunks0, chunkByte, 1, itemb, constNew|Chunks0 ←
memberchunkByte, N, Item, Chunks0
→
N =:= 1
Item = itemb, constOld
→
true
else
Old = 0
else
Old = 0
Shift is Start−8 × Byte
Mask is 1 << W−1 << Shift + 64 >> 64 /\ 255
Bits is V /\ 1 << W−1 << Shift + 64 >> 64 /\ 255
New is Old /\ \Mask /\ 255 \/ Bits
2450chunk_withinLo, Hi, chunkOff, _, _ ←
Off ≥ Lo
Off < Hi
2452chunk_endchunkOff, N, _, End0, End ←
End is maxEnd0, Off + N
expr_chunk+Type, +Off, +Expr, +Loc, +Env0, -Env, -Chunk

Chunk holds the constant value of Expr, of scalar Type, at Off.

2456expr_chunkType, Off, Expr, Loc, Env, Env, Chunk ←
!
catchconstant_valueExpr, Env, V0, From, not_constant, fail
const_convertV0, From, Type, V
integerV
→
true
else
sema_errorLoc, 'initializer is not a constant', []
→
type_classType, K
type_size_alignType, Env, N, _
Chunk = chunkOff, N, itemK, constV
else
int_chunkOff, Type, V, Chunk
2469expr_chunkptr_, Off, Expr, Loc, Env0, Env, Chunk ←
!
elaborateExpr, Env0, Loc, X
→
valueX, XV
else
sema_errorLoc, 'initializer is not an address constant', []
address_constantXV, Loc, Env0, Env, R
R = symSym, SymOff
→
Chunk = chunkOff, 8, itemp, symSym, SymOff
else
Chunk = chunkOff, 8, iteml, const0
2478expr_chunkType, _, _, Loc, _, _, _ ←
sema_errorLoc, 'cannot statically initialize a ~w', Type

address_constant(+XV, +Loc, +Env0, -Env, -R): the value tree XV is an address constant (C11 6.6p9), R = sym(Symbol, Offset), or a null pointer, R = null. It is the address of a static object or a function, perhaps cast, plus or minus an integer constant; string literals and compound literals become data of their own.

2486address_constantval_, decayX, Loc, Env0, Env, R ←
!
place_constantX, Loc, Env0, Env, R
2488address_constantval_, addrX, Loc, Env0, Env, R ←
!
place_constantX, Loc, Env0, Env, R
2490address_constantval_, castTypeName, XV, Loc, Env0, Env, R ←
!
type_nameTypeName, Type, Env0, Loc
Type = ptr_
→
address_constantXV, Loc, Env0, Env, R
else
sema_errorLoc, 'initializer is not a constant', []
2495address_constantvalptr_, binOp, XA, XB, Loc, Env0, Env, R ←
memberchkOp, add, sub
loweredXA, TA
loweredXB, TB
TA = ptrElem
→
XP = XA
XK = XB
else
TB = ptrElem
XP = XB
XK = XA
foldedXK, Env0, K
integerK
!
address_constantXP, Loc, Env0, Env, R0
offset_constOp, R0, Elem, K, Env, Loc, R
2505address_constantXV, Loc, Env, Env, null ←
loweredXV, T
foldedXV, Env, 0
→
true
else
sema_errorLoc, 'initializer is not an address constant', []
offset_const+Op, +R0, +Elem, +K, +Env, +Loc, -R

R is address constant R0 plus (add) or minus (sub) K elements of type Elem.

2512offset_constOp, symSym, Off0, Elem, K, Env, Loc, symSym, Off ←
!
catchtype_size_alignElem, Env, Size, _, badWhy, sema_errorLoc, 'pointer arithmetic on ~w (~w)', Elem, Why
Op ≡ add
→
Off is Off0 + K × Size
else
Off is Off0−K × Size
2518offset_const_, _, _, _, _, Loc, _ ←
sema_errorLoc, 'initializer is not an address constant', []
place_constant+X, +Loc, +Env0, -Env, -R

typed tree X designates a static object or a function at R = sym(Symbol, Offset).

2523place_constantobj_, idName, Loc, Env, Env, symSym, 0 ←
!
lookup_ordinaryEnv, Name, gvar_, Sym, _, _
→
true
else
sema_errorLoc, '~w is not a global', Name
2527place_constantfun_, idName, _, Env, Env, symSym, 0 ←
lookup_ordinaryEnv, Name, fn_, Sym, _
!
2529place_constantobj_, strBytes, _, Env0, Env, symSym, 0 ←
!
new_literalBytes, Sym, Env0, Env
2531place_constantobj_, wstrWideType, Values, _, Env0, Env, symSym, 0 ←
!
new_literalwWideType, Values, Sym, Env0, Env
2533place_constantobj_, complitTypeName, Init, Loc, Env0, Env, symSym, 0 ←
!
type_nameTypeName, Type0, Env0, Loc
new_complitType0, Init, Loc, Sym, Env0, Env
2536place_constantobj_, derefXV, Loc, Env0, Env, R ←
!
address_constantXV, Loc, Env0, Env, R0
R0 = sym_, _
→
R = R0
else
sema_errorLoc, 'initializer is not an address constant', []
2541place_constantobj_, memberobjT, F, Field, Loc, Env0, Env, symSym, Off ←
!
place_constantobjT, F, Loc, Env0, Env, symSym, Off0
unqualifiedT, SUType
member_offSUType, Field, Env, Loc, _, MemberOff
Off is Off0 + MemberOff
2546place_constant_, Loc, _, _, _ ←
sema_errorLoc, 'initializer is not an address constant', []
member_off+SUType, +Field, +Env, +Loc, -Type, -Off

member Field of struct or union SUType has Type and offset Off.

2551member_offSUType, Field, Env, Loc, Type, Off ←
SUType = struct_
or
SUType = union_
member_type_offsetSUType, Field, Env, Type, Off
→
true
else
sema_errorLoc, 'no member named ~w', Field