如何强制用C读取未使用的内存不会被优化掉?(How to force an unused memor

2019-07-04 22:56发布

微处理器经常需要被读取寄存器来清除某些状态条件。 有没有在C移植的方式,以确保在不使用的数据进行读取时不优化掉? 是不是足够的指针到内存映射寄存器被声明为volatile吗? 换句话说,将始终下面就符合标准的编译器的工作?

void func(void)
{
   volatile unsigned int *REGISTER = (volatile unsigned int *) 0x12345678;

   *REGISTER;
}

据我所知,对付这样的功能,运行到编译器相关的问题。 所以,我的便携式的定义是有点在这种情况下松动。 我只是说,这将与最流行的工具链,尽可能广泛地工作。

Answer 1:

人们争论颇为吃力究竟什么volatile手段。 我想大多数人都认为你展示构建打算做你想做的,但在标准C语言实际上保证了它作为C99的没有一致的。 (这种情况可能在C2011已经有所好转,我没有看过那呢。)

非标准,但还算嵌入式编译器的广泛支持,替代方案,可能是更容易的工作

void func(void)
{
  asm volatile ("" : : "r" (*(unsigned int *)0x12345678));
}

(在“挥发”这里appies到“ASM”,并表示“即使它没有输出操作数,这可能不会被删除。这是没有必要把它的指针也。)

这个结构的主要剩余的缺点是,你还有没有保证,编译器将生成读入一个指令存储器。 随着C2011,使用_Atomic unsigned int 可能就足够了,但在没有该功能,你几乎必须写一个真实(非空的)组件插入自己,如果你需要这样的保证。

编辑:另一种皱纹今天早上发生在我身上。 如果从存储位置读取具有相应位置内存更改值的副作用,你需要

void func(void)
{
  unsigned int *ptr = (unsigned int *)0x12345678;
  asm volatile ("" : "=m" (*ptr) : "r" (*ptr));
}

以防止错优化等从该位置读取。 (100%清楚,这种变化不会改变用于生成汇编语言func本身,但可能会影响周围的代码,尤其是当优化func内联)。



Answer 2:

是的,C标准保证代码访问的易失性变量将不会被优化。

C11 5.1.2.3/2

“访问一个volatile对象,” ...“都是副作用”

C11 5.1.2.3/4

“实际上,在实现无法计算表达式的一部分,如果它可以推断其值未被使用且没有需要的副作用产生(包括任何引起调用一个函数或访问的易失性对象)”。

C11 5.1.2.3/6

“对符合标准的实现最低要求是:

- 访问根据所述抽象机的规则评价严格挥发性物“。



Answer 3:

IIRC,C标准是在使用中的定义有点松,所以*REGISTER不一定解释为做一

但下面应该做的:

int x = *REGISTER;

即,存储器参考的结果在某处被使用。 该x不需要挥发,但是。

更新 :为了避免_unused变量你可以用无操作功能做的警告。 静态和/或内联函数应该被优化掉,而不增加运行时间:

static /*inline*/ void no_op(int x)
{ }

no_op(*REGISTER);

更新2:我刚刚想出了一个更好的功能:

static unsigned int read(volatile unsigned int *addr)
{
    return *addr;
}

read(REGISTER);

现在,这个功能既可以用于读取和使用以及读取和放弃使用。 8-)



Answer 4:

编译器通常不优化组合内联(这是很难正确地分析它们)。 此外,这似乎是一个妥善的解决办法:你要在寄存器更明确的控制,它是自然的组装。

既然你正在编写一个微控制器,我认为有一些组件已经在你的代码,所以有点内嵌汇编的将不会是一个问题。



Answer 5:

也许GNU C特定扩展名不被认为是非常便携,但这里是另一种选择。

#define read1(x)  \
({ \
  __typeof(x) * _addr = (volatile __typeof(x) *) &(x); \
  *_addr; \
})

这将转化为以下汇编行(用gcc编译x86和优化,-02): movl SOME_REGISTER(%rip), %eax?

我从同一汇编:

inline read2(volatile uint32_t *addr) 
{ 
   return *addr; 
}`

...所建议的另一个答案,但read1()将处理不同的寄存器大小。 虽然我不知道,如果使用read2()与8位或16位寄存器将永远是一个问题,至少有上参数类型没有任何警告。



文章来源: How to force an unused memory read in C that won't be optimized away?