0

In the code below I'm ading two 64 bit numbers a and b to get the sum and the carry. Its working. But now I wanted to change such that I can also add a former carry to this summation and get the total result and carry . In my case formercarry is say 1, so i want a+b+formercarry = result + carry. I tried but couldn't access the formercarry value using the gcc asm. I think I'm not very sure about %2, %3 or %1 what to use at the asm code. Can anyone suggest anything please? I want the answer to be result =0x4B100361BFB66EB2 and carry =1

#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>

void add64(int carry,uint64_t a, uint64_t b, uint64_t *d, int *c)
{
    *c=0;
    char carry1;
     __asm__("mov %2,%%rax\n\t"
             "adc %3,%%rax\n\t"
             "setc %1\n\t"
             "mov %%rax,%0\n\t"
              :"=r"(*d),"=r"(carry1)
              :"r"(a),"r"(b)
              :"%rax");
    if (carry1)
        *c=1;
    else 
        *c=0;
}

int main()
{
    uint64_t a=0xA234BDFA12CD4379, b=0xA8DB4567ACE92B38;
    uint64_t result;
    int carry;
    int formercarry = 1;
    c1 = cpucycles();
    add64(formercarry, a, b, &result, &carry);
    printf("Sum = %lx,carry= %d\n", result, carry);
}
Mikhail Maltsev
  • 1,632
  • 11
  • 21
  • You could set a register to all 1's and then add the 0/1 value of the carry argument. – Gene Oct 13 '17 at 19:02
  • how will setting a register to all 1's help ? more importantly I think I'm ending up accessing wrong locations using % syntax. Thats where I need help. – Tanushree Banerjee Oct 13 '17 at 19:14
  • 1
    If you really want to use inline assembly (Peter Cordes previously suggested to you other ways to do it) then you could try this: `void add64(int carryin, uint64_t a, uint64_t b, uint64_t * d, int *c) { *d = a; __asm__("neg %1\n\t" "adc %3, %0\n\t" :"+r"(*d), "+&r"(carryin), "=@ccc"(*c) :"r"(b)); }` – Michael Petch Oct 13 '17 at 19:37
  • The side effect of `neg` is that it happens to set the CF flag to 1 if the value in the register is 1 and 0 if it happened to be zero. Since the CF flag has now been to the value of the `carryin` variable we can use that to feed into the `adc` instruction. The assumption though is that the carryin flag will always be either 0 or 1 (and nothing else). As well rather than using %0, %1 etc I'd personally use symbolic names using the square brackets before the constraints. Easier to maintain and easier to read. – Michael Petch Oct 13 '17 at 19:42
  • As for your question about how the %0, %1 etc are arrived at. Each constraint you pass into the assembly template is given anumber. The first constraint that appears is %0 and each successive one is one higher. In your code for example `"=r"(*d)` is %0 ,`"=r"(carry1)` is %1, `"r"(a)` is %2, `"r"(b)` is %3. – Michael Petch Oct 13 '17 at 19:46
  • Inline assembly is not for the faint of heart. I recommend against it if you can code it in _C_. – Michael Petch Oct 13 '17 at 19:50
  • 1
    What exactly are you really trying to do with this function? There's probably [an XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) here. **There's no way that asm like this can be part of a high-performance extended-precision function, because getting the carry out into an actual integer and then putting it back into the carry flag is much worse than `add / adc / adc`**. If you already have the carry in a separate integer register, add it with `add`. (But then CF won't be the carry-out from the entire operation, just the last `add`). – Peter Cordes Oct 14 '17 at 04:49
  • AFAIK, it's not possible to build your own `_addcarry_u64()` with inline asm that will compile efficiently on gcc and clang, because there's no syntax for taking flag inputs, only gcc6 flag *outputs*. So use ICC with the intrinsic, or write your whole function in asm (like http://gmplib.org/) – Peter Cordes Oct 14 '17 at 04:50

0 Answers0