Date: Wed, 3 Sep 2025 13:36:11 +0000
> > What says who? What guarantees your compiler when you specify _BitInt(9) to go "nah he wants a specialized 9bit FPGA register" instead of just giving you a 16bit register with 7 unused bits?
>That will be up to the compiler.
Precisely! It's going to choose whatever, not what you whished it would have been.
It's Voodoo programming.
>>> How can something that is not yet a feature of the language, have "always" been UB?
>> It already exists in C. That the argument, whatever C is doing C++ will adopt for interoperability, there is no room for alternative behavior here.
> In C, alignment is not UB.
Alignment is implementation defined. Aliasing is the UB part!
> Alignment is about memory addresses. [...] /Values/ don't have alignments.
That's wrong, that's just wrong.
The compiler can optimize "values" to not have an address, because it maybe a temporary that is short lived enough to fit and complete it's journey in a register, and constants can be embedded in the instructions themselves.
But your CPU only has so many registers, at which point it has to swap them to memory, values in memory must have an address and that address must be aligned.
To claim that "values don't have alignments", and therefore it doesn't matter is just wrong. That's not how it works.
Not to mention that the registers that will be used for general arithmetic are likely different from the ones used for sha operations.
I can just summarize this entire conversation as: That's not how it works.
-----Original Message-----
From: David Brown <david.brown_at_[hidden].no>
Sent: Wednesday, September 3, 2025 10:39
To: Tiago Freire <tmiguelf_at_hotmail.com>; std-proposals_at_lists.isocpp.org
Subject: Re: [std-proposals] D3666R0 Bit-precise integers
On 03/09/2025 07:58, Tiago Freire wrote:
>
>> Well, you can only get the exact sizes on platforms that support those exact sizes. On other platforms, the _BitInt types need to be contained in something larger if they are cannot be provided directly. So a _BitInt(9) is likely to need an FPGA or special hardware for its size to be exactly 9 bits. But they can be implemented in an obvious way on other platforms, just with padding bits. Alignment will be implementation dependent, just as it is for all other types.
>
> What says who? What guarantees your compiler when you specify _BitInt(9) to go "nah he wants a specialized 9bit FPGA register" instead of just giving you a 16bit register with 7 unused bits?
>
That will be up to the compiler.
"Compilers" for FPGA hardware are not like compilers for normal conventional serial cpus. The trend for at least some parts of FPGA design is to use languages like C (and to a currently much lesser extent, a subset of C++) to describe the operation. But the compiler does not allocate variables to memory addresses with alignments, or use cpu registers, or generate a sequence of cpu instructions for doing arithmetic. It generates hardware registers, hardware adder or arithmetic blocks, hardware step machines and sequencers. You write your code in a particular specialised way, so that it can be turned into hardware.
And then you take that same code, compile it with a desktop C++ compiler, and get the same results from the calculations. (Actually, you usually do the desktop simulations first.) You are not concerned about alignment - you are concerned about having the same values.
>
>> How can something that is not yet a feature of the language, have "always" been UB?
>
> It already exists in C. That the argument, whatever C is doing C++ will adopt for interoperability, there is no room for alternative behavior here.
>
In C, alignment is not UB.
>
>> Alignment is not an issue when converting anything. Converting between types is done by value. Alignment only matters for pointers.
>> And the alignment of different types is not UB, it is implementation-defined behaviour.
>
> What? It applies to operations on "values".
No, it does not.
Alignment is about memory addresses. If a particular type (say, a
uint32_t) on a given implementation has a 4-byte alignment, then all
valid addresses to uint32_t have their lowest two bits clear. If you
construct an address where the lowest two bits are not 0 (say, by
converting from a char pointer, or an uintptr_t) and try to use that as
a pointer to access a uint32_t, then you have UB. It /might/ work -
many hardware platforms support unaligned accesses. It /might/ cause a
hardware exception or other crash - some instructions on some cpus do
not support aligned access (such as some SIMD instructions). Since it
is UB, the compiler can assume it never happens, which can affect code
generation - it's UB, so you have no guarantees of any kind.
/Values/ don't have alignments.
> Why do think there exists such a thing as an alignment requirement?
> If it's not a register or is part of the instruction. EVERYTHING is in memory and has an address.
No, everything is /not/ in memory, nor does everything have an address.
/Values/ don't have addresses. /rvalues/ do not have addresses -
objects and lvalues have addresses. The number 42 does not have an
address. If x and y are ints, then (x + y) is has a value and type int,
but does not have an address. If you write "int foo = 23;", then the
/object/ "foo" has an address, and it contains the /value/ 23. The
value in foo has no address. The object "foo" will be put in memory
(baring optimisation and the "as if" rule) at an address that satisfies
the implementation-defined (not UB!) alignment requirements for an "int"
in that particular implementation. The value 23 that is contained in
"foo" has no alignment requirements, because it is a value.
> You can't emit an instruction to operate on your "values" (which are in memory and therefore referenced in assembly by an address) because that is out of spec for the hardware. And because you can't control exactly what instructions your compiler is going to emit, it's not "implementation-defined", it's undefined. Nobody knows what's going to happen.
> This is not how computers work.
I know how computers work. I know how programming languages work. I
suspect you are misunderstanding what objects are (in C and C++
terminology) and what values are. Hopefully my explanation above has
made things a bit clearer, so we can move on.
>
> That's why when you say things like "When you are dealing with hardware with 9-bit integers, or an SHA accelerator with 384-bit registers, you want types that have the exact size."
> And I ask "How does _BitInt(384) operate on SHA accelerator with 384-bit registers when it's not aligned?", and now you are telling me "It doesn't! We just copy it over to a type that does."...what are we talking about?
>
> Do you see the problem?
I see that you are misunderstanding me, and that is a problem for this
conversation.
Forget about alignment - it is a minor implementation detail.
Forget, for now, about the actual sizes of the containers for the
_BitInt types. In C and C++ it is perfectly allowable for types to have
containers that are bigger than the value bits, so that you have extra
padding bits. (A prime example is "bool", which is typically
implemented in an 8-bit byte with one value bit and 7 padding bits.)
Then understand what the _BitInt types do - what they model. Understand
how these can be useful in software when you want to model an integer
type with a specific number of bits. C and C++ are high-level languages
- they are defined in terms of an abstract machine, not specific
hardware, and their types are for use in software. At the high level,
alignment is irrelevant and the size of containers is usually equally
irrelevant (or easily handled - such as by using "sizeof" in your
memcpy() calls).
Then understand that C and C++ are languages intended to be efficient on
real hardware, and for low-level non-portable systems programming.
Appreciate how fixed-size integer types can map to hardware. Sometimes
they can be used in ways that map to common hardware, sometimes to very
specialised hardware. In some cases, precise size of containers now
becomes relevant because your accesses are not simply free accesses
within a read-write memory block. And at the lowest level of the
hardware, if you are talking about objects in memory rather than just
values, alignment must match up.
>
> -----Original Message-----
> From: David Brown <david.brown_at_hesbynett.no>
> Sent: Tuesday, September 2, 2025 23:17
> To: Tiago Freire <tmiguelf_at_[hidden]>; std-proposals_at_lists.isocpp.org
> Subject: Re: [std-proposals] D3666R0 Bit-precise integers
>
>
>
> On 02/09/2025 18:38, Tiago Freire wrote:
>> See here's my problem:
>>
>>> When you are dealing with hardware with 9-bit integers, or an SHA accelerator with 384-bit registers, you want types that have the exact size.
>>
>>> Ultimately, this will be implementation-dependent. But it seems
>>> natural (to me) that alignment of a _BitInt should normally be that
>>> of the any standard unsigned integer type that can contain them, or
>>> uint64_t for larger _BitInt's. (That's what C23 does.)
>>
>> These 2 statements are contradictory. It is either one or the other and it can't be both.
>
> Well, you can only get the exact sizes on platforms that support those exact sizes. On other platforms, the _BitInt types need to be contained in something larger if they are cannot be provided directly. So a
> _BitInt(9) is likely to need an FPGA or special hardware for its size to be exactly 9 bits. But they can be implemented in an obvious way on other platforms, just with padding bits. Alignment will be implementation dependent, just as it is for all other types.
>
>> The real answer is actually neither, this is actually UB. It's all UB, it always has been.
>
> How can something that is not yet a feature of the language, have "always" been UB?
>
>> Since alignment is not specified converting it to anything is UB.
>
> Alignment is not an issue when converting anything. Converting between types is done by value. Alignment only matters for pointers.
>
> And the alignment of different types is not UB, it is implementation-defined behaviour.
>
>> Maybe it works on some hardware that are tolerant with alignment, and some implementations may try to align with what you probably want to do anyways, but it's still UB.
>>
>
> _BitInt types would always be accessed via pointers that are appropriately aligned, unless you are playing silly buggers with converting pointer types - just like for all other types.
>
>> You want to work with specialized registers? The best way is for the compiler to provide concrete types that satisfy both the size and alignment requirements for those registers.
>
> If you have hardware that works best with types of a particular bit size, why not have standardised types that are of that particular bit size - to the best of the target implementation's abilities?
>
>> You can't just use a _BitInt and expect it to align because it doesn't have too, and according to you own words it can't be used for larger specialized registers, that's what you and everyone wants to do but that is not what it actually does.
>>
>
> Why would you not have to have appropriate alignment for _BitInt types?
>
>> That's why I picked that example. It's either a chunky boy or it doesn't align, the end.
>>
>
> For C23 _BitInt, _BitInt(512) will have the same alignment as uint64_t.
> I would expect the same for C++ _BitInt.
>
>
>That will be up to the compiler.
Precisely! It's going to choose whatever, not what you whished it would have been.
It's Voodoo programming.
>>> How can something that is not yet a feature of the language, have "always" been UB?
>> It already exists in C. That the argument, whatever C is doing C++ will adopt for interoperability, there is no room for alternative behavior here.
> In C, alignment is not UB.
Alignment is implementation defined. Aliasing is the UB part!
> Alignment is about memory addresses. [...] /Values/ don't have alignments.
That's wrong, that's just wrong.
The compiler can optimize "values" to not have an address, because it maybe a temporary that is short lived enough to fit and complete it's journey in a register, and constants can be embedded in the instructions themselves.
But your CPU only has so many registers, at which point it has to swap them to memory, values in memory must have an address and that address must be aligned.
To claim that "values don't have alignments", and therefore it doesn't matter is just wrong. That's not how it works.
Not to mention that the registers that will be used for general arithmetic are likely different from the ones used for sha operations.
I can just summarize this entire conversation as: That's not how it works.
-----Original Message-----
From: David Brown <david.brown_at_[hidden].no>
Sent: Wednesday, September 3, 2025 10:39
To: Tiago Freire <tmiguelf_at_hotmail.com>; std-proposals_at_lists.isocpp.org
Subject: Re: [std-proposals] D3666R0 Bit-precise integers
On 03/09/2025 07:58, Tiago Freire wrote:
>
>> Well, you can only get the exact sizes on platforms that support those exact sizes. On other platforms, the _BitInt types need to be contained in something larger if they are cannot be provided directly. So a _BitInt(9) is likely to need an FPGA or special hardware for its size to be exactly 9 bits. But they can be implemented in an obvious way on other platforms, just with padding bits. Alignment will be implementation dependent, just as it is for all other types.
>
> What says who? What guarantees your compiler when you specify _BitInt(9) to go "nah he wants a specialized 9bit FPGA register" instead of just giving you a 16bit register with 7 unused bits?
>
That will be up to the compiler.
"Compilers" for FPGA hardware are not like compilers for normal conventional serial cpus. The trend for at least some parts of FPGA design is to use languages like C (and to a currently much lesser extent, a subset of C++) to describe the operation. But the compiler does not allocate variables to memory addresses with alignments, or use cpu registers, or generate a sequence of cpu instructions for doing arithmetic. It generates hardware registers, hardware adder or arithmetic blocks, hardware step machines and sequencers. You write your code in a particular specialised way, so that it can be turned into hardware.
And then you take that same code, compile it with a desktop C++ compiler, and get the same results from the calculations. (Actually, you usually do the desktop simulations first.) You are not concerned about alignment - you are concerned about having the same values.
>
>> How can something that is not yet a feature of the language, have "always" been UB?
>
> It already exists in C. That the argument, whatever C is doing C++ will adopt for interoperability, there is no room for alternative behavior here.
>
In C, alignment is not UB.
>
>> Alignment is not an issue when converting anything. Converting between types is done by value. Alignment only matters for pointers.
>> And the alignment of different types is not UB, it is implementation-defined behaviour.
>
> What? It applies to operations on "values".
No, it does not.
Alignment is about memory addresses. If a particular type (say, a
uint32_t) on a given implementation has a 4-byte alignment, then all
valid addresses to uint32_t have their lowest two bits clear. If you
construct an address where the lowest two bits are not 0 (say, by
converting from a char pointer, or an uintptr_t) and try to use that as
a pointer to access a uint32_t, then you have UB. It /might/ work -
many hardware platforms support unaligned accesses. It /might/ cause a
hardware exception or other crash - some instructions on some cpus do
not support aligned access (such as some SIMD instructions). Since it
is UB, the compiler can assume it never happens, which can affect code
generation - it's UB, so you have no guarantees of any kind.
/Values/ don't have alignments.
> Why do think there exists such a thing as an alignment requirement?
> If it's not a register or is part of the instruction. EVERYTHING is in memory and has an address.
No, everything is /not/ in memory, nor does everything have an address.
/Values/ don't have addresses. /rvalues/ do not have addresses -
objects and lvalues have addresses. The number 42 does not have an
address. If x and y are ints, then (x + y) is has a value and type int,
but does not have an address. If you write "int foo = 23;", then the
/object/ "foo" has an address, and it contains the /value/ 23. The
value in foo has no address. The object "foo" will be put in memory
(baring optimisation and the "as if" rule) at an address that satisfies
the implementation-defined (not UB!) alignment requirements for an "int"
in that particular implementation. The value 23 that is contained in
"foo" has no alignment requirements, because it is a value.
> You can't emit an instruction to operate on your "values" (which are in memory and therefore referenced in assembly by an address) because that is out of spec for the hardware. And because you can't control exactly what instructions your compiler is going to emit, it's not "implementation-defined", it's undefined. Nobody knows what's going to happen.
> This is not how computers work.
I know how computers work. I know how programming languages work. I
suspect you are misunderstanding what objects are (in C and C++
terminology) and what values are. Hopefully my explanation above has
made things a bit clearer, so we can move on.
>
> That's why when you say things like "When you are dealing with hardware with 9-bit integers, or an SHA accelerator with 384-bit registers, you want types that have the exact size."
> And I ask "How does _BitInt(384) operate on SHA accelerator with 384-bit registers when it's not aligned?", and now you are telling me "It doesn't! We just copy it over to a type that does."...what are we talking about?
>
> Do you see the problem?
I see that you are misunderstanding me, and that is a problem for this
conversation.
Forget about alignment - it is a minor implementation detail.
Forget, for now, about the actual sizes of the containers for the
_BitInt types. In C and C++ it is perfectly allowable for types to have
containers that are bigger than the value bits, so that you have extra
padding bits. (A prime example is "bool", which is typically
implemented in an 8-bit byte with one value bit and 7 padding bits.)
Then understand what the _BitInt types do - what they model. Understand
how these can be useful in software when you want to model an integer
type with a specific number of bits. C and C++ are high-level languages
- they are defined in terms of an abstract machine, not specific
hardware, and their types are for use in software. At the high level,
alignment is irrelevant and the size of containers is usually equally
irrelevant (or easily handled - such as by using "sizeof" in your
memcpy() calls).
Then understand that C and C++ are languages intended to be efficient on
real hardware, and for low-level non-portable systems programming.
Appreciate how fixed-size integer types can map to hardware. Sometimes
they can be used in ways that map to common hardware, sometimes to very
specialised hardware. In some cases, precise size of containers now
becomes relevant because your accesses are not simply free accesses
within a read-write memory block. And at the lowest level of the
hardware, if you are talking about objects in memory rather than just
values, alignment must match up.
>
> -----Original Message-----
> From: David Brown <david.brown_at_hesbynett.no>
> Sent: Tuesday, September 2, 2025 23:17
> To: Tiago Freire <tmiguelf_at_[hidden]>; std-proposals_at_lists.isocpp.org
> Subject: Re: [std-proposals] D3666R0 Bit-precise integers
>
>
>
> On 02/09/2025 18:38, Tiago Freire wrote:
>> See here's my problem:
>>
>>> When you are dealing with hardware with 9-bit integers, or an SHA accelerator with 384-bit registers, you want types that have the exact size.
>>
>>> Ultimately, this will be implementation-dependent. But it seems
>>> natural (to me) that alignment of a _BitInt should normally be that
>>> of the any standard unsigned integer type that can contain them, or
>>> uint64_t for larger _BitInt's. (That's what C23 does.)
>>
>> These 2 statements are contradictory. It is either one or the other and it can't be both.
>
> Well, you can only get the exact sizes on platforms that support those exact sizes. On other platforms, the _BitInt types need to be contained in something larger if they are cannot be provided directly. So a
> _BitInt(9) is likely to need an FPGA or special hardware for its size to be exactly 9 bits. But they can be implemented in an obvious way on other platforms, just with padding bits. Alignment will be implementation dependent, just as it is for all other types.
>
>> The real answer is actually neither, this is actually UB. It's all UB, it always has been.
>
> How can something that is not yet a feature of the language, have "always" been UB?
>
>> Since alignment is not specified converting it to anything is UB.
>
> Alignment is not an issue when converting anything. Converting between types is done by value. Alignment only matters for pointers.
>
> And the alignment of different types is not UB, it is implementation-defined behaviour.
>
>> Maybe it works on some hardware that are tolerant with alignment, and some implementations may try to align with what you probably want to do anyways, but it's still UB.
>>
>
> _BitInt types would always be accessed via pointers that are appropriately aligned, unless you are playing silly buggers with converting pointer types - just like for all other types.
>
>> You want to work with specialized registers? The best way is for the compiler to provide concrete types that satisfy both the size and alignment requirements for those registers.
>
> If you have hardware that works best with types of a particular bit size, why not have standardised types that are of that particular bit size - to the best of the target implementation's abilities?
>
>> You can't just use a _BitInt and expect it to align because it doesn't have too, and according to you own words it can't be used for larger specialized registers, that's what you and everyone wants to do but that is not what it actually does.
>>
>
> Why would you not have to have appropriate alignment for _BitInt types?
>
>> That's why I picked that example. It's either a chunky boy or it doesn't align, the end.
>>
>
> For C23 _BitInt, _BitInt(512) will have the same alignment as uint64_t.
> I would expect the same for C++ _BitInt.
>
>
Received on 2025-09-03 13:36:16