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:

View File

@@ -105,6 +105,17 @@ fn emitrex(a: *asm_, regbit: i32, rmbit: i32, w: i32) void = {
else { if (w != 0) { emitbyte(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.
fn emitrex8(a: *asm_, regbit: i32, rmbit: i32, r8: i32) void = {
let b: u8 = 64u8; // 0x40
if (regbit != 0) { b = b | 4u8; };
if (rmbit != 0) { b = b | 1u8; };
if (b != 64u8) { emitbyte(a, b); }
else { if (rhi(r8) == 0 && rcode(r8) >= 4) { emitbyte(a, b); }; };
};
// ModR/M + (optional) SIB + displacement for [base+disp].
// Special-cases SP (needs SIB) and BP (forces explicit disp).
fn emitmodrmmem(a: *asm_, regfield: i32, base: i32, disp: i64) void = {
@@ -283,7 +294,8 @@ export fn encode(a: *asm_) i32 = {
let msg: str = "w6a: DATAR slot not yet defined as DATAW: ";
os.write(2, msg.ptr, msg.len: u64);
os.write(2, p.from.asym.ptr, p.from.asym.len: u64);
os.write(2, "\n".ptr, 1u64);
let wm1: str = "\n";
os.write(2, wm1.ptr, wm1.len: u64);
a.errs += 1;
p = p.link; continue;
};
@@ -382,27 +394,31 @@ export fn encode(a: *asm_) i32 = {
addreloc(a, reloff, 2, s, -4i64);
p = p.link; continue;
};};
os.write(2, "w6a: unsupported MOVQ shape\n".ptr, 27u64);
let wm2: str = "w6a: unsupported MOVQ shape\n";
os.write(2, wm2.ptr, wm2.len: u64);
a.errs += 1;
p = p.link; continue;
};
if (op == A_MOVB) {
// emitrex8 forces the bare REX so SI/DI/BP/SP name
// SIL/DIL/BPL/SPL, never AH/CH/DH/BH.
let ft: i32 = p.from.atype;
let tt: i32 = p.to.atype;
if (isgpr(ft)) { if (tt == D_INDIR) {
emitrex(a, rhi(ft), rhi(p.to.reg), 0);
emitrex8(a, rhi(ft), rhi(p.to.reg), ft);
emitbyte(a, 136u8); // 0x88
emitmodrmmem(a, rcode(ft), p.to.reg, p.to.offset);
p = p.link; continue;
};};
if (ft == D_INDIR) { if (isgpr(tt)) {
emitrex(a, rhi(tt), rhi(p.from.reg), 0);
emitrex8(a, rhi(tt), rhi(p.from.reg), tt);
emitbyte(a, 138u8); // 0x8A
emitmodrmmem(a, rcode(tt), p.from.reg, p.from.offset);
p = p.link; continue;
};};
os.write(2, "w6a: unsupported MOVB shape\n".ptr, 27u64);
let wm3: str = "w6a: unsupported MOVB shape\n";
os.write(2, wm3.ptr, wm3.len: u64);
a.errs += 1;
p = p.link; continue;
};
@@ -426,7 +442,8 @@ export fn encode(a: *asm_) i32 = {
emitmodrmmem(a, rcode(tt), p.from.reg, p.from.offset);
p = p.link; continue;
};};
os.write(2, "w6a: unsupported MOVW shape\n".ptr, 27u64);
let wm4: str = "w6a: unsupported MOVW shape\n";
os.write(2, wm4.ptr, wm4.len: u64);
a.errs += 1;
p = p.link; continue;
};
@@ -442,7 +459,8 @@ export fn encode(a: *asm_) i32 = {
emitmodrmmem(a, rcode(tt), p.from.reg, p.from.offset);
p = p.link; continue;
};};
os.write(2, "w6a: unsupported MOVZWQ shape\n".ptr, 29u64);
let wm5: str = "w6a: unsupported MOVZWQ shape\n";
os.write(2, wm5.ptr, wm5.len: u64);
a.errs += 1;
p = p.link; continue;
};
@@ -465,7 +483,8 @@ export fn encode(a: *asm_) i32 = {
emitbyte(a, modrmbyte(3, rcode(tt), rcode(ft)));
p = p.link; continue;
};};
os.write(2, "w6a: unsupported MOVSWQ shape\n".ptr, 29u64);
let wm6: str = "w6a: unsupported MOVSWQ shape\n";
os.write(2, wm6.ptr, wm6.len: u64);
a.errs += 1;
p = p.link; continue;
};
@@ -488,7 +507,8 @@ export fn encode(a: *asm_) i32 = {
emitbyte(a, modrmbyte(3, rcode(tt), rcode(ft)));
p = p.link; continue;
};};
os.write(2, "w6a: unsupported MOVSBQ shape\n".ptr, 29u64);
let wm7: str = "w6a: unsupported MOVSBQ shape\n";
os.write(2, wm7.ptr, wm7.len: u64);
a.errs += 1;
p = p.link; continue;
};
@@ -503,7 +523,8 @@ export fn encode(a: *asm_) i32 = {
emitmodrmmem(a, rcode(tt), p.from.reg, p.from.offset);
p = p.link; continue;
};};
os.write(2, "w6a: unsupported MOVZBQ shape\n".ptr, 29u64);
let wm8: str = "w6a: unsupported MOVZBQ shape\n";
os.write(2, wm8.ptr, wm8.len: u64);
a.errs += 1;
p = p.link; continue;
};
@@ -529,7 +550,8 @@ export fn encode(a: *asm_) i32 = {
emitbyte(a, modrmbyte(3, rcode(ft), rcode(tt)));
p = p.link; continue;
};};
os.write(2, "w6a: unsupported MOVL shape\n".ptr, 27u64);
let wm9: str = "w6a: unsupported MOVL shape\n";
os.write(2, wm9.ptr, wm9.len: u64);
a.errs += 1;
p = p.link; continue;
};
@@ -549,7 +571,8 @@ export fn encode(a: *asm_) i32 = {
emitbyte(a, modrmbyte(3, rcode(tt), rcode(ft)));
p = p.link; continue;
};};
os.write(2, "w6a: unsupported MOVSXD shape\n".ptr, 29u64);
let wm10: str = "w6a: unsupported MOVSXD shape\n";
os.write(2, wm10.ptr, wm10.len: u64);
a.errs += 1;
p = p.link; continue;
};
@@ -569,7 +592,8 @@ export fn encode(a: *asm_) i32 = {
ssemrload(a, 242u8, 17u8, ft, p.to.reg, p.to.offset);
p = p.link; continue;
};};
os.write(2, "w6a: unsupported MOVSD shape\n".ptr, 28u64);
let wm11: str = "w6a: unsupported MOVSD shape\n";
os.write(2, wm11.ptr, wm11.len: u64);
a.errs += 1;
p = p.link; continue;
};
@@ -596,7 +620,8 @@ export fn encode(a: *asm_) i32 = {
ssemrload(a, 243u8, 17u8, ft, p.to.reg, p.to.offset);
p = p.link; continue;
};};
os.write(2, "w6a: unsupported MOVSS shape\n".ptr, 28u64);
let wm12: str = "w6a: unsupported MOVSS shape\n";
os.write(2, wm12.ptr, wm12.len: u64);
a.errs += 1;
p = p.link; continue;
};
@@ -737,6 +762,9 @@ export fn encode(a: *asm_) i32 = {
addreloc(a, reloff, 2, s, -4i64);
p = p.link; continue;
};};
let wml: str = "w6a: unsupported LEAQ shape\n";
os.write(2, wml.ptr, wml.len: u64);
a.errs += 1;
p = p.link; continue;
};
@@ -762,6 +790,9 @@ export fn encode(a: *asm_) i32 = {
emitbyte(a, modrmbyte(3, 2, rcode(tt)));
p = p.link; continue;
};
let wmc: str = "w6a: unsupported CALL shape\n";
os.write(2, wmc.ptr, wmc.len: u64);
a.errs += 1;
p = p.link; continue;
};
@@ -799,7 +830,8 @@ export fn encode(a: *asm_) i32 = {
p = p.link; continue;
};
os.write(2, "w6a: unsupported opcode\n".ptr, 23u64);
let wm13: str = "w6a: unsupported opcode\n";
os.write(2, wm13.ptr, wm13.len: u64);
a.errs += 1;
p = p.link;
};
@@ -808,10 +840,12 @@ export fn encode(a: *asm_) i32 = {
let f: *afixup = a.fixups;
for (f != nil) {
if (!labeldefined(a, f.label)) {
os.write(2, "w6a: undefined label '".ptr, 21u64);
let wm14: str = "w6a: undefined label '";
os.write(2, wm14.ptr, wm14.len: u64);
let lbl: str = f.label;
os.write(2, lbl.ptr, lbl.len: u64);
os.write(2, "'\n".ptr, 2u64);
let wm15: str = "'\n";
os.write(2, wm15.ptr, wm15.len: u64);
a.errs += 1;
f = f.fnext;
continue;

View File

@@ -73,16 +73,19 @@ export fn main(argc: i32, argv: **u8) i32 = {
if (cstreq(a, "-o")) {
i += 1;
if (i >= argc) {
os.write(2, "w6a: -o requires arg\n".ptr, 20u64);
let wm1: str = "w6a: -o requires arg\n";
os.write(2, wm1.ptr, wm1.len: u64);
return 2;
};
out = argv[i];
} else { if (a[0u64] == 45u8) {
os.write(2, "w6a: unknown flag\n".ptr, 17u64);
let wm2: str = "w6a: unknown flag\n";
os.write(2, wm2.ptr, wm2.len: u64);
return 2;
} else {
if (src != nil) {
os.write(2, "w6a: only one input\n".ptr, 19u64);
let wm3: str = "w6a: only one input\n";
os.write(2, wm3.ptr, wm3.len: u64);
return 2;
};
src = a;
@@ -91,11 +94,13 @@ export fn main(argc: i32, argv: **u8) i32 = {
};
if (src == nil) {
os.write(2, "usage: w6a_ww -o file.o file.s\n".ptr, 30u64);
let wm4: str = "usage: w6a_ww -o file.o file.s\n";
os.write(2, wm4.ptr, wm4.len: u64);
return 2;
};
if (out == nil) {
os.write(2, "w6a: missing -o\n".ptr, 15u64);
let wm5: str = "w6a: missing -o\n";
os.write(2, wm5.ptr, wm5.len: u64);
return 2;
};
@@ -103,7 +108,8 @@ export fn main(argc: i32, argv: **u8) i32 = {
let blen: u64;
buf, blen = slurp(src);
if (buf == nil) {
os.write(2, "w6a: cannot read input\n".ptr, 22u64);
let wm6: str = "w6a: cannot read input\n";
os.write(2, wm6.ptr, wm6.len: u64);
return 1;
};
@@ -120,7 +126,8 @@ export fn main(argc: i32, argv: **u8) i32 = {
let fd: i32 = os.open(pathstr(out), os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
if (fd < 0) {
os.write(2, "w6a: cannot open output\n".ptr, 23u64);
let wm7: str = "w6a: cannot open output\n";
os.write(2, wm7.ptr, wm7.len: u64);
return 1;
};
let rc: i32 = emitelf(&s, fd);