Live data from Hacker News

GCC always assumes aligned pointer accesses

trust-in-soft.com

61–70 of 93 posts

Re: GCC always assumes aligned pointer accesses

#61
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 wel…

I genuinely can't follow you completely, but half suspect you're violently agreeing with me. Are you saying the optimization in the linked article is, or is not, in violation of the standard?

Edit: this is the text I was remembering, from 6.5.16.1 ("Simple Assignment"): "If the value being stored in an object is read from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have qualified or unqualified versions of a compatible type; otherwise, the behavior is undefined.".

That pretty much matches exactly what I was saying: compilers are free to assume that basic types don't overlap, because if they do then any generated code will be undefined behavior anyway.

Re: GCC always assumes aligned pointer accesses

#62
post #45
post #9

Earlier quoted context omitted.

Irrelevant to the post, even without strict aliasing the compiler will assume pointers/objects are properly aligned.

Yeah but alignment is a really really stupid thing to enforce on modern hardware. It makes absolutely no sense at all. All you are doing is increasing your cache pressure and limiting memory bandwidth.

movaps, movups (granted, those perform similarly on the latest intel chips)

Alignment can make caching simpler: an aligned load/store will never cross a cache boundary. So some architectures will have faster aligned access. This is the case for RISC-V. It's not so much that unaligned access is being punished there, as aligned access being optimized

Re: GCC always assumes aligned pointer accesses

#63
post #61

Earlier quoted context omitted.

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 wel…

I genuinely can't follow you completely, but half suspect you're violently agreeing with me. Are you saying the optimization in the linked article is, or is not, in violation of the standard? Edit: this is the text I was remembering, from 6.5.16.1 ("Simple Assignment"): " If the value being stored in an object is read from another object that overlaps in any way the storage of the first object, then the overlap shall…

6.5.16.1 is the “rule that only apply to “lvalue = lvalue;” assignments and is not relevant here”

It does not apply to “lvalue = 1;” or to “lvalue = 2;”, which are the two relevant assignments in the example in the article.

For context, I think I made it clear in the article that the program being discussed is UB, and therefore that the compiler is not to blame. But since I wrote this article, I have had people telling me “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”.

My reply to this specific sentence is:

No. You are wrong. There are no words in the standard that say that “basic types cannot overlap in memory”. There is not even a notion of “basic type”. There are clauses about pointer alignment, that are explicitly cited in the article, and there are clauses about strict aliasing, that are shown in the article not to be the reason for GCC optimizing the program by using -fno-strict-aliasing. There are no rules about “basic types not overlapping” in the C standard. You only think there are. Or please cite them. (6.5.16.1 is a rule about assignment, it only applies for the code pattern lvalue1 = lvalue2;)

Re: GCC always assumes aligned pointer accesses

#64
Why not? We program with a model that assumes NULL is zero, that we have a flat memory model...

If it's meant to be in a certain way, it is because it would not only simplify the implementation, but also - "I hope you know what you are doing."

Of course, the point of this is that if you feel strongly against it, I'd rather you submit a patch / use a fork where it shows benefit. If enough people require this behaviour, they may enable it in the tree. They're looking for more contributors, not less.

Re: GCC always assumes aligned pointer accesses

#65
post #62
post #45

Earlier quoted context omitted.

Yeah but alignment is a really really stupid thing to enforce on modern hardware. It makes absolutely no sense at all. All you are doing is increasing your cache pressure and limiting memory bandwidth.

movaps, movups (granted, those perform similarly on the latest intel chips) Alignment can make caching simpler: an aligned load/store will never cross a cache boundary. So some architectures will have faster aligned access. This is the case for RISC-V. It's not so much that unaligned access is being punished there, as aligned access being optimized

Yeah but your cache and memory layout are swiss cheese.

Re: GCC always assumes aligned pointer accesses

#66
post #61

Earlier quoted context omitted.

