use __builtin_add_overflow() in st_add() with Clang

Clang and GCC optimize away comparisons of overflow checks by checking
the carry flag on x64.  GCC does the same on ARM64, but Clang currently
(version 22.1) doesn't.

It does this optimization for overflow checks that use its builtin
function __builtin_add_overflow(), though.  Provide a non-generic
lookalike for size_t that does the same checks as before as a fallback
and use the original with Clang.  Use it on all platforms for simplicity.

On an Apple M1 I get a nice speedup for a command that builds lots of
strings using a strbuf, which exercises the st_add3() in strbuf_grow()
for every line of output:

Benchmark 1: ./git_main cat-file --batch-all-objects --batch-check='%(objectname)'
  Time (mean ± σ):     120.4 ms ±   0.2 ms    [User: 113.8 ms, System: 6.0 ms]
  Range (min … max):   120.1 ms … 121.1 ms    24 runs

Benchmark 2: ./git cat-file --batch-all-objects --batch-check='%(objectname)'
  Time (mean ± σ):     115.5 ms ±   0.1 ms    [User: 108.6 ms, System: 5.8 ms]
  Range (min … max):   115.2 ms … 115.8 ms    25 runs

Summary
  ./git cat-file --batch-all-objects --batch-check='%(objectname)' ran
    1.04 ± 0.00 times faster than ./git_main cat-file --batch-all-objects --batch-check='%(objectname)'

Suggested-by: Jeff King <peff@peff.net>
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
main
René Scharfe 2026-05-18 22:25:02 +02:00 committed by Junio C Hamano
parent c13d0f7bd4
commit 29d9fdcf10
1 changed files with 20 additions and 2 deletions

View File

@ -614,12 +614,30 @@ static inline bool strip_suffix(const char *str, const char *suffix,
int git_open_cloexec(const char *name, int flags);
#define git_open(name) git_open_cloexec(name, O_RDONLY)

static inline size_t st_add(size_t a, size_t b)

/*
* Help Clang; GCC generates the same instructions for both variants on
* x64 and aarch64.
*/
#ifdef __clang__
#define st_add_overflow __builtin_add_overflow
#else
static inline bool st_add_overflow(size_t a, size_t b, size_t *out)
{
if (unsigned_add_overflows(a, b))
return true;
*out = a + b;
return false;
}
#endif

static inline size_t st_add(size_t a, size_t b)
{
size_t result;
if (st_add_overflow(a, b, &result))
die("size_t overflow: %"PRIuMAX" + %"PRIuMAX,
(uintmax_t)a, (uintmax_t)b);
return a + b;
return result;
}
#define st_add3(a,b,c) st_add(st_add((a),(b)),(c))
#define st_add4(a,b,c,d) st_add(st_add3((a),(b),(c)),(d))