为什么通过指针进行int加法比通过指针进行int乘法少一条x86指令?
我有以下 C/C++ 代码(编译器资源管理器链接):
void update_mul(int *x, int *amount) {
*x *= *amount;
}
void update_add(int *x, int *amount) {
*x += *amount;
}
在 clang 和 gcc 编译为 C 或 C++ 并至少-O1启用的情况下,以上转换为以下程序集:
update_mul: # @update_mul
mov eax, dword ptr [rdi]
imul eax, dword ptr [rsi]
mov dword ptr [rdi], eax
ret
update_add: # @update_add
mov eax, dword ptr [rsi]
add dword ptr [rdi], eax
ret
似乎添加它正在做类似的事情:
register = *amount;
*x += register;
但是对于乘法,它正在做:
register = *x;
register *= *amount;
*x = register;
为什么乘法需要额外的加法指令,或者不需要但只是更快?
回答
在IA-32体系结构规范(替换的单页链路)示出了是根本不存在编码IMUL其中目的地(第一自变量)是操作数的存储器:
Encoding | Meaning
IMUL r/m8* | AX ? AL ? r/m byte.
IMUL r/m16 | DX:AX ? AX ? r/m word.
IMUL r/m32 | EDX:EAX ? EAX ? r/m32.
IMUL r/m64 | RDX:RAX ? RAX ? r/m64.
IMUL r16, r/m16 | word register ? word register ? r/m16.
IMUL r32, r/m32 | doubleword register ? doubleword register ? r/m32.
IMUL r64, r/m64 | Quadword register ? Quadword register ? r/m64.
IMUL r16, r/m16, imm8 | word register ? r/m16 ? sign-extended immediate byte.
IMUL r32, r/m32, imm8 | doubleword register ? r/m32 ? sign- extended immediate byte.
IMUL r64, r/m64, imm8 | Quadword register ? r/m64 ? sign-extended immediate byte.
IMUL r16, r/m16, imm16 | word register ? r/m16 ? immediate word.
IMUL r32, r/m32, imm32 | doubleword register ? r/m32 ? immediate doubleword.
IMUL r64, r/m64, imm32 | Quadword register ? r/m64 ? immediate doubleword.
- 历史原因:`imul` 的多操作数形式是新的,有 186(立即数)和 386(r,r/m)。与 `add` 不同,来自原始 8086 的 ALU 指令之一因此具有 `r, r/m` 和 `r/m, r` 形式的操作码。与其他一些限制/设计选择不同,没有内存目标乘法对于 x86 来说不是一个明显的问题。在现实生活中,您总是希望内联像这样的小函数,并且通常至少有一个操作数已经在寄存器中。