其他分享
首页 > 其他分享> > c – 优化存储/构建易失性堆栈变量是否合法?

c – 优化存储/构建易失性堆栈变量是否合法?

作者:互联网

我注意到clang和gcc在某些情况下优化了堆栈上声明的volatile结构的构造或赋值.例如,以下代码:

struct nonvol2 {
    uint32_t a, b;
};

void volatile_struct2()
{
    volatile nonvol2 temp = {1, 2};
}

Compiles on clang to:

volatile_struct2(): # @volatile_struct2()
  ret

另一方面,gcc不会删除商店,虽然它确实将两个隐含商店优化为一个商店:

volatile_struct2():
        movabs  rax, 8589934593
        mov     QWORD PTR [rsp-8], rax
        ret

奇怪的是,clang不会将易失性存储优化为单个int变量:

void volatile_int() {
    volatile int x = 42;
}

编译为:

volatile_int(): # @volatile_int()
  mov dword ptr [rsp - 4], 1
  ret

此外,没有优化具有1个成员而不是2个成员的结构.

虽然gcc在这种特殊情况下不会删除构造,但在结构成员本身被声明为volatile的情况下,它可能甚至更积极的优化,而不是构造点上的struct本身:

typedef struct {
    volatile uint32_t a, b;
} vol2;

void volatile_def2()
{
    vol2 temp = {1, 2};
    vol2 temp2 = {1, 2};
    temp.a = temp2.a;
    temp.a = temp2.a;
}

简单地编译成一个简单的ret.

虽然删除这些几乎不可能通过任何合理的过程观察到的商店似乎完全“合理”,但我的印象是,在标准的挥发性载荷和商店被认为是程序的可观察行为的一部分(除了调用IO功能),完全停止.这意味着它们不会被“似乎”删除,因为它将根据定义改变程序的可观察行为.

我错了,还是在这里违反了规则?也许建筑被排除在必须假设挥发性有副作用的情况下?

解决方法:

从标准的角度来看,没有要求实现记录任何对象如何物理存储在内存中.即使实现记录了使用unsigned char *类型的指针来访问某种类型的对象的行为,也可以允许实现以其他方式物理存储数据,然后让基于字符的读写代码适当地调整行为.

如果执行平台指定抽象机器对象与CPU看到的存储之间的关系,并定义访问某些CPU地址可能触发编译器不知道的副作用的方式,那么适合低级编程的质量编译器在该平台上应该生成代码,其中volatile限定对象的行为与该规范一致.标准没有试图强制要求所有实现都适用于低级编程(或任何其他特定目的).

如果自动变量的地址永远不会暴露给外部代码,则volatile限定符只需要有两个效果:

>如果在函数内调用setjmp,编译器必须执行必要的操作以确保longjmp不会破坏任何volatile限定对象的值,即使它们是在setjmp和longjmp之间写入的.如果没有限定符,则在执行longjmp时,setjmp和longjmp之间写入的对象的值将变得不确定.
>允许编译器假定任何没有副作用的循环将运行完成的规则​​不适用于在循环内访问volatile对象的情况,无论实现是否定义任何方法访问将是可观察的.

除了这些情况之外,as-if规则允许编译器以与物理机无关的方式在抽象机中实现volatile限定符.

标签:c,x86,language-lawyer,optimization,volatile
来源: https://codeday.me/bug/20191006/1861499.html