#Multi-operand IMUL on x86
Multi-operand IMUL on x86
Not quite sure if it was entirely driven by programming languages, but I believe they must have high influences to Intel's decision
Originally there were only IMUL r/m16
and IMUL r/m8
that output a product twice as wide as the operands. However in reality modern high-level languages generally produce a multiplication result that has the same type as the two operands unless you cast the them to a wider type. For example in C if we have int a
and int b
then a*b
will also have type int
. Same to most other languages. Doing a non-widening multiplication is also faster than getting the full result, so Intel added the 2 and 3-operand forms that don't calculate the high bits
There are two additional forms for the IMUL instruction which do not fit the above pattern. The first is a two-operand version that follows the pattern for ADD:
IMUL r, r/m ; d *= s (signed)
This is a more traditional-looking two-operand instruction² that updates the destination register in place.
There is even a (gasp) three-operand version similar to what you see in other processors.
IMUL r, r/m, i ; d = s * t (signed)
This three-operand version accepts an immediate as the third operand, and it's the one the compiler typically generates. For example,
IMUL EAX, ECX, 212 ; EAX = ECX * 212 (signed)
These additional forms produce only single-precision results, but that's what the C and C++ languages produce, so it fits well with those languages. If you need a double-precision result, then you can use the single-operand MUL and IMUL instructions.
Note that there is no unsigned version of these additional forms. Fortunately, you can use the signed version for unsigned multiplication because the single-precision result is the same for both signed and unsigned multiplication. However, the flags are always set according to the signed result, so you cannot use them to detect unsigned overflow.
In practice, this is not a problem because the C language doesn't give you access to the overflow flags anyway.
Since they're more flexible, fit perfectly with the type model, and don't care about signness (because non-widening multiplication is the same for signed and unsigned values), compilers began to use it exclusively for almost all multiplications. As a result, Intel focused to optimize even more for those forms of imul
. In fact nowadays single-operand mul
and imul
are very rarely used and may be slow on many μarchs. See Why is imul
used for multiplying unsigned numbers?