Live data from Hacker News

GCC always assumes aligned pointer accesses

trust-in-soft.com

51–60 of 93 posts

Re: GCC always assumes aligned pointer accesses

#51
post #19
post #16

The example of bad gcc behavior was essentially type punning, which is well-known to cause trouble. Is there an example where things break on misaligned access to non-overlapping objects?

Misaligned accesses, in general, without any qualification, can earn you a SIGSEGV or SIGBUS. It's mostly x86 that's being very tolerant here; other architectures are much less forgiving to varying degrees. That's also why this rule is in the C standard to begin with. Misaligned accesses need different / multiple instructions on some of these architectures, making them significantly more expensive. So the decision wa…

All (or almost all) of the big architectures that have survived to this day and attempt to compete with x86 permit misaligned accesses in general purpose memory. It's true that if you threw a dart at a historical computer architecture, they would likely not be tolerated. But e.g., aarch64 tolerates misaligned access.

Re: GCC always assumes aligned pointer accesses

#52
post #34

Earlier quoted context omitted.

Indeed, PDP/11s don't like it. Older ARM designs too, though I believe they allow it since about ARM6 (perhaqps because x86 does?).

The arms allow it since ARMv5, but it's still not quite what one would expect until ARMv7. However it has a significant performance penalty when doing it. https://medium.com/@iLevex/the-curious-case-of-unaligned-acc...

Cortex-M0 still does not allow it, and fun fact: Cortex-M4 allows unaligned access, but can be configured run-time to not allow it.

Re: GCC always assumes aligned pointer accesses

#53
post #50

Earlier quoted context omitted.

Basically yeah, but it gets much more complicated. For example, when a write hits memory is actually not the same thing as when it's observable by some particular other processor. There might be particular rules for writes that have dependencies too. Here's some good reading material if you have a spare afternoon. https://www.kernel.org/doc/Documentation/memory-barriers.txt Amusingly, it treats dependency issues as o…

The modern thing is using acquire/release semantics on the variables you actually care about intra-thread ordering on, rather than full barriers, which impose a higher cost.

I think we're talking about different kinds of barriers. Acquire/release at the C++ level translates roughly into an rmb/wmb pair at the processor level, unless it's implemented using an actual interlocked instruction which is worse than barriers.

Re: GCC always assumes aligned pointer accesses

#54
post #8

