dereferencing type-punned pointer will break strict-aliasing rules

D

David Mathog

I have a program for which this line:

if(! lstrtol(&atoken[2],length-2,(long *) &(lclparams->pad)) ||
(lclparams->pad< 0)){

generates the warning below, but ONLY if the gcc compiler is at -O2 or
-O3. I don't see any reason why optimization should change things much
in this piece of code - there's no way to optimize it out and I have
verified that this particular line does what it should no matter how the
program is compiled. Anyway, this is the warning:

warning: dereferencing type-punned pointer will break
strict-aliasing rules

The function lstrtol has this prototype:

int lstrtol(char *string,int length, long *ival);

and the pad field in lclparams in an int.

So two questions:

1. What doesn't the compiler like? Is it the cast of (long *) for
&(int_storage)?
2. Any ideas why the compiler only flags this when it's optimizing?
Seems like whatever the issue is it shouldn't have anything to do
with optimization levels.

I know that using a scratch long variable to retrieve the value
and then copying that into the int field eliminates the warning,
and it won't overflow as the values are going to be small integers, but
I am curious why the compiler acts this way.

Thanks,

David Mathog
 
R

Robert Gamble

I have a program for which this line:

if(! lstrtol(&atoken[2],length-2,(long *) &(lclparams->pad)) ||
(lclparams->pad< 0)){

generates the warning below, but ONLY if the gcc compiler is at -O2 or
-O3. I don't see any reason why optimization should change things much
in this piece of code - there's no way to optimize it out and I have
verified that this particular line does what it should no matter how the
program is compiled. Anyway, this is the warning:

warning: dereferencing type-punned pointer will break
strict-aliasing rules

The function lstrtol has this prototype:

int lstrtol(char *string,int length, long *ival);

and the pad field in lclparams in an int.

So two questions:

1. What doesn't the compiler like? Is it the cast of (long *) for
&(int_storage)?
Yes.

2. Any ideas why the compiler only flags this when it's optimizing?
Seems like whatever the issue is it shouldn't have anything to do
with optimization levels.

The example breaks the aliasing rules and hence invokes undefined
behavior so anything is allowed including working properly in one case
and giving you a diagnostic in another. The point of the aliasing
rules is to allow the compiler to perform certain optimizations by
assuming that a variable of type A won't be accessed through a pointer
to type B. You are accessing a variable of type int through a pointer
to an incompatible type which probably isn't an issue on your
implementation unless the compiler is performing these optimizations
hence the warning appears only in this case.

Robert Gamble
 
B

Barry Schwarz

I have a program for which this line:

if(! lstrtol(&atoken[2],length-2,(long *) &(lclparams->pad)) ||
(lclparams->pad< 0)){

generates the warning below, but ONLY if the gcc compiler is at -O2 or
-O3. I don't see any reason why optimization should change things much
in this piece of code - there's no way to optimize it out and I have
verified that this particular line does what it should no matter how the
program is compiled. Anyway, this is the warning:

warning: dereferencing type-punned pointer will break
strict-aliasing rules

The function lstrtol has this prototype:

int lstrtol(char *string,int length, long *ival);

and the pad field in lclparams in an int.

Since pad is an int, it is possible that it is not aligned properly
for a long. If that happens to be the case, then casting it's address
to type long* invokes undefined behavior.

Furthermore, since you passing a long* to the function, it is
reasonable that the function would use this to store a long value in
the memory pointed to. If your function in fact does this and if
sizeof(long)!=sizeof(int), you have undefined behavior because the
function is storing the wrong number of bytes at the address.


Remove del for email
 
C

Chris Torek

(Others have already answered pointing out other problems.)

warning: dereferencing type-punned pointer will break strict-aliasing rules

]
but ONLY if the gcc compiler is at -O2 or -O3. I don't see any
reason why optimization should change things much in this piece
of code ...

Optimizers love to make assumptions. The assumptions they are
allowed to make are those defined by the language [%]. In this
case, at "higher" optimization levels, gcc wants to make use of
the rule that any lvalue can only be accessed by:

- its name, or
- a pointer that points to its type, or
- a pointer that points to "individual bytes" (e.g., char *).

In particular, for instance, suppose we have the following code
fragment:

float x;
int *p;

if (sizeof(int) != sizeof(float)) {
printf("this program assumes sizeof(int) == sizeof(float)\n");
exit(EXIT_FAILURE);
}

p = (int *)&x;
x = 42.0;

<do lots of work with x that leaves it nonzero>

*p = 0;
if (x == 0.0)
printf("integer zero seems to be floating point zero too\n");
else
printf("verrry interesting! x = %g\n", x);

Now, on some machines (like the x86 for instance), it is very
helpful, for speed reasons, to keep "x" in something other than
ordinary RAM. If x can live in the FPU stack, for instance, the
compiler can use shorter and faster instructions to work with it
(in the <do lots of work> section).

But if the compiler *does* do this, then "p" points only to the
"ordinary RAM copy of x" (as opposed to the "live, useful copy of
x" inside the FPU stack). Changing *p changes only the non-live,
non-useful copy of x. When examining x after assigning to *p, the
compiler should use the live copy of x (in the FPU stack), which
-- assuming the <do lots of work> section really does leave x
nonzero -- will not be equal to 0.0, and the code fragment will
claim that the x86 makes all-zero-bits a non-zero floating point
number (which is in fact false).

The compiler is certainly *allowed* to do this, because modifying
an int (*p) is not supposed to change a float (x).

GCC's complaint:
warning: dereferencing type-punned pointer will break
strict-aliasing rules

is supposed to come out in those cases where it can detect, at
compile time, that some sort of source-level chicanery (such as
the above) could cause later optimization passes to make assumptions
that, while allowed by the Standard, will surprise some programmers.
The detector is probably imperfect, and is clearly only run at
higher optimization levels. (This is true of many of gcc's useful
warnings. In at least some cases, this is because the data structures
that allow the compiler to detect the problem it will warn about are
only built during optimization.)

[% In some cases, compilers may have extra optimization flags that
allow them to make assumptions *not* guaranteed by the language.
In other words, the programmer can, on the compilation flags line,
"write checks that the language can't cash", to borrow a phrase.
If you are such a programmer, make sure your code, at least, *can*
cash them. :) ]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,228
Members
46,816
Latest member
nipsseyhussle

Latest Threads

Top