I have the following C/C++ code (compiler explorer link):
void update_mul(int *x, int *amount) {
*x *= *amount;
}
void update_add(int *x, int *amount) {
*x += *amount;
}
Under both clang and gcc compiling as C or as C++ with at least -O1
enabled, the above translates to this assembly:
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
It seems like for add it's doing something like:
register = *amount;
*x += register;
But for multiply it's doing:
register = *x;
register *= *amount;
*x = register;
Why does the multiplication require an extra instruction over the add, or is it not required but just faster?
The IA-32 architecture specification (alternative single-page link) shows that there is simply no encoding for IMUL where the destination (first argument) is a memory operand:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With