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.

67module_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.

77item_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.

91itemfuncName, 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
104itemdataName, 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
113exportexportdata
114locallocaldata
115roLinkageLinkagerodata
global+Linkage, +Name//

the .globl directive an export needs.

118globalexport, Name ⟶
globlName
119globallocal, _ ⟶
[]

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.

124frameParams, 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).

146defineslettmpN, _, _, _, N
147definescallresulttmpN, _, _, _, N

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

150assign_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.

157plain_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
165plain_incoming[], [], _ ⟶
[]
166plain_incomingparamK, T|Ps, L|Ls, Ctx ⟶
169plain_incoming_valueregR, _ ⟶
reg_nameR, l, RN
insmov, q, regRN, regrax
171plain_incoming_valuexregX, K ⟶
from_xmmK, X, a
172plain_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.

177store_params[], _, _ ⟶
[]
178store_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
1910di
1911si
1912d
1923c
1924r8
1925r9
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.

198reg_namea, b, al
198reg_namea, h, ax
199reg_namea, w, eax
199reg_namea, l, rax
200reg_namec, b, cl
200reg_namec, h, cx
201reg_namec, w, ecx
201reg_namec, l, rcx
202reg_named, b, dl
202reg_named, h, dx
203reg_named, w, edx
203reg_named, l, rdx
204reg_namesi, w, esi
204reg_namesi, l, rsi
205reg_namedi, w, edi
205reg_namedi, l, rdi
206reg_namer8, w, r8d
206reg_namer8, l, r8
207reg_namer9, w, r9d
207reg_namer9, l, r9
208reg_nameR, s, RN ←
reg_nameR, w, RN
209reg_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
214bb
214hw
214wl
214lq
215sl
215dq

Blocks and jumps

blocks+Blocks, +Ctx//

each block's label, instructions and jump.

220blocks[], _ ⟶
[]
221blocksblockLabel, _, 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.

230block_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.

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

the code for the jump ending block From.

239jumpgotoTo, From, Ctx ⟶
edgeFrom, To, Ctx
240jumpbranchV, 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
248jumpretV, _, Ctx ⟶
Ctx = f_, Class, _, _
loadV, Class, a, Ctx
→
to_xmmClass, a, xmm0
else
[]
253jumpret, _, _ ⟶
254jumpunreachable, _, _ ⟶
insud2, none, []
256epilogue ⟶
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.

261edgeFrom, 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.

271phi_reads[], _, _ ⟶
[]
272phi_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.

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

Instructions

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

the code for one instruction.

294instletD, Class, Op, Args, Ctx ⟶
compute_opOp, Class, Args, Ctx
store_regrax, D, Ctx
297inststoreWidth, V, Addr, Ctx ⟶
width_classWidth, Class
loadV, Class, c, Ctx
loadAddr, l, a, Ctx
store_insnWidth, c, Store
Store
303instcallRes, 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
313instcallRes, 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
326bw
327hw
328ww
329ll
330ss
331dd
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.

335call_resultvoid, _ ⟶
[]
336call_resultresultD, K, Ctx ⟶
!
from_xmmK, xmm0, a
store_regrax, D, Ctx
338call_resultresultD, _, Ctx ⟶
store_regrax, D, Ctx
339call_resulttuple_, _ ⟶
[]
call_args+Args, +I, +Ctx//

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

343call_args[], _, _ ⟶
[]
344call_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).

353store_insnW, R, insmov, S, regRN, mem0, rax ←
reg_nameR, W, RN

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.

360compute_oplocal, Op, K, X, constN, Ctx ⟶
integerN
immediate_instructionOp, K, N, Insn
!
loadX, K, a, Ctx
Insn
363compute_op_, Op, K, Args, Ctx ⟶
compute_opOp, K, Args, Ctx
365immediate_instructionOp, K, N, insInsn, S, immV, regA ←
Op ≡ mul
→
Insn = imul
else
memberchkOp, add, sub, and, or, xor
Insn = Op
reg_namea, K, A
369immediate_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

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

376integer_immediatew, N, V ←
V is N + 2147483648 mod 4294967296−2147483648
377integer_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.

