我试图了解地址计算指令的工作原理,尤其是使用leaq命令。然后,当我看到使用leaq进行算术运算的示例时,我会感到困惑。例如,以下C代码,

long m12(long x) {
return x*12;
}

在组装中
leaq (%rdi, %rdi, 2), %rax
salq $2, $rax

如果我的理解是正确的,则leaq应该将地址(%rdi, %rdi, 2)(应该为2*%rdi+%rdi)移动到%rax中。我感到困惑的是,因为值x存储在%rdi中,它只是内存地址,为什么将%rdi乘以3然后将该内存地址左移2等于x乘以12?难道当我们将%rdi乘以3时,我们跳到另一个不保存值x的内存地址吗?

最佳答案

leaq不必对内存地址进行操作,它可以计算一个地址,它实际上不会从结果中读取数据,因此,直到mov等尝试使用它,这只是添加一个数字的深奥方式,再加上1、2、4或8倍于另一个数字(在这种情况下为相同数字)。如您所见,出于数学目的,它经常被“滥用”†。 2*%rdi+%rdi只是3 * %rdi,因此它在计算x * 3时不涉及CPU上的乘法器单元。
类似地,由于二进制数的工作方式(对于十进制数,向右加零乘以10),对于整数,向左移对于整数(每向右加零)会使值加倍。
因此,这是在滥用leaq指令以完成3的乘法运算,然后将结果移位以进一步实现4的乘法运算,从而最终获得12的乘法结果,而实际上并未使用过乘法指令(它认为运行速度会更慢,就我所知,这可能是正确的;再次猜测编译器通常是一场失败的游戏)。
†:清楚一点,它不是滥用,而是以与您的名字所暗示的隐含目的不符的方式使用。以这种方式使用它是100%可以的。

09-27 18:07