9

Is there any way to get either Clang, GCC or VS to generate adc (add with carry) instructions only using Standard-C++(98/11/14)? (Edit: I mean in x64 mode, sorry if that wasn't clear.)

cooky451
  • 3,460
  • 1
  • 21
  • 39
  • those are `c` compilers – tay10r Jun 28 '13 at 18:48
  • 6
    @Taylor: "GCC" and "Visual Studio" are the names of compiler collections, which contain C++ compilers among others. `clang` is a C++ compiler. – Ben Voigt Jun 28 '13 at 18:50
  • @BenVoigt oh, then I guess that makes sense. – tay10r Jun 28 '13 at 18:51
  • FWIW, In x64, I've *never* been able to get any compiler to generate `adc` without inline assembly. – Mysticial Jun 28 '13 at 18:55
  • 2
    Is there any particular reason you want to do this? – Oliver Charlesworth Jun 28 '13 at 18:56
  • You can play around with the gcc explorer, maybe that will get you somewhere. – PlasmaHH Jun 28 '13 at 19:03
  • 3
    The whole point of C or C++ is to isolate the programmer from platform-specific assembly. The C or C++ standards don't ever mention ADC (which is an implementation detail) so the answer is **no**. There's no guarantee that a particular processor provides an ADC instruction... – syam Jun 28 '13 at 19:03
  • A definite no. *but*, there might be compiler extensions that are likely to yield one, e.g., clang's [__builtin_addc](http://clang.llvm.org/docs/LanguageExtensions.html#builtin-functions) functions. – Brett Hale Jun 28 '13 at 19:56
  • @syam I never wanted a guarantee. I'm just looking for code examples which include enough hints for the optimizer to generate an adc instruction. – cooky451 Jun 28 '13 at 20:07
  • 1
    @OliCharlesworth Yes, I'd like to implement efficient arbitrary precision arithmetic in ISO-C++, but I don't think it's possible at the moment. – cooky451 Jun 28 '13 at 20:08
  • The problem is also related to https://stackoverflow.com/questions/56101507/is-there-anything-special-about-1-0xffffffff-regarding-adc – Friedrich -- Слава Україні Sep 18 '19 at 15:15
  • The problem is also related to https://stackoverflow.com/questions/56101507/is-there-anything-special-about-1-0xffffffff-regarding-adc – Friedrich -- Слава Україні Sep 18 '19 at 15:17

3 Answers3

3

If your code makes a comparison and adds the result of the comparison to something, then an adc is typically emitted by gcc 5 (incidentally, gcc 4.8 does not emit an adc here). For example,

unsigned foo(unsigned a, unsigned b, unsigned c, unsigned d)
{
    return (a + b + (c < d));
}

assembles to

foo:
    cmpl    %ecx, %edx
    movl    %edi, %eax
    adcl    %esi, %eax
    ret

However, it is a bit tricky to get gcc to really emit an adc.

fuz
  • 88,405
  • 25
  • 200
  • 352
  • 1
    Yup, it is hard to get gcc to emit `adc` for conditional increments like the above. I _think_ gcc has a peephole or other stage that takes an `adc` with a `0` immediate and turns into a (generally worse on modern hardware) `xor eax, eax; setb al; add ..., eax` sequence. For example, if you remove `b` from the above so it is `(a + (c < d))` the natural sequence would be to use an `adc` with `0` but gcc does the weird `setb` thing. Same for ternary expression like `a += (c < d) ? 1 : 0`. However, if you do `a += (c < d) ? 3 : 2` or any other off-by-one values it _does_ use `adc eax, 2`. – BeeOnRope Jul 17 '18 at 21:35
  • 1
    So it seems only when `0` would be the immediate it does this weirdness. Also, if you use a branch, like `if (c < d) a++` it uses `adc`! I tried out [many examples on godbolt](https://godbolt.org/g/cGiU5p). – BeeOnRope Jul 17 '18 at 21:37
2

There's an __int128_t type available on GCC for amd64 and other 64bit targets, which will use a pair of add/adc instructions for a simple addition. (See the Godbolt link below).

Also, this pure ISO C code may compile to an adc:

uint64_t adc(uint64_t a, uint64_t b)
{
    a += b;
    if (a < b) /* should simplify to nothing (setting carry is implicit in the add) */
        a++; /* should simplify to adc r0, 0 */
    return a;
}

For me (ARM) it generated something kind of silly, but it compiles for x86-64 (on the Godbolt compiler explorer) to this:

    mov     rax, rdi  # a, a
    add     rax, rsi  # a, b
    adc     rax, 0    # a,
    ret
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
sh1
  • 4,324
  • 17
  • 30
  • 1
    I was going to add that I think `-funsafe-math-optimizations` might kill off that whole `if` block, but it doesn't seem to. I think C might allow such an optimisation, though, so look out there. – sh1 Jun 28 '13 at 23:17
  • 2
    unsigned wraparound is well-defined in C/C++, as binary integer. It's guaranteed to work properly. `-funsafe-math-optimizations` only affects floating point behaviour. Maybe you're thinking of signed integer wraparound being undefined behaviour? `-fwrapv` makes it well-defined (as 2's complement), but is *not* enabled by default. – Peter Cordes Jun 29 '16 at 14:50
1

If you compile a 64-bit signed addition for X86 (int64_t in C++ 11), the compiled code will contain an adc instruction.

Edit: code sample:

int64_t add_numbers(int64_t x, int64_t y) {
    return x + y;
}

On X86, the addition is implemented using an add instruction followed by an adc instruction. On X64, only a single add instruction is used.

Sam Harwell
  • 97,721
  • 20
  • 209
  • 280
  • Can you give a minimal example function? I have never seen it in any of my binaries, only lea(q) and similar. – PlasmaHH Jun 28 '13 at 19:05
  • Please see my edit, I should have included this before. The background here is to see if it is possible to build efficient arbitrary integer arithmetic in Standard-C++. :) – cooky451 Jun 28 '13 at 20:06