Add mem_rip_label(label_id) so a RIP-relative memory operand can
reference a label: the encoder writes a placeholder disp32 and emits a
REL32 relocation (addend 0) at the field's byte offset, mirroring the
existing .RELATIVE jump/call path. This expresses lea reg, [rip + <label>]
(position-independent data addressing).
Add op_imm_label(label_id) for movabs reg, <label>: the imm stays kind
.IMMEDIATE (form matching unchanged) but is flagged so the encoder emits
an ABS64 relocation for the imm64 instead of a literal; imm_matches_inline
forces the full IMM64 form so a small id can't collapse to mov r64, imm32.
Both labeled forms bypass the contextless recipe fast-path (which cannot
append relocations) and fall back to the interpreter. Flags reuse spare
bits in Memory (disp_is_label) and Operand_Flags (imm_is_label) -- no
struct growth. Section 11 tests cover resolved/unresolved disp, the ABS64
movabs, and an end-to-end executed lea+load.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fix the operand-size/prefix bug class reported by the sigil backend, then close
every failure a whole-ISA encode->decode->re-encode audit surfaced -- 247 -> 0
over the 1165 round-trippable legacy forms.
Encode/decode:
- MOVSX/MOVZX take 66h from the destination, not the source (CRC32, whose r/m
source drives 66h, is unchanged)
- operand-less 16-bit forms (CBW/CWD, MOVSW/CMPSW/SCASW/LODSW/STOSW,
IRET/PUSHF/POPF) emit 66h via a new opsize_16 flag
- CRC32/MOVBE and POPCNT/LZCNT/TZCNT: mandatory F2/F3 beats 66h on decode
- mandatory-prefix REP double-encode: F2/F3 no longer re-emitted as REP
- fixed-ModR/M decode: x87 (FNOP/FCHS/FADD ST(i)/...) and the 0F 01/AE/C7/1E
system-op groups (VMCALL/RDTSCP/LFENCE/ENDBR/...); gen.odin preserves the
fixed ModR/M byte, the decoder matches and consumes it
- implicit-operand round-trip (accumulator short forms, x87 ST(i), xchg) via a
positional matcher + emit path; accumulators stay implicit so hand-built
`add eax, imm` keeps the general form (typed-builder consistency)
- MOV to/from CR/DR/segment (were decoding as GPRs); reg-vs-mem disambiguation
(RDRAND vs VMPTRLD, MOVLHPS vs MOVHPS); x87 M80 operand size; PINSRW; NOP vs
XCHG at 0x90
- LOCK standalone; ENTER two immediates; PUSH/POP FS/GS (segment fixed by the
opcode + non-cacheable); BOUND marked 32-bit-only; RDRAND/RDSEED r16
(66 is operand-size, resolved against the mandatory-66 VMCLEAR by ModR/M)
Tools/tests:
- revive dump_verify_input + verify_against_llvm (stale Result API; drop the
aliases that were masking the fixed bugs); align verify_tables with the
preserved fixed-ModR/M ext
- new tests/test_narrow_widths.odin: byte-exact vs llvm-mc + round-trip guards
- fold the bit-rotted tests32 package into the main suite (i386 Mode._32 paths)
248 tests pass; verify_tables passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01115ktMoX5qGAfp1VemBzCP