Search code examples
coptimizationgccinline-assembly

"unsupported for mov" GCC inline assembler


While playing around with GCC's inline assembler feature, I tried to make a function which immediately exited the process, akin to _Exit from the C standard library.

Here is the relevant piece of source code:

void immediate_exit(int code)
{
#if defined(__x86_64__)
    asm (
            //Load exit code into %rdi
            "mov %0, %%rdi\n\t"
            //Load system call number (group_exit)
            "mov $231, %%rax\n\t"
            //Linux syscall, 64-bit version.
            "syscall\n\t"
            //No output operands, single unrestricted input register, no clobbered registers because we're about to exit.
            :: "" (code) :
    );
//Skip other architectures here, I'll fix these later.
#else
#   error "Architecture not supported."
#endif
}

This works fine for debug builds (with -O0), but as soon as I turn optimisation on at any level, I get the following error:

immediate_exit.c: Assembler messages:
immediate_exit.c:4: Error: unsupported for `mov'

So I looked at the assembler output for both builds (I've removed .cfi* directives and other things for clarity, I can add that in again if it's a problem). The debug build:

immediate_exit:
.LFB0:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    %edi, -4(%rbp)

    mov -4(%rbp), %rdi
    mov $231, %rax
    syscall

    popq    %rbp
    ret

And the optimised version:

immediate_exit:
.LFB0:
    mov %edi, %rdi
    mov $231, %rax
    syscall

    ret

So the optimised version is trying to put a 32-bit register edi into a 64-bit register, rdi, rather than loading it from rbp, which I presume is what is causing the error.

Now, I can fix this by specifying 'm' as a register constraint for code, which causes GCC to load from rbp regardless of optimisation level. However, I'd rather not do that, because I think the compiler and its authors has a much better idea about where to put stuff than I do.

So (finally!) my question is: how do I persuade GCC to use rdi rather than edi for the assembly output?


Solution

  • Cast your variable into the appropriate length type.

    #include <stdint.h>
    
    asm (
                //Load exit code into %rdi
                "mov %0, %%rdi\n\t"
                //Load system call number (group_exit)
                "mov $231, %%rax\n\t"
                //Linux syscall, 64-bit version.
                "syscall\n\t"
                //No output operands, single unrestricted input register, no clobbered registers because we're about to exit.
                :: "g" ((uint64_t)code)
        );
    

    or better have your operand type straight away of the right size:

    void immediate_exit(uint64_t code) { ...