I genuinely can't follow you completely, but half suspect you're violently agreeing with me. Are you saying the optimization in the linked article is, or is not, in violation of the standard? Edit: this is the text I was remembering, from 6.5.16.1 ("Simple Assignment"): " If the value being stored in an object is read from another object that overlaps in any way the storage of the first object, then the overlap shall…

6.5.16.1 is the “rule that only apply to “lvalue = lvalue;” assignments and is not relevant here” It does not apply to “lvalue = 1;” or to “lvalue = 2;”, which are the two relevant assignments in the example in the article. For context, I think I made it clear in the article that the program being discussed is UB, and therefore that the compiler is not to blame. But since I wrote this article, I have had people telli…

I'm sorry, can you explain how that's not relevant here? You're being incongruously combative, but I still think you're mostly agreeing with me.

The section on "simple" assignments doesn't say that the rvalue must be an lvalue expression syntactically . I think it applies very well to "*p = 1;", which is the statement in the linked code. What am I missing?

> There are no words in the standard that say that “basic types cannot overlap in memory”.

I don't believe I said there were. I said the standard expressly allowed the optimization in the linked article. And as far as I can see, absent a clearer explanation for why that section doesn't apply, it does.

Re: GCC always assumes aligned pointer accesses

#67
post #50

Earlier quoted context omitted.

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.

Nope. Acquire/release at C level translates into a compiler barrier and the exact machine implementation will depend on hardware. However, on x86, for example, with its strong memory semantics, ordinary stores already have release semantics and do not require an architectural barrier (wmb/sfence). No sfence, no lock prefix.

Re: GCC always assumes aligned pointer accesses

#68
post #67

Earlier quoted context omitted.

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.

Nope. Acquire/release at C level translates into a compiler barrier and the exact machine implementation will depend on hardware. However, on x86, for example, with its strong memory semantics, ordinary stores already have release semantics and do not require an architectural barrier (wmb/sfence). No sfence, no lock prefix.

First you said that the modern way is to use acquire/release instead of a barrier, then admitted that it is a barrier at the compiler level. The fact that it doesn't require a barrier instruction on the x86 is pretty much irrelevant. Not all the world's an x86, and even the x86 hasn't always been the same in this regard. So your rude "nope" is unwarranted. This attitude of "as it is (in my world) now, so it has ever been and ever will be (everywhere)" is exactly why these bugs occur.

Re: GCC always assumes aligned pointer accesses

#69
post #67

Earlier quoted context omitted.

Nope. Acquire/release at C level translates into a compiler barrier and the exact machine implementation will depend on hardware. However, on x86, for example, with its strong memory semantics, ordinary stores already have release semantics and do not require an architectural barrier (wmb/sfence). No sfence, no lock prefix.

First you said that the modern way is to use acquire/release instead of a barrier, then admitted that it is a barrier at the compiler level. The fact that it doesn't require a barrier instruction on the x86 is pretty much irrelevant. Not all the world's an x86, and even the x86 hasn't always been the same in this regard. So your rude "nope" is unwarranted. This attitude of "as it is (in my world) now, so it has ever…

Compiler barriers and machine barriers are completely different things, despite having a similar name.

In about the era you described, the Linux kernel community did go around throwing rmb()s and wmb()s everywhere, and those did translate to hardware fences. (And in general, it is not safe to elide a hardware fence on x86 despite relatively strong memory ordering.) This is what I believed you were talking about; maybe I misunderstood: apologies.

Putting explicit compiler barriers in code is still not quite the modern model for relaxed atomic consumers. You use the abstract acquire/release load/store pseudo-functions, and they do the right thing depending on implementation. The compiler barriers in the relaxed atomics model are an internal implementation detail, not the API.

x86 is just an example where the cheapest way to do a release-semantics store is a plain store. I never said all the world was x86 — if it were, abstract acquire/release relaxed atomics would be kind of pointless.

Re: GCC always assumes aligned pointer accesses

#70
post #3

Earlier quoted context omitted.

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.

https://news.ycombinator.com/item?id=22911286
Post reply on HN