w6a: MOVB REX for SI/DI/BP/SP; LEAQ/CALL shapes err; honest diag lengths

Three w6a defects, one component. MOVB with an SP/BP/SI/DI register
operand encoded AH/CH/DH/BH -- emit_rex suppressed the bare 0x40
that selects SPL/BPL/SIL/DIL (the comment claimed "we always emit
REX"; encode probe: `MOVB SI,(AX)` -> `88 30` = %dh). emit_rex8
forces the byte for low-byte codes 4-7; no current w6c output emits
those shapes, so all existing objects are unchanged. LEAQ and CALL
unsupported operand shapes fell through with zero bytes and no
errs++ (every MOV arm reports); both now err. Every hardcoded
os.write diagnostic length in the wwstage was one byte short
(truncating the newline/quote); all converted to the .len idiom so
the length cannot drift again.
This commit is contained in:
2026-08-09 01:07:06 +09:00
parent f06c95e66b
commit 06c40a8bef
3 changed files with 91 additions and 28 deletions

View File

@@ -147,6 +147,19 @@ emit_rex(Asm *a, int regbit, int rmbit, int w)
if (b != 0x40 || w) a_emit_byte(a, b);
}
/* 8-bit register operands: SPL/BPL/SIL/DIL exist only WITH a REX
* prefix — bare, codes 4-7 select AH/CH/DH/BH. Force the 0x40 byte
* when the named r8 is one of them. */
static void
emit_rex8(Asm *a, int regbit, int rmbit, int r8)
{
u8 b = 0x40;
if (regbit) b |= 0x04;
if (rmbit) b |= 0x01;
if (b != 0x40 || (!rhi(r8) && rcode(r8) >= 4))
a_emit_byte(a, b);
}
/* Special-cases SP (needs SIB) and BP (forces disp). */
static void
emit_modrm_mem(Asm *a, int reg_field, int base, i64 disp)
@@ -521,17 +534,20 @@ a_encode(Asm *a)
}
break;
case A_MOVB:
/* MOV r/m8, r8 — 88 /r. No REX.W. We always emit REX
* to allow access to SIL/DIL/BPL/SPL. */
/* MOV r/m8, r8 — 88 /r. No REX.W. emit_rex8 forces
* the bare REX so SI/DI/BP/SP name SIL/DIL/BPL/SPL,
* never AH/CH/DH/BH. */
if (p->from.type >= D_AX && p->from.type <= D_R15
&& p->to.type == D_INDIR) {
emit_rex(a, rhi(p->from.type), rhi(p->to.reg), 0);
emit_rex8(a, rhi(p->from.type), rhi(p->to.reg),
p->from.type);
a_emit_byte(a, 0x88);
emit_modrm_mem(a, rcode(p->from.type),
p->to.reg, p->to.offset);
} else if (p->from.type == D_INDIR
&& p->to.type >= D_AX && p->to.type <= D_R15) {
emit_rex(a, rhi(p->to.type), rhi(p->from.reg), 0);
emit_rex8(a, rhi(p->to.type), rhi(p->from.reg),
p->to.type);
a_emit_byte(a, 0x8A); /* MOV r8, r/m8 */
emit_modrm_mem(a, rcode(p->to.type),
p->from.reg, p->from.offset);
@@ -772,6 +788,9 @@ a_encode(Asm *a)
Asym *s = a_intern(a, p->from.sym);
/* R_X86_64_PC32 (2) with addend -4 */
a_addreloc(a, reloff, 2, s, -4);
} else {
fprintf(stderr, "w6a: line %d: unsupported LEAQ shape\n", p->line);
a->errs++;
}
break;
case A_CALL:
@@ -790,6 +809,9 @@ a_encode(Asm *a)
if (rhi(p->to.type)) a_emit_byte(a, 0x41);
a_emit_byte(a, 0xFF);
a_emit_byte(a, modrm(3, 2, rcode(p->to.type)));
} else {
fprintf(stderr, "w6a: line %d: unsupported CALL shape\n", p->line);
a->errs++;
}
break;
case A_JMP: