Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC: accuracy of strict aliasing warnings

Tags:

I'm trying to check some of my code for strict aliasing violations, but it looks like I've missed something while trying to understand the strict aliasing rule.

Imagine the following code:

#include <stdio.h>

int main( void )
{
    unsigned long l;

    l = 0;

    *( ( unsigned short * )&l ) = 1;

    printf( "%lu\n", l );

    return 0;
}

Classic and basic example. With GCC 4.9 (-Wall -fstrict-aliasing -Wstrict-aliasing -O3), it actually reports the error:

 dereferencing type-punned pointer will break strict-aliasing rules

But the following compiles fine:

#include <stdio.h>

int main( void )
{
    unsigned long    l;
    unsigned short * sp;

    l       = 0;
    sp      = ( unsigned short * )&l;
    *( sp ) = 1;

    printf( "%lu\n", l );

    return 0;
}

In my understanding, the second example also violates the struct aliasing rule.
So why does it compile? Is it an accuracy issue in GCC, or have I missed something with strict aliasing?

I also found the following topic: Why are no strict-aliasing warnings generated for this code?

Compiling with -Wstrict-aliasing=2 or -Wstrict-aliasing=3 makes no difference.
But -Wstrict-aliasing=1 does report the error on the second example.

GCC documentation says level 1 is the least accurate, and can produce a lot of false positives, while level 3 is the most accurate...

So what's happening here? An issue with my own understanding or an issue with GCC?

Bonus question

I usually prefer Clang/LLVM over GCC for my projects, but it seems Clang doesn't issue any warning about strict aliasing.
Does anyone knows why?
Is it because it is not able to detect violations, or because it does not follow the rule when generating code?

like image 898
Macmade Avatar asked Jan 19 '14 09:01

Macmade


People also ask

How does GCC treat warning errors?

The warning is emitted only with --coverage enabled. By default, this warning is enabled and is treated as an error. -Wno-coverage-invalid-line-number can be used to disable the warning or -Wno-error=coverage-invalid-line-number can be used to disable the error. Suppress warning messages emitted by #warning directives.

Which option of GCC inhibit all warning messages?

-w is the GCC-wide option to disable warning messages.

What is the strict aliasing rule and why do we care?

"Strict aliasing is an assumption, made by the C (or C++) compiler, that dereferencing pointers to objects of different types will never refer to the same memory location (i.e. alias each other.)"

How do I enable warnings in GCC?

GCC 4.3+ now has -Q --help=warnings , and you can even specify --help=warnings,C to just print out the C related warnings.


2 Answers

Your understanding is correct. Alias analysis is generally complicated and in this case apparently the mere use of a temporary pointer between the cast and dereference was enough to throw it off. Surprisingly, GCC 4.8.2 does a better job on this code, warning at -Wstrict-aliasing=2 as well as level 1, so this is a regression.

As for clang, it simply does not currently have the facility to warn about aliasing violations. It does absolutely take advantage of the rule in optimization. To see this in action, take this example straight from the C standard (N1570 §6.5.2.3 9))

struct t1 { int m; };
struct t2 { int m; };

int f(struct t1 *p1, struct t2 *p2) {
    if (p1->m < 0)
        p2->m = -p2->m;
    return p1->m;
}

If p1 and p2 point to the same struct, Clang (and GCC) will nevertheless return the value of p1->m before negation, since they may assume p2 does not alias p1 and therefore the previous negation never affects the result. Here's the full example and output with and without -fstrict-aliasing. For more examples, see here and the oft-cited What Every C Programmer Should Know About Undefined Behavior; strict aliasing optimizations are the final topic of the introductory post.

As for when warnings will be implemented, the devs are quiet, but they are mentioned in clang's test suite, which lists -Wstrict-aliasing=X under the title (emphasis mine)

These flags are currently unimplemented; test that we output them anyway.

So it seems likely to happen at some point.

like image 55
tab Avatar answered Oct 25 '22 00:10

tab


There is nothing inherently wrong with

sp      = ( unsigned short * )&l;

For all the compiler knows, you might simply cast back the pointer to the correct type again.

There is also nothing inherently wrong with

*( sp ) = 1;

It's the combination of the two that's wrong.

The compiler just can't put the two together to tell you the combination is problematic (it is impossible in the general case, and not so easy in this particular case).

But

*( ( unsigned short * )&l ) = 1;

is far easier to detect, and hence the compiler does its best to do so.

like image 26
user541686 Avatar answered Oct 25 '22 00:10

user541686