1

I have the following function:

struct sbi_i128_t {
  union {
    unsigned long long r[2];
    unsigned __int128 i;
  };
};

/// Unsigned 128-bit integers addition with overflow indication.
static inline sbi_i128_t sbi_addo_i128(sbi_i128_t a, sbi_i128_t b, unsigned char *o) {
  sbi_i128_t r;
  *o = _addcarry_u64(0, a.r[0], b.r[0], &r.r[0]);
  *o = _addcarry_u64(*o, a.r[1], b.r[1], &r.r[1]);
  return r;
}

To modify it for signed integers I need to return overflow flag (OF) instead of carry one (CF) from the last call. What is best way you may suggest, taking into account that the function must keep inlining, and inline assembly is avoided? Compiler - GCC v11+, but using other compilers is also an option.

-edit-:

With __builtin_add_overflow() the best implementation I see is:

/// Unsigned 128-bit integers addition with overflow indication.
static inline sbi_i128_t sbi_addo_i128(sbi_i128_t a, sbi_i128_t b, unsigned char *o) {
  sbi_i128_t r;
  
  *o = _addcarry_u64(0, a.r[0], b.r[0], &r.r[0]);
  uint64_t ov = *o ? -1 : 0;  
  
  bool o1 = __builtin_add_overflow(a.r[1], b.r[1], &r.r[1]);
  bool o2 = __builtin_sub_overflow(r.r[1], ov, &r.r[1]);
  
  *o = o1 | o2;
  return r;
}

-edit-: But with __int128 directly (thx Peter Cordes):

__int128 foo(__int128 a, __int128 b, unsigned char *signed_OF){
    __int128 retval;
    *signed_OF = __builtin_add_overflow(a,b, &retval);

    return retval;
}
Akon
  • 335
  • 1
  • 11
  • If you can use GNU C extensions, use `__int128` with `__builtin_add_overflow`. https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html (note the function signature: it returns a bool overflow and takes an output operand by reference) – Peter Cordes Jan 17 '23 at 16:35
  • Thx for answer. As far as I know __int128 is not supported by __builtin_xxx_overflow(). But actually I need int64 addition with carry-in flag and overflow-out flag. Taking into account carry-in flag is a key moment. __builtin_add_overflow() breaks carry chain. – Akon Jan 17 '23 at 17:20
  • `__builtin_add_overflow` works for me with `__int128` with GCC12: https://godbolt.org/z/oMhc5nn74 making the asm you want. Also GCC11 works, and I assume much earlier. – Peter Cordes Jan 17 '23 at 17:38
  • Yep! Many thanks for the exceptional answer. I was confused by the gcc's docs where __int128 doesn't appear explicitly. – Akon Jan 17 '23 at 18:38
  • ..but still return to the original question when I need signed 128-bit integers addition with input carry and overflow indication. I.e. it's needed something like __builtin_addcarry_overflow – Akon Jan 17 '23 at 18:53
  • Oh, you're using this as a building-block for integers wider than 128-bit? Most compilers are incapable of making efficient asm even for unsigned chains of `adc` instructions. That's one reason libraries like GMP use hand-written asm. But why would you need both carry-out and signed-overflow from the same same addition? The highest limb has the sign bit, but all lower limbs are effectively unsigned. – Peter Cordes Jan 17 '23 at 23:43
  • If for some reason you actually did need it, you can do the same add twice with both `__int128` and `unsigned __int128`. Hopefully GCC is able to CSE (common subexpression elimination) it down to 1 `add` instruction and use both FLAGS outputs. Or `(unsigned __int128)retval < (unsigned __int128)a`. But unfortunately no, https://godbolt.org/z/nr9z5Gde1 shows compilers don't optimize that way. – Peter Cordes Jan 17 '23 at 23:53
  • You can't just `pushf pop eax` and then `and eax` with the desired bitmask? – puppydrum64 Jan 18 '23 at 11:44
  • @Peter: Yep, that function might be a building block of int256, etc. addition. On the final step there is signed addition, all previous are unsigned ones, as you mentioned. And yes, you feel my thought - I'm trying to find a way to force compiler to generate optimal code (as ideal solution) without falling down to inline assembly. If it would be exist an intrinsic _addcarry_out_overflow_i64() that would be a solution. – Akon Jan 18 '23 at 12:12
  • @puppydrum64: That breaks the function inlining, I afraid. – Akon Jan 18 '23 at 12:14

0 Answers0