Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
[...]
But
uint32_t bf : 1;
is meaningfully different from
unsigned bf : 1;
only because in most implementations (and ABIs), the underlying type
of a bit field affects the layout of the entire structure.
[...]
I accept that this is the case, but it's never made any sense to me,
and there's no hint of it in the C standard.
I think saying there is not even a hint is an overstatement. The C
standard says that an implementation "may allocate any addressable
storage unit large enough to hold a bit-field." It shouldn't be a
surprise that how much storage is allocated depends on the type of
the bit-field member. For example, a bit-field of type 'unsigned'
might very well choose a larger storage unit than what is chosen
for a bit-field of type '_Bool'. It seems obvious that the type of
a bit-field might affect what size and layout is chosen.
I'm sure it seems obvious to you. As I said, it's not at all
obvious to me.
Prior to C99, C didn't even require compilers to support bit-field
types other than int, unsigned int, and signed int.
The declared
type might typically be used only to determine the signedness of the bit-field (though I *think* most compilers permitted other types).
Implementations are certainly not *required* to use the declared
type of a bit-field as a factor in deciding how to allocate it,
or how to allocate the rest of the structure. Allocating just one
byte for an isolated 1-bit bit-field of any declared type would
be conforming. A conforming compiler could use the declared type
only to determine the signedness and the maximum allowed width of
a bit-field (and its conversion behavior in the case of bool)
For example, if I write:
uint64_t bf : 1;
then the containing struct is typically at least 64 bits, even
though those other 63 bits aren't part of the bit field and other
members can be allocated within them.
It would make a lot more sense *to me* if an N-bit bit field were
simply N bits.
Two problems with that. One, it seems to be in conflict with what
the C standard says about 0-width bit-fields.
0-width bit-fields are obviously a special case.
Two, the C standard
explicitly allows allocating bit-fields using a high-to-low order
or a low-to-high order (implementation-defined choice). Presumably
this freedom is given to accommodate both big- and little-endian
platforms. The idea that an N-bit bit-field should simply be N
bits doesn't work in big-endian environments. It seems better to
allow little-endian implementations to choose a size that matches
what a big-endian implementation would use, rather than insisting
that they be different.
I honestly don't understand your point here. How does making
N-bit bit-fields N bits not work in a big-endian environment?
Can you elaborate? Of course endianness can affect how bit-fields
are allocated within a "storage unit".
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
[...]
It would make a lot more sense *to me* if an N-bit bit field were
simply N bits.
[...]
Two, the C standard
explicitly allows allocating bit-fields using a high-to-low order or
a low-to-high order (implementation-defined choice). Presumably
this freedom is given to accommodate both big- and little-endian
platforms. The idea that an N-bit bit-field should simply be N bits
doesn't work in big-endian environments. It seems better to allow
little-endian implementations to choose a size that matches what a
big-endian implementation would use, rather than insisting that they
be different.
I honestly don't understand your point here. How does making
N-bit bit-fields N bits not work in a big-endian environment?
Can you elaborate? Of course endianness can affect how bit-fields
are allocated within a "storage unit".
Perhaps you read more than I intended into my statement about N-bit bit-fields being "simply N bits".
Thinking about this a bit more.
As of C90, "A bit-field shall have a type that is a qualified or
unqualified version of one of int, unsigned int, or signed int."
The "shall" is outside a constraint, so an implementation could allow bit-fields of other types without triggering a required diagnostic,
and many implementations did so.
C99 added _Bool bit-fields, and explicitly allowed "some other implementation-defined type". C23 allows bit-fields of bit-precise
integer types; I'll avoid thinking about that for now.
Implementions commonly use the declared type of a bit-field to
affect the layout, not necessarily of the bit-field itself, but
of the containing structure. Given that the standard doesn't
require support for types other than bool and the int types (and
now bit-precise integer types), the idea that `short bf:1` and
`long bf:1` have different semantics is not, as far as I can tell,
implied by anything in the standard.
I understand that implementations *can* allow other integer types
in bit-field declarations, and that they can use the declared type
in implementation-defined ways.
One possible approach would be to use the declared type only to
determine the signedness of the bit-field (and its conversion
behavior in the case of bool), and the upper bound for the number
of bits (`int bf:33` is a constraint violation if int is 32 bits).
In this relatively simple approach, there's no point in defining
a bit-field with one of the char or short types.
Using gcc on Linux, if I define a 1-bit bit-field with a 64-bit type,
that forces the containing structure to be at least 64 bits -- but
not by reserving a 64-bit region to hold the bit-field. If I define
a struct containing a 1-bit unsigned long long bit-field followed by
a 1-byte ordinary member, the second member is at a 1-bytes offset.
I had gotten the impression that the behavior is imposed by ABIs,
but my copy of the "System V Application Binary Interface AMD64
Architecture Processor Supplement" just says:
- bit-fields are allocated from right to left
- bit-fields must be contained in a storage unit appropriate for
its declared type
- bit-fields may share a storage unit with other struct / union
members
which doesn't seem to be enough to specify the behavior I see
(and I find it annoyingly vague).
Is there a document (ABI, compiler document, whatever) that specifies
the (odd, to me) behavior I'm seeing?
Here's a test program:
#include <stdio.h>
#include <stddef.h>
int main(void) {
struct s1 { unsigned char bf:1; unsigned char c; };
struct s2 { unsigned short bf:1; unsigned char c; };
struct s3 { unsigned int bf:1; unsigned char c; };
struct s4 { unsigned long bf:1; unsigned char c; };
struct s5 { unsigned long long bf:1; unsigned char c; };
printf("%-18s %-4s %-6s %s\n",
"type", "size", "offset", "struct-size");
printf("%-18s %-4zu %-6zu %-1zu\n",
"unsigned char",
sizeof (unsigned char),
offsetof(struct s1, c),
sizeof (struct s1));
printf("%-18s %-4zu %-6zu %-1zu\n",
"unsigned short",
sizeof (unsigned short),
offsetof(struct s2, c),
sizeof (struct s2));
printf("%-18s %-4zu %-6zu %-1zu\n",
"unsigned int",
sizeof (unsigned int),
offsetof(struct s3, c),
sizeof (struct s3));
printf("%-18s %-4zu %-6zu %-1zu\n",
"unsigned long",
sizeof (unsigned long),
offsetof(struct s4, c),
sizeof (struct s4));
printf("%-18s %-4zu %-6zu %-1zu\n",
"unsigned long long",
sizeof (unsigned long long),
offsetof(struct s5, c),
sizeof (struct s5));
}
and its output on my system (Ubuntu, x86_64):
type size offset struct-size
unsigned char 1 1 2
unsigned short 2 1 2
unsigned int 4 1 4
unsigned long 8 1 8
unsigned long long 8 1 8
Again, the declared type of a bit-field doesn't affect how the
bit-field itself is allocated, but it does affect the size of the
containing struct, but it doesn't prevent other members from being
allocated within that space.
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:<snip>
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
[...]
It would make a lot more sense *to me* if an N-bit bit field were
simply N bits.
[...]
I had gotten the impression that the behavior is imposed by ABIs,
but my copy of the "System V Application Binary Interface AMD64
Architecture Processor Supplement" just says:
- bit-fields are allocated from right to left
I think that means they are allocated in order of low-to-high,
because the AMD64 architecture is little-endian.
- bit-fields must be contained in a storage unit appropriate for
its declared type
- bit-fields may share a storage unit with other struct / union
members
which doesn't seem to be enough to specify the behavior I see
(and I find it annoyingly vague).
Is there a document (ABI, compiler document, whatever) that specifies
the (odd, to me) behavior I'm seeing?
As best I can tell the layout you are seeing is consistent with
the rules stated above. Perhaps the rules are deliberately meant
to be an under-specification (which IMO is not a bad thing).
Here's a test program:
#include <stdio.h>
#include <stddef.h>
int main(void) {
struct s1 { unsigned char bf:1; unsigned char c; };
struct s2 { unsigned short bf:1; unsigned char c; };
struct s3 { unsigned int bf:1; unsigned char c; };
struct s4 { unsigned long bf:1; unsigned char c; };
struct s5 { unsigned long long bf:1; unsigned char c; };
type size offset struct-size
unsigned char 1 1 2
unsigned short 2 1 2
unsigned int 4 1 4
unsigned long 8 1 8
unsigned long long 8 1 8
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
[...]
It would make a lot more sense *to me* if an N-bit bit field were
simply N bits.
[...]
<snip>
I had gotten the impression that the behavior is imposed by ABIs,
but my copy of the "System V Application Binary Interface AMD64
Architecture Processor Supplement" just says:
- bit-fields are allocated from right to left
I think that means they are allocated in order of low-to-high,
because the AMD64 architecture is little-endian.
- bit-fields must be contained in a storage unit appropriate for
its declared type
- bit-fields may share a storage unit with other struct / union
members
which doesn't seem to be enough to specify the behavior I see
(and I find it annoyingly vague).
Is there a document (ABI, compiler document, whatever) that specifies
the (odd, to me) behavior I'm seeing?
As best I can tell the layout you are seeing is consistent with
the rules stated above. Perhaps the rules are deliberately meant
to be an under-specification (which IMO is not a bad thing).
Here's a test program:
#include <stdio.h>
#include <stddef.h>
int main(void) {
struct s1 { unsigned char bf:1; unsigned char c; };
struct s2 { unsigned short bf:1; unsigned char c; };
struct s3 { unsigned int bf:1; unsigned char c; };
struct s4 { unsigned long bf:1; unsigned char c; };
struct s5 { unsigned long long bf:1; unsigned char c; };
FWIW, adding GCC's __attribute__((packed)) to each of those, we see:
type size offset struct-size
unsigned char 1 1 2
unsigned short 2 1 2
unsigned int 4 1 4
unsigned long 8 1 8
unsigned long long 8 1 8
type size offset struct-size
unsigned char 1 1 2
unsigned short 2 1 2
unsigned int 4 1 2
unsigned long 8 1 2
unsigned long long 8 1 2
Which doesn't seem to violate the ABI rules you quoted.
scott@slp53.sl.home (Scott Lurndal) writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Here's a test program:
#include <stdio.h>
#include <stddef.h>
int main(void) {
struct s1 { unsigned char bf:1; unsigned char c; };
struct s2 { unsigned short bf:1; unsigned char c; };
struct s3 { unsigned int bf:1; unsigned char c; };
struct s4 { unsigned long bf:1; unsigned char c; };
struct s5 { unsigned long long bf:1; unsigned char c; };
FWIW, adding GCC's __attribute__((packed)) to each of those, we see:
type size offset struct-size
unsigned char 1 1 2
unsigned short 2 1 2
unsigned int 4 1 4
unsigned long 8 1 8
unsigned long long 8 1 8
type size offset struct-size
unsigned char 1 1 2
unsigned short 2 1 2
unsigned int 4 1 2
unsigned long 8 1 2
unsigned long long 8 1 2
Which doesn't seem to violate the ABI rules you quoted.
That's good to know I guess, although I'm not sure what it
tells me. My impression is that using attribute__((packed))
produces code that may be less portable than not using it.
Generally I try to write code that avoids compiler-specific
constructs whenever feasible.
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:<snip>
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:[...]
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
One possible approach would be to use the declared type only to<snip>
determine the signedness of the bit-field (and its conversion
behavior in the case of bool), and the upper bound for the number
of bits (`int bf:33` is a constraint violation if int is 32 bits).
In this relatively simple approach, there's no point in defining
a bit-field with one of the char or short types.
Using gcc on Linux, if I define a 1-bit bit-field with a 64-bit type,
that forces the containing structure to be at least 64 bits -- but
not by reserving a 64-bit region to hold the bit-field. If I define
a struct containing a 1-bit unsigned long long bit-field followed by
a 1-byte ordinary member, the second member is at a 1-bytes offset.
I had gotten the impression that the behavior is imposed by ABIs,
but my copy of the "System V Application Binary Interface AMD64
Architecture Processor Supplement" just says:
- bit-fields are allocated from right to left
- bit-fields must be contained in a storage unit appropriate for
its declared type
- bit-fields may share a storage unit with other struct / union
members
which doesn't seem to be enough to specify the behavior I see
(and I find it annoyingly vague).
Is there a document (ABI, compiler document, whatever) that specifies
the (odd, to me) behavior I'm seeing?
Here's a test program:
and its output on my system (Ubuntu, x86_64):
type size offset struct-size
unsigned char 1 1 2
unsigned short 2 1 2
unsigned int 4 1 4
unsigned long 8 1 8
unsigned long long 8 1 8
Again, the declared type of a bit-field doesn't affect how the
bit-field itself is allocated, but it does affect the size of the
containing struct, but it doesn't prevent other members from being
allocated within that space.
Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:[...]
I had gotten the impression that the behavior is imposed by ABIs,
but my copy of the "System V Application Binary Interface AMD64
Architecture Processor Supplement" just says:
- bit-fields are allocated from right to left
- bit-fields must be contained in a storage unit appropriate for
its declared type
- bit-fields may share a storage unit with other struct / union
members
which doesn't seem to be enough to specify the behavior I see
(and I find it annoyingly vague).
Is there a document (ABI, compiler document, whatever) that specifies
the (odd, to me) behavior I'm seeing?
I leave it to language and standard lawyers to discuss if such
behaviour is mandated, but I find results of the test program
obvious given how gcc behaves is general. First, IIUC given
bit field should be at fixed bit offset within its storage unit.
Second, storage units are allocated at byte addresses with
appropriate alignment. Third, the passage above says that
storage unit must be approptiate for its type, so 1 byte in
case of unsigned char, 2 bytes in case of unsigend short,
4 bytes in case of unsigned int, 8 bytes in case of unsignend
long.
Other post mentioned gcc "packed". gcc "packed" does not
respect alignment rules, so in such cases 2 byte are enough
regardless of type.
antispam@fricas.org (Waldek Hebisch) writes:[...]
Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
Other post mentioned gcc "packed". gcc "packed" does not
respect alignment rules, so in such cases 2 byte are enough
regardless of type.
gcc "packed" can also cause quiet crashes on some systems. It can
result in, for example, an int member being allocated at an odd address. Passing the address of such a member to a function that assumes the pointed-to object is correctly aligned can result in Bad Things
Happening. (On x86, as I understand it, misaligned accesses
are merely somewhat slower than aligned accesses.)
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51628> <https://stackoverflow.com/q/8568432/827263> <https://stackoverflow.com/a/8568441/827263>
On 20/08/2026 00:26, Keith Thompson wrote:
antispam@fricas.org (Waldek Hebisch) writes:[...]
Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
Other post mentioned gcc "packed". gcc "packed" does notgcc "packed" can also cause quiet crashes on some systems. It can
respect alignment rules, so in such cases 2 byte are enough
regardless of type.
result in, for example, an int member being allocated at an odd address.
Passing the address of such a member to a function that assumes the
pointed-to object is correctly aligned can result in Bad Things
Happening. (On x86, as I understand it, misaligned accesses
are merely somewhat slower than aligned accesses.)
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51628>
<https://stackoverflow.com/q/8568432/827263>
<https://stackoverflow.com/a/8568441/827263>
gcc "packed" can cause problems only if you don't use the compiler
correctly (or if there are bugs in the compiler, as happens
occasionally. gcc's bugzilla seems to be having trouble, so I have
not checked your bug link).
As you say, "packed" means that struct fields can be misaligned. This
has two potential problems.
One is that if you take the address of a field, you then have a
pointer whose value is invalid for dereferencing as that type - so dereferencing it is UB. (You can still convert it to a character
pointer and dereference that.) A compiler could therefore assume that
if you have "int * p;" and you dereference it, then "p" must be
correctly aligned. AFAIUI gcc does not make such assumptions,
precisely because misaligned pointers do turn up in some code (either
from the use of "packed" or by other means), and it is not an
optimisation that is likely to be useful in any but the most obscure
niche cases. (And if you have code in that category, you can always
use __builtin_assume_aligned() to give the compiler the additional information.)
gcc is smart enough to use smaller accesses when it knows it is
necessary. Accessing a misaligned 32-bit field when targeting a
Cortex-M4 will give normal load/store 32-bit instructions - when
targeting an M23, it will generate multiple 8-bit or 16-bit
load/stores. And if you take a pointer to a packed field, you get a warning. So I think you have to put a bit of effort into getting in
trouble here - you have to use pointers and ignore warnings, or use inappropriate command-line switches or generate code targeting one
processor and try to run it on a different processor.
David Brown <david.brown@hesbynett.no> writes:
On 20/08/2026 00:26, Keith Thompson wrote:
antispam@fricas.org (Waldek Hebisch) writes:[...]
Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
Other post mentioned gcc "packed". gcc "packed" does notgcc "packed" can also cause quiet crashes on some systems. It can
respect alignment rules, so in such cases 2 byte are enough
regardless of type.
result in, for example, an int member being allocated at an odd address. >>> Passing the address of such a member to a function that assumes the
pointed-to object is correctly aligned can result in Bad Things
Happening. (On x86, as I understand it, misaligned accesses
are merely somewhat slower than aligned accesses.)
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51628>
<https://stackoverflow.com/q/8568432/827263>
<https://stackoverflow.com/a/8568441/827263>
gcc "packed" can cause problems only if you don't use the compiler
correctly (or if there are bugs in the compiler, as happens
occasionally. gcc's bugzilla seems to be having trouble, so I have
not checked your bug link).
That may be the case now, but it was definitely causing runtime bus
errors on some systems when I reported it. It was later marked as
FIXED.
As you say, "packed" means that struct fields can be misaligned. This
has two potential problems.
One is that if you take the address of a field, you then have a
pointer whose value is invalid for dereferencing as that type - so
dereferencing it is UB. (You can still convert it to a character
pointer and dereference that.) A compiler could therefore assume that
if you have "int * p;" and you dereference it, then "p" must be
correctly aligned. AFAIUI gcc does not make such assumptions,
precisely because misaligned pointers do turn up in some code (either
from the use of "packed" or by other means), and it is not an
optimisation that is likely to be useful in any but the most obscure
niche cases. (And if you have code in that category, you can always
use __builtin_assume_aligned() to give the compiler the additional
information.)
I'm not sure what assumptions gcc makes now. Once you add something
outside the sco of the standard like `__attribute__((packed))`, the
behavior is arguably undefined anyway. I can't reproduce the original symptom with a modern gcc, but that may be because I don't have access
to a system that traps on unaligned accesses.
[...]
gcc is smart enough to use smaller accesses when it knows it is
necessary. Accessing a misaligned 32-bit field when targeting a
Cortex-M4 will give normal load/store 32-bit instructions - when
targeting an M23, it will generate multiple 8-bit or 16-bit
load/stores. And if you take a pointer to a packed field, you get a
warning. So I think you have to put a bit of effort into getting in
trouble here - you have to use pointers and ignore warnings, or use
inappropriate command-line switches or generate code targeting one
processor and try to run it on a different processor.
The problem is that if you have a separately compiled function like:
void inc(int *p) {
(*p)++;
}
the compiler has no way to know whether smaller (and perhaps
significantly more expensive) accesses are necessary. Direct access to
a packed member is easy enough, but once you take its address and pass
it somewhere, there's no good way to deal with it.
gcc added a warning option -Waddress-of-packed-member.
I haven't been able to get any code to run on SPARC via godbolt.org.
antispam@fricas.org (Waldek Hebisch) writes:<snip>
Other post mentioned gcc "packed". gcc "packed" does not
respect alignment rules, so in such cases 2 byte are enough
regardless of type.
gcc "packed" can also cause quiet crashes on some systems. It can
result in, for example, an int member being allocated at an odd address. >Passing the address of such a member to a function that assumes the >pointed-to object is correctly aligned can result in Bad Things
Happening. (On x86, as I understand it, misaligned accesses
are merely somewhat slower than aligned accesses.)
(On x86, as I understand it, misaligned accesses
are merely somewhat slower than aligned accesses.)
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
antispam@fricas.org (Waldek Hebisch) writes:<snip>
Other post mentioned gcc "packed". gcc "packed" does not
respect alignment rules, so in such cases 2 byte are enough
regardless of type.
gcc "packed" can also cause quiet crashes on some systems. It can
result in, for example, an int member being allocated at an odd address.
Passing the address of such a member to a function that assumes the
pointed-to object is correctly aligned can result in Bad Things
Happening. (On x86, as I understand it, misaligned accesses
are merely somewhat slower than aligned accesses.)
In modern systems, so long as the misaligned access is
fully contained within a single cache line, there is
zero additional latency for the misaligned access.
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
antispam@fricas.org (Waldek Hebisch) writes:<snip>
Other post mentioned gcc "packed". gcc "packed" does not
respect alignment rules, so in such cases 2 byte are enough
regardless of type.
gcc "packed" can also cause quiet crashes on some systems. It can
result in, for example, an int member being allocated at an odd address. >>Passing the address of such a member to a function that assumes the >>pointed-to object is correctly aligned can result in Bad Things
Happening. (On x86, as I understand it, misaligned accesses
are merely somewhat slower than aligned accesses.)
In modern systems, so long as the misaligned access is
fully contained within a single cache line, there is
zero additional latency for the misaligned access.
If you can make an example where a modern gcc for the target hardware generates multiple small accesses, and an older version generates a misaligned big access, then that would probably show the issue even
without access to the hardware.
Scott Lurndal <scott@slp53.sl.home> wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
antispam@fricas.org (Waldek Hebisch) writes:<snip>
Other post mentioned gcc "packed". gcc "packed" does not
respect alignment rules, so in such cases 2 byte are enough
regardless of type.
gcc "packed" can also cause quiet crashes on some systems. It can
result in, for example, an int member being allocated at an odd address. >>> Passing the address of such a member to a function that assumes the
pointed-to object is correctly aligned can result in Bad Things
Happening. (On x86, as I understand it, misaligned accesses
are merely somewhat slower than aligned accesses.)
In modern systems, so long as the misaligned access is
fully contained within a single cache line, there is
zero additional latency for the misaligned access.
But most efficient access fetches the whole line. If it stays
within a single cache line, then by neccessity it is aligned.
And adjective "modern" can be applied to microcontrollers.
Microcontrollers typically have word-sized data bus. Misaligned
word sized access needs two transfers over data bus, clearly
less efficient than single access.
It certainly applies to some of the smaller and
cheaper ARM Cortex-M cores (M0, M1, M23 - perhaps more).
On Thu, 20 Aug 2026 11:00:54 +0200
David Brown <david.brown@hesbynett.no> wrote:
It certainly applies to some of the smaller and
cheaper ARM Cortex-M cores (M0, M1, M23 - perhaps more).
Are you sure about M1?
If true, it is disappointing.
I would not touch Cortex-M0 or M23 for many other reasons so can't
be disappointed about them.
OTOH, M1 is something that in theory I like.
Never used it, because original licensing terms were absolute
non-starter for sort of projects that we are specializing in (volumes
rarely reaching 1000 units and never 10,000), but I am very likely to
use it if we ever chose to work with Microsemi, on which licensing of
M1 is much saner.
OTOH, M1 is something that in theory I like.
Never used it, because original licensing terms were absolute
non-starter for sort of projects that we are specializing in (volumes
rarely reaching 1000 units and never 10,000), but I am very likely to
use it if we ever chose to work with Microsemi, on which licensing of
M1 is much saner.
On 20/08/2026 21:55, Waldek Hebisch wrote:
Scott Lurndal <scott@slp53.sl.home> wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
antispam@fricas.org (Waldek Hebisch) writes:<snip>
Other post mentioned gcc "packed". gcc "packed" does not
respect alignment rules, so in such cases 2 byte are enough
regardless of type.
gcc "packed" can also cause quiet crashes on some systems. It can
result in, for example, an int member being allocated at an odd address. >>>> Passing the address of such a member to a function that assumes the
pointed-to object is correctly aligned can result in Bad Things
Happening. (On x86, as I understand it, misaligned accesses
are merely somewhat slower than aligned accesses.)
In modern systems, so long as the misaligned access is
fully contained within a single cache line, there is
zero additional latency for the misaligned access.
But most efficient access fetches the whole line. If it stays
within a single cache line, then by neccessity it is aligned.
The fetch of the cache line from memory is aligned, but the fetch of the >actual data from the cache to the processor may not be aligned.
On 21/08/2026 13:31, Michael S wrote:
On Thu, 20 Aug 2026 11:00:54 +0200
David Brown <david.brown@hesbynett.no> wrote:
It certainly applies to some of the smaller and
cheaper ARM Cortex-M cores (M0, M1, M23 - perhaps more).
Are you sure about M1?
If true, it is disappointing.
I haven't dug through the documentation, but gcc certainly thinks so - >packed fields are accessed by byte rather than misaligned word accesses.
It is possible, of course, that gcc is pessimistic here, or that
misaligned accesses are an optional feature for the core.
Since the M1 targets soft cores on programmable logic, and support for >misaligned accesses takes extra logic (consuming space and possibly
reducing maximum clock speed) for a feature that is rarely useful on >embedded systems, it does not surprise me that the M1 does not support it.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
scott@slp53.sl.home (Scott Lurndal) writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Here's a test program:
#include <stdio.h>
#include <stddef.h>
int main(void) {
struct s1 { unsigned char bf:1; unsigned char c; };
struct s2 { unsigned short bf:1; unsigned char c; };
struct s3 { unsigned int bf:1; unsigned char c; };
struct s4 { unsigned long bf:1; unsigned char c; };
struct s5 { unsigned long long bf:1; unsigned char c; };
FWIW, adding GCC's __attribute__((packed)) to each of those, we
see:
type size offset struct-size
unsigned char 1 1 2
unsigned short 2 1 2
unsigned int 4 1 4
unsigned long 8 1 8
unsigned long long 8 1 8
type size offset struct-size
unsigned char 1 1 2
unsigned short 2 1 2
unsigned int 4 1 2
unsigned long 8 1 2
unsigned long long 8 1 2
Which doesn't seem to violate the ABI rules you quoted.
That's good to know I guess, although I'm not sure what it
tells me. My impression is that using attribute__((packed))
produces code that may be less portable than not using it.
Generally I try to write code that avoids compiler-specific
constructs whenever feasible.
I generally only use the packed attribute when creating
a C struct to match a hardware register, data structure or
data packet.
scott@slp53.sl.home (Scott Lurndal) writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
scott@slp53.sl.home (Scott Lurndal) writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Here's a test program:
#include <stdio.h>
#include <stddef.h>
int main(void) {
struct s1 { unsigned char bf:1; unsigned char c; };
struct s2 { unsigned short bf:1; unsigned char c; };
struct s3 { unsigned int bf:1; unsigned char c; };
struct s4 { unsigned long bf:1; unsigned char c; };
struct s5 { unsigned long long bf:1; unsigned char c; };
FWIW, adding GCC's __attribute__((packed)) to each of those, we
see:
type size offset struct-size
unsigned char 1 1 2
unsigned short 2 1 2
unsigned int 4 1 4
unsigned long 8 1 8
unsigned long long 8 1 8
type size offset struct-size
unsigned char 1 1 2
unsigned short 2 1 2
unsigned int 4 1 2
unsigned long 8 1 2
unsigned long long 8 1 2
Which doesn't seem to violate the ABI rules you quoted.
That's good to know I guess, although I'm not sure what it
tells me. My impression is that using attribute__((packed))
produces code that may be less portable than not using it.
Generally I try to write code that avoids compiler-specific
constructs whenever feasible.
I generally only use the packed attribute when creating
a C struct to match a hardware register, data structure or
data packet.
I would use the packed attribute if it were necessary to conform
to some externally imposed layout, such as a hardware register or
network packet. I don't know of any cases were it is necessary.
Maybe the data formats you need to match are more exotic than
ones I am used to. Can you give an example of a format where the
format cannot be matched using only language features available
in standard C?
| Sysop: | Tetrazocine |
|---|---|
| Location: | Melbourne, VIC, Australia |
| Users: | 9 |
| Nodes: | 8 (0 / 8) |
| Uptime: | 245:20:41 |
| Calls: | 220 |
| Files: | 21,513 |
| Messages: | 83,782 |