Draconian Times
Draconian Times

Reputation: 319

Addition in C using Assembly

I am pretty good at C, not so much with assembly, but out of interest i wanted to get some working inside C using gcc. Problem is my program either gives some silly number or crashes.

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

I'm sure i've done something silly, if it's obvious to anyone?? ;)

Upvotes: 2

Views: 2149

Answers (3)

nrz
nrz

Reputation: 10580

Edit: Code corrected, as Michael commented that this is saturated add.

The main points on how to use gcc inline assembly:

  1. Put the inline assembly it in one asm( ... ) block.
  2. Define input, output and clobbered registers in extended assembly.
  3. For optimal register usage, let gcc make decisions on which registers to use, when any general register works equally fine for your code.
  4. You don't need to use mov to set the values in the beginning of inline assembly block, just define the inputs.
  5. Check GCC-Inline-Assembly-HOWTO carefully, especially the part on Extended Asm.

Here's the code:

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

Upvotes: 2

Brett Hale
Brett Hale

Reputation: 22348

Apart from the advice about using a single __asm__ 'instruction', and AT&T syntax, it might be better to let the compiler load the register with the maximum value. Ideally:

/* 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);
}

This form also works well on x86-64, where the first and second arguments will be in %rdi (using %edi) and %rsi (using %esi) respectively. It would be even better to specify this function with an inline qualifier: the compiler might have 'max' already loaded, or may be able to schedule a better way to load it, etc. It also need not return the value in %rax or %eax.

A lot of tutorials for gcc inline asm are out of date. The best I've found is the Lockless Inc tutorial for x86[-64].

Upvotes: 2

Binayaka Chakraborty
Binayaka Chakraborty

Reputation: 1335

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

Referring to : http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html

From what I see, your code has some flaws::

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

Also, I believe you did not understand the "=$" concept. From what I see, you are just writing the variable name there, but that is not how it works. From the site, the constraint restraints(as they are known), are as follows:

  1. "m" : A memory operand is allowed, with any kind of address that the machine supports in general.
  2. "o" : A memory operand is allowed, but only if the address is offsettable. ie, adding a small offset to the address gives a valid address.
  3. "V" : A memory operand that is not offsettable. In other words, anything that would fit the m’ constraint but not theo’constraint.
  4. "i" : An immediate integer operand (one with constant value) is allowed. This includes symbolic constants whose values will be known only at assembly time.
  5. "n" : An immediate integer operand with a known numeric value is allowed. Many systems cannot support assembly-time constants for operands less than a word wide. Constraints for these operands should use ’n’ rather than ’i’.
  6. "g" : Any register, memory or immediate integer operand is allowed, except for registers that are not general registers.

Refer to the site for more examples and other constraints. Hope this helped! :)

Upvotes: 2

Related Questions