使用汇编在C中添加

时间:2013-04-19 14:00:04

标签: c gcc assembly x86 add

我非常擅长C,而不是组装,但出于兴趣,我想让一些人在C中使用gcc。问题是我的程序要么提供一些愚蠢的数字或崩溃。

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;
}

我确定我做过傻事,如果这对任何人都很明显? ;)

3 个答案:

答案 0 :(得分: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 the o'约束的东西。
  4. “i”:立即整数 允许操作数(具有常量值的操作数)。这包括象征性的 仅在汇编时才知道其值的常量。
  5. “n”: 允许具有已知数值的立即整数操作数。 许多系统不支持操作数的汇编时常量 不到一个字宽。这些操作数的约束应该使用'n' 而不是'我'。
  6. “g”:任何寄存器,内存或立即数 允许操作数,但非一般的寄存器除外 寄存器。
  7. 有关更多示例和其他限制,请参阅网站。希望这有帮助! :)

答案 1 :(得分:1)

修改:代码已更正,因为迈克尔评论说这是饱和添加

关于如何使用gcc内联汇编的要点:

  1. 将内联汇编放在一个asm( ... )块中。
  2. 在扩展汇编中定义输入,输出和修改寄存器。
  3. 为了优化寄存器的使用,让gcc决定使用哪个寄存器,当任何通用寄存器对你的代码同样合适时。
  4. 您无需使用mov设置内联汇编块开头的值,只需定义输入。
  5. 仔细检查GCC-Inline-Assembly-HOWTO,尤其是Extended Asm上的部分。
  6. 以下是代码:

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

答案 2 :(得分:1)

除了使用单__asm__'指令'和AT&amp; 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(使用%edi)和%rsi(使用%esi) 。使用inline限定符指定此函数会更好:编译器可能已经加载了'max',或者可能能够安排更好的方法来加载它,等等。它也不需要返回值在%rax%eax

gcc inline asm的很多教程已经过时了。我找到的最好的是x86 [{64]的Lockless Inc教程。