How Do I Move a Floating Point Constant into an Fp Register?
So, I Am Programming in Assembly Form Arm Using the A64 Instruction Set. I Am Using the Instruction Fmov D1, #31.0 to Move Values to the Dx Register. However...
So, I am programming in assembly form ARM using the A64 instruction set. I am using the instruction fmov d1, #31.0 to move values to the dx register. However, when I use 0.0 or any value higher than 31.0, it shows the error:
"Error: invalid floating-point constant at operand 2 -- `fmov d1,#32.0'"
So, how do I define a floating-point constant on A64? Why can't I use any value above 31 or use 0? How can I represent the values in hex?
Another question is: According to the arm's website it supports the use of floating point registers as Bx, Hx, Sx, Dx and Qx (8,16 ,34,64 and 128 bits respectively), but I just can't use the Bx,Hx and Qx registers, it shows:
" Error: operand mismatch -- `fmov b1,#1.0'"
" Error: selected processor does not support `fmov h1,#2.0"
" Error: operand mismatch -- `fmov q1,#2.0'"
How can I properly set the second operand?
1 Answer
Only a very small set of floating point constants can be used with fmov because the constant is encoded as an 8 bit immediate in the instruction. Specifically, it must be representable as ±n/16×2r where n is an integer in the range 16 ≤ n ≤ 31 and r is an integer in the range of −3 ≤ n ≤ 4.
The exact list of supported numbers is given in the ARMv8 Architecture Reference Manual. Additionally, fmov is only available for 16, 32, and 64 bit data sizes as no 8 bit or 128 bit floating point formats are specified for ARMv8. For ARMv8 cores not supporting FEAT_FP16, a 16 bit data size is not supported either. ARMv8 lacks orthogonality in quite a few places like this; not all instructions are available with all operand sizes.
For a simple alternative solution, use ldr with a constant in a literal pool instead (which you need to manually translate into an integer). For example, to load 32.0, translate 32.0 into its IEEE 754 representation, giving you 0x4040000000000000. You can then load this constant like this:
ldr d1, =0x4040000000000000
The ldr instruction with SIMD&FP register and a value in a literal pool is available with operand sizes 32 bit, 64 bit, and 128 bit. Smaller operand sizes are not available for literal-pool addressing modes. If you want to load an 8 or 16 bit register, load the corresponding 32 bit register instead.
A slightly faster solution is to first load the desired number into a general purpose register (which support more flexible generation of immediates) and then to move it into a SIMD&FP register:
mov x0, #0x4040000000000000
fmov d1, x0
To load 0.0 or masks, use the movi instruction. The set of legal immediates for this instruction depends on the operand size. But for your case, this would just be
movi d1, #0
This clears the d1 register (and thus the b1, h1, s1, and q1 registers, too).