Fil-C on Nix

Filnix wiki

mbrock/filnix/nanofil

amd64.pl

The naive x86-64 SysV backend for the phase-one IR in plain (non-Fil-C) mode (milestone 2). It is the reference the later instruction selection and register allocation milestones are checked against, so it favours being obviously right over being fast.

The output is a list of asm.pl lines, instruction and directive terms, which asm:print_asm/1 prints as GNU as text. Functions that call setjmp are refused.

The frame

Every temporary lives in its own 8-byte slot below %rbp, and each phi destination also has a shadow slot. alloc moves %rsp down by its size rounded up to 16, which keeps calls aligned.

← %rbp
tmp(N) slots, 8 bytes-8, -16, …
shadow(N) slots, 8 bytes
padding to 16 bytes
← %rsp after the prologue
alloc'd blocks, each a
multiple of 16 bytes
← %rsp

Instructions

Every instruction loads its operands into fixed scratch registers (%rax, %rcx, %rdx, %xmm0, %xmm1), computes, and stores %rax back to its destination's slot. A float computation moves the bits into the SSE2 scratch registers and its result back to %rax: slots hold bits, not floats. A w result is always zero-extended into %rax, so every slot is 8 bytes and a w read is the low 4 bytes of its slot.

Phis are lowered on each incoming edge as a parallel copy: every incoming value goes into its phi's shadow slot, then every shadow into its phi. A branchV, IfTrue, IfFalse gets a labelled edge for its false side, so each edge carries its own copies.

Variable conventions

VariableMeans
Ka class, w, l, s or d, or where stated a store width, b, h, w, l, s or d
San AT&T suffix
Ra register: a, c, d, si, di, r8 or r9
RNthe name of R at a width
Ctxthe function context, fName, Ret, Slots, Blocks
useslists
usesdcgs
usesformat
usesassoc
usesir
module_asm+Module, -Linesdet

Lines are the asm.pl lines of the plain (non-Fil-C) Module. Throws a nanofil_error naming the first item the backend cannot emit.

70module_asmModule, Lines ←
without_locsModule, moduleItems
memberF, Items
→
thrownanofil_error'plain backend does not support setjmp/nonlocal returns'
else
true
maplistitem_lines, Items, Liness
appendLiness, Lines0
appendLines0, sectionnote_gnu_stack, Lines
item_lines+Item, -Lines

Lines are the asm.pl lines of Item.

80item_linesItem, Lines ←
phraseitemItem, Lines
→
true
else
arg1, Item, Name
formatatomMsg, 'backend cannot emit ~w', Name
thrownanofil_errorMsg

Functions

The context of a function's code is f(Name, Ret, Slots, Blocks). filc_amd64 builds the same term to call the exported nonterminals, so its shape is shared.

item+Item//

the lines of a function or data definition.

94itemfuncName, Linkage, Ret, Params, Blocks ⟶
frameParams, Blocks, Slots, Size
Ctx = fName, Ret, Slots, Blocks
sectiontext, p2align4
globalLinkage, Name
typeName, function, labelName, inspush, q, regrbp, insmov, q, regrsp, regrbp
Size > 0
→
inssub, q, immSize, regrsp
else
[]
plain_paramsParams, Ctx
blocksBlocks, Ctx
sizeName
107itemdataName, Linkage0, Align, Items ⟶
linkage_sectionLinkage0, Linkage, Section
sectionSection, balignAlign
globalLinkage, Name
labelName
linkage_section?Linkage0, ?Linkage, ?Section

data with linkage Linkage0 goes in Section with symbol linkage Linkage.

linkage_section
116exportexportdata
117locallocaldata
118roLinkageLinkagerodata
global+Linkage, +Name//

the .globl directive an export needs.

121globalexport, Name ⟶
globlName
122globallocal, _ ⟶
[]

frame(+Params, +Blocks, -Slots, -Size): one slot per temporary name and one shadow slot per phi destination. Slots maps a key, tmp(N) or shadow(N), to its offset below %rbp.

127frameParams, Blocks, Slots, Size ←
findalltmpN,
memberparam_, tmpN, Params
or
memberblock_, Phis, Insts, _, Blocks
memberphitmpN, _, _, Phis
or
memberInst, Insts
definesInst, N
,
Temps0
findallshadowN,
memberblock_, Phis, _, _, Blocks
memberphitmpN, _, _, Phis
,
Shadows0
sortTemps0, Temps
sortShadows0, Shadows
appendTemps, Shadows, Keys
empty_assocSlots0
foldlassign_slot, Keys, Slots0−0, Slots−Count
Size is Count × 8 + 15//16 × 16
defines+Inst, -N

Inst writes temporary tmp(N).

149defineslettmpN, _, _, _, N
150definescallresulttmpN, _, _, _, N
151definesasm_fixed_, _, Outs, N ←
member_−tmpN, Outs

The next 8-byte slot below %rbp, counting slots in Count.

154assign_slotKey, Slots0−Count0, Slots−Count ←
Count is Count0 + 1
Offset is Count × 8
put_assocKey, Slots0, Offset, Slots

Float arguments use an independent bank, with source-ordered stack overflow. Keep the integer-only emission unchanged.

161plain_paramsParams, Ctx ⟶
memberparamF, _, Params
→
findallK, memberparamK, _, Params, Ks
bank_locationsKs, di, si, d, c, r8, r9, Locs, _, _
plain_incomingParams, Locs, Ctx
else
store_paramsParams, 0, Ctx
169plain_incoming[], [], _ ⟶
[]
170plain_incomingparamK, T|Ps, L|Ls, Ctx ⟶
173plain_incoming_valueregR, _ ⟶
reg_nameR, l, RN
insmov, q, regRN, regrax
175plain_incoming_valuexregX, K ⟶
from_xmmK, X, a
176plain_incoming_valuestackI, _ ⟶
Off is 16 + 8 × I
insmov, q, memOff, rbp, regrax
store_params+Params, +I, +Ctx//

spill the incoming argument registers, from the I-th on, to the slots of Params.

181store_params[], _, _ ⟶
[]
182store_paramsparam_, Temp|Params, I, Ctx ⟶
arg_regI, Reg
→
[]
else
Ctx = fFn, _, _, _
formatatomMsg, 'in ~w: more than six parameters', Fn
thrownanofil_errorMsg
reg_nameReg, l, RegName
store_regRegName, Temp, Ctx
I1 is I + 1
store_paramsParams, I1, Ctx
arg_reg?I, ?Reg

Reg is the I-th (from 0) SysV integer argument register.

arg_reg
1950di
1951si
1952d
1963c
1964r8
1965r9
reg_name+Reg, +Width, -Namesemidet

Name is the name of register Reg at Width, a store width b, h or a class w, l. Only a, c and d have byte and halfword names.

202reg_namea, b, al
202reg_namea, h, ax
203reg_namea, w, eax
203reg_namea, l, rax
204reg_namec, b, cl
204reg_namec, h, cx
205reg_namec, w, ecx
205reg_namec, l, rcx
206reg_named, b, dl
206reg_named, h, dx
207reg_named, w, edx
207reg_named, l, rdx
208reg_namesi, w, esi
208reg_namesi, l, rsi
209reg_namedi, w, edi
209reg_namedi, l, rdi
210reg_namer8, w, r8d
210reg_namer8, l, r8
211reg_namer9, w, r9d
211reg_namer9, l, r9
212reg_nameR, s, RN ←
reg_nameR, w, RN
213reg_nameR, d, RN ←
reg_nameR, l, RN
att_suffix+Width, -Suffixsemidet

Suffix is the AT&T suffix for a store width or class Width.

att_suffix
218bb
218hw
218wl
218lq
219sl
219dq

Blocks and jumps

blocks+Blocks, +Ctx//

each block's label, instructions and jump.

224blocks[], _ ⟶
[]
225blocksblockLabel, _, Insts, Jump|Blocks, Ctx ⟶
block_labelCtx, Label, AsmLabel
labelAsmLabel
instsInsts, Ctx
jumpJump, Label, Ctx
blocksBlocks, Ctx
block_label+Ctx, +Label, -AsmLabel

AsmLabel is the local assembly label of block Label.

234block_labelfFn, _, _, _, Label, AsmLabel ←
formatatomAsmLabel, '.L~w.~w', Fn, Label
false_label+Ctx, +Label, -AsmLabel

AsmLabel labels the edge block for the false side of the jnz ending block Label.

239false_labelfFn, _, _, _, Label, AsmLabel ←
formatatomAsmLabel, '.L~w.~w.false', Fn, Label
jump+Jump, +From, +Ctx//

