fix(asm): settle the byte-form width from the register operands
The suffixed scalar families derived the operand width from the mnemonic alone, so a byte-spelled register under the L spelling or no suffix at all encoded the widened form: XADDL DL, DL emitted 0F C1 where the byte form is 0F C0, CMPL AL, $7 emitted the 32-bit immediate form where the AL form is 3C 07, and CRC32 DL, R11 widened past the F0 byte opcode. operandWidth now reconciles the suffix with the operands: a byte register (AL, DL, R8B, ...) forces the 8-bit form, which is the text the toolchain's own disassembly prints for those encodings, while the W and Q spellings never ride a byte register and are refused as go tool asm refuses them (MOVQ AL, AX). The shift count and the two- and three-operand IMUL forms stay out of the reconciliation, and the byte accumulator short forms now belong to the AL spelling alone, matching the toolchain's division (ADDB $3, AX is 80 c0 03, TESTB $7, AX is f6 c0 07). Assisted-by: GLM 5.3
This commit is contained in:
1 parent
33e7fdac98
commit
98a562d8b3
3 files changed
+110
-5
No files matched your search
+9
-4
@@ -529,8 +529,10 @@ func (e *enc) encodeALUImm(digit int, dst Operand, imm int64, size int) error {
|
||||
return err
|
||||
}
|
||||
// The byte accumulator short form (0x04+digit*8, no ModR/M) when
|
||||
// the destination is AL, the form the Go assembler prefers here.
|
||||
if r, ok := dst.(Reg); ok && r.idx == 0 {
|
||||
// the destination is spelled AL itself; the size-agnostic AX takes
|
||||
// the generic 0x80 /digit form, the division go tool asm makes
|
||||
// (ADDB $7, AL is 04 07 while ADDB $3, AX is 80 c0 03).
|
||||
if r, ok := dst.(Reg); ok && r.idx == 0 && byteReg(r) {
|
||||
i := &instr{opcode: []byte{byte(0x04 + digit*8)}, modrm: -1, sib: -1}
|
||||
i.imm = immBytes
|
||||
return e.emit(i)
|
||||
@@ -587,8 +589,11 @@ func (e *enc) encodeTest(ops []Operand, size int) error {
|
||||
if imm, ok := src.(Imm); ok {
|
||||
// TEST r/m, imm: 0xF6 (8-bit) / 0xF7 /0, but the Go assembler
|
||||
// always uses the accumulator forms (A8/A9, no ModR/M) when the
|
||||
// register operand is AL/AX, whatever the immediate's width.
|
||||
if r, ok := dst.(Reg); ok && r.idx == 0 {
|
||||
// register operand is AL/AX, whatever the immediate's width. At
|
||||
// byte width the short form belongs to the AL spelling alone; the
|
||||
// size-agnostic AX takes the generic F6 /0 (TESTB $7, AX is
|
||||
// f6 c0 07), the same division the ALU accumulator makes.
|
||||
if r, ok := dst.(Reg); ok && r.idx == 0 && (size != 1 || byteReg(r)) {
|
||||
op := byte(0xA9)
|
||||
if size == 1 {
|
||||
op = 0xA8
|
||||
|
||||
Reference in new issue
Block a user