我正在查看为这个简单的x64程序生成的程序集Visual Studio:

struct Point {
    int a, b;

    Point() {
        a = 0; b = 1;
    }
};

int main(int argc, char* argv[])
{
    Point arr[3];
    arr[0].b = 2;
    return 0;
}


当它遇到arr [0] .b = 2时,将生成以下代码:

mov eax, 8
imul rax, rax, 0
mov dword ptr [rbp+rax+4],2


为什么要执行imul rax,rax,0而不是简单的mov rax,0甚至xor rax,rax?如果有的话,imul如何更有效率?

最佳答案

卡马奇

原因是因为程序集正在计算数组中Point对象(恰好在堆栈上)的偏移量以及变量b的偏移量。

具有三(3)个操作数的imul的英特尔文档状态:


  三操作数形式-此形式需要目标操作数(
  第一个操作数)和两个源操作数(第二个和第三个)
  操作数)。在这里,第一个源操作数(可以是
  通用寄存器或存储位置)乘以
  第二个源操作数(立即数)。中间产品
  (第一个源操作数的大小的两倍)被截断并存储
  在目标操作数(通用寄存器)中。


在您的情况下,它正在计算对象在数组中的偏移量,从而导致寻址堆栈中的第一个(第零个)Point位置。解决该问题后,便要添加.b的偏移量,即+4。如此细分:

mov  eax,8                   ; prepare to offset into the Point array
imul rax, rax, 0             ; Calculate which Point object is being referred to
mov  dword ptr [rbp+rax+4],2 ; Add the offset to b and move value 2 in


指令。所有这些都解析为arr[0].b = 2

我认为您没有进行积极的优化。在进行直接编译(不进行优化,调试等)时,编译器不会对寻址进行任何假设。

与c比较

在具有clang 3.9.0并且没有优化标志的OS X(El Capitan)上,一旦在数组中实例化了Point对象,对.b = 2的分配就很简单:

mov dword ptr [rbp - 44], 2


在这种情况下,clang非常了解偏移量,并在默认优化过程中解析地址。

关于c++ - 为什么VS imul rax,rax,0而不是简单的举动?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37629283/

10-11 19:11