Prefer BLENDVPD over VBLENDVPD if the latter doesn't save any
instructions.
VBLENDVPD allows separate source and destination registers, which can
eliminate a MOVAPD/MOVSD. However, on Intel since Skylake, VBLENDVPD
takes additional uops to execute compared to BLENDVPD (according to
https://uops.info). On AMD and older Intel microarchitectures there is no
difference.
The value being stored must be loaded into a register. In the case of an
immediate value, this means it must be materialized. The value is
eventually byteswapped before performing the store.
This can be simplified for the value 0 for two reasons:
- ARM64 has a dedicated zero register, so does not need to be
materialized.
- Byteswapping zero is still zero, so we can skip this step.
We could skip byteswapping for other values by immediately materializing
the byteswapped value in a register, but the benefits are not so clear
there (if the value needs to be materialized anyway, it is better to do
it up front).
Before:
0x5280001b mov w27, #0x0 ; =0
0xb9404fba ldr w26, [x29, #0x4c]
0x12881862 mov w2, #-0x40c4 ; =-16580
0x0b020342 add w2, w26, w2
0x5ac00b61 rev w1, w27
0xb8226b81 str w1, [x28, x2]
After:
0xb9404fbb ldr w27, [x29, #0x4c]
0x12881862 mov w2, #-0x40c4 ; =-16580
0x0b020362 add w2, w27, w2
0xb8226b9f str wzr, [x28, x2]
No need to materialize the immediate if it is zero, we can just use WZR.
Before:
mov w27, #0x0 ; =0
str w27, [x29, #0x1178]
After:
str wzr, [x29, #0x1178]
When the immediate value is zero, we can do a negation. On ARM64 the NEG
/NEGS instructions are just an alias for SUB/SUBS with a hardcoded WZR.
Before:
```
ldr w22, [x29, #0x28]
mov w21, #0x0 ; =0
subs w22, w21, w22
```
After:
```
ldr w22, [x29, #0x28]
negs w22, w22
```