Condition Codes 4: Floating-Point Comparisons Using VFP

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.

Setting the Flags with VFP

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.

.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.

Flag Meanings

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:

        

IEEE-754 RelationshipARM APSR Flags
NZCV
Equal0110
Less Than1000
Greater Than0010
Unordered (At least one argument was NaN.)0011

Compare with Zero

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.

Interpreting the Flags

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:

CodeMeaning (when set by vcmp)Meaning (when set by cmp)Flags Tested
eqEqual to.Equal to.Z==1
neUnordered, or not equal to.Not equal to.Z==0
cs or hsGreater than, equal to, or unordered.Greater than or equal to (unsigned).C==1
cc or loLess than.Less than (unsigned).C==0
miLess than.Negative.N==1
plGreater than, equal to, or unordered.Positive or zero.N==0
vsUnordered. (At least one argument was NaN.)Signed overflow.V==1
vcNot unordered. (No argument was NaN.)No signed overflow.V==0
hiGreater than or unordered.Greater than (unsigned).(C==1) && (Z==0)
lsLess than or equal to.Less than or equal to (unsigned).(C==0) || (Z==1)
geGreater than or equal to.Greater than or equal to (signed).N==V
ltLess than or unordered.Less than (signed).N!=V
gtGreater than.Greater than (signed).(Z==0) && (N==V)
leLess than, equal to or unordered.Less than or equal to (signed).(Z==1) || (N!=V)
al (or omitted)Always executed.Always executed.None tested.

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.

Performance Considerations

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.

Examples

VFP Version of 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.

Complex Number Addition with Special NaN Handler

@ 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

Loop Condition

  @ 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

Implementation of 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.

vcmpdemo.tar.gz
Anonymous
Related