# включает
/* The name says it all. Multiply two 32 bit unsigned ints and get
* one 64 bit unsigned int.
*/
uint64_t mul_U32xU32_u64(uint32_t a, uint32_t x) {
return a * (uint64_t)b; /* Note about the cast below. */
}
Это производит:
mul_U32xU32_u64:
movl 8(%esp), %eax
mull 4(%esp)
popl %ebp
ret
При компиляции с:
gcc -m32 -O3 -fomit-frame-pointer -S mul.c
Который использует инструкцию mul
(здесь она называется mull
для умножения long, как это нравится ассемблеру gnu для x86) так, как вы хотите.
В этом случае один из параметров был извлечен непосредственно из стека, а не помещен в регистр (вещь 4(%esp)
означает 4 байта выше указателя стека, а пропущенные 4 байта являются адресом возврата), поскольку числа были переданы в функцию и были бы помещены в стек (в соответствии с x86 ABI (двоичный интерфейс приложения)).
Если вы встроили функцию или просто сделали в ней математические вычисления, это, скорее всего, во многих случаях приведет к использованию инструкции mul
, хотя оптимизирующие компиляторы могут также заменить некоторые умножения на более простой код, если они могут сказать, что это сработает (например, оно может превратить это в сдвиг или даже в константу, если известен один или несколько аргументов).
В коде C хотя бы один из аргументов должен был быть приведен к 64-битному значению, чтобы компилятор выдал 64-битный результат. Даже если компилятор должен был использовать код, который выдает 64-битный результат при умножении 32-битных значений, он, возможно, не считал верхнюю половину этого значения важной, поскольку в соответствии с правилами операций C обычно приводят к значению с тем же типом как значение с наибольшим диапазоном из его компонентов (за исключением того, что иногда можно утверждать, что это не совсем то, что он делает).