Earlier quoted context omitted.
I'm not making a one-way argument. If the underlying object actually is a Derived, you can freely cast between pointer-to-Base and pointer-to-Derived. That is what "and vice versa" means. But if the object isn't actually a Derived, you can't cast to pointer-to-Derived: Derived derived; Derived *pDerived = &derived; // This is legal because it's equivalent to: // Base *pb = &derived.base; // // ie. there actually is a…
Are you sure about 'illegal' there? Is any compiler going to complain? All the compilers I have used will cheerfully reference unallocated memory; I thought the behavior was undefined.
The Strict Aliasing Situation Is Pretty Bad
41–50 of 70 posts
Re: The Strict Aliasing Situation Is Pretty Bad
#42This was an eye opener for me. Bell Labs tech is as usual a mountain of hidden complexity hiding under a "simple" facade. No more excuses for me. Time to learn Rust.
The tension between weakly typed pointers and the desire to generate efficient code is where there is a problem. More or less anywhere you violate the aliasing rules you are doing something that Fortran doesn't allow at all, and by disallowing it semantically the hope was that C programmers could have their cake and eat it too. The reality is that all systems code should probably be compiled with alias analysis disabled.
Re: The Strict Aliasing Situation Is Pretty Bad
#43Earlier quoted context omitted.
> You can't cast a character array to any type and dereference it. So making your own malloc backed by a static char array is undefined behavior?
Yes, I believe it would be. That's a good point, now that you mention it -- I have code that does just that, and I hadn't realized it's probably undefined. The "effective type" (this is a term defined in the standard) of the char array elements would be "char", whereas the memory returned from malloc() is considered to be an object that initially has no effective type. I don't know of any way to take the char array a…
We implement our own allocators all the time. If you can't even do such a basic thing legally, then the rules are obvious nonsense.
Re: The Strict Aliasing Situation Is Pretty Bad
#44Earlier quoted context omitted.
Translation phases are all fun and games, but WPO can still break your code thanks to as-if rules - and because nothing prevent alias analysis to be performed regardless of the TU boundaries. And compilers are doing it.
> nothing prevent alias analysis to be performed regardless of the TU boundaries Standard conformance does. You know, that principle in the name of which the alias-breaking optimizations are done in the first place. The "as if" principle (there is only one) means that optimized code produces the same results as the abstract semantics (under a certain set of requirements of what it means for the abstract and actual se…
Now that is the situation, in regard with the standard. Should we be happy with it or not is another story.
Re: The Strict Aliasing Situation Is Pretty Bad
#45Although I have no evidence that it is being miscompiled, OpenSSL’s AES implementation uses chunking and is undefined. Oh, that's nice. :/
It's nonsense. The function is external, called from a separately compiled file. The pointer comes in as a char *. The code checks its alignment before assuming it can be cast to a block. There is no way in it could be "miscompiled". The ivec argument could in fact have come from an object that is of type aes_block_t. The only thing which might reveal that it didn't is wrong alignment. In other regards, there is no w…
Re: The Strict Aliasing Situation Is Pretty Bad
#46Earlier quoted context omitted.
> Did you even make sure the memory was aligned? Of course, what do you take me for? :) > I'm sure you know alloc() Do you mean alloca? It has a lot of problems, and is generally prohibited at Google. > and malloc() is fast for small sizes. Not nearly fast enough for my purposes.
Ok, what's wrong with alloca? Other than blowing the stack, that is.
At least that's the rationale for why we don't use it at Google.
Re: The Strict Aliasing Situation Is Pretty Bad
#47This was an eye opener for me. Bell Labs tech is as usual a mountain of hidden complexity hiding under a "simple" facade. No more excuses for me. Time to learn Rust.
C is actually quite simple, even the aliasing rules (I think the aliasing rules all fit on about a quarter page). Programming in C though is anything but. The tension between weakly typed pointers and the desire to generate efficient code is where there is a problem. More or less anywhere you violate the aliasing rules you are doing something that Fortran doesn't allow at all, and by disallowing it semantically the h…
A spec of a quarter page can already be incredibly convoluted and with very hard to anticipate consequences -- even more so when crazy people are interpreting it without caring about the consequence of their acts in the real world. And C is not just about aliasing rules; the current situation is that ANY undefined behavior is a landmine waiting to kill you, regardless of whether is seems to makes sens for your target architecture. And that is mostly because compiler writer have an insane interpretation of the standard: the definition of "undefined behavior" is "behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements"
A key word here is "nonportable" and it is clearly not considered often enough by some compiler writers, who generally prefer to see all undefined behaviors as a licence to "optimize" your code without carrying too much about warning you about potential bad side effects because it's "hard" according to them.
This does not make even the beginning of any sense. If it is not what many programmers are expecting (a majority ? -- most coworkers I know including my direct boss are not even aware of all that mess), is costly during the translation phase, has unclear/unquantified runtime performance benefits, is dangerous in the real world and is hard to detect when bugs are activated by those "optimizations", then WHY they are doing them in the first place? From an engineering point of view this is just plain insane. Correct executions and in depth safety are extremely valuable, and only becoming more so year after year, and when they pretend that it's not their fault that programs are breaking they are being even more ridiculous; a compiler does not exist in a vacuum, and neither just to reproduce itself and satisfy the curiosity of geeks for mathematical logic.
Obviously some amount of alias analysis can be useful, and this is clearly one of the topic really intended from scratch in the standard to address some performance issues, but maybe it would be enough to explicitly identify what you want to not alias. Seeing the bug when they discuss about allowing uint8_t to not behave in general as an unsigned char in regard with aliasing is just plain disgusting and makes me lose yet again a portion of the tiny remaining trust I had in them.
It will soon get to the point that C/C++ will not be realistic languages to consider if you want any kind of reliability. Maybe I'm even deluded in thinking this is not already the case.
Re: The Strict Aliasing Situation Is Pretty Bad
#48I thought this article was unnecessarily dire. One section claims "Physical Subtyping is Broken", where "physical subtyping" is defined as "the struct-based implementation of inheritance in C." I assume this means the typical pattern of: typedef struct { int base_member_1; int base_member_2; } Base; typedef struct { Base base; int derived_member 1; } Derived; The article claims physical subtyping is broken because ca…
void copy_8_bytes(char *dst, const char *src) {
*(uint64_t*)dst = *(uint64_t*)src;
}
Only maybe excuses. And poor ones.Now I don't remember the article where I saw that, but technically given the current orientation of compiler writers there are some even more ridiculous situations. Like (a>(32-n)) having the obviously desired effect on all current architectures when you look at what would be an obvious direct translation (and quite efficient one already), and yet given the current orientation of compiler writers I would not like to see that code AT ALL unless it is proved that n is always strictly between 1 and 31. And now if they want to restore any kind of efficiency after all that madness, they would have to implement yet another case of convoluted peephole optim. Stupid. Give me my original intent of the langage back, because virtually everybody is using it like that consciously or not, and that will just not change.
Re: The Strict Aliasing Situation Is Pretty Bad
#49Earlier quoted context omitted.
It's nonsense. The function is external, called from a separately compiled file. The pointer comes in as a char *. The code checks its alignment before assuming it can be cast to a block. There is no way in it could be "miscompiled". The ivec argument could in fact have come from an object that is of type aes_block_t. The only thing which might reveal that it didn't is wrong alignment. In other regards, there is no w…
Citing the translation phases in the standard as evidence that undefined behavior is ok, as long as it's divided between two translation units, strikes me as wishful thinking.
Suppose a document tells you that for some special situation X, there is an absence of requirements. However, suppose that some other general rules elsewhere in that document in fact imply a requirement for that situation.
That just means that the claim that situation X has no requirements is incorrect.
For instance, ISO C says that two struct types appearing in separate translation unit are only compatible of they have the same typed members in the same order ... with the same names.
This says that if you do aliasing with otherwise identical structures that don't have the same names, the behavior is undefined: i.e. that there is no requirement that it work.
But, we can infer that it must work by the logical fact that during the semantic processing of one translation unit, the translator has no clue what the names of struct members are in another translation unit. They disappear at translation time and turn into offsets.
I mean, we can fwrite a struct to a file, right? We can send that file over a network. According to ISO C, every program (or at least every C program) will have to use a structure with the correct names to fread that area of the file! Ridiculous!
Suppose we take ISO C and add a statement to it like, "the consequences are undefined if one of the operands of the + operator is the integer 42". The rest of the document would still be exactly what it is, and if we strike out that sentence with a black marker, nothing has changed. The rest of the document continues to inform us that adding 42 to something is well-defined (in the absence of overflow, or overflow-like issues with pointer displacement and so on).
Basically, it's a contradiction: the document gives a description which adds up to some requirements, but then some sentence tries to take them away.
In such a situation, we can just proceed as if the requirements apply. That is to say, when a requirement conflicts with the claim that there is no requirement, just let the requirement prevail.
(In a situation where conflicting requirements are asserted, it's a different story, of course.)
Re: The Strict Aliasing Situation Is Pretty Bad
#50Earlier quoted context omitted.
Citing the translation phases in the standard as evidence that undefined behavior is ok, as long as it's divided between two translation units, strikes me as wishful thinking.
Undefined behavior is the absence of requirements: there not being any requirements for some situation. Suppose a document tells you that for some special situation X, there is an absence of requirements. However, suppose that some other general rules elsewhere in that document in fact imply a requirement for that situation. That just means that the claim that situation X has no requirements is incorrect. For instanc…
Now they just add their "optimizations" at the highest levels so of course even for targets where it should makes no sense, and without asking for your permission, and even by default, and even some they consider "aggressive". So either you have provisions to avoid all that shit, like having a guy disabling all the new ones each time you upgrade you compiler, and you better have some defenses in depth, and I agree with you that using TU as boundaries is also a good idea, if your compiler+build-sys have an option to NOT do WPO.
But it is just not in any way guaranteed by the ISO standard, and still just an implementation detail from its pov. And honestly that's a problem. Because compiler writers will takes more drugs and come up with new imaginative way to break your code more, in the name of their "strictly-conformance and nothing more" crazy ideal.