the code for the jump ending block From.

243jumpgotoTo, From, Ctx ⟶
edgeFrom, To, Ctx
244jumpbranchV, IfTrue, IfFalse, From, Ctx ⟶
false_labelCtx, From, FalseLabel
loadV, w, a, Ctx
instest, l, regeax, regeax, insjz, none, symFalseLabel
edgeFrom, IfTrue, Ctx
labelFalseLabel
edgeFrom, IfFalse, Ctx
252jumpretV, _, Ctx ⟶
Ctx = f_, Class, _, _
loadV, Class, a, Ctx
→
to_xmmClass, a, xmm0
else
[]
257jumpret, _, _ ⟶
258jumpunreachable, _, _ ⟶
insud2, none, []
260epilogue ⟶
insleave, none, [], insret, none, []
edge+From, +To, +Ctx//

the copies for the phis of To on the edge From -> To, then the jump. All sources are read before any destination is written.

265edgeFrom, To, Ctx ⟶
Ctx = f_, _, _, Blocks
memberchkblockTo, Phis, _, _, Blocks
block_labelCtx, To, ToLabel
phi_readsPhis, From, Ctx
phi_writesPhis, Ctx
insjmp, none, symToLabel
phi_reads+Phis, +From, +Ctx//

copy each phi's incoming value from From into its shadow slot.

275phi_reads[], _, _ ⟶
[]
276phi_readsphitmpD, Class, Incoming|Phis, From, Ctx ⟶
memberchkFrom−V, Incoming
loadV, Class, a, Ctx
store_regrax, shadowD, Ctx
phi_readsPhis, From, Ctx
phi_writes+Phis, +Ctx//

copy each phi's shadow slot into its destination's slot.

284phi_writes[], _ ⟶
[]
285phi_writesphitmpD, _, _|Phis, Ctx ⟶
load_slotshadowD, l, rax, Ctx
store_regrax, tmpD, Ctx
phi_writesPhis, Ctx

Instructions

292insts[], _ ⟶
[]
293instsInst|Insts, Ctx ⟶
instInst, Ctx
instsInsts, Ctx
inst+Inst, +Ctx//

the code for one instruction.

298instletD, Class, Op, Args, Ctx ⟶
compute_opOp, Class, Args, Ctx
store_regrax, D, Ctx
301instfence, _ ⟶
insmfence, none, []

In plain mode C's semantics are the chunks' (docs/vector-access.md).

303instaccess_, _, _, _, _ ⟶
[]
304instasm_fixedM, Ins, Outs, Ctx ⟶
asm_fixedM
309inststoreWidth, V, Addr, Ctx ⟶
width_classWidth, Class
loadV, Class, c, Ctx
loadAddr, l, a, Ctx
store_insnWidth, c, Store
Store
315instcallRes, globalF, Args, Ctx ⟶
memberargFK, _, Args
!
findallK, memberargK, _, Args, Ks
bank_locationsKs, di, si, d, c, r8, r9, Ls, NS, NX
Pop is NS + 1//2 × 16
Pop > 0
→
inssub, q, immPop, regrsp
else
[]
plain_stack_argsArgs, Ls, Ctx
plain_register_argsArgs, Ls, Ctx
insmov, b, immNX, regal, inscall, none, symF
Pop > 0
→
insadd, q, immPop, regrsp
else
[]
call_resultRes, Ctx
325instcallRes, globalF, Args, Ctx ⟶
lengthArgs, N
N ≤ 6
→
true
else
Ctx = fFn, _, _, _
formatatomMsg, 'in ~w: call with more than six arguments', Fn
thrownanofil_errorMsg
call_argsArgs, 0, Ctx
inscall, none, symF
call_resultRes, Ctx
width_class?Width, ?Class

a store of Width reads its value at Class.

width_class
338bw
339hw
340ww
341ll
342ss
343dd
call_result+Res, +Ctx//

the store of a call's result Res from %rax; nothing for void, and the plain backend has no tuple results.

347call_resultvoid, _ ⟶
[]
348call_resultresultD, K, Ctx ⟶
!
from_xmmK, xmm0, a
store_regrax, D, Ctx
350call_resultresultD, _, Ctx ⟶
store_regrax, D, Ctx
351call_resulttuple_, _ ⟶
[]
call_args+Args, +I, +Ctx//

