gcc:从硬件寄存器读取时出现'-fno-strict-aliasing'的奇怪行为

时间:2013-09-24 00:11:06

标签: c pointers optimization gcc compiler-construction

我正在尝试使用gdb调试C程序。我正在使用的编译标志如下

-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 

我正在使用的编译器版本是

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.

有争议的代码是以下行

spm->num_streams = (uint16_t)((MkIV->stream_counts >> 16 ) & 0xfff);

num_streams的值with -fno-strict-aliasing

 0xffff (4095)

num_streams WITHOUT-fno-strict-aliasing

的值
 0x1 (1)

现在值得注意的是mkIV->stream_counts的实际值是0x10020。这是从HARDWARE REGISTER

中读取的

我们感兴趣的值是spm->num_streams BIT27:BIT16。因此预期值为'1'

如果我要替换

spm->num_streams = (uint16_t)((MkIV->stream_counts >> 16 ) & 0xfff);

spm->num_streams = (uint16_t)((MkIV->stream_counts & 0xfff0000) >> 16);

然后我得到0x1(1)的值为-fno-strict-aliasing

MkIV结构中的stream_counts(MkIV->stream_countsuint32_t type

spm->num_streams is of type uint16_t

有人可以解释为什么会这样吗?

2 个答案:

答案 0 :(得分:2)

您是'as-if'规则的受害者。编译器不知道你是从硬件寄存器读取的。当你写:

spm->num_streams = (uint16_t)((MkIV->stream_counts & 0xfff0000) >> 16);

编译器完全有权这样做:

uint16_t j = MkIV->stream_counts;
MkIV->stream_counts &= 0xfff0000;
MkIV->stream_counts >>= 16;
sp->num_streams = MKIV->stream_counts;
MkIV->stream_counts = j;

最有可能的是,您可以通过使stream_counts易失性或通过易失性指针“清洗”读取来解决此问题。

而不是MkIV->stream_counts,请使用*(volatile uint16_t *)&MkIV->stream_counts

答案 1 :(得分:1)

由于符合条件stream_countsvolatile修复了问题,因此您似乎受到 as-if 规则的影响,该规则基本上表示如果编译器可以修改程序,只要它可以确定它不会影响可观察行为。 cppreference has a nice explanation here虽然它是针对 C ++ 编写的,但大多数也适用于 C 。代码示例特别有用。

草案C ++标准不同,C99 draft standard仅明确引用索引中的 as-if 规则,该规则指向5.1.2.3 < em>程序执行在段落 3 中说(强调我的):

  

在抽象机器中,所有表达式都按语义指定的方式进行计算。一个   实际的实现不需要评估表达式的一部分,如果它可以推断出它的那个   没有使用价值,也没有产生所需的副作用(包括由...造成的任何副作用)   调用函数或访问volatile对象。)

它确实在某些示例中使用,如同短语,从 11 段开始,我们有以下示例:

float f1, f2;
double d;
/* ... */
f1 = f2 * d;

以下文字说明(强调我的):

  

如果实现可以确定结果与使用双精度算术执行 >

,则可以使用单精度算法执行乘法运算

如果我们查看部分6.7.3 类型限定符 6 说(强调我的):

  

具有volatile限定类型的对象可能会以未知的方式进行修改   实施或有其他未知的副作用。 因此任何表达都是指   对于这样的对象,应严格按照抽象机的规则进行评估,如5.1.2.3所述。此外,在每个序列点,最后存储在对象中的值应与抽象机器规定的值一致,除非由前面提到的未知因素修改.116)[...]

它限制了编译器可以对volatile限定对象进行的快捷方式。第5.1.2.3 5 涵盖符合实施的最低要求