388compute_opOp, K, Args, Ctx ⟶
!
float_opOp, K, Args, Ctx
391compute_opcopy, K, X, Ctx ⟶
loadX, K, a, Ctx
392compute_opneg, K, X, Ctx ⟶
loadX, K, a, Ctx
insneg, S, regA
397compute_opadd, K, Args, Ctx ⟶
aluadd, K, Args, Ctx
398compute_opsub, K, Args, Ctx ⟶
alusub, K, Args, Ctx
399compute_opmul, K, Args, Ctx ⟶
aluimul, K, Args, Ctx
400compute_opand, K, Args, Ctx ⟶
aluand, K, Args, Ctx
401compute_opor, K, Args, Ctx ⟶
aluor, K, Args, Ctx
402compute_opxor, K, Args, Ctx ⟶
aluxor, K, Args, Ctx

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

404compute_opshl, K, Args, Ctx ⟶
shiftshl, K, Args, Ctx
405compute_opshr, K, Args, Ctx ⟶
shiftshr, K, Args, Ctx
406compute_opsar, K, Args, Ctx ⟶
shiftsar, K, Args, Ctx
407compute_opdiv, K, Args, Ctx ⟶
divisionsigned, quot, K, Args, Ctx
408compute_oprem, K, Args, Ctx ⟶
divisionsigned, rem, K, Args, Ctx
409compute_opudiv, K, Args, Ctx ⟶
divisionunsigned, quot, K, Args, Ctx
410compute_opurem, K, Args, Ctx ⟶
divisionunsigned, rem, K, Args, Ctx
411compute_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
422compute_opextType, K, X, Ctx ⟶
loadX, w, a, Ctx
ext_insnType, K, Insn
Insn
426compute_oploadType, K, Addr, Ctx ⟶
loadAddr, l, a, Ctx
load_insnType, K, Insn
Insn
430compute_opalloc_, _, Size, Ctx ⟶
loadSize, l, a, Ctx
insadd, q, imm15, regrax, insand, q, imm-16, regrax, inssub, q, regrax, regrsp, insmov, q, regrsp, regrax
alu+Insn, +K, +Args, +Ctx//

the x86 two-operand instruction Insn on Args.

439aluInsn, 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.

449shiftInsn, 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).

459divisionSignedness, Result, K, X, Y, Ctx ⟶
loadX, K, a, Ctx
loadY, K, c, Ctx
divideSignedness, K, S, C
division_resultResult, K, S
divide+Signedness, +K, +S, +C//

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

469dividesigned, K, S, C ⟶
sign_extensionK, SignExtend
insSignExtend, none, [], insidiv, S, regC
472divideunsigned, _, 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.

477sign_extensionw, cltd
478sign_extensionl, cqto
division_result+Result, +K, +S//

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

482division_resultquot, _, _ ⟶
[]
483division_resultrem, K, S ⟶
reg_named, K, D
reg_namea, K, A
insmov, S, regD, regA
cond_code?Cond, ?CC

CC is the x86 condition code suffix for the comparison Cond.

cond_code
490eqe
490nene
491sltl
491slele
492sgtg
492sgege
493ultb
493ulebe
494ugta
494ugeae
ext_insn+Type, +Class, -Ins

Ins extends the low part of %rax that Type names into %rax at Class.

498ext_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
504sbwmovsbleax
504sblmovsbqrax
505ub_movzbleax
506shwmovswleax
506shlmovswqrax
507uh_movzwleax
508swlmovslqrax
509uwlmovleax
ext_from?Type, ?From

From is the part of %rax that Type extends.

ext_from
512sbal
512ubal
512shax
512uhax
513sweax
513uweax
load_insn+Type, +Class, -Inssemidet

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

518load_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.

523load_opl, l, mov, q, rax
524load_ops, s, mov, l, eax
525load_opd, d, mov, q, rax
526load_opsw, K, Op, S, To ←
sw_load_opK, Op, S, To
527load_opuw, _, mov, l, eax
528load_opsb, K, Op, S, To ←
ext_opsb, K, Op, S, To
529load_opub, K, Op, S, To ←
ext_opub, K, Op, S, To
530load_opsh, K, Op, S, To ←
ext_opsh, K, Op, S, To
531load_opuh, K, Op, S, To ←
ext_opuh, K, Op, S, To
533sw_load_opw, mov, l, eax
534sw_load_opl, Op, S, To ←
ext_opsw, l, Op, S, To

Operands

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

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

541loadtmpN, K, R, Ctx ⟶
reg_nameR, K, RN
load_slottmpN, K, RN, Ctx
544loadconstN, K, R, _ ⟶
545loadglobalG, _, 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.

551load_constantw, N, R ⟶
reg_nameR, w, RN
V is N mod 4294967296
insmov, l, immV, regRN
555load_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
562load_constants, N, R ⟶
563load_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.

569load_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.

579store_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.