load each argument, from the I-th on, into its argument register.

355call_args[], _, _ ⟶
[]
356call_argsargClass, V|Args, I, Ctx ⟶
arg_regI, Reg
I1 is I + 1
loadV, Class, Reg, Ctx
call_argsArgs, I1, Ctx
store_insn+Width, +Reg, -Inssemidet

Ins stores the Width-sized part of register Reg to (%rax).

365store_insnW, R, insmov, S, regRN, mem0, rax ←
reg_nameR, W, RN
compute_op+Selection, +Op, +Class, +Args, +Ctx//semidet
As compute_op//4, but under Selection local (filc_amd64.pl's
immediate selection outside setjmp functions) an integer constant
right operand of add, sub, mul, and, or, xor or a shift is encoded
as an immediate. Selection stack is exactly compute_op//4.

Local Fil-C selection can encode an integer RHS directly. The reference selector below stays unchanged. Only RCX's dead scratch result differs; RAX and the operation's defined flags agree, even for a zero shift.

379compute_oplocal, Op, K, X, constN, Ctx ⟶
integerN
immediate_instructionOp, K, N, Insn
!
loadX, K, a, Ctx
Insn
382compute_op_, Op, K, Args, Ctx ⟶
compute_opOp, K, Args, Ctx
384immediate_instructionOp, K, N, insInsn, S, immV, regA ←
Op ≡ mul
→
Insn = imul
else
memberchkOp, add, sub, and, or, xor
Insn = Op
reg_namea, K, A
388immediate_instructionOp, K, N, insOp, S, immV, regA ←
memberchkOp, shl, shr, sar
K ≡ w
→
Mask = 31
else
K ≡ l
Mask = 63
V is N /\ Mask
reg_namea, K, A
integer_immediate+Class, +N, -Vsemidet
V is the signed imm32 spelling of the Class-wide value N. Always
succeeds for w; for l fails unless N, read as 64 bits, is a
sign-extended imm32.

GNU as accepts the signed spelling of every imm32 bit pattern. Qword operations sign-extend that encoding: e.g. +0xffffffff is NOT -1.

401integer_immediatew, N, V ←
V is N + 2147483648 mod 4294967296−2147483648
402integer_immediatel, N, V ←
U is N mod 18446744073709551616
U ≥ 9223372036854775808
→
V is U−18446744073709551616
else
V = U
V ≥ -2147483648
V ≤ 2147483647
compute_op+Op, +Class, +Args, +Ctx//

The code that computes Op at Class on Args into %rax, zero-extended for class w. Ctx is the f/4 context; filc_amd64 also calls this for its integer instructions.

413compute_opOp, K, Args, Ctx ⟶
!
float_opOp, K, Args, Ctx
416compute_opcopy, K, X, Ctx ⟶
loadX, K, a, Ctx
417compute_opneg, K, X, Ctx ⟶
loadX, K, a, Ctx
insneg, S, regA
422compute_opadd, K, Args, Ctx ⟶
aluadd, K, Args, Ctx
423compute_opsub, K, Args, Ctx ⟶
alusub, K, Args, Ctx
424compute_opmul, K, Args, Ctx ⟶
aluimul, K, Args, Ctx
425compute_opand, K, Args, Ctx ⟶
aluand, K, Args, Ctx
426compute_opor, K, Args, Ctx ⟶
aluor, K, Args, Ctx
427compute_opxor, K, Args, Ctx ⟶
aluxor, K, Args, Ctx

x86 masks the count in %cl to the operand width, as the IR specifies.

