如何根据架构一般指定AX,EAX或RAX?

时间:2015-01-11 10:02:44

标签: c gcc assembly inline-assembly

我试图编写一些适用于16,32,64,128位英特尔机器的内联汇编(将来某个时候有128位)。想法是使用通用寄存器名称,以便编译器或汇编器在i866(AX),{{1}上的8086(-m16?),EAX上选择-m32 }在x86_64(RAX)上,等等。

我认为我可以使用-m64aax的{​​{1}}注册来实现此目的。 eaxraxb的{​​{1}};等等。但是,我在调用" generic" bx在代码中注册:

ebx

编译器抱怨:

rbx

如果我从clobber列表中删除a(以便它以unsigned char* ptr = ...; size_t size = ...; __asm__ __volatile__ ( "xor %a, %a" "lea ptr, %b\n" "lea size, %c\n" "1:\n" "movb 0, %b(%a)\n" "inc %a\n" "loop 1b\n" : /* no outputs */ : "b" (ptr), "c" (size) : "a", "b", "c", "cc" ); 开头),那么我得到:

error: unknown register name 'a' in asm
    : "a", "b", "c", "cc"

我还尝试在clobber列表中将其指定为"a",但我收到了相同的错误消息。

根据GCC手册中的Machine Constraints"b"error: unknown register name 'b' in asm : "b", "c", "cc" 。所以我很确定我的名字是正确的。但是,我也非常确定我做错了什么,或者我不太了解事物的全貌。

问题:我如何一般性地指定英特尔注册名称,以便内联汇编"正常工作"当那一天到来时,"%a"a(甚至是a register)?


OS X是10.8.5,x64,完全打补丁。汇编程序是:

-m32

相关:在上面的代码中,我使用-m64来支持单步机字大小。例如,我以这种方式编写它,因此生成的代码是-m128(32位)或$ /usr/bin/as -v Apple Inc version cctools-855, GNU assembler version 1.38 (64位)。我不确定它是否是推荐的方法(或者它是否有效,因为我无法运行它)。请更正。

3 个答案:

答案 0 :(得分:3)

您似乎无法检查代码中是否(size == 0),因此我将遵循该断言。如果任何输出参数被更新而不是随后使用,则__volatile__是必要的 - 编译器不知道存储器ptr[size]已经归零,并且看不到副作用,它将会只是省略了asm块。

我们使用'临时'参数,所以我们可以更新值,然后丢弃它们。编译器知道临时参数已被修改,并且看到它们从未再次使用过,不需要维护这些寄存器。我建议像:

{
    size_t tmp_size = size;

    __asm__ __volatile__ (

        "%=:\n\t" /* generate a unique label. */

        "sub $1, %0\n\t"
        "movb $0, %1(%0)\n\t" /* 0 -> ptr[size - 1] .. ptr[0] */
        "jnz %=b\n\t" /* jump 'back' */

        : "+r" (tmp_size) : "r" (ptr) : "memory", "cc");
}

这也让编译器选择寄存器,这是更好的选择。我不认为这会给你共同的64位,32位(或16位)代码。您可能希望查看operand modifiers。否则,您可能需要' q'和' l'分别为64位和32位版本的指令后缀。

BTW,sub/jnz在现代处理器上通常比inc/dec(部分标志失速危险)和loop(复杂微码'东西')更好。

答案 1 :(得分:1)

我尽了最大努力,但不能比这更接近。

#include <stddef.h>

void f(unsigned char* ptr, size_t size) {

__asm__ __volatile__
(
    "xor %%eax, %%eax\n\t"
    "lea ptr, %0\n\t"
    "lea size, %1\n\t"

    "1:\n\t"
    "movb 0, %0\n\t"
    "inc %%eax\n\t"
    "loop 1\n\t"

    : /* no outputs */
    : "b" (ptr), "c" (size)
    : "0", "1", "%eax", "cc"
 );

}

它与你的不同,但显示正确的路径:很明显,"a"不能在clobber列表中使用,为什么呢。所以我这样做了。

使用gcc -S x.c -o-编译此模块会向我显示

... [ start of function, irrelevant here ]
#APP
# 5 "x.c" 1
    xor %eax, %eax
    lea ptr, %ebx
    lea size, %ecx
    1:
    movb 0, %ebx
    inc %eax
    loop 1

# 0 "" 2
#NO_APP
... [ end of function, irrelevant here ]

我希望尽管有所帮助。


编辑:根据海湾合作委员会的文件显示,这是非法的。 (虽然我的编译器没有抱怨,与我链接的问题不同。)

让我们再试一次:

#include <stddef.h>
#include <stdint.h>

void f(unsigned char* ptr, size_t size) {

uint32_t junk;
size_t countdown;

__asm__ __volatile__
(
    "xor %0, %0\n\t"
    "lea ptr, %2\n\t"
    "lea size, %3\n\t"

    "1:\n\t"
    "movb 0, %2(%0)\n\t"
    "inc %0\n\t"
    "dec %1\n\t"
    "loopnz 1\n\t"

    : "=a" (junk) /* junk output */, "=c" (countdown)
    : "b" (ptr), "c" (size)
    : "cc", "memory"
 );

}

(顺便说一句,我在某处添加了dec %1loopnz ...

答案 2 :(得分:1)

你不能。 使用特定于体系结构的预定义和复制粘贴。甚至更好地使用编译器内在函数或单独的asm文件。

其他一些有用的信息

  • 苹果这样预定义的宏对我有用_11P64__它在x86_64架构上设置 所以你的代码看起来像:

     #ifdef __LP64__
         void myfunctionfor64bitArch()
     #else
         void myfunctionfor32bitArch()
     #endif
    

使用__x86_64__也应该更正确,但我没有尝试。

  • 想想你为什么要这么多跨平台汇编程序?您的代码可能不大:ABI有所不同,请参阅http://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions,因此您的代码不能以通用形式存在,汇编程序太不同

  • 在clang Visual-Studio样式汇编程序的最新版本中工作。恕我直言,它更方便。试试

    __asm
    {
         mov eax, your_variable    ; Get first argument
    }
    

有趣的是它适用于x64,而在原版工作室它只适用于32位