No. [as in: this isn't about GCC.] The C standard requires that pointers generally be created from referencing a valid, existing object. A misaligned integer is not a valid "object", thus the compiler may assume that all pointers to ints are aligned (to 4 bytes.) https://en.cppreference.com/w/cpp/language/pointer Every value of pointer type is one of the following: - a pointer to an object or function (in which case…

>> The C standard requires that pointers generally be created from referencing a valid, existing object. It is common to reference memory mapped peripherals by casting an integer to a struct pointer. In that case only a human can certify the validity of the object.

The C language does not support what you're trying to do. You should use assembly to do this.

Re: GCC always assumes aligned pointer accesses

#55
post #26

Few seem to know x86 can generate misaligned access exception. Enabling EFLAGS Alignment Check (bit 18) flag causes an exception on any misaligned access, if the kernel has enabled this feature by setting CR0 register Alignment Mask (bit 18).

You do not need to enable anything. SSE instructions require aligned access, and will crash if used with wrong alignment, irrespective of the flag.

I noticed this when I discovered the NVIDIA OpenGL driver used SSE without thinking.

Nothing in the OpenGL specs says the buffers passed to it (think it was glBufferData) has to be 16byte aligned, but with NVIDIA it would crash with an alignment exception if they were not. The memory allocater in the language I used, Delphi, only used 4 byte alignment (32bit).

They might have fixed this, this was well over a decade ago.

Re: GCC always assumes aligned pointer accesses

#56

Earlier quoted context omitted.

Why "no", you're agreeing with the conclusion

Sort of. He definitely talks about the fact that it is undefined behaviour to create a misaligned pointer: > Strictly speaking, the function f invokes Undefined Behavior when it computes [the misaligned pointer] But he thinks it is still a problem with GCC, in that GCC should not be allowed to take advantage of this particular UB. He explains his view much better in the bug report: > GCC assumes that pointers must be…

Author here!

I am not saying or thinking that there is a problem with GCC. I do think that GCC and Clang would be more useful with an option to make them not assume that every pointer is aligned if the target architecture does not impose this, but that's not the same thing as saying there is something wrong with GCC.

The message of the post, rather than “something is wrong with GCC”, is, “Beware. You might think that this is okay to do in your C programs, but it is not and here is why.”

Also before I post something like this, I need a confirmation that the behavior is intended and not accidental. It has happened to me before that I was about to document that GCC had an agressive behavior with respect to a kind of optimization (while remaining arguably in line with the intent of the standard, even if the word of the standard was in this case ambiguous enough to be interpreted any which way), and my co-author and I had to use a “missed optimization” ticket on GCC's bugzilla in order to have them confirm that GCC was doing the thing in question on purpose. GCC's developers, seeing the bug report, changed the behavior to remove the optimization entirely instead: https://gcc.gnu.org/ml/gcc/2016-11/msg00111.html

Coming back to the example at hand, if I had phrased the ticket as “GCC shouldn't optimize this”, it would have been closed instantly as “well it's UB”. I hoped for a more interesting search for a trade-off that would satisfy everyone, from people who just want legacy C code to keep working with new compilers to people who want programs to run as fast as possible if I phrased it this way.

(And yes, you have to ask in the bugzilla if you need some sort of official answer for this kind of thing. If you ask on a mailing list, you'll get a “no that was UB from the start” answer from someone you have never heard of who is in fact a power user who subscribed to the mailing list, and whose opinion, while useful, should not be assumed to be that of the compiler developers.)

Re: GCC always assumes aligned pointer accesses

#57
post #2

The complaint isn't about alignment at all, it's that the optimizer assumes that two pointers to the same basic type cannot overlap in memory. The generated code is correct except for the fact that the two arguments are distinct pointers to the same three bytes in memory. I believe this behavior is actually specified in the standard, actually, in the same section that defines the aliasing rules.

I have heard this reaction to this article a lot, but sorry, there is nothing in the C standard that says that objects should not overlap, except a rule that only apply to “lvalue = lvalue;” assignments and is not relevant here.

Plus on a some 32-bit ISA, a long long and a double only need to be aligned to 32-bit boundaries, so I note that in the made-up C rules that you are referring to, “basic type” is not very well defined.

> I believe this behavior is actually specified in the standard, actually,

> in the same section that defines the aliasing rules.

The strict aliasing rules are here: https://port70.net/~nsz/c/c11/n1570.html#6.5p7

Go ahead and point to the rule that says that “basic types” cannot overlap with themselves.

Re: GCC always assumes aligned pointer accesses

#58
post #3
post #2

The complaint isn't about alignment at all, it's that the optimizer assumes that two pointers to the same basic type cannot overlap in memory. The generated code is correct except for the fact that the two arguments are distinct pointers to the same three bytes in memory. I believe this behavior is actually specified in the standard, actually, in the same section that defines the aliasing rules.

Yes, this is the compiler optimizing UB due to aliasing , not due to alignment.

Could you clarify which clause of the C standard you are referring to when you say “due to aliasing, not due to alignment”?

I make sense of the C standard for a living (this is literally my day job) and I do not see what clause of the C standard you are referring to. It would be very useful to me to know which clause you are referring to, and I would be eternally thankful.

Re: GCC always assumes aligned pointer accesses

#59

This isn't just an issue with GCC but embedded compilers as well. On embedded systems its common to work with packed data structures to maximize serial protocol efficiency. Lets say you have a packed structure with a type of struct MyPackedStruct { uint8_t byteValue; uint32_t intValues[5]; } Normally a compiler would add 3 extra bytes between the uint8_t value and the uint32_t array to keep alignment of the int array…

I would argue that this is a bug and should probably be fixed in a future C/C++ standard that allows declaring misaligned types. Something like "members of a packed struct have [[aligned(1)]] attribute and cannot be converted to a pointer type with different alignment except with a reinterpret_cast", so you'd have to do

    uint32_t [[aligned(1)]]*intValues = myPackedStruct.intValues;
Would be nice if we also got e.g. float/intNua_t for unaligned types. Any code that gets broken by this change was broken to begin with.

Re: GCC always assumes aligned pointer accesses

#60

Earlier quoted context omitted.

>> The C standard requires that pointers generally be created from referencing a valid, existing object. It is common to reference memory mapped peripherals by casting an integer to a struct pointer. In that case only a human can certify the validity of the object.

The C language does not support what you're trying to do. You should use assembly to do this.

I bet plenty of embedded C code is full of such casts.
Post reply on HN