2

我很擅长 C,不太擅长汇编,但出于兴趣,我想使用 gcc 在 C 中进行一些工作。问题是我的程序要么给出一些愚蠢的数字,要么崩溃。

unsigned sadd32(unsigned a, unsigned b)
{
    unsigned c = 0;

    __asm__("movl %%eax, %0" : "=a"(a));
    __asm__("addl %%eax, %0" : "=b"(b));
    __asm__("movl %edx, 0xffffffff");
    __asm__("cmovc %eax, %edx");

    __asm__("movl %0, %%eax" : "=c"(c));


    return c;
}

我确定我做了一些愚蠢的事情,如果这对任何人来说都是显而易见的??;)

4

3 回答 3

2

除了关于使用单个__asm__“指令”和 AT&T 语法的建议之外,让编译器加载具有最大值的寄存器可能会更好。理想情况下:

/* static __inline__ */ unsigned sadd32 (unsigned a, unsigned b)
{
    __asm__ ("addl %k1, %k0\n\t"
             "cmovcl %k2, %k0\n\t"
             : "+r" (a) : "r" (b), "r" (0xffffffff) : "cc");

    return (a);
}

这种形式也适用于 x86-64,其中第一个和第二个参数将分别位于%rdi(using %edi) 和%rsi(using %esi) 中。使用限定符指定此函数会更好inline:编译器可能已经加载了“max”,或者可能能够安排更好的方式来加载它,等等。它也不需要返回%raxor中的值%eax

很多 gcc 内联汇编的教程已经过时了。我发现的最好的是 x86[-64] 的Lockless Inc教程。

于 2013-04-19T17:35:57.717 回答
2

编辑:代码更正,正如迈克尔评论说这是饱和的 add

gcc内联汇编的使用要点:

  1. 将内联程序集放在一个asm(...)块中。
  2. 在扩展汇编中定义输入、输出和破坏寄存器。
  3. 为了优化寄存器使用,让 gcc 决定使用哪些寄存器,当任何通用寄存器对您的代码同样适用时。
  4. 您不需要使用mov在内联汇编块的开头设置值,只需定义输入。
  5. 仔细检查GCC-Inline-Assembly-HOWTO,尤其是关于Extended Asm的部分。

这是代码:

#include <stdio.h>

unsigned my_sadd(unsigned a, unsigned b)
{
    asm(
            "addl %2, %0;"
            "cmovcl %3, %0;"
            /* outputs */
            : "=r"(a)
            /* inputs */
            : "0"(a), "r"(b), "r" (0xffffffff)
        );
    return a;
}

int main(void)
{
    unsigned a;
    unsigned b;
    unsigned c;

    a = 123456;
    b = 314159;

    c = my_sadd(a, b);
    printf("%u + %u = %u\n", a, b, c);

    a = 0x80000000;
    b = 0xF0000000;
    c = my_sadd(a, b);

    printf("%u + %u = %u\n", a, b, c);
    return 0;
}
于 2013-04-19T14:49:58.970 回答
2
#include <stdio.h>
#include <stdlib.h>

unsigned sadd32(unsigned a, unsigned b)
{
    unsigned c = 0;
   __asm__ ("movl %2, %%eax\n\t"
                    "addl %1, %%eax\n\t"
                    "movl %%eax, %0\n\t"
                    :"=r"(c)
                    :"r"(a),"r"(b)
                    :"%eax"
             );
    return c;
}

int main()
{
    unsigned int a=3,b=5;
    printf("The sum of %u and %u is %u\n",a,b,sadd32(a,b));
    return 0;
}

参考:http ://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html

据我所知,您的代码有一些缺陷::

unsigned sadd32(unsigned a, unsigned b)
{
    unsigned c = 0;

    __asm__("movl %%eax, %0" : "=a"(a));
    __asm__("addl %%eax, %0" : "=b"(b));
    __asm__("movl %edx, 0xffffffff"); /* this here, you are specifying a manual location, may not be accessible or valid */
    __asm__("cmovc %eax, %edx"); /* resulting in its error */

    __asm__("movl %0, %%eax" : "=c"(c));


    return c;
}

另外,我相信您不了解“=$”的概念。据我所知,您只是在那里写变量名,但这不是它的工作方式。从该站点来看,约束约束(众所周知)如下:

  1. "m" :允许使用内存操作数,机器通常支持的任何类型的地址。
  2. "o" :允许使用内存操作数,但前提是地址是可偏移的。即,向地址添加一个小的偏移量会给出一个有效的地址。
  3. "V" :不可偏移的内存操作数。换句话说,任何符合m’ constraint but not theo'约束的东西。
  4. "i" :允许立即整数操作数(具有常数值的操作数)。这包括符号常量,其值仅在汇编时才知道。
  5. "n" :允许使用具有已知数值的立即整数操作数。许多系统不支持小于一个字宽的操作数的汇编时间常数。这些操作数的约束应该使用“n”而不是“i”。
  6. "g" :允许任何寄存器、内存或立即整数操作数,但不是通用寄存器的寄存器除外。

有关更多示例和其他限制,请参阅该站点。希望这有帮助!:)

于 2013-04-19T14:50:41.350 回答