On Thu, Jun 1, 2023 at 9:42 AM Mason <slash.tmp@xxxxxxx> wrote: > > Hello, > > As far as I can tell, intrinsics _addcarry_u64() and _addcarryx_u64() are > plain wrappers around the same __builtin_ia32_addcarryx_u64() function. > > https://github.com/gcc-mirror/gcc/blob/master/gcc/config/i386/adxintrin.h > > Thus, I wonder: what is the use-case for the wrappers? > Why would a programmer not call the builtin directly? > Is it for compatibility with Intel compilers? Builtins are internal implementation detail, it is not published API. Although rarely, builtins can be changed for some reason or another, while intrinsic functions from adxintrin.h follow published API. > Also, based on the names, I would have assumed that > _addcarry_u64 generates adc > while > _addcarryx_u64 generates adcx/adox ? No, they all generate add/adc. There is no use case to maintain two interleaved carry chains, IOW rewriting: --cut here-- #include <immintrin.h> int foo (int A, int B, int D, int E) { _Bool carry1 = 0, carry2 = 0; int C, F; carry1 = _addcarryx_u32 (carry1, A, B, &C); carry2 = _addcarryx_u32 (carry2, D, E, &F); carry1 = _addcarryx_u32 (carry1, A, B, &C); carry2 = _addcarryx_u32 (carry2, D, E, &F); return C + F; } --cut here-- to: --cut here-- #include <immintrin.h> int foo (int A, int B, int D, int E) { _Bool carry1 = 0, carry2 = 0; int C, F; carry1 = _addcarryx_u32 (carry1, A, B, &C); carry1 = _addcarryx_u32 (carry1, A, B, &C); carry2 = _addcarryx_u32 (carry2, D, E, &F); carry2 = _addcarryx_u32 (carry2, D, E, &F); return C + F; } --cut here-- will give you: movl %edi, %eax addl %esi, %eax movl %edx, %eax adcl %esi, %edi addl %ecx, %eax adcl %ecx, %edx leal (%rdi,%rdx), %eax ret Uros. > Relevant past discussion: > https://gcc.gnu.org/legacy-ml/gcc-help/2017-08/msg00100.html > > Regards >