429compute_opshl, K, Args, Ctx ⟶
shiftshl, K, Args, Ctx
430compute_opshr, K, Args, Ctx ⟶
shiftshr, K, Args, Ctx
431compute_opsar, K, Args, Ctx ⟶
shiftsar, K, Args, Ctx
432compute_opdiv, K, Args, Ctx ⟶
divisionsigned, quot, K, Args, Ctx
433compute_oprem, K, Args, Ctx ⟶
divisionsigned, rem, K, Args, Ctx
434compute_opudiv, K, Args, Ctx ⟶
divisionunsigned, quot, K, Args, Ctx
435compute_opurem, K, Args, Ctx ⟶
divisionunsigned, rem, K, Args, Ctx
436compute_opcmpCond, CmpClass, _, X, Y, Ctx ⟶
loadX, CmpClass, a, Ctx
loadY, CmpClass, c, Ctx
att_suffixCmpClass, S
reg_namea, CmpClass, A
reg_namec, CmpClass, C
cond_codeCond, CC
atom_concatset, CC, Set
inscmp, S, regC, regA, insSet, none, regal, insmovzb, l, regal, regeax
447compute_opextType, K, X, Ctx ⟶
loadX, w, a, Ctx
ext_insnType, K, Insn
Insn
451compute_oploadType, K, Addr, Ctx ⟶
loadAddr, l, a, Ctx
load_insnType, K, Insn
Insn
455compute_opallocAlign, _, Size, Ctx ⟶
loadSize, l, a, Ctx
insadd, q, imm15, regrax, insand, q, imm-16, regrax, inssub, q, regrax, regrsp
Align > 16
→
Mask is −Align
insand, q, immMask, regrsp
else
[]
insmov, q, regrsp, regrax
alu+Insn, +K, +Args, +Ctx//

the x86 two-operand instruction Insn on Args.

468aluInsn, K, X, Y, Ctx ⟶
loadX, K, a, Ctx
loadY, K, c, Ctx
reg_namea, K, A
reg_namec, K, C
insInsn, S, regC, regA
shift+Insn, +K, +Args, +Ctx//

the x86 shift Insn of the first of Args by the second, a w count in %cl.

478shiftInsn, K, X, Y, Ctx ⟶
loadX, K, a, Ctx
loadY, w, c, Ctx
insInsn, S, regcl, regA
division+Signedness, +Result, +K, +Args, +Ctx//

the division of that Signedness of the first of Args by the second, leaving in %rax its Result, the quotient (quot) or remainder (rem). It is total, as Fil-C's is since deluge (KillUB; docs/ub.md): the operands are first rewritten, without branches, so that the divide cannot fault.

490divisionSignedness, Result, K, X, Y, Ctx ⟶
loadX, K, a, Ctx
loadY, K, c, Ctx
reg_namea, K, A
reg_namec, K, C
reg_named, K, D
zero_divisorS, A, C, D
minus_one_divisorSignedness, S, A, C, D
divideSignedness, K, S, C
division_resultResult, K, S
zero_divisor+S, +A, +C, +D//

a zero divisor C becomes 1 and the dividend A 0, so quotient and remainder are 0. With E = (C == 0), C += E and A &= E - 1.

505zero_divisorS, A, C, D ⟶
instest, S, regC, regC, inssete, none, regdl, insmovzb, l, regdl, regedx, insadd, S, regD, regC, inssub, S, imm1, regD, insand, S, regD, regA
minus_one_divisor+Signedness, +S, +A, +C, +D//

a signed divisor of -1 becomes 1 and the dividend its negation, which wraps MIN to MIN where idiv would fault; the remainder is 0 either way. With E = (C == -1), C += 2E and A = (A ^ -E) + E.

517minus_one_divisorunsigned, _, _, _, _ ⟶
[]
518minus_one_divisorsigned, S, A, C, D ⟶
inscmp, S, imm-1, regC, inssete, none, regdl, insmovzb, l, regdl, regedx, insadd, S, regD, regC, insadd, S, regD, regC, insneg, S, regD, insxor, S, regD, regA, inssub, S, regD, regA
divide+Signedness, +K, +S, +C//

%rdx:%rax (or %edx:%eax) divided by register C, extended from %rax by Signedness.

530dividesigned, K, S, C ⟶
sign_extensionK, SignExtend
insSignExtend, none, [], insidiv, S, regC
533divideunsigned, _, S, C ⟶
insxor, l, regedx, regedx, insdiv, S, regC
sign_extension?K, ?Insn

Insn sign-extends %eax or %rax at K into %edx or %rdx.

538sign_extensionw, cltd
539sign_extensionl, cqto
division_result+Result, +K, +S//

the move of the remainder from %rdx to %rax; the quotient is already there.

543division_resultquot, _, _ ⟶
[]
544division_resultrem, K, S ⟶
reg_named, K, D
reg_namea, K, A
insmov, S, regD, regA
cond_code?Cond, ?CCnondet

CC is the x86 condition code suffix for the integer comparison Cond (eq, ne, slt, ..., uge).

