Date: Sat, 9 Jul 2022 21:24:47 -0700 From: Patrick Mahan <plmahan@gmail.com> To: Dimitry Andric <dim@freebsd.org> Cc: Marc Veldman <marc@bumblingdork.com>, freebsd-hackers@freebsd.org Subject: Re: Canonical way / best practice for 128-bit integers Message-ID: <CAFDHx1KtHcvy0Ma8oDASsfRWSz%2B0N3McKDM60uteyPoX_Y6_Eg@mail.gmail.com> In-Reply-To: <903B6B84-4AFE-4BB5-94F5-5DFD65BAC1B6@FreeBSD.org> References: <86CB4C22-9CAB-4578-8F11-962B4B08F756@bumblingdork.com> <903B6B84-4AFE-4BB5-94F5-5DFD65BAC1B6@FreeBSD.org>
next in thread | previous in thread | raw e-mail | index | archive | help
[-- Attachment #1 --]
On Sat, Jul 9, 2022 at 10:06 AM Dimitry Andric <dim@freebsd.org> wrote:
> On 9 Jul 2022, at 18:26, Marc Veldman <marc@bumblingdork.com> wrote:
> >
> > I’m working on some bluetooth code, and that involves handling 128-bit
> uuids.
> > There are various ways to handle in the FreeBSD codebase, like in sdp.h:
> >
> > /*
> > * SDP int128/uint128 parameter
> > */
> >
> > struct int128 {
> > int8_t b[16];
> > };
> > typedef struct int128 int128_t;
> > typedef struct int128 uint128_t;
> >
> > and in sys/dev/random/uint128.h
> >
> > #ifdef USE_REAL_UINT128_T
> > typedef __uint128_t uint128_t;
> > #define UINT128_ZERO 0ULL
> > #else
> > typedef struct {
> > /* Ignore endianness */
> > uint64_t u128t_word0;
> > uint64_t u128t_word1;
> > } uint128_t;
> > static const uint128_t very_long_zero = {0UL,0UL};
> > #define UINT128_ZERO very_long_zero
> > #endif
> >
> > Is there any recommended / standard way to handle 128 bit integers in a
> portable way?
>
> Of course recent compilers have built-in support for __int128_t and
> __uint128_t, but it comes with a *lot* of caveats: not supported on all
> architectures, definitely never on 32-bit ones, differences between
> compilers, and even compiler versions, etc etc.
>
> If you want it portable, the only way is to handle them as two 64 bit
> integers, and leave any arithmetic or conversion to library routines.
>
>
I second this, especially if you want to use any of the underlying assembly
code. For example, the Intel architecture supports the data type but does
not support 128-bit instructions but instead requires you to shift the
upper 64 bits to operate with a 64-bit instruction.
I found this out when we needed to speed up the ability to walk a 128-bit
bitmask (we were doing it the hard way). I wound up converting everything
back to 64-bit and saw a tremendous speed up in our code.
I mostly use it for defining the storage of an IP address (either IPv4 or
IPv6) where storage does not need to be exceedingly tight.
Also, I have only used it on Intel/AMD architectures. Back when I was
working in MIPS architectures (mostly Cavium multi-core) we did not have
this data type available. And I have only briefly looked at AArch64 so it
may or may not be supported there.
Patrick
[-- Attachment #2 --]
<div dir="ltr"><div dir="ltr">On Sat, Jul 9, 2022 at 10:06 AM Dimitry Andric <<a href="mailto:dim@freebsd.org">dim@freebsd.org</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On 9 Jul 2022, at 18:26, Marc Veldman <<a href="mailto:marc@bumblingdork.com" target="_blank">marc@bumblingdork.com</a>> wrote:<br>
> <br>
> I’m working on some bluetooth code, and that involves handling 128-bit uuids.<br>
> There are various ways to handle in the FreeBSD codebase, like in sdp.h:<br>
> <br>
> /*<br>
> * SDP int128/uint128 parameter<br>
> */<br>
> <br>
> struct int128 {<br>
> int8_t b[16];<br>
> };<br>
> typedef struct int128 int128_t;<br>
> typedef struct int128 uint128_t;<br>
> <br>
> and in sys/dev/random/uint128.h<br>
> <br>
> #ifdef USE_REAL_UINT128_T<br>
> typedef __uint128_t uint128_t;<br>
> #define UINT128_ZERO 0ULL<br>
> #else<br>
> typedef struct {<br>
> /* Ignore endianness */<br>
> uint64_t u128t_word0;<br>
> uint64_t u128t_word1;<br>
> } uint128_t;<br>
> static const uint128_t very_long_zero = {0UL,0UL};<br>
> #define UINT128_ZERO very_long_zero<br>
> #endif<br>
> <br>
> Is there any recommended / standard way to handle 128 bit integers in a portable way?<br>
<br>
Of course recent compilers have built-in support for __int128_t and<br>
__uint128_t, but it comes with a *lot* of caveats: not supported on all<br>
architectures, definitely never on 32-bit ones, differences between<br>
compilers, and even compiler versions, etc etc.<br>
<br>
If you want it portable, the only way is to handle them as two 64 bit<br>
integers, and leave any arithmetic or conversion to library routines.<br>
<br></blockquote><div><br></div><div>I second this, especially if you want to use any of the underlying assembly code. For example, the Intel architecture supports the data type but does not support 128-bit instructions but instead requires you to shift the upper 64 bits to operate with a 64-bit instruction.</div><div><br></div><div>I found this out when we needed to speed up the ability to walk a 128-bit bitmask (we were doing it the hard way). I wound up converting everything back to 64-bit and saw a tremendous speed up in our code.</div><div><br></div><div>I mostly use it for defining the storage of an IP address (either IPv4 or IPv6) where storage does not need to be exceedingly tight.</div><div><br></div><div>Also, I have only used it on Intel/AMD architectures. Back when I was working in MIPS architectures (mostly Cavium multi-core) we did not have this data type available. And I have only briefly looked at AArch64 so it may or may not be supported there.</div><div><br></div><div>Patrick</div></div></div>
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAFDHx1KtHcvy0Ma8oDASsfRWSz%2B0N3McKDM60uteyPoX_Y6_Eg>
