Signed - SMNEGL, Unsigned - UMNEGL, ?? - MNEG
Ok so what is the third multiply-negate (MNEG)? There's a few operations like this one. The descriptions are the exact same. Is it just the same as unsigned or signed? I noticed MNEG has a 32-bit version while UMNEGL doesn't.
Let's break this down a bit.
First of all, multiplication by itself. It comes in four flavors:
32 x 32 = 32 (i.e. multiply two 32-bit inputs and get a 32-bit output, with high bits truncated): e.g. mul w0, w1, w2
. There is no signed/unsigned version, because non-widening multiplication is bitwise the same operation for either signed or unsigned operands - a fact from modular arithmetic.
64 x 64 = 64, e.g. mul x0, x1, x2
. Same deal, no signed/unsigned distinction.
32 x 32 = 64 "long multiply", e.g. smull x0, w1, w2
. Here it does make a difference whether the operation is to work for signed or unsigned values, so we have separate smull
and umull
instructions. For instance, 0xffffffff * 0xffffffff
should be 0x1
if treated as signed, or 0xfffffffe00000001
if treated as unsigned. (But as noted above, the low 32 bits are the same for both.)
64 * 64 = 128. This uses mul / smulh
or mul / umulh
depending on whether it should be signed or unsigned. It's not directly relevant to your question.
Now, mul
and smull/umull
are also available as multiply-add and multiply-subtract operations, where for instance madd w0, w1, w2, w3
computes w0 = (w1 * w2) + w3
. Likewise smaddl x0, w1, w2, x3
computes x0 = (w1 * w2) + x3
, with the multiplication done as signed 32x32=64, and umaddl x0, w1, w2, x3
similar for unsigned. Indeed, mul w0, w1, w2
is just an alias of madd w0, w1, w2, wzr
, and ditto for smull
and umull
.
As for the multiply-subtract versions, for instance msub w0, w1, w2, w3
computes w0 = w3 - (w1 * w2)
. If the fourth operand is the zero register, then msub w0, w1, w2, wzr
would compute w0 = -(w1*w2)
. For convenience, this instruction is aliased as mneg w0, w1, w2
. As above, it makes no difference whether you think of it as signed or unsigned, because it's the same binary operation either way.
The long versions likewise have a multiply-subtract, which becomes multiply-negate with the zero register. smnegl x0, w1, w2
computes x0 = -(w1 * w2)
, signed widening, alias of smsubl x0, w1, w2, xzr
. Likewise umnegl x0, w1, w2
computes x0 = -(w1 * w2)
, unsigned widening. You may think negation looks odd on an unsigned value, but it's all mod 2^64 so you can also think of it as x0 = 2^64 - (w1 * w2)
.
There may not be much particular value in the multiply-negate instructions, but they come for free in the hardware as special cases of multiply-subtract when you use the zero register, so the ARM folks figured they might as well provide an alias in the assembler, just in case somebody found it useful.