易失性不按预期工作

Dar*_*bik 9 c++ g++ volatile language-lawyer

考虑以下代码:

struct A{ 
  volatile int x;
  A() : x(12){
  }
};

A foo(){
  A ret;
  //Do stuff
  return ret;
}

int main()
{
  A a;
  a.x = 13;
  a = foo();
}
Run Code Online (Sandbox Code Playgroud)

使用g++ -std=c++14 -pedantic -O3我得到这个程序集:

foo():
        movl    $12, %eax
        ret
main:
        xorl    %eax, %eax
        ret
Run Code Online (Sandbox Code Playgroud)

根据我的估计,变量x应写入至少三次(可能是四次),但它甚至不写一次(函数foo甚至不被调用!)

更糟糕的是,当您向其添加inline关键字时,foo结果如下:

main:
        xorl    %eax, %eax
        ret
Run Code Online (Sandbox Code Playgroud)

我认为volatile意味着即使编译器看不到读/写的点,每次读或写都必须发生.

这里发生了什么?

更新:

A a;像这样把外部主要的声明:

A a;
int main()
{  
  a.x = 13;
  a = foo();
}
Run Code Online (Sandbox Code Playgroud)

生成此代码:

foo():
        movl    $12, %eax
        ret
main:
        movl    $13, a(%rip)
        xorl    %eax, %eax
        movl    $12, a(%rip)
        ret
        movl    $12, a(%rip)
        ret
a:
        .zero   4
Run Code Online (Sandbox Code Playgroud)

这更接近你所期望的......我甚至更加困惑

Sam*_*nen 2

Visual C++ 2015 does not optimize away the assignments:

A a;
mov         dword ptr [rsp+8],0Ch  <-- write 1
a.x = 13;
mov         dword ptr [a],0Dh      <-- write2
a = foo();
mov         dword ptr [a],0Ch      <-- write3
mov         eax,dword ptr [rsp+8]  
mov         dword ptr [rsp+8],eax  
mov         eax,dword ptr [rsp+8]  
mov         dword ptr [rsp+8],eax  
}
xor         eax,eax  
ret  
Run Code Online (Sandbox Code Playgroud)

/O2(最大化速度)和 /Ox(完全优化)也会发生同样的情况。

gcc 3.4.4 也使用 -O2 和 -O3 保留易失性写入

_main:
pushl   %ebp
movl    $16, %eax
movl    %esp, %ebp
subl    $8, %esp
andl    $-16, %esp
call    __alloca
call    ___main
movl    $12, -4(%ebp)  <-- write1
xorl    %eax, %eax
movl    $13, -4(%ebp)  <-- write2
movl    $12, -8(%ebp)  <-- write3
leave
ret
Run Code Online (Sandbox Code Playgroud)

使用这两个编译器,如果我删除 volatile 关键字,main() 基本上会变成空。

我想说的是,在这种情况下,编译器过于激进(并且错误地恕我直言)决定,由于未使用“a”,因此不需要对它进行操作,并且忽略了易失性成员。使 'a' 本身变得易失性可以让你得到你想要的东西,但由于我没有可以重现这一点的编译器,我不能肯定地说。

最后(虽然这确实是微软特有的),https://msdn.microsoft.com/en-us/library/12a04hfd.aspx说:

如果结构体成员被标记为易失性,则易失性将传播到整个结构。

这也表明您所看到的行为是编译器问题。

最后,如果将“a”设置为全局变量,那么编译器不太愿意将其视为未使用并删除它,这在某种程度上是可以理解的。全局变量默认是extern的,所以仅仅看main函数并不能说全局'a'没有被使用。其他一些编译单元(.cpp 文件)可能正在使用它。