11

I am a bit confused by the comment in one of the header files for the Linux kernel, arch/x86/include/asm/nops.h. It states that

<...> the following instructions are NOT nops in 64-bit mode, for 64-bit mode use K8 or P6 nops instead
movl %esi,%esi
leal 0x00(%esi),%esi
<...>

I guess the author implied the machine instructions ('89 F6' and '8D 76 00', respectively) there rather than assembly instructions. It follows from the description of LEA in Intel Software Developer's Manual Vol 2A that the latter instruction (lea 0x00(%rsi), %esi) does the same as the the former, mov %esi,%esi.

So this reduces to the question, whether mov %esi,%esi is actually a no-op on x86-64.

mov does not change flags. This kind of mov does not change memory either. It seems, if it changes something besides %rip, that should be general purpose registers. But I have no clue how it can change the contents of %rsi or whatever. If you manipulate the lower half of a general purpose register, the upper half should not change, right?

Eugene
  • 5,977
  • 2
  • 29
  • 46

2 Answers2

25
mov %esi, %esi

zeros out the high 32 bits of %rsi, and is therefore not a no-op on x86_64.

See Why do x86-64 instructions on 32-bit registers zero the upper part of the full 64-bit register?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
  • 3
    seeing as your an assembly guy, and this is your 486th answer, does this mean you'll stop answering questions now? (Sorry, lame 486 joke... I remember my 486DX2... and now I'm COMPLETELY off topic) – Steve Jul 11 '11 at 18:13
  • 2
    Just to add to the answer, the relevant section of the Intel dev manuals is 3.4.1.1 (in Volume 1). – Michael Foukarakis Jul 11 '11 at 18:48
  • 4
    @Steve: The first platform for which I wrote assembly was the 68k, so I think I have a while to go before I hang it up... – Stephen Canon Jul 11 '11 at 18:54
  • @Stephen Canon, I see now what happens, thank you. Indeed, it says in the volume 1 of Intel's manual: "32-bit operands generate a 32-bit result, zero-extended to a 64-bit result in the destination general-purpose register". – Eugene Jul 11 '11 at 19:31
  • @Michael Foukarakis - thank you for pointing this out. This saves me a lot of time. – Eugene Jul 11 '11 at 19:32
  • @Michael: Yes, thanks. I would have pointed out the relevant section myself, but I was at lunch, posting from my phone =) – Stephen Canon Jul 11 '11 at 19:34
7
#include <stdio.h>

int main(int argc, char * argv[])
{
    void * reg_rsi = 0;

    asm (
        "movq $0x1234567812345678, %%rsi;\n"
        "movl %%esi, %%esi;\n"
        "movq %%rsi, %0;\n"
        : "=r" (reg_rsi)
        : /* no inputs */
        : /* no clobbered */
    );

    printf("reg_rsi = %p\n", reg_rsi);

    return 0;
}

This gives "reg_rsi = 0x12345678" for my x86_64 machine.

Ilya Matveychikov
  • 3,936
  • 2
  • 27
  • 42
  • 1
    You are correct, the same applies to `rax` and `eax`. HOWEVER: swap the `movl` for an `xor` and it will zero the entire 64 bits. – Orwellophile Jul 18 '16 at 18:25
  • Note that **this inline asm is unsafe**: it doesn't declare a clobber on RSI. The other fix would be to use `"=S"` instead of `"=r"`, to tell the compiler the output register is RSI. (And omit the last `mov`.) Or with `"=r"`, use `mov %k0, %k0` to fill in that spot in the template with the name of the 32-bit version of the register it picked. (Look at the compiler-generated asm, or disassembly as you single-step, to see how it filled in the template.) – Peter Cordes Sep 18 '21 at 07:35