Types

The supported data types and their aliases are listed below:

Scalar:

  • int8 (i8)

  • uint8 (u8)

  • int16 (i16)

  • uint16 (u16)

  • int32 (i32)

  • uint32 (u32)

  • float16 (fp16)

  • float32 (fp32)

  • bool

Vector:

  • int8x32 (i8x32)

  • uint8x32 (u8x32)

  • int16x16 (i16x16)

  • uint16x16 (u16x16)

  • int32x8 (i32x8)

  • uint32x8 (u32x8)

  • float16x16 (fp16x16)

  • float32x8 (fp32x8)

  • boolx8

  • boolx16

  • boolx32

When vector data is visualized in one line, the sequence from the leftmost element to the rightmost element means the vector data from the lowest one to the highest one. See below:

   x: 1  2  3  4  5  6  7  8
   y: 1  2  3  4  5  6  7  8
mask: T  T  T  T  F  F  T  T
   z: 9  8  7  6  4  3  2  1

As mentioned above, the leftmost element of “x”, “y”, “mask” and “z” is the lowest element of them.

Compass DSL is a strongly typed language. You can use the following APIs for type casting or type reinterpretation.

Conversion

Scalar type casting:

  • s1 = S.cast(s0, new_dtype)

  • s1_signed = S.i(s0_unsigned)

  • s2_unsigned = S.u(s1_signed)

Vector type casting:

Explicit cast/conversion

  • Cast to same bits: v1 = S.cast(v0, new_dtype), v1_signed = S.i(v0_unsigned), v1_unsigned = S.u(v0_signed)

  • Cast to wider bits: v0_low = S.cast(v0, new_dtype, "low"), v1 = S.cast(v0, new_dtype)

  • Cast to narrower bits: v0 = S.cast((v0_low, v0_high), new_dtype), v1 = S.cast(v0, new_dtype)

The S.i and S.u are two convenient interfaces used for integer data, converting unsigned numbers to signed types and signed to unsigned respectively. Its internal implementation is based on S.cast.

The S.cast(x, dtype, part) has abilities beyond basic type casting:

  • Support for broadcast when input is scalar and dtype is vector dtype.

  • Argument part (only for vector conversion): used to specify which part data of the given expression or value needs to be converted.

    • all: Represents all data, and this is the default value.

    • low, high, even, odd: Represent the corresponding half data.

    • ll, lh, hl, hh: Represent the corresponding quarter data.

More examples about S.cast:

Cast input x to type which has same bitwidth.

# int8_to_uint8
# integer signed and unsigned conversion
v0 = a_int8[0:32]
v1 = S.cast(v0, "uint8")  # a_int8[0:32] with u8 dtype

# float16_to_int16
# float and int conversion
v0 = a_fp16[0:16]
v1 = S.cast(v0, "int16")  # a_fp16[0:16] with i16 dtype

Cast input x to type which has double bitwidth.

# int8_to_int16
v0 = a_int8[0:32]
v0_all_i16 = S.cast(v0, "int16")           # a_int8[0:32] with i16 dtype
v0_low_i16 = S.cast(v0, "int16", "low")    # a_int8[0:16] with i16 dtype
v0_high_i16 = S.cast(v0, "int16", "high")  # a_int8[16:32] with i16 dtype

# int8_to_int32
v0 = a_int8[0:32]
v0_all_i32 = S.cast(v0, "int32")       # a_int8[0:32] with i32 dtype
v0_ll_i32 = S.cast(v0, "int32", "ll")  # a_int8[0:8] with i32 dtype
v0_lh_i32 = S.cast(v0, "int32", "lh")  # a_int8[8:16] with i32 dtype
v0_hl_i32 = S.cast(v0, "int32", "hl")  # a_int8[16:24] with i32 dtype
v0_hh_i32 = S.cast(v0, "int32", "hh")  # a_int8[24:32] with i32 dtype

# float16_to_float32
v0_fp16 = a_fp16[0:16]
v0_all_fp32 = S.cast(v0_fp16, "fp32")           # a_fp16[0:16] with fp32 dtype
v0_low_fp32 = S.cast(v0_fp16, "fp32", "low")    # a_fp16[:8] with fp32 dtype
v0_high_fp32 = S.cast(v0_fp16, "fp32", "high")  # a_fp16[8:16] with fp32 dtype
v0_even_fp32 = S.cast(v0_fp16, "fp32", "even")  # a_fp16[0:16:2] with fp32 dtype
v0_odd_fp32 = S.cast(v0_fp16, "fp32", "odd")    # a_fp16[1:16:2] with fp32 dtype

Cast input x to type which has half bitwidth.

# int16_to_int8
vl, vh = a_int16[0:16], a_int16[16:32]
v0_i8 = S.cast((vl, vh), "int8")  # a_int16[0:32] with i8 dtype
v0_i8 = S.cast(a_int16, "int8")   # a_int16[0:32] with i8 dtype

# float32_to_float16
vl, vh = a_fp32[0:8], a_fp32[8:16]
v0_fp16 = S.cast((vl, vh), "fp16")  # a_fp32[0:16] with fp16 dtype
v0_fp16 = S.cast(a_fp32, "fp16")    # a_fp32[0:16] with fp16 dtype

The S.cast has limitations below:

  1. Unsupported direct cast pairs (first is from type, second is to type):

  • “uint32”, “float32”.

  • “float32”, “uint32”. If precision loss is tolerated, you need to call S.cast twice manually, such as “uint32” -> “int32” -> “float32”.

  1. No hardware instructions for direct conversion:

  • “float16”, “int8”/”uint8”/”int16”/”uint16”

  • “int8”/”uint8”/”int16”/”uint16”, “float16”. You can call S.cast twice manually, such as “float16” -> “float32” -> “uint16”.

Reinterpret

The S.reinterpret just takes the address of variable and reinterprets as new_dtype, while the binary of data is unchanged.

v1 = S.reinterpret(v0, "int16x16")