Commit 9f4aa4d8 authored by jan.koester's avatar jan.koester
Browse files

x86_64: provide the __divti3 family in libc

<chrono>'s time_point formatter divides __int128 values, which the
compiler lowers to __divti3. x86_64 links -nostdlib without a builtins
archive, so blogi_stdlib_gaps_test failed to link. Add __divti3,
__udivti3, __modti3, __umodti3 and __udivmodti4 (native divq fast path,
shift-and-subtract otherwise), picked up by the arch rt/ glob like the
powerpc counterpart. Differentially tested against libgcc; 157/157 e2e.

Co-Authored-By: default avatarClaude Opus 5.5 <noreply@anthropic.com>
parent 9a8df3eb
Loading
Loading
Loading
Loading
+77 −0
Changes for src/linux/arch/x86_64/rt/int128.cpp: 77 added lines, 0 removed lines.
Original line number Diff line number Diff line
/* 128-bit integer division for x86_64: __int128 division (used by
 * <chrono>'s formatter, <ratio>, <random>, see type_traits' __spp_i128)
 * compiles to calls of the __divti3 family, which normally come from
 * libgcc/compiler-rt -- systempp links with -nostdlib and has no builtins
 * archive on x86_64, so libc.so provides them itself (the powerpc
 * counterpart is arch/powerpc/rt/int128.*).
 *
 * Only shifts, compares and subtraction on unsigned __int128 are used
 * here, which clang/gcc emit inline; anything that divides two 128-bit
 * values would recurse into these very functions. */

typedef unsigned __int128 u128;
typedef __int128 i128;
typedef unsigned long long u64;

namespace {

u128 udivmod(u128 n, u128 d, u128* rem) {
    if (d == 0) {           /* undefined behaviour; don't trap */
        if (rem) *rem = 0;
        return 0;
    }
    if ((n >> 64) == 0 && (d >> 64) == 0) {     /* fast path: native divq */
        u64 a = (u64)n, b = (u64)d;
        if (rem) *rem = a % b;
        return a / b;
    }
    if (d > n) {
        if (rem) *rem = n;
        return 0;
    }
    /* shift-and-subtract over the bits where the quotient can be nonzero */
    int shift = __builtin_clzll((u64)(d >> 64) ? (u64)(d >> 64) : (u64)d) + ((d >> 64) ? 0 : 64)
              - (__builtin_clzll((u64)(n >> 64) ? (u64)(n >> 64) : (u64)n) + ((n >> 64) ? 0 : 64));
    u128 q = 0;
    d <<= shift;
    for (; shift >= 0; shift--) {
        q <<= 1;
        if (n >= d) {
            n -= d;
            q |= 1;
        }
        d >>= 1;
    }
    if (rem) *rem = n;
    return q;
}

inline u128 uabs(i128 v) { return v < 0 ? -(u128)v : (u128)v; }

} // namespace

extern "C" {

u128 __udivti3(u128 a, u128 b) { return udivmod(a, b, nullptr); }

u128 __umodti3(u128 a, u128 b) {
    u128 r;
    udivmod(a, b, &r);
    return r;
}

/* C semantics: truncating division, the remainder takes the dividend's sign */
i128 __divti3(i128 a, i128 b) {
    u128 q = udivmod(uabs(a), uabs(b), nullptr);
    return (i128)(((a < 0) != (b < 0)) ? -q : q);
}

i128 __modti3(i128 a, i128 b) {
    u128 r;
    udivmod(uabs(a), uabs(b), &r);
    return (i128)(a < 0 ? -r : r);
}

u128 __udivmodti4(u128 a, u128 b, u128* rem) { return udivmod(a, b, rem); }

}