c when would you not want to use strict aliasing?

Viewed 909

I've recently learned about strict aliasing in C and have read this post

What is the strict aliasing rule?

but I am confused about when one would not want to enable strict aliasing. I work on embedded C applications with GCC and I've found a number of projects have -fno-strict-aliasing added to the extra flags for a release build but I don't understand why they wouldn't want to enable strict-aliasing for performance. Does anyone have a good example/situation where you wouldn't want to use strict aliasing in C?

Also, does that mean it's not undefined behaviour if we break the strict-aliasing rules but tell the compiler to not do any strict-aliasing optimisations?

Thanks

5 Answers

... when one would not want to enable strict aliasing

Strict aliasing is a requirement (or rather a number of requirements) set by the C standard. So C code that are compliant with the standard must obey the (strict) aliasing rules set by the standard.

Compilers can use these rules to optimize the generated code. If some C code violates the standards aliasing rules, the compiler may generate code that behaves unexpectedly due to thus optimizations. Most compilers have an option to turn off optimizations based on aliasing rules.

So the answer is: If your code violates the "strict aliasing" rules, you can tell the compiler not to do optimization based on aliasing rules.

That leads to a new question: Why would you want to write code that violates the aliasing rule?

One reason could be performance. In other words - if you know exactly how your system will behave in case of a violation and your code performs better by doing that violation, you may want to say: I prefer performance above standard compliance.

One example I have seen mentioned is calculation of (network) packet checksums. The structure holding the packet could for instance contain many different fields like mac, ip, etc. but to calculate a checksum of some kind, you might want to view the packet as an array of integers. This is easy to do with code like uint32_t * p = &packet. It violates the aliasing rules but may work fine on the target system as along as the compiler doesn't do optimization based on the strict aliasing rule.

Another reason could be code readability. To avoid violating the aliasing rules you typically need to write "some extra code", e.g. placing things in unions, doing bit-shifts and logical-or to calculate int values and so on. Some find such code less readable and maintainable so they prefer to "write more simple code" by violating the aliasing rules (and therefore switch it off in the compiler).

Some code, like that generated by SWIG for generating bindings between C and many scripting languages, violates all the aliasing rules. It must be compiled with the no strict aliasing flag or nothing works.

Ideally you would use strict aliasing always. Unfortunately aliasing rules are complicated. As a result, most code is not written with strict aliasing in mind, and would break if that optimization would be enabled. Look at any C project and you are likely to find some fishy pointer cast followed by dereference.

Reality is that it's safest to not use strict aliasing for most of the code, and only enable it for the most performance critical code that you have yourself verified to be correct.

Given that maintainers of clang and gcc make no effort to handle all of the aliasing corner cases mandated by the Standard (they regard some tricky corner cases they don't handle as being defects in the Standard rather than their compilers), nor to precisely specify what subset of cases they should or should not be expected to handle reliably, I would advise writing code in such a way as to minimize any loss of performance that would result from disabling aliasing, and then documenting a requirement that code be processed with -fno-strict-aliasing. Otherwise, even if code seems to work today, there would be no way of knowing whether the authors of clang and gcc would will perceive any obligation to avoid having future versions break it.

Note that many of the corner cases where clang and gcc fall down involve things like writing storage with data of a new type whose bit pattern happens to match what was already there. In such cases, clang and gcc are prone to optimize out the write without remembering that the write--as written--changed the Effective Type of the storage in question. To be sure, in most cases where code might write data whose bit pattern matches what the storage already held, clang and gcc wouldn't optimize out the write and lose the change to the Effective Type, but I am unaware of any specification as to when they would be guaranteed not to do so.

Worse, some corner cases involve situations where the fact that code might violate gcc's interpretation of the "Strict Aliasing Rule" if executed is sufficient to cause it to jump the rails, even if the code wouldn't be executed.

For example, given the function:

long test(long *p1, long long *p2, int mode)
{
    *p1 = 1;
    if (mode)
        *(long*)p2 = 2;
    else
        *p2 = 2;
    return *p1;
}

x86-64 gcc 10.2 will generate code that, even if mode is 1, will ignore the possibility that storing the value 2 to *(long*)p2 might affect the value of p1.

Contrary to what is claimed above, strict aliasing is NOT required by any version of the C standard but is a choice by some compilers. And not necessarily a good choice. The reason Linux turns off strict aliasing is that it interferes with standard C programming methods used in the Linux kernel and elsewhere, notably with so-called "type punning". The obvious example is for any type of memory allocator where a freed object of potentially any type is then allocated again as any type. E.g. "x = mymalloc(100); ... myfree(x); .... y = myalloc(100);"Here, myfree gets an object of some type, say, an array of 100 characters, puts it onto an internal queue of freed memory, say as some linked list element, and then may even combine it with adjacent free memory to allocate it as part of a different object, say an array of 200 floats. The ANSI version of the K&R book has an example of an implementation of malloc that is incompatible with strict aliasing and Dennis Ritchie argued, persuasively, that this type of compiler "optimization" was both dangerous and of limited utility.

Of course, I could be wrong - any of the people claiming strict aliasing is mandatory are welcome to cite the provision of some C standard that mandates strict aliasing. They won't be able to do more than cite a provision in which aliasing of some sort is defined to be "undefined behavior" which, according to some interpretations of the current standard permits compilers to use something like strict aliasing.

Related