Type promotion rules for the new Allo frontend.
Allo uses explicit type-promotion tables when lowering frontend expressions.
The active table is selected by KernelOptions(typing_style=...):
@kernel(options=allo.KernelOptions(typing_style="hls")) # default
def hls_kernel(...):
...
@kernel(options=allo.KernelOptions(typing_style="cpp"))
def cpp_kernel(...):
...The two supported styles are:
hls: hardware-oriented promotion. Integer +, -, and * are widened to
preserve full intermediate precision and are lowered as balanced trees.cpp: C++-like promotion. Integer binary operations select a common integer
type and lower according to the expression tree.If no rule exists for an operator/type combination, compilation fails with a
diagnostic such as No hls type promotion rule for operator ....
Typing rules operate on frontend DType categories:
| Category | Examples | Notes |
|---|---|---|
| Signed integer | i8, i32, apint(23, signed=True) | Arbitrary-width signed APInt. |
| Unsigned integer | u1, u32, apint(17) | Arbitrary-width unsigned APInt. |
| Floating point | f16, bf16, f32, f64 | APFloat values supported by the frontend. |
| Index | index | Opaque index type used for loop bounds and indexing. |
| Boolean | bool, u1 | bool is the frontend alias of u1. |
The rules below describe element types. For shaped values, the same element promotion is used inside elementwise or linalg operations; shape compatibility is checked separately by the operator.
Several rules reuse a common integer type selector.
For two integer types with widths L and R:
| Inputs | Common integer result |
|---|---|
| Both signed | signed max(L, R) |
| Both unsigned | unsigned max(L, R) |
One signed, one unsigned, and unsigned_width >= signed_width | unsigned unsigned_width |
One signed, one unsigned, and unsigned_width < signed_width | signed signed_width |
Examples:
| Expression types | Common integer |
|---|---|
i16, i32 | i32 |
u8, u32 | u32 |
i32, u32 | u32 |
i32, u16 | i32 |
Floating-point common rules are shared by both styles:
float op float promotes to the wider float.float op integer keeps the float type.f32 if their bit width is at
most 32, otherwise to f64; floating inputs keep their type.index has dedicated rules rather than behaving like an integer of a fixed
width.
HLS is the default typing style. It is designed to make arithmetic bit growth explicit for hardware generation.
For integer-only + and -, HLS uses an n-ary promotion rule over the whole
add/sub expression, not just pairwise promotion.
For an expression with N terms:
width + 1 to
account for sign conversion.max(adjusted_term_widths) + ceil_log2(N).Examples:
| Expression types | HLS result |
|---|---|
i32 + i32 | i33 |
u32 + u32 | u33 |
u8 + i8 | i10 |
i32 + i32 - i32 | i34 |
u8 + u8 + u8 + u8 | u10 |
The balanced-tree behavior matters for generated hardware. For:
out[0] = a + b + c + dHLS lowering forms a pairwise tree similar to:
t0 = a + b
t1 = c + d
out = t0 + t1For a mixed add/sub expression:
out[0] = a + b - c + dAll terms are first cast to the n-ary result type, c is negated, and the
normalized terms are reduced by the same balanced addition tree.
Floating-point add/sub is not reassociated by default. When fast_math=False,
any expression that may be floating-point is lowered according to the original
expression tree. With fast_math=True, floating-point expressions may use the
same n-ary lowering path.
For integer-only multiplication, HLS also uses an n-ary promotion rule:
Examples:
| Expression types | HLS result |
|---|---|
i32 * i32 | i64 |
u16 * u16 | u32 |
i32 * i32 * i32 | i96 |
u8 * i8 * u4 | i20 |
For div, floordiv, mod, pow, comparisons, bitwise operators, and
max/min, HLS uses the common numeric rules unless a more specific rule is
listed below.
| Operator group | HLS promotion |
|---|---|
div, floordiv, mod | Common numeric type, including index rules. |
pow | Common numeric type, but index is not accepted. |
eq, ne, lt, le, gt, ge | Operands use common numeric type; result is bool/u1. |
max, min | Common numeric type; operation result has that type. |
bitwise_and, bitwise_or, bitwise_xor | Common integer type for integer pairs; index only with index. |
Shift operators keep the left-hand type.
| Inputs | Result |
|---|---|
signed integer shifted by signed/unsigned integer or index | left-hand signed type |
unsigned integer shifted by signed/unsigned integer or index | left-hand unsigned type |
index shifted by index | index |
index shifted by a plain integer is not currently covered by the rule table.
| Operator | HLS rule |
|---|---|
Unary - on signed/unsigned integer | signed integer with width + 1 |
Unary - on float | same float type |
Unary ~ | same signed/unsigned/index type |
logical_and, logical_or | accepts integer/float numeric pairs and index/index; result is bool/u1 |
logical_not | accepts integer, float, and index; result is bool/u1 |
abs | same signed/unsigned/float type; index is not accepted |
Special math functions in HLS accept signed integers, unsigned integers, and
floats. They do not accept index.
C++ typing style uses the common numeric rules more uniformly. It does not use
the HLS integer bit-growth rules for +, -, or *.
| Operator group | C++ promotion |
|---|---|
add, sub, mul, div, floordiv, mod | Common numeric type, including index rules. |
pow | Common numeric type, including index rules. |
max, min | Common numeric type; operation result has that type. |
Examples:
| Expression types | C++ result |
|---|---|
i32 + i32 | i32 |
u32 + u32 | u32 |
i32 + u32 | u32 |
i16 * i32 | i32 |
f32 + i32 | f32 |
f32 + f64 | f64 |
Promotion is pairwise when an expression contains more than two operands. For
a + b + c, the frontend promotes a + b first, then promotes that result with
c.
| Operator group | C++ promotion |
|---|---|
eq, ne, lt, le, gt, ge | Operands use common numeric type; result is bool/u1. |
bitwise_and, bitwise_or, bitwise_xor | Common integer type for integer pairs; index only with index. |
lshift, rshift | Same as HLS: result is the left-hand type, with the same supported input pairs. |
| Operator | C++ rule |
|---|---|
Unary - | same signed/unsigned/index/float type |
Unary ~ | same signed/unsigned/index type |
logical_and, logical_or | accepts integer/float numeric pairs and index/index; result is bool/u1 |
logical_not | accepts integer, float, and index; result is bool/u1 |
abs | same signed/unsigned/float type; index is not accepted |
| Special math functions | integer/index inputs convert to f32 or f64; float inputs keep their type |
Because index has a very large opaque primitive width internally, special math
functions on index promote to f64 under the current C++ rule table.
| Operator key | HLS | C++ |
|---|---|---|
add, sub | Integer n-ary bit growth; otherwise numeric rules | Common numeric |
mul | Integer n-ary full-width product; otherwise numeric rules | Common numeric |
div, floordiv, mod | Common numeric | Common numeric |
pow | Common numeric, no index | Common numeric |
eq, ne, lt, le, gt, ge | Common numeric operands, bool result | Common numeric operands, bool result |
lshift, rshift | Left-hand type | Left-hand type |
bitwise_and, bitwise_or, bitwise_xor | Common integer or index/index | Common integer or index/index |
neg | Integer grows to signed width + 1; float unchanged | Type unchanged |
invert | Type unchanged for integer/index | Type unchanged for integer/index |
logical_and, logical_or, logical_not | bool result | bool result |
| Special math functions | Integers/floats only | Integers, index, and floats |
abs | Type unchanged for integer/float | Type unchanged for integer/float |
TypeRuleTable.promote() first asks the style-specific n-ary promoter. Only
HLS provides one, and it only handles integer add, sub, and mul.None, promotion falls back to pairwise lookup
from left to right.bool/u1.