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

问题描述 投票:0回答:2

我正在尝试使用 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_counts
uint32_t type

spm->num_streams is of type uint16_t

有人可以解释为什么会发生这种情况吗?

c pointers gcc optimization
2个回答
2
投票

您是“假设”规则的受害者。编译器不知道您正在从硬件寄存器中读取数据。当你写下:

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
投票

既然用

stream_counts
限定
volatile
解决了问题,那么您似乎受到了 as-if 规则的影响,该规则基本上说,如果编译器可以修改程序,只要它可以确定它不会影响可观察的行为。 cppreference 在这里有一个很好的解释,尽管它是针对 C++ 编写的,但大部分也适用于 C。代码示例特别有启发性。

C++标准草案不同,C99标准草案仅明确引用索引中的as-if规则,该规则指向第

5.1.2.3
第3段中的程序执行强调我的) ):

在抽象机中,所有表达式都按照语义指定的方式进行计算。一个 实际的实现不需要评估表达式的一部分,如果它可以推断出它的 未使用值并且不会产生所需的副作用(包括由以下原因引起的任何副作用) 调用函数或访问易失性对象)。

它确实在一些示例中使用了 as if 短语,从第 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段涵盖了符合实施的最低要求是

© www.soinside.com 2019 - 2024. All rights reserved.