2

It is common to find assembly-code lines of the form

xorq, %rdx, %rdx

One use for this operation is setting the register %rd to zero, exploiting the fact that x^x = 0. In C, it is the same as setting x = 0.

Another, more straightforward way to express this operation is

movq $0, %rdx

My question is, how do we calculate the number of bytes it takes to encode these two different implementations? I believe the first answer is 3 bytes, while the second requires 7 bytes.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 4
    "how do we calculate the number of bytes it takes to encode [...]" Check the reference manual. – Thomas Jager Aug 24 '20 at 14:03
  • 5
    Try to assemble the two instructions and then use a disassembler to inspect the encoding. Note that usually, `xor %edx, %edx` is used instead for a shorter encoding. – fuz Aug 24 '20 at 14:06
  • 3
    @ThomasJager The x86 reference manual is not easy to read. It assumes you already know a great deal of stuff which, if OP already knew it, they probably wouldn't have asked this question. – zwol Aug 24 '20 at 14:18
  • related: [How many ways to set a register to zero?](https://stackoverflow.com/q/4829937) mentions the length for most of these. [What is the best way to set a register to zero in x86 assembly: xor, mov or and?](https://stackoverflow.com/a/33668295) mentions that qword xor is 3 bytes, including the useless REX prefix. – Peter Cordes Aug 24 '20 at 22:06
  • 1
    [`48 31 d2` vs. `48 c7 c2 00 00 00 00`](https://godbolt.org/z/M1oM36). – dxiv Aug 24 '20 at 22:13
  • Also related: [Tips for golfing in x86/x64 machine code](https://codegolf.stackexchange.com/q/132981) has a bunch of info about code size, mostly focused on short encodings at the cost of speed. – Peter Cordes Aug 25 '20 at 00:13

2 Answers2

4

You can dig the answer to questions of this type out of the x86 reference manual but it's usually much faster and easier to write a tiny test assembly program, assemble it, and then disassemble it.

$ cat > test.s <<EOF
        .text
        .globl x
x:
        xorl %edx, %edx
        xorq %rdx, %rdx
        movl $0, %edx
        movq $0, %rdx
EOF
$ as test.s -o test.o
$ objdump -d test.o

test.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <x>:
   0:   31 d2                   xor    %edx,%edx
   2:   48 31 d2                xor    %rdx,%rdx
   5:   ba 00 00 00 00          mov    $0x0,%edx
   a:   48 c7 c2 00 00 00 00    mov    $0x0,%rdx

All four of these instructions clear RDX, because x86-64 automatically zero-extends the result of any 32-bit operation to the full width of the register. You can see from the disassembly dump that they are encoded with two, three, five, and seven bytes respectively, so your original surmise was correct.

A reason to use the longer instructions is that XOR sets the condition codes (so after xor %edx, %edx you will have ZF=1, OF=SF=PF=CF=0, and AF undefined) but MOV does not. This could matter if you were trying to fine-tune the scheduling of some hand-written assembly.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • It seems 32-bit operations are zero-extended instead of sign-extending. [demo](https://wandbox.org/permlink/UgitxY4hWG3a8sEB). – MikeCAT Aug 24 '20 at 14:32
  • As MikeCAT commented 32-bit register writes **zero**-extend, they do not sign-extend. Also, both of the `xor` instructions will set Carry Flag = 0, Zero Flag = 1, etc. The `mov` does not alter the flags. – ecm Aug 24 '20 at 15:02
4

In the old days, assemblers would produce listing files showing the encoded instructions, and you could see how many bytes each instruction took. Failing that, you can put this code in some file.s:

a:  xorq  %rdx, %rdx
b:  movq $0, %rdx
c:

then assemble it with as -o file.o file.s and look at the symbols with nm file.o, which shows something like:

0000000000000000 t a
0000000000000003 t b
000000000000000a t c

from which you can see xorq %rdx, %rdx requires 316−016 = 3 bytes, and movq $0, %rdx requires a16−316 = 7 bytes.

You can also disassemble the object file with objdump -disassemble file.o or otool -tv file.o. (Commands and their switches may vary; these are current Apple tools.)

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 1
    Assemblers these days can still produce listings :) `as -al` and `nasm -l` for example. – Jester Aug 24 '20 at 18:01
  • @Jester: Mine says “clang: warning: argument unused during compilation: '-al' [-Wunused-command-line-argument]”. (It is Apple clang version 11.0.0 (clang-1100.0.33.17).) – Eric Postpischil Aug 24 '20 at 18:14
  • That sounds more like a C compiler (driver) warning than an assembler one. I don't have mac but on linux if you want to use gcc you need to do something like `-Wa,-al` to make it pass the option to the assembler. – Jester Aug 24 '20 at 18:45
  • 1
    @Jester: `as` (in the Apple tools) invokes Clang with appropriate options for assembly. There appears to be no option for requesting a listing. – Eric Postpischil Aug 24 '20 at 18:48
  • Oh, okay. Bummer. – Jester Aug 24 '20 at 19:41
  • You could install YASM; it has an AT&T syntax parser and can output a flat binary with a listing. But either way, disassembly to actually see the bytes as well as their addresses is a clearer way to go. – Peter Cordes Aug 25 '20 at 00:12