I believe this behavior is actually specified in the standard, actually, in the same section that defines the aliasing rules.
I believe this behavior is actually specified in the standard, actually, in the same section that defines the aliasing rules.
It is, indirectly, since pointers are required to point to valid objects, and valid objects are required to be correctly aligned.
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.
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.
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;)
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.
You are interpreting the C standard as if it were a philosophy text. It contains a rule that says that in very precise circumstances (for an assignment from one to the other) objects must not partially overlap, and you are claiming that it means that “two pointers to the same basic type cannot overlap in memory”. The clause does not say that, sorry. The clause applies to the objects that are on one side and the other of an assignment.
> I said the standard expressly allowed the optimization in the linked article.
I hope that the article makes it clear that the standard expressly allows the optimization. Specific, explicit rules, cited in the article, about pointer alignment, allow the optimization.
For this reason, I, “violently” as you say, disagree with the sentence “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”. This sentence gets it all wrong. It is about alignment; it is not about “pointers to basic types”, whatever that is, not being allowed to overlap in memory; they are allowed to overlap for large enough “basic types” because it is about alignment, not overlap:
https://gcc.godbolt.org/z/ZAMkeH
You could argue that GCC 9.3 only missed the optimization in the example in this Compiler Explorer link for some other reason and that absence of optimization doesn't mean that p and q cannot overlap. This would be correct, this aspect is one of the difficulties in studying the rules that these compilers implement. However, what I am saying is that if you reported this missed optimization to GCC developers, they would tell you that GCC can't optimize the function f because p and q can overlap. There is no clause in the C standard that prevent them to (apart from strict aliasing rules, but I used the option to tell the compiler I didn't want it to take advantage of these ones).
(Please do not bother them with this, or if you do, at least leave me out of it; I have nothing better to do than to write this because it's the week-end but they have better things to do.)
> No. You are wrong. There are no words in the standard that say that “basic types cannot overlap in memory”.
§ J.2, Undefined Behavior
An object is assigned to an inexactly overlapping object or to an exactly overlapping object with incompatible type (6.5.16.1).
> There is not even a notion of “basic type”.
"Object."
You repeatedly (in this thread, and on your blog) express that you don't really understand "strict" (ISO standard) aliasing rules, and that seems to be the case.
You keep quoting this clause as if it applied to any of the assignments in the program being discussed.
It doesn't.
That clause says that in an assignment of the form “lvalue1 = lvalue2;”, there must only be exact overlap or no overlap between lvalue1 and lvalue2. This does not apply to assignments of the form “lvalue = 1;” or “lvalue = 2;” which are the interesting assignments in the program being discussed.
Objects are not “basic types” for the original sentence that claimed that “basic types cannot overlap in memory”. Objects overlap in memory all the time.
> You repeatedly (in this thread, and on your blog) express that you don't really understand "strict" (ISO standard) aliasing rules, and that seems to be the case.
If you say so. I'm not the one who thinks that “* p” and “1” overlap.
> It doesn't.
You keep asserting that 6.5.16.1 is not relevant, as if it makes it so; but it doesn't. It's your opinion; the assertions are not persuasive.
void f(void) {
char *t = malloc(1 + sizeof(int));
if (!t) abort();
int *fp = (int*)t;
int *fq = (int*)(t+1);
h(fp, fq);
int h(int *p, int *q){
*p = 1;
*q = 1;
return *p;
}
Please explain to me why you continue to believe that is not an object being assigned to an inexactly overlapping object or to an exactly overlapping object with incompatible type?The clause says:
“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.”
Under “6.5.16.1 Simple assignment”, so this describes a rule about assignment.
Which assignment in the program are you claiming stores in an object a value read from another object that overlaps in any way the storage of the first object?
C was portable assembly back in the early 1980s. The result was that it was crushed in performance by Fortran for scientific codes, and most of the difference had to do with assumptions the compiler could make about aliasing (or the lack of it). This was fixed by the first ANSI C standard (finalized in 1989).
Yet I still see people who weren't even born then, or were young children then, talk as if C is supposed to be "portable assembly". It isn't.
Calling it "fixed", as if it were a problem of the language rather than programmers' expectations, is a rather slanted view of the history.
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.
Torvalds rant: https://www.yodaiken.com/2018/06/07/torvalds-on-aliasing
edit: I misread the parent as wondering about a compiler flag
Many programs rely on misaligned access, and features like pragma packed do too. I doubt they’ll be able to break all of that legacy code.
It's already broken. You're just not seeing it on x86. (Not sure how tolerant ARM is.)
I work in network software engineering, i.e. PowerPC used to be a common architecture. Those never accepted misaligned pointers (albeit you could of course trap the misalignment exception and emulate the access, or print a backtrace to identify the location.)
Really this is a problem caused by the "common case" (x86) being permissive in what it accepts, creating fallout further down the line if you ever need the code to run on something less permissive.
[Edit: actually, oops, it's not PowerPC; those are relatively permissive too. Pretty sure DEC Alpha and m68k are restrictive.]
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
It's true that fancy tricks like LTO can make that kind of behavior visible to the optimizer, but the standard predates that kind of magic by several decades.
On an architecture where "long long" is 8 bytes, but only has a 4 byte alignment requirement, the compiler knows this, and knows that 2 pointers of type "long long * " may overlap. Therefore it would disable the optimization. (Except the pointers still aren't supposed to overlap, but...)