cond_code
553eqe
553nene
554sltl
554slele
555sgtg
555sgege
556ultb
556ulebe
557ugta
557ugeae
ext_insn+Type, +Class, -Inssemidet

Ins extends the low part of %rax that Type (sb, ub, sh, uh, sw, uw) names into %rax at Class.

563ext_insnType, K, insOp, S, regFrom, regTo ←
ext_opType, K, Op, S, To
ext_fromType, From
ext_op?Type, ?Class, ?Op, ?Suffix, ?To

Op with Suffix is the move that extends Type to Class, and To its destination register.

ext_op
569sbwmovsbleax
569sblmovsbqrax
570ub_movzbleax
571shwmovswleax
571shlmovswqrax
572uh_movzwleax
573swlmovslqrax
574uwlmovleax
ext_from?Type, ?From

From is the part of %rax that Type extends.

ext_from
577sbal
577ubal
577shax
577uhax
578sweax
578uweax
load_insn+Type, +Class, -Inssemidet

Ins loads a Type value from (%rax) into %rax, extended to Class.

583load_insnType, K, insOp, S, mem0, rax, regTo ←
load_opType, K, Op, S, To
load_op+Type, +Class, -Op, -Suffix, -To

as ext_op/5, for a load from memory; a uw load, or an sw load at w, needs only a 32-bit move.

588load_opl, l, mov, q, rax
589load_ops, s, mov, l, eax
590load_opd, d, mov, q, rax
591load_opsw, K, Op, S, To ←
sw_load_opK, Op, S, To
592load_opuw, _, mov, l, eax
593load_opsb, K, Op, S, To ←
ext_opsb, K, Op, S, To
594load_opub, K, Op, S, To ←
ext_opub, K, Op, S, To
595load_opsh, K, Op, S, To ←
ext_opsh, K, Op, S, To
596load_opuh, K, Op, S, To ←
ext_opuh, K, Op, S, To
598sw_load_opw, mov, l, eax
599sw_load_opl, Op, S, To ←
ext_opsw, l, Op, S, To
asm_fixed_inputs+Ins, +Frame//

The moves of a fixed-register asm's w inputs (docs/asm-fixed.md) into the registers it reads, eax and ecx; each move writes only its own.

605asm_fixed_inputs[], _ ⟶
[]
606asm_fixed_inputsR−V|Ins, F ⟶
loadV, w, R, F
asm_fixed_out_reg?R, ?Regnondet

Reg holds the asm_fixed result R after the asm line; cpuid's b comes out in rdi (asm.pl), its upper half zeroed.

asm_fixed_out_reg
612arax
612brdi
613crcx
613drdx
615asm_fixed_outputs[], _ ⟶
[]
616asm_fixed_outputsR−T|Outs, F ⟶

Operands

load+Value, +Class, +Reg, +Ctx//

The code that puts Value, read at Class, in register Reg.

624loadtmpN, K, R, Ctx ⟶
reg_nameR, K, RN
load_slottmpN, K, RN, Ctx
627loadconstN, K, R, _ ⟶
628loadglobalG, _, R, _ ⟶
reg_nameR, l, RN
inslea, q, ripG, regRN
load_constant+Class, +N, +Reg//

the move of the constant N, reduced modulo Class, into Reg.

634load_constantw, N, R ⟶
reg_nameR, w, RN
V is N mod 4294967296
insmov, l, immV, regRN
638load_constantl, N, R ⟶
reg_nameR, l, RN
U is N mod 18446744073709551616
U ≥ 9223372036854775808
→
V is U−18446744073709551616
else
V = U
insmovabs, q, immV, regRN
645load_constants, N, R ⟶
646load_constantd, N, R ⟶
load_slot+Key, +Class, +RegName, +Ctx//semidet

The move that reads Key's frame slot at Class into RegName. Fails if Key has no slot in Ctx.

652load_slotKey, K, RN, f_, _, Slots, _ ⟶
get_assocKey, Slots, Offset
Disp is −Offset
insmov, S, memDisp, rbp, regRN
store_reg+RegName, +Key, +Ctx//semidet

The move that writes all 8 bytes of register RegName to Key's frame slot. Fails if Key has no slot in Ctx.

662store_regRN, Key, f_, _, Slots, _ ⟶
get_assocKey, Slots, Offset
Disp is −Offset
insmov, q, regRN, memDisp, rbp

Data

data_items+Items//

the directives for the data items.

670data_items[] ⟶
[]
671data_itemsItem|Items ⟶
675data_itemzeroN ⟶
zeroN
676data_itemitemW, Value ⟶
item_valueValue, W
item_value+Value, +W//

the directive for a data item of width W and value Value.

680item_valuestrCs, b ⟶
asciiCs
681item_valuesymS, Off, l ⟶
quadS + Off
682item_valueconstN, W ⟶
data_directiveW, U, Directive, Bits
U is N mod 1 << Bits
Directive
data_directive?Width, ?Value, ?Directive, ?Bits

Directive emits the unsigned Value as one Width item of Bits bits.

data_directive
689bUbyteU8
690hUshortU16
691wUintU32
692lUquadU64
693sUintU32
694dUquadU64

SSE2. Private slots and compute_op's result hold bits, not host floats. Only ABI boundaries use XMM registers as the persistent value carrier.

float_class?Classnondet

Class is a float class, s or d.

701float_classs
701float_classd
702float_bitss, w
702float_bitsd, l
703float_suffixs, ss
703float_suffixd, sd
704xmm_regI, R ←
formatatomR, 'xmm~d', I
bank_locations+Classes, +Regs, -Locs, -NStack, -NXmmdet
Locs places each word of class Classes the SysV way: s and d in
xreg(xmm0..7), the others in reg(R) from Regs, and the rest in
stack(I), counted in order over both banks. NStack and NXmm are the
stack words and XMM registers used.

Shared SysV bank allocation for a flattened scalar/flight-word list. Each exhausted bank spills independently; stack positions retain order.

715bank_locationsKs, Regs, Ls, NS, NX ←
bank_locationsKs, Regs, 0, 0, Ls, NS, NX
717bank_locations[], _, X, S, [], S, X
718bank_locationsK|Ks, Rs, X0, S0, L|Ls, S, X ←
→
X0 < 8
→
xmm_regX0, R
L = xregR
X1 is X0 + 1
S1 = S0
else
L = stackS0
S1 is S0 + 1
X1 = X0
Rs1 = Rs
else
X1 = X0
Rs = R|Rs1
→
L = regR
S1 = S0
else
Rs1 = []
L = stackS0
S1 is S0 + 1
bank_locationsKs, Rs1, X1, S1, Ls, S, X
728plain_stack_args[], [], _ ⟶
[]
729plain_stack_argsargK, V|As, L|Ls, Ctx ⟶
L = stackI
Off is 8 × I
→
loadV, K, a, Ctx
insmov, q, regrax, memOff, rsp
else
[]
plain_stack_argsAs, Ls, Ctx
733plain_register_args[], [], _ ⟶
[]
734plain_register_argsargK, V|As, L|Ls, Ctx ⟶
736plain_register_argstack_, _, _, _ ⟶
[]
737plain_register_argregR, K, V, Ctx ⟶
loadV, K, R, Ctx
738plain_register_argxregX, K, V, Ctx ⟶
float_loadV, K, X, Ctx
to_xmm+Class, +R, +Xmm//det

Moves the float bits of Class s or d from scratch register R (a, c, ...) to Xmm.

744to_xmmK, R, X ⟶
insOp, none, regRN, regX
from_xmm+Class, +Xmm, +R//det

Moves the float bits of Class s or d from Xmm to scratch register R.

750from_xmmK, X, R ⟶
insOp, none, regX, regRN
753xmm_moves, movd
753xmm_moved, movq
755float_computeOp, K ←
memberchkOp, add, sub, mul, div, neg
756float_computecmp_, K, _ ←
757float_computeexts, d
758float_computetruncd, s
759float_computesqrt, K ←
760float_computecast, _
761float_computeOp, _ ←
memberchkOp, stosi, stoui, dtosi, dtoui, swtof, uwtof, sltof, ultof
763float_loadV, K, X, Ctx ⟶
loadV, K, a, Ctx
to_xmmK, a, X
765float_opOp, K, X, Y, Ctx ⟶
memberchkOp, add, sub, mul, div
float_loadX, K, xmm0, Ctx
float_loadY, K, xmm1, Ctx
insOp, S, regxmm1, regxmm0
from_xmmK, xmm0, a
769float_opneg, K, X, Ctx ⟶
loadX, K, a, Ctx
reg_namea, B, A
K ≡ s
→
Mask = 2147483648
else
Mask = 9223372036854775808
loadconstMask, B, c, Ctx
reg_namec, B, C
insxor, S, regC, regA
776float_opcmpCond, K, w, X, Y, Ctx ⟶
float_loadX, K, xmm0, Ctx
float_loadY, K, xmm1, Ctx
insOp, none, regxmm1, regxmm0
insmovzb, l, regal, regeax
782float_opexts, d, X, Ctx ⟶
float_loadX, s, xmm0, Ctx
inscvtss2sd, none, regxmm0, regxmm0
from_xmmd, xmm0, a
785float_optruncd, s, X, Ctx ⟶
float_loadX, d, xmm0, Ctx
inscvtsd2ss, none, regxmm0, regxmm0
from_xmms, xmm0, a
788float_opsqrt, K, X, Ctx ⟶
atom_concatsqrt, S, I
float_loadX, K, xmm0, Ctx
insI, none, regxmm0, regxmm0
from_xmmK, xmm0, a
792float_opcast, K, X, Ctx ⟶
loadX, K, a, Ctx
793float_opOp, K, X, Ctx ⟶
float_to_intOp, F, Sign
float_loadX, F, xmm0, Ctx
float_integerSign, F, K, Ctx
796float_opOp, F, X, Ctx ⟶
int_to_floatOp, K, Sign
loadX, K, a, Ctx
integer_floatSign, K, F
from_xmmF, xmm0, a
800float_compares, ucomiss
800float_compared, ucomisd
float_condition+Cond//semidet

Sets %al to the float comparison Cond (eq, ne, lt, le, gt, ge, o, uo) after ucomiss or ucomisd compared the operands; eq, ne, lt and le also consult the parity flag, so an unordered (NaN) compare gives false, or true for ne. Clobbers %cl.

807float_conditioneq ⟶
inssete, none, regal, inssetnp, none, regcl, insand, b, regcl, regal
808float_conditionne ⟶
inssetne, none, regal, inssetp, none, regcl, insor, b, regcl, regal
809float_conditionlt ⟶
inssetb, none, regal, inssetnp, none, regcl, insand, b, regcl, regal
810float_conditionle ⟶
inssetbe, none, regal, inssetnp, none, regcl, insand, b, regcl, regal
811float_conditiongt ⟶
insseta, none, regal
812float_conditionge ⟶
inssetae, none, regal
813float_conditiono ⟶
inssetnp, none, regal
814float_conditionuo ⟶
inssetp, none, regal
float_to_int
816stosissigned
816stouisunsigned
817dtosidsigned
817dtouidunsigned
int_to_float
818swtofwsigned
818uwtofwunsigned
819sltoflsigned
819ultoflunsigned
821truncate_floatF, K ⟶
atom_concatcvtt, S, P
atom_concatP, '2si', Op
reg_namea, K, R
insOp, W, regxmm0, regR
825float_integersigned, F, K, _ ⟶
826float_integerunsigned, F, w, _ ⟶
insmov, l, regeax, regeax

SSE2 has no unsigned qword conversion. Subtract 2^63 in the high half, convert the now signed-representable value, then restore bit 63. Numeric labels are local to each sequence, including repeated calls.

831float_integerunsigned, F, l, Ctx ⟶
F ≡ s
→
Bits = 1593835520
else
Bits = 4890909195324358656
float_loadconstBits, F, xmm1, Ctx
insCmp, none, regxmm1, regxmm0, insjb, none, sym'1f', inssub, S, regxmm1, regxmm0
insbtc, q, imm63, regrax, insjmp, none, sym'2f', label'1'
label'2'
841convert_integerK, F ⟶
atom_concatcvtsi2, S, Op
reg_namea, K, R
insOp, W, regR, regxmm0
844integer_floatsigned, K, F ⟶
845integer_floatunsigned, w, F ⟶

Preserve a sticky low bit before halving; converting then doubling is correctly rounded even above 2^63, unlike subtract/convert/add.

848integer_floatunsigned, l, F ⟶
instest, q, regrax, regrax, insjns, none, sym'1f', insmov, q, regrax, regrcx, insand, l, imm1, regecx, insshr, q, imm1, regrax, insor, q, regrcx, regrax
insadd, S, regxmm0, regxmm0, insjmp, none, sym'2f', label'1'
label'2'