8

In a simple program written for Microsoft's x64 assembler, I want to move a 64-bit value between an SSE register (say xmm0) and a general-purpose register (say rcx), as in <Intel syntax in MASM>:

mov xmm0, rcx
...
mov rcx, xmm0

These two lines generate the following error messages, respectively, from ml64.exe:

  • error A2152: coprocessor register cannot be first operand
  • error A2070: invalid instruction operands

However, it is clearly possible to accomplish this simple task in x64. For example, the following is a functioning x64 program that I can assemble and run in GAS <AT&T syntax using GCC 4.8.2>:

.text
    .globl main
main:
    movl $1, %ecx
    movq %rcx, %xmm0
    movq %xmm0, %rax
    ret

As expected, the return value of this program is 1 and the objdump output for main() is:

1004010d0:   b9 01 00 00 00          mov    $0x1,%ecx
1004010d5:   66 48 0f 6e c1          movq   %rcx,%xmm0
1004010da:   66 48 0f 7e c0          movq   %xmm0,%rax
1004010df:   c3                      retq

So my question is, how can I accomplish this in MASM given that ml64.exe is producing the above errors?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
0xbe5077ed
  • 4,565
  • 6
  • 35
  • 77
  • 1
    Funnily enough, in this case `movq` isn't an at&t invention, that's the actual instruction. See the instruction set reference. PS: you can switch objdump into intel syntax too. – Jester Jul 16 '14 at 19:53
  • @Jester, I forgot to mention that `ml64.exe` rejects `movq` (if I use in place of `mov`) with error message "error A2150: word register cannot be first operand" and "error A2070: invalid instruction operands", respectively. Using MSFT's `dumpbin.exe` on my simple program assembled with GAS, I found MSFT is listing the same instructions as `movd`, so I tried that and it worked. I'll post that as the answer if no-one else does. – 0xbe5077ed Jul 16 '14 at 20:01
  • Strange, `movd` should be the 32 bit version. – Jester Jul 16 '14 at 20:12
  • @Jester: exactly. May be a bug in MASM. – 0xbe5077ed Jul 16 '14 at 20:30

1 Answers1

11

The MOV instruction cannot move data between a general-purpose register and an xmm register. The instruction you are looking for is MOVQ (like in the A&T syntax code you show), as defined in Intel's instruction set manuals. (HTML extract here: https://www.felixcloutier.com/x86/movd:movq)

The fact that ML64 does not accept MOVQ is in disagreement with Intel's manuals, and therefore - in my view at least - a bug (or at least an inconsistency).

ML64 does seem to use MOVD in its place, even for 64-bit registers. You can verify this by disassembling the code it generates.


Note that there are two different movq instructions (not counting load and store forms as separate):

  • One is movq xmm, xmm/m64 form, the MMX/SSE2 instruction that copies between vector registers or loads/stores. This existed in 32-bit mode with MMX (and SSE2), and the opcode always implies a 64-bit transfer (zero-extending to 128 with an XMM destination). ML64 uses movq for this form.

  • The other is the 64-bit version of movd xmm, r/m32 that can move data between XMM or MMX registers and GP-integer registers like RCX, or memory. This form is new with x86-64 (which includes MMX and SSE2); the opcode is the same as movd, with a REX.W prefix for 64-bit operand-size. ML64 apparently always uses movd for this form, regardless of the actual operand-size.

A 64-bit load or store between an XMM register and memory can use either opcode, but the first form is shorter, not needing a REX prefix.

(AT&T syntax movq %rax, %rcx is just mov with a q operand-size suffix; in that case the q is not part of the true mnemonic.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
PhiS
  • 4,540
  • 25
  • 35
  • see the above post and comments. I did disassemble. It seems that ml64 accepts `movd` but emits `movq`. For the time being, I have posted a [bug report on Microsoft Connect](https://connect.microsoft.com/VisualStudio/feedbackdetail/view/922022/ml64-requires-movd-move-doubleword-mnemonic-to-move-quadword-between-sse-and-gp-registers). – 0xbe5077ed Aug 14 '14 at 15:35
  • yes indeed; I had the same issue some time ago, so my answer was my recollection from memory. I suppose the behaviour makes sense to some degree if you look at the opcodes, but I think they should definitely stick to Intel's (or AMD's) mnemonic naming conventions -- otherwise it's a certain descent into chaos. – PhiS Aug 14 '14 at 20:26
  • 1
    it's helpful to know you had the same issue! Do you have a spare moment to head to https://connect.microsoft.com/VisualStudio/feedbackdetail/view/922022/ml64-requires-movd-move-doubleword-mnemonic-to-move-quadword-between-sse-and-gp-registers and upvote the "reproduce" count? – 0xbe5077ed Aug 14 '14 at 21:02
  • 1
    @0xbe5077ed upvoted the bug report, although I didn't see how to increase the "reproduce" count... – PhiS Aug 16 '14 at 21:22
  • I added a section to your answer about the different opcodes and their origin. It's easy to speculate that this ML64 design choice might have been some kind of hack to make the assembler internals easier in terms of resolving mnemonics to opcodes. But other assemblers have no problem with it, even GAS where `mov` with a `q` operand-size suffix is also only disambiguated by the operands: [What does AT&T syntax do about ambiguity between other mnemonics and operand-size suffixes?](https://stackoverflow.com/q/27990177) – Peter Cordes Sep 20 '20 at 16:57
  • LLVM is bug compatible (for MMX). However, it has this explanatory comment. // Match 'movd GR64, MMX' as an alias for movq to be compatible with gas, // which supports this due to an old AMD documentation bug when 64-bit mode was // created. – Olsonist Dec 24 '20 at 04:55