587data_items[] ⟶
[]
588data_itemsItem|Items ⟶
592data_itemzeroN ⟶
zeroN
593data_itemitemW, Value ⟶
item_valueValue, W
item_value+Value, +W//

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

597item_valuestrCs, b ⟶
asciiCs
598item_valuesymS, Off, l ⟶
quadS + Off
599item_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
606bUbyteU8
607hUshortU16
608wUintU32
609lUquadU64
610sUintU32
611dUquadU64

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

615float_classs
615float_classd
616float_bitss, w
616float_bitsd, l
617float_suffixs, ss
617float_suffixd, sd
618xmm_regI, R ←
formatatomR, 'xmm~d', I

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

622bank_locationsKs, Regs, Ls, NS, NX ←
bank_locationsKs, Regs, 0, 0, Ls, NS, NX
624bank_locations[], _, X, S, [], S, X
625bank_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
635plain_stack_args[], [], _ ⟶
[]
636plain_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
640plain_register_args[], [], _ ⟶
[]
641plain_register_argsargK, V|As, L|Ls, Ctx ⟶
643plain_register_argstack_, _, _, _ ⟶
[]
644plain_register_argregR, K, V, Ctx ⟶
loadV, K, R, Ctx
645plain_register_argxregX, K, V, Ctx ⟶
float_loadV, K, X, Ctx
647to_xmmK, R, X ⟶
insOp, none, regRN, regX
650from_xmmK, X, R ⟶
insOp, none, regX, regRN
653xmm_moves, movd
653xmm_moved, movq
655float_computeOp, K ←
memberchkOp, add, sub, mul, div, neg
656float_computecmp_, K, _ ←
657float_computeexts, d
658float_computetruncd, s
659float_computecast, _
660float_computeOp, _ ←
memberchkOp, stosi, stoui, dtosi, dtoui, swtof, uwtof, sltof, ultof
662float_loadV, K, X, Ctx ⟶
loadV, K, a, Ctx
to_xmmK, a, X
664float_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
668float_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
675float_opcmpCond, K, w, X, Y, Ctx ⟶
float_loadX, K, xmm0, Ctx
float_loadY, K, xmm1, Ctx
insOp, none, regxmm1, regxmm0
insmovzb, l, regal, regeax
681float_opexts, d, X, Ctx ⟶
float_loadX, s, xmm0, Ctx
inscvtss2sd, none, regxmm0, regxmm0
from_xmmd, xmm0, a
684float_optruncd, s, X, Ctx ⟶
float_loadX, d, xmm0, Ctx
inscvtsd2ss, none, regxmm0, regxmm0
from_xmms, xmm0, a
687float_opcast, K, X, Ctx ⟶
loadX, K, a, Ctx
688float_opOp, K, X, Ctx ⟶
float_to_intOp, F, Sign
float_loadX, F, xmm0, Ctx
float_integerSign, F, K, Ctx
691float_opOp, F, X, Ctx ⟶
int_to_floatOp, K, Sign
loadX, K, a, Ctx
integer_floatSign, K, F
from_xmmF, xmm0, a
695float_compares, ucomiss
695float_compared, ucomisd
696float_conditioneq ⟶
inssete, none, regal, inssetnp, none, regcl, insand, b, regcl, regal
697float_conditionne ⟶
inssetne, none, regal, inssetp, none, regcl, insor, b, regcl, regal
698float_conditionlt ⟶
inssetb, none, regal, inssetnp, none, regcl, insand, b, regcl, regal
699float_conditionle ⟶
inssetbe, none, regal, inssetnp, none, regcl, insand, b, regcl, regal
700float_conditiongt ⟶
insseta, none, regal
701float_conditionge ⟶
inssetae, none, regal
702float_conditiono ⟶
inssetnp, none, regal
703float_conditionuo ⟶
inssetp, none, regal
float_to_int
705stosissigned
705stouisunsigned
706dtosidsigned
706dtouidunsigned
int_to_float
707swtofwsigned
707uwtofwunsigned
708sltoflsigned
708ultoflunsigned
710truncate_floatF, K ⟶
atom_concatcvtt, S, P
atom_concatP, '2si', Op
reg_namea, K, R
insOp, W, regxmm0, regR
714float_integersigned, F, K, _ ⟶
715float_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.

720float_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'
730convert_integerK, F ⟶
atom_concatcvtsi2, S, Op
reg_namea, K, R
insOp, W, regR, regxmm0
733integer_floatsigned, K, F ⟶
734integer_floatunsigned, w, F ⟶

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

737integer_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'