“不支持mov”GCC内联汇编程序

时间:2013-09-10 14:31:42

标签: c optimization gcc inline-assembly

在玩GCC的内联汇编程序功能时,我尝试创建一个立即退出流程的函数,类似于C标准库中的_Exit

以下是相关的源代码:

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
}

这适用于调试版本(使用-O0),但只要我在任何级别启用优化,我都会收到以下错误:

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

所以我查看了两个版本的汇编程序输出(为了清楚起见,我删除了.cfi*指令和其他内容,如果这是一个问题,我可以再次添加它)。调试版本:

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

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

    popq    %rbp
    ret

优化版本:

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

    ret

因此,优化版本试图将32位寄存器edi放入64位寄存器rdi,而不是从rbp加载,我认为这是什么导致错误。

现在,我可以通过将“m”指定为code的寄存器约束来解决此问题,这会导致GCC从rbp加载,而不管优化级别如何。但是,我宁愿不这样做,因为我认为编译器及其作者对于放置东西比我更好的想法。

所以(最后!)我的问题是:我如何说服GCC使用rdi而不是edi来组装输出?

2 个答案:

答案 0 :(得分:7)

总的来说,使用约束将值放入正确的寄存器而不是显式移动会更好:

asm("syscall" :: "D" ((uint64_t)code), "a" ((uint64_t)231));

如果有用的话,编译器可以在代码中提前提升移动,如果值可以安排在正确的寄存器中,甚至可以完全避免移动...

答案 1 :(得分:2)

将变量转换为适当的长度类型。

#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)
    );

或者更好地将您的操作数类型直接放在正确的大小上:

void immediate_exit(uint64_t code) { ...