10
votes

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?

2

2 Answers

23
votes
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?

7
votes
#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.