I'm writing a language frontend for LLVM, and I noticed that the IR docs say that integers of (almost)any width can be used without limit. I presume this to mean that LLVM or its backends convert numbers with a width larger than the target's width into multiple chunks during execution. I also see that the docs make no mention of not using 64-bit ints on 32-bit platforms, since the backends presumably split 64-bit ints in half in that case. However, this splitting might incur a runtime cost of some sort. So, how big is the performance cost (if any; maybe LLVM can optimize this away in some situations) of making my frontend emit 64-bit integers regardless of the width of the target platform, as opposed to having it only emit 32-bit ones on 32-bit platforms and only emit 64-bit ones on 64-bit platforms? (The performance cost will of course vary by context; I'm asking if it is consistently large enough to warrant making the compiler check for it.)
On a 32-bit platform 64-bit integers will be more than twice as expensive as 32-bit ones.
First there are the operations to consider.
- Simple addition and subtraction will require two 32-bit operation
- Multiplication will require at best, a "multiply long" operation and two "multiply accumulate" operations. If the architecture doesn't have multiply long and multiply accumulate then things get more complex.
- Division is likely to result in calling a library routine.
Then there is the issue of register pressure. A 64-bit value on a 32-bit system takes up two registers. So you have essentially cut your usable registers in half. That means more spills, and (on architectures that pass parameters in registers) more chance of running past the limit for arguments in registers and having to pass them on the stack.
An alternative would be to use SIMD units, however there are some difficulties with doing so (the list below is focused on x86 and arm as those are the architectures I'm most familiar with but I expect similar issues to show up on other architectures).
- The design of MMX was horribly busted, it would leave the FPU in an invalid state requiring it to be reset before doing any floating point operations. SSE2 fixed that problem, but SSE2 only appeared much later.
- Baseline targets often either don't have SIMD instructions at all or only have a very limited choice of them.
- The set of availble operations is often quite limited. SSE2 has addition and bitwise operations, but not subtraction, multiplication or division.
- SIMD instructions work with their own registers. This is a mixed blessing, on the one hand it reduces pressure on the main integer registers. On the other hand it often entails extra operations to move values in to and out of the SIMD registers.
I don't think moving individual 64-bit integer operations to the SIMD unit is something I have ever seen a compiler do. Presumably because the aforementioned difficulties make it not worthwhile.