Note: This was originally published on the 23rd of February 2011 at blogs.arm.com.
Floating-point comparisons in the ARM architecture use the same mechanism as integer comparisons. However, there are some unavoidable caveats because the range of supported relationships is different for floating-point values. There are two problems to consider here: Setting the flags from a VFP comparison, and interpreting the flags with condition codes.
This post is applicable to all processors with VFP. The mechanisms I will describe do not differ between VFP variants. Similarly, the mechanisms are equally available in ARM and Thumb-2 modes. I described conditional execution in Thumb-2 in my last article.
As I described at the start of this series, the integer cmp instruction performs an integer comparison and updates the APSR (Application Processor Status Register) with information about the result of the comparison. The APSR holds the condition flags used by the processor for conditional execution. When VFP is used to perform a floating-point comparison, the vcmp instruction is used to update the FPSCR (Floating- Point System Control Register). This isn't usually useful by itself, however, as the processor cannot directly use the FPSCR for conditional execution. The vmrs instruction must be used to transfer the flags to the APSR 1.
cmp
APSR
vcmp
FPSCR
vmrs
.syntax unified @ Remember this! [...] vcmp d0, d1 vmrs APSR_nzcv, FPSCR @ Get the flags into APSR. [...] @ Do something with the condition flags.
Note that some versions of the GNU assembler do not accept all of the new instruction variants (with the "v" prefix). In this case, use fcmp in place of vcmp, and fmstat (with no arguments) in place of vmrs.
v
fcmp
fmstat
The integer comparison flags support comparisons which are not applicable to floating-point numbers. For example, floating-point values are always signed, so there is no need for unsigned comparisons. On the other hand, floating- point comparisons can result in the unordered result (meaning that one or both operands was NaN, or "not a number"). IEEE-754 defines four testable relationships between two floating-point values, and they map onto the ARM condition codes as follows:
NaN
Unlike the integer instructions, most VFP (and NEON) instructions can operate only on registers, and cannot accept immediate values encoded in the instruction stream. The vcmp instruction is a notable exception in that it has a special-case variant that allows quick and easy comparison with zero.
Once the flags are in the APSR, they may be used almost as if an integer comparison had set the flags. However, floating-point comparisons support different relationships, so the integer condition codes do not always make sense. The following table is equivalent to the condition code table from the first post in this series, but it describes floating-point comparisons as well as integer comparisons:
eq
Z==1
ne
Z==0
cs
hs
C==1
cc
lo
C==0
mi
N==1
pl
N==0
vs
V==1
vc
V==0
hi
(C==1) && (Z==0)
ls
(C==0) || (Z==1)
ge
N==V
lt
N!=V
gt
(Z==0) && (N==V)
le
(Z==1) || (N!=V)
al
It should be obvious that the condition code is attached to the instruction reading the flags, and the source of the flags makes no difference to the flags that are tested. It is the meaning of the flags that differs when you perform a vcmp rather than a cmp. Similarly, it is clear that the opposite conditions still hold. (For example, hs is still the opposite of lo.)
The flags when set by cmp generally have analogous meanings when set by vcmp. For example, gt still means "greater than". However, the unordered condition and the removal of the signed conditions can confuse matters. Often, for example, it is desirable to use lo — normally an unsigned "less than" check — in place of lt, because it does not match in the unordered case.
Be aware than vmrs effectively implements a data transfer between VFP and the integer core, and this operation can be relatively expensive on some cores. In addition, there is clearly a data dependency between vcmp and vmrs and another between vmrs and the conditional instruction. It is advisable to structure your code such that the flags are set and transferred many instructions before they are actually read. This is also true of integer comparisons, though the effect is likely to be more significant when using VFP.
Some instruction timing information and latency information is available for the Cortex-A8 and Cortex-A9 processors.
ccdemo
In my first post in this series, I provided an example program ("ccdemo") to show how the flags and condition codes interact. A VFP version (using vcmp) is attached to this article.
@ Add complex numbers (or two-element vectors) in s3:s2 and s5:s4, storing @ the result in s1:s0. If either element of the result is NaN, jump to a @ special handler. vadd s0, s2, s4 vadd s1, s3, s5 vcmp s0, s1 vmrs APSR_nzcv, FPSCR bvs nan_handler
@ This implements a loop that calculates d0=d0-(1/d0) until d0 is negative. vmov d0, #10.0 @ Some starting value. vmov d2, #1.0 @ We need the constant 1.0 in the loop. 1: [...] @ Do something interesting with d0. vdiv d1, d2, d0 @ d1=(1/d0) vsub d0, d0, d1 @ d0=d0-(1/d0) vcmp d0, #0 @ Special case of vcmp for compare-with-zero. vmrs APSR_nzcv, FPSCR bge 1b
fmax
@ A typical implementation of "fmax". @ Put into d0 the greatest of d1 and d2. @ - If one argument is NaN, the result is the other argument. @ - If both arguments are NaN, the result is NaN. @ I have used ["it" blocks][cc3] here so the sequence can be assembled as either @ ARM or Thumb-2 code. vcmp d1, d2 vmrs APSR_nzcv, FPSCR it vs @ Code "vs" means "unordered". bvs 1f @ Jump to the NaN handler. @ Normal-case (not-NaN) handler. ite ge vmovge d0, d1 @ Select d1 if it is the greatest (or equal). vmovlt d0, d2 @ Select d2 if it is the greatest. b 2f @ Jump over the NaN handler. 1: @ NaN handler. We know that at least one argument was NaN. vcmp d1, #0 vmrs APSR_nzcv, FPSCR ite vc @ Code "vc" means "not unordered". vmovvc d0, d1 @ d1 wasn't NaN, so make it the result. vmovvs d0, d2 @ d1 was NaN, so choose d2. (This might be NaN too.) 2: @ Done. The result is in d0. [...]
1The vmrs instruction can also transfer the flags (along with the rest of the FPSCR) to an arbitrary general-purpose integer register, but this is usually only useful for accessing fields in the FPSCR other than the condition flags.
Can you document the use of vmin/vmax/vsel as alternatives to vcmp+vmrs?