I am trying to debug a C program using gdb.The compile flags that I am using are as below
-fno-strict-aliasing -Wall -DHAVE_CONFIG_H -DNO_OLD_ERF_TYPES -Werror -Wredundant-decls -O2 -DNDEBUG -DBYTESWAP -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -g
The version of the compiler that I am using is
gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-52)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
The disputed code is the following line
spm->num_streams = (uint16_t)((MkIV->stream_counts >> 16 ) & 0xfff);
Value of num_streams with
-fno-strict-aliasing
0xffff (4095)
Value of num_streams WITHOUT-fno-strict-aliasing
0x1 (1)
Now it is worth noting the actual value of mkIV->stream_counts
is 0x10020
.This is read from a HARDWARE REGISTER
the value we are interested in is spm->num_streams
is BIT27:BIT16
.Therefore the expected value is '1'
If I were to replace
spm->num_streams = (uint16_t)((MkIV->stream_counts >> 16 ) & 0xfff);
with
spm->num_streams = (uint16_t)((MkIV->stream_counts & 0xfff0000) >> 16);
then I get the value of 0x1(1)
with and without -fno-strict-aliasing
The stream_counts in MkIV structure (MkIV->stream_counts
is of uint32_t type
)
spm->num_streams is of type uint16_t
Can someone explain why this is happening?
Since qualifying stream_counts
with volatile
fixes the problem then it would seem that you were impacted by the as-if rule which basically says that if the compiler can modify the program as long as it can determine that it will not effect the observable behavior. cppreference has a nice explanation here and although it is written with respect to C++ most of it applies to C as well. The code examples are especially instructive.
Unlike the draft C++ standard the C99 draft standard only explicitly refers to the as-if rule in the index which points to section 5.1.2.3
Program execution in paragraph 3 it says(emphasis mine):
In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).
it does use the as if phrase in some of the examples, from paragraph 11 we have the following example:
float f1, f2;
double d;
/* ... */
f1 = f2 * d;
and the following text says(emphasis mine):
the multiplication may be executed using single-precision arithmetic if the implementation can ascertain that the result would be the same as if it were executed using double-precision arithmetic
and if we look at section 6.7.3
Type qualifiers paragraph 6 says(emphasis mine):
An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.3. Furthermore, at every sequence point the value last stored in the object shall agree with that prescribed by the abstract machine, except as modified by the unknown factors mentioned previously.116)[...]
which puts restrictions on the short cuts a compiler can take with respect to volatile
qualified objects. Section 5.1.2.3
paragraph 5 covers the The least requirements on a conforming implementation are.