Implement small integer optimization for IMath
Most integers occuring during ISL operations are small and a big integer
library is not necessary. It is, however, still necessary for
correctness. We therefore optimize for the common small integer case
with a fallback to big integers.
This implementation applies to IMath only. The data structure is either
an int32_t or a pointer to the IMath data structure. The least
significant bit is used to distinguish the two cases, which is always
zero for pointers due to alignment requirements and one in case of an
int32_t, which is shifted to the upper bits to avoid overlapping with
the discriminator bit.
int32_t was chosen as small representation because operations can be
done in 64-bit arithmetic and tested for overflow afterwards. There is
no standardized 128-bit integer so using int64_t would involve more
complicated overflow checks.
For architectures with pointer sizes smaller than 64 bits, we just
ensure the data type to be at least 64 bits because these platforms are
not our priority.
Other implementations have been considered as well, of which this one
turned out to be the fastest in practice. The speed-up are up 2.7x,
depending on the application. Other implementations were:
- The bigint struct as stack object
- A union of int32_t and the bigint struct
- long long
The last was not considered acceptable because of potential overflows.
The isl_int functions are also declared in the header file and with
inline qualifier, which reduces function call overhead. Calls to IMath
are only done if the int32_t (potentially) overflows.
We are using the C99 inline model (inline qualifier in header
declaration, one 'extern' declaration in a .c file) over
'static inline' declarations to avoid these function to appear multiple
times when the compiler decides not to inline a function. For instance,
the clang-compiled code profits from this.
Signed-off-by: Michael Kruse <ppcg@meinersbur.de>
Signed-off-by: Sven Verdoolaege <skimo@kotnet.org>