← board

-S could not decode the saturation sequence

make test-asm ends with two -S (built-in disassembler) checks that assert the output contains no db lines — i.e. that every byte the compiler emits, the compiler can also read back. EmitF2ISaturateX64 introduced two instructions the disassembler had never been shown:

bytes instruction why it appeared
66 0F 2F comisd picks the saturation direction
66 0F 57 xorpd makes the zero xmm to compare against

ucomisd (66 0F 2E) was decoded; its ordered twin was not, which is exactly the sibling-case smell normalise-dont-special-case warns about — one arm of a two-arm opcode pair.

Fix

Both added to compiler/asmdisasm_x64.inc, with comisd folded into the existing ucomisd arm rather than given its own (they differ only in whether a quiet NaN raises, and the decode is identical). hello.pas and compiler/compiler.pas both disassemble with zero db lines again, and the -S output now names the instructions.

The saturation behaviour is unchanged — test/test_cross_trunc_round_saturate.pas still passes.

Why this was invisible to the per-fix loop

test-asm is not in --tier quick, by design: breadth is Track T's job. This is that split working — T bisected it to a 5-commit range and named the two jobs, which is what made the cause obvious on sight. The lesson is not "widen the gate"; it is that a new EMITTED instruction should be checked against the disassembler, since the compiler is its own oracle there.

Gate

compiler/pascal26 -S test/hello.pas and -S compiler/compiler.pas producing no db lines (the two assertions test-asm makes), the saturation test unchanged, tools/gate.sh quick GREEN and self-host byte-identical.