diff --git a/asm/encode.go b/asm/encode.go index da976ba..94da113 100644 --- a/asm/encode.go +++ b/asm/encode.go @@ -184,8 +184,11 @@ func (e *enc) encode(mnem string, ops []Operand) error { return fmt.Errorf("RETFL expects an immediate") } return e.emit(&instr{opcode: []byte{0xCA}, modrm: -1, sib: -1, imm: le16(int64(imm))}) - // MOVDQ2Q/MOVQ2DQ cross the MMX and XMM banks (F2 0F D6), the register - // in the reg field, the other bank's in r/m. + // MOVDQ2Q/MOVQ2DQ cross the MMX and XMM banks, and each direction + // carries its own mandatory prefix: the toolchain renders F3 0F D6 as + // MOVQ2DQ (the MMX source, XMM destination) and F2 0F D6 as MOVDQ2Q + // (the XMM source, MMX destination), the register in the reg field, the + // other bank's in r/m. case "MOVDQ2Q", "MOVQ2DQ": if len(ops) != 2 { return fmt.Errorf("%s expects 2 operands, got %d", upper, len(ops)) @@ -199,7 +202,11 @@ func (e *enc) encode(mnem string, ops []Operand) error { upper == "MOVQ2DQ" && (!srcReg.mmx || !dstReg.isVec()) { return fmt.Errorf("%s crosses the XMM and MMX banks in that order", upper) } - i := &instr{prefix: 0xF2, opcode: []byte{0x0F, 0xD6}, modrm: -1, sib: -1} + prefix := byte(0xF2) + if upper == "MOVQ2DQ" { + prefix = 0xF3 + } + i := &instr{prefix: prefix, opcode: []byte{0x0F, 0xD6}, modrm: -1, sib: -1} if err := setRM(i, dstReg, srcReg, 8); err != nil { return err } diff --git a/asm/rendered_text_test.go b/asm/rendered_text_test.go index e3fe9e1..3232043 100644 --- a/asm/rendered_text_test.go +++ b/asm/rendered_text_test.go @@ -24,6 +24,9 @@ func TestRenderedTextEncodes(t *testing.T) { // The segment-absolute rendering lowers to the FS-prefixed disp32 // absolute, exactly what the 0(FS) spelling encodes. {"MOVQ FS:0, DX", "64488b142500000000"}, + // The bank-crossing quadword move takes the prefix by direction: the + // MMX source carries F3, the XMM source F2. + {"MOVQ2DQ M2, X11", "f3440fd6da"}, } { src := "TEXT \u00b7k(SB), NOSPLIT, $0\n\t" + tt.text + "\n\tRET\n" f, errs := parser.Parse("k.s", src)