The Airsource Blog

Floating Point on ARM

This should really be read before my previous post (Floating Point on BREW). It's a brief description of how floating point arithmetic works, and why the BREW environment is limited.

When you write some code like this:

    int a = x + y;

the compiler, be that GNUDE, ADS, RVCT, or WinARM, compiles it into something like

    ADD r0, r1, r2

i.e. take the contents of the registers R1, and R2, which have hopefully been preloaded with the variables x and y, add the two together, and stick the result in R0, which we may later store in the memory location relating to the variable a.

If you now want to do the same in floating point, that's fine:

    double a = x + y;

compiles to

    ADF F0,F1,F2

We just take the contents of the floating point registers F1, and F2, execute the instruction ADF, which funnily enough adds two floats together, and there you are.

It's not quite that simple, as you might have guessed. While the above is valid assembly (and hopefully the compiler is capable of turning it into valid machine code), no ARM7 or ARM9 core has any hardware support for floating point. Just because the ARM instruction set defines floating point instructions doesn't mean that a particular ARM core has to implement them. They'll take one look at ADF F0,F1,F2 and promptly throw an illegal instruction trap. And that means that you aren't going to get hardware floating point working on any BREW handset.

So how can I use floats in C?

Historically, there have been a couple of approaches. The first is to write a piece of code that handles the illegal instruction trap, figures out what the instruction was, calls an appropriate function, and then jumps back into the original code just after the instruction that triggered the trap. If this sounds complicated, it is. It's effectively implementing a coprocessor in software, and is known as a Software FPU. It's not unique to ARM; there was a Software FPU available for the 680x0 series processor back in the days of the Mac IIsi and so forth.

The second approach requires compiler support. Instead of generating floating point instructions, the example above is treated as though we actually wrote:

    double a = _dadd(x,y);

_dadd being a function that is present in a library thoughtfully provided by the suppliers of the compiler. The compiler handles the marshalling of the parameters when calling the function, no floating point instruction is ever generated, and the processor remains blissfully unaware that floating point was ever used.

The only point left to make is that GCC, ADS, and so forth do all this for you. But the library provided is compiled without the /norwpi and /ropi flags needed for BREW development, and consequently can't be used. If you are using elf2mod, of course, and don't need those flags, then there's every possibility that it may work, but there's no need to risk it; we can use floats.o, supplied by QUALCOMM, instead. See Floating Point on BREW for a full explanation.