简体   繁体   English

gcc:从硬件寄存器读取时,“-fno-strict-aliasing”行为异常

[英]gcc: strange behaviour with '-fno-strict-aliasing' when reading from hardware register

I am trying to debug a C program using gdb.The compile flags that I am using are as below 我正在尝试使用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 

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 with -fno-strict-aliasing的num_streams的值

 0xffff (4095)

Value of num_streams WITHOUT-fno-strict-aliasing 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 现在值得注意的是mkIV->stream_counts的实际值为0x10020 。这是从HARDWARE REGISTER读取的

the value we are interested in is spm->num_streams is BIT27:BIT16 .Therefore the expected value is '1' 我们感兴趣的值为spm->num_streamsBIT27:BIT16因此预期值为'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 然后在有和没有-fno-strict-aliasing情况下得到0x1(1)的值

The stream_counts in MkIV structure ( MkIV->stream_counts is of uint32_t type ) MkIV结构中的stream_counts( MkIV->stream_countsuint32_t type

spm->num_streams is of type uint16_t

Can someone explain why this is happening? 有人可以解释为什么会这样吗?

You are a victim of the 'as-if' rule. 您是“假设”规则的受害者。 The compiler has no idea you're reading from a hardware register. 编译器不知道您要从硬件寄存器中读取内容。 When you write: 当你写:

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

The compiler has every right to do this: 编译器有权执行此操作:

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

Most likely, you can work around the problem by making stream_counts volatile or "laundering" the read through a volatile pointer. 您很可能可以通过使stream_counts易失性或通过易失性指针“清洗”读取来解决此问题。

Instead of MkIV->stream_counts , use *(volatile uint16_t *)&MkIV->stream_counts . 代替MkIV->stream_counts ,使用*(volatile uint16_t *)&MkIV->stream_counts

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 . 由于使用volatile修饰了stream_counts可以解决问题,因此看来您似乎受到了“ 假设”规则的影响,该规则基本上说,只要编译器可以修改程序,只要它可以确定它不会影响可观察到的行为即可 cppreference has a nice explanation here and although it is written with respect to C++ most of it applies to C as well. cppreference 在这里有一个很好的解释 ,尽管它是针对C ++编写的,但大多数也适用于C。 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 ): C ++标准草案不同, C99标准草案仅在索引中明确引用了as-if规则,该规则指向5.1.2.3节中第3段中的程序执行强调我的意思 ):

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: 它确实在某些示例中使用了if短语,在第11段中,我们有以下示例:

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 ): 如果我们看第6.7.3节“ 类型限定符”,6段说( 强调我的话 ):

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. 因此,任何引用该对象的表达式都必须严格按照抽象机器的规则进行评估 ,如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)[...] 此外,在每个序列点上,对象中最后存储的值应与抽象机所规定的值一致,除非之前提到的未知因素对其进行了修改。116)[...]

which puts restrictions on the short cuts a compiler can take with respect to volatile qualified objects. 这就限制了编译器对于volatile合格对象的捷径。 Section 5.1.2.3 paragraph 5 covers the The least requirements on a conforming implementation are . 5.1.2.3节第5段涵盖了对符合的实现的最低要求

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM