As we all know, LLVM-IR supports arbitrary precision integers via the iN
syntax. Integers that aren’t powers-of-two aren’t particularly
interesting/useful on most hardware however, so this functionality isn’t
typically used, and in fact, Clang does not have such support. However,
certain architectures DO exist where the ability to express these values
(and do math without promotion to full integers) is valuable from a
performance perspective, since these values can be expressed more
efficiently in hardware.
I’ve developed a solution to expose these types in clang, and am
proposing to contribute it back to the Clang community if it is
sufficiently acceptable to the group.
The syntax I’ve chosen for this is as a Typedef Attribute. This permits
the consumer to define various ways to expose this functionality in
their code. Additionally, we’ve limited it to int/unsigned typedefs
only for simplicity’s sake. The code looks something like:
// Typical way to expose this in C:
typedef int attribute((ap_int(3))) ap_int3;
typedef unsigned attribute((ap_int(3))) ap_uint3;
// Better way to expose it in modern C++:
template<unsigned bits> using ap_int = int
template<unsigned bits> using ap_uint = unsigned
For our usages, we just wrapped these in a type that would better
express the further configurable semantics, but I suspect these are
useful in their own right.
We consider conversions to/from integers integral promotions that follow
normal conversion rules with the exception of automatic promotion to
int, and bool conversions are also supported (just like integers).
AP-Int math is done at the size of the largest operand in order to
prevent promotions that would inflate the size required hardware (such
as in an FPGA).
One important consideration that was made is how size/alignment is
expressed in the language. Sizeof, for example works exclusively on
bytes, so an ap_int<13> couldn’t be directly expressed. Alignments are
required to be a power-of-two, otherwise they are not expressable on
some hardware. In implementation, we chose to use the next largest
alignment (up to 64 bits). Additionally, for the sake of the standard
library (and common array patterns), we are required to make sizeof
also round up to the next power of two. This is most consistent with
what a majority of LLVM backends will do (for example, an ap_int<24>
will be expressed as a int-32 on most platforms), and was required to
make common array patterns work. Unfortunately, this results in arrays
on these platforms having ‘padding’ bits, but sufficiently motivated
code generation can repair this problem.