On Wed, Jul 29, 2026 at 09:43:37AM +0200, Jakub Jelinek wrote:
> On Wed, Jul 29, 2026 at 12:03:22AM -0400, Michael Meissner wrote:
> > I need to look into this more, but given TImode integer support is more
> > limited in the PowerPC, I wonder if maybe we should use DImode for
> > limbs on 64-bit systems (and possibly SImode if we do BitInt for the
> > older 32-bit systems).
> 
> No 64-bit target has a great support for TImode or 32-bit target for DImode,
> in both cases those are emulated with some limited hardware support at
> times, some operations are done through libgcc libcalls, others through
> double-world operations, some emulated in vector instructions, ...
> 
> That is why GCC differentiates between what limb should be used for
> ABI purposes (info->abi_limb_mode), so what scalar mode is used to determine
> the size and alignment of the larger _BitInt(N) N > 128 (for small _BitInt(N),
> the psABIs typically treat it if N <= 8 as QImode, if N <= 16 as HImode,
> if N <= 32 as SImode and then vary for bits until N <= 128), and
> another limb size used internally and in libgcc APIs (info->limb_mode).
> Currently, the only 2 supported cases are that the modes are always the same
> (what is used e.g. on x86_64/i686 or s390x) or the same for some of the
> really small cases and abi_limb_mode being twice the size of limb_mode for
> the larger ones.
> 
> So, the decision which mode to use in the psABI doesn't need to take into
> account whether efficient instructions exist to implement all the needed
> operations (at least as long as you don't choose something larger than
> twice the size of what can be efficiently done).
> 
> The question is about size/alignment.  E.g. on x86_64
> for N in [1, 8]:
> sizeof(char) == alignof(char) == sizeof(_BitInt(N)) == alignof(_BitInt(N)) == 
> 1
> for N in [9, 16]:
> sizeof(short) == alignof(short) == sizeof(_BitInt(N)) == alignof(_BitInt(N)) 
> == 2
> for N in [17, 32]:
> sizeof(int) == alignof(int) == sizeof(_BitInt(N)) == alignof(_BitInt(N)) == 4
> for N in [33, 64]:
> sizeof(long) == alignof(long) == sizeof(_BitInt(N)) == alignof(_BitInt(N)) == 
> 8
> sizeof(__int128) == alignof(__int128) == 16
> for N in [65, 65535]:
> sizeof(_BitInt(N)) == (N + 63) / 64 * 8
> alignof(_BitInt(N)) == 8
> E.g. on aarch64 it is the same, except:
> for N in [65, 65535]:
> sizeof(_BitInt(N)) == (N + 127) / 128 * 16
> alignof(_BitInt(N)) == 16
> In all cases the larger _BitInt (for x86_64 for N >= 65, for aarch64 for
> N >= 129) are passed/returned as if it is
> struct { unsigned long bitint[(N + 63) / 64]; }
> for x86-64 and
> struct { unsigned __int128 bitint[(N + 127) / 128]; }
> for aarch64.
> Now, under the hood both of the targets handle the really large _BitInt
> using loops or straight line code over 64-bit limbs because 64-bit
> operations are efficient on the target.
> 
> The ugly thing about the x86_64 (or s390x) decision that some people
> don't like is that we have the __int128 type and that passing of
> _BitInt(N) for N in [65, 128] may differ from __int128 (on x86_64
> actually it doesn't), and more importantly that
> alignof (__int128) != alignof (_BitInt(128)).

Can you elaborate on that?  On s390x, alignment/size of __int128 and
_BitInt(128) are the same and both are passed via memory.  Also
_BitInt(N) with 64<N<128 should have same sizeof/alignof as __int128 and
being passed via memory.  Since those _BitInt(N) even require extension,
there shouldn't be any difference compared to __int128.

That being said, it was deliberate to make __int128 and _BitInt(128)
behave the same.  Otherwise we could have went for passing _BitInt(128)
via vector registers for example.

Cheers,
Stefan

> 
> The ugly thing about the aarch64 (and arm, loongarch, riscv) is
> that it wastes for some values of N more storage, so e.g.
> sizeof (_BitInt(513)) on x86_64 is 72, but on aarch64 is 80.
> 
> That is one choice the psABI needs to be made (and one problematic
> point which is solvable by 1-2 days of work in the generic code hopefully
> is that we don't support big-endian abi_limb_mode != limb_mode right now:
> for little endian padding bits are at highest addresses, so if we
> treat the _BitInt as a sequence of say 64-bit limbs even when the psABI
> says 128-bit limbs, worst case we have one full 64-bit limb of padding bits
> at the highest address and depending on the choice below either don't
> touch that limb at all or do some extra extension into it; but on big-endian
> the extra full padding limb comes first and all of sudden all accesses have
> to be biased by extra 8 bytes).
> 
> Another important choice is what to do with padding bits which are
> present in most of the _BitInts.  One choice is say the padding bits
> are undefined, this in theory requires less care during storing of values
> and more care during reading of them (one has to sign or zero extend them
> to full (internal) limb).  Another choice is to say it must be always
> sign-extended (for signed _BitInt) or zero-extended (for unsigned _BitInt),
> extra effort during storing (needs to extend there), less effort during
> reading (in some cases it can avoid the extension).  There is a third
> choice used for loongarch, don't repeat that, that is just weird.
> 
> Now, the GCC implementation of either of the strategy isn't perfect yet,
> in some cases we extend in both cases, some of it could be improved
> over time.  One limitation is that RTL generally for the really small
> sizes (single limb _BitInt) expects extension before operation rather
> than after it, so for those with undefined padding bits we can emit
> unnecessarily extension both before and after operations.  And also
> when emitting straight line code or loops (padding in that case is
> typically used outside of those loops just for one limb) some extension
> can happen unnecessarily too.  But the extended case does have to
> add extra extension not present on the undefined padding bits targets,
> especially for the case of internal limbs full of padding bits (and that
> again is right now supported only for little-endian but can in theory
> be adjusted also for big endian).
> 
> And endianity, what ordering of limbs is used for _BitInt using more
> than one psABI limb.  Currently GCC doesn't really support endianity
> of the limb ordering being different from endianity of the target
> and some choices (different abi_limb_mode from mode and different endianity
> from target endianity at the same time) are impossible to support.
> 
> So, the above are the choices a target needs to make (plus decide whether
> passing as a struct mentioned above is best for _BitInt or something
> different).  And I'm arguing that the choice should be made primarily
> based on what you think is best for the architecture in question and not
> based on current GCC limitations, the psABI will apply to other compilers
> too.  But sure, some choices will be harder to implement than others.
> I'd strongly prefer if you don't choose something really strange, e.g.
> using little-endian ordering of limbs on big-endian target or big-endian
> ordering of limbs on little-endian target, that both requires extra effort
> to support and ressurrect PDP-endianish stuff.  Similarly, going for
> absurd sizes, say for N >= 2048 go with
> sizeof(_BitInt(N)) == (N + 2047) / 2048 * 256
> alignof(_BitInt(N)) == 256
> etc.
> Under the hood, most _BitInt operations aren't really vectorizable.  There
> is a small subset of them which are, moves, bitwise and/or/xor/not, and
> that is about it, even basic ops like addition, subtraction, comparison
> (especially non-equality one), etc. are hardly vectorizable.  And
> some operations are done through libgcc.a libcalls only (multiplication,
> division/modulo, conversions to/from binary and decimal floating point),
> because it would require too much code.
> 
> The primary question is, how much do I care about
> alignof (_BitInt(128)) == alignof (__int128)
> and is it so important that I want to pay extra 8 bytes for many of those?
> Obviously for 32-bit targets it is more about
> alignof (_BitInt(64)) == alignof (long long)
> and extra 4 bytes for many.
> 
>       Jakub
> 

Reply via email to