looking at http://ref.x86asm.net/coder32.html I found two opcodes that match for the statement

xor eax,eax

1) opcode 31 XOR r/m16/32 r16/32

2) opcode 33 XOR r16/32 r/m16/32

both refers to 32bit register for operand1 and operand2. So, is there any differences in this specific case of the XORing two 32bit registers ?


Solution 1:

x86 has 2 redundant ways to encode a 2-register instance of any of the basic ALU instructions (that date back to 8086), using either the r/m source and r/m destination forms.

This redundancy for reg,reg encoding is a consequence of how x86 machine code allows a memory-destination or a memory-source for most instructions: instead of spending bits in the ModR/M byte to have a flexible encoding for both operands, there are simply two separate opcodes for most instructions.

(This is why two explicit memory operands, like xor [eax], [ecx], isn't allowed for any instruction. Only a few instructions where one or both memory operands are implicit, like rep movs or push [mem] allow two memory operands, never one instruction with two separate ModR/M-encoded addressing modes.)


There are patterns to the encodings

Notice that 31 vs. 33 for word/dword/qword-sized xor differ only in bit #1. Other instructions like 29 vs. 2B sub follow the same pattern. Bit #1 is sometimes called the "direction" bit of the opcode. (Not to be confused with DF in EFLAGS, the direction flag).

Also note that byte vs. word/dword/qword operand-size versions of those instructions differ only in the low bit, like 30 XOR r/m8, r8 vs. 31 XOR r/m16, r16. Again, this pattern shows up in the ALU instruction encodings that date back to 8086. Bit #0 of those opcodes is sometimes called the "size" bit.

These "basic ALU" instructions that have an encoding for each direction and size combo date back to original 8086; many later instructions like 386 bsf r, r/m or 186 imul r, r/m, imm don't have a form that could allow a memory destination. Or for bt* r/m, r only the destination can be reg/mem.

That's also why later instructions (or new forms of them like imul) usually don't have a separate opcode for byte operand-size, only allowing word/dword/qword via the normal prefix mechanisms. 8086 used up much of the coding space, and later extensions wanted to leave room for more future extensions. So that's why there's no imul r, r/m8.

(dword and qword operand size were themselves extensions; 8086 didn't have operand-size or REX prefixes. So original 8086 was fairly sensible in terms of using its opcode coding space, and having patterns to make decoding not a total mess.)


No execution differences between forms

For reg,reg instructions, there's no difference in how they decode and execute on any CPUs I'm aware of; the only time you need to care about which encoding your assembler uses is when you want the machine code to meet some other requirement, like using only bytes that represent printable ASCII characters. (e.g. for an exploit payload).


Specifying which form you want the assembler to use

Some assemblers have syntax for overriding their default choice of encoding, e.g. GAS had a .s suffix to get the non-default encoding. That's now deprecated, and you should use {load} or {store} prefixes before the mnemonic (see the docs), like so:

{load} xor %eax, %ecx
{store} xor %eax, %ecx
{vex3} vpaddd %xmm0, %xmm1, %xmm1
vpaddd %xmm0, %xmm1, %xmm1        # default is to use 2-byte VEX when possible

gcc -c foo.S && objdump -drwC foo.o

0:   31 c1                   xor    %eax,%ecx
2:   33 c8                   xor    %eax,%ecx
4:   c4 e1 71 fe c8          vpaddd %xmm0,%xmm1,%xmm1
9:   c5 f1 fe c8             vpaddd %xmm0,%xmm1,%xmm1

(Related: What methods can be used to efficiently extend instruction length on modern x86? for use-cases for {vex3}, {evex} and {disp32}.)

NASM also has {vex2}, {vex3}, and {evex} prefixes with the same syntax as GAS, e.g. {vex3} vpaddd xmm1, xmm1, xmm0. But I don't see a way to override the op r/m, r vs. op r, r/m choice of opcodes.


Related Q&As, some basically duplicates

  • Why does the Solaris assembler generate different machine code than the GNU assembler here? - some assemblers have a different default choice of "direction".
  • Some assemblers have even used that choice (and maybe other redundancies) as a way to footprint / watermark in their output machine code. Notably A86 which was shareware, using this as a way to detect binaries from people that didn't pay the shareware fee. A86 tag wiki.
  • What is the ".s" suffix in x86 instructions? (the precursor to the {load} and {store} overrides in GAS source).
  • Encoding ADC EAX, ECX - 2 different ways to encode? (arch x86)
  • x86 sub instruction opcode confusion