THISTOGRAM

Implementation status: THISTOGRAM provides C++ built-in implementations on the Ascend 950PR/Ascend 950DT, Kirin9030 backends, and CPU simulation (__CPU_SIM), and has been registered in the virtual ISA index (PTOISA, isa/README, manifest.yaml, the instruction family matrix appendix-d, and the mkdocs navigation). It is available only on Ascend 950PR/Ascend 950DT/Kirin9030/CPU simulation (Atlas A2/A3 training products/Atlas A2/A3 inference products are not supported); no public bytecode encoding is available yet.

Introduction

Computes a histogram over a specific byte of each source tile element (the occurrence count of each byte value 0–255), and supports cascade filtering based on "the already-processed higher-order bytes equal a given index". It is the byte bucket counting primitive for radix sort: the first pass counts the most significant byte (MSB); subsequent passes count lower-order bytes but only over elements whose higher-order bytes match the bucket index from the previous pass, thereby obtaining the distribution of the current radix digit within the prefix bucket.

A single call independently produces a set of 256 uint32 counts (bins) for each valid row of the source tile.

Mathematical Semantics

Assume that the source src has valid shape R×CR \times C, with elements of type uint16_t or uint32_t. Denote Bk(x)B_k(x) as the kk-th byte of element xx:

B0=bits 7–0 (LSB),B1=bits 15–8,B2=bits 23–16,B3=bits 31–24 (MSB)B_0 = \text{bits } 7\text{–}0\ (\text{LSB}),\quad B_1 = \text{bits } 15\text{–}8,\quad B_2 = \text{bits } 23\text{–}16,\quad B_3 = \text{bits } 31\text{–}24\ (\text{MSB})

The template parameter byte selects the byte k∈{0,1,2,3}k\in\{0,1,2,3\} to be counted. For each source row r∈[0,R)r\in[0,R) and each bucket value b∈[0,256)b\in[0,256):

dstr,b=∣{ j∈[0,C) ∣ Bk(srcr,j)=b ∧ Fk(r,j) }∣\mathrm{dst}_{r,b} = \bigl|\{\,j\in[0,C)\ \big|\ B_k(\mathrm{src}_{r,j})=b\ \wedge\ F_{k}(r,j)\ \}\bigr|

Where the cascade filtering FkF_k is defined in high-order-first order (process k=3k=3 first, then k=2,1,0k=2,1,0):

Source Dtypebyte kkFilter Fk(r,j)F_k(r,j)Meaning of idx
uint16BYTE_1 (MSB)Always true (first pass, no filtering)Unused
uint16BYTE_0 (LSB)B1(srcr,j)=idxrB_1(\mathrm{src}_{r,j})=\mathrm{idx}_{r}1 matching byte per row (higher-order)
uint32BYTE_3 (MSB)Always true (first pass, no filtering)Unused
uint32BYTE_2B3=idxr,0B_3=\mathrm{idx}_{r,0}1 filter byte per row
uint32BYTE_1B3=idxr,0 ∧ B2=idxr,1B_3=\mathrm{idx}_{r,0}\ \wedge\ B_2=\mathrm{idx}_{r,1}2 filter bytes per row
uint32BYTE_0 (LSB)B3=idxr,0∧B2=idxr,1∧B1=idxr,2B_3=\mathrm{idx}_{r,0}\wedge B_2=\mathrm{idx}_{r,1}\wedge B_1=\mathrm{idx}_{r,2}3 filter bytes per row
  • dst always has 256 uint32 buckets per row (corresponding to byte values 0–255).
  • uint16 sources support only BYTE_0 / BYTE_1 (only two bytes can be extracted).
  • Unless otherwise stated, the semantics are defined within the valid region; the specific memory interleaving layout of the bucket counts (N0/N1 dual banks, even/odd split) is implementation-defined, and the logical result is 256 counts per row.

src and idx are both ISA-visible tile operands (not compiler scratch).

C++ Built-in APIs

Declared in include/pto/common/pto_instr.hpp, available on Ascend 950PR/Ascend 950DT/Kirin9030/CPU simulation (PTO_NPU_ARCH_A5 || PTO_NPU_ARCH_KIRIN9030 || __CPU_SIM). The HistByte enum is defined in include/pto/common/type.hpp.

The public include header is <pto/pto-inst.hpp>, and the internal declaration is located in pto/common/pto_instr.hpp.

enum class HistByte : uint8_t {
    BYTE_0 = 0, // LSB (bits 7-0)
    BYTE_1 = 1, // bits 15-8
    BYTE_2 = 2, // bits 23-16
    BYTE_3 = 3  // MSB (bits 31-24)
};

template <HistByte byte, typename TileDataDst, typename TileDataSrc, typename TileDataIdx, typename... WaitEvents>
PTO_INST RecordEvent THISTOGRAM(TileDataDst &dst, TileDataSrc &src, TileDataIdx &idx, WaitEvents &...events);
ParameterDirectionMeaning
byteTemplateByte to be counted (HistByte::BYTE_0…BYTE_3)
dstOutputHistogram result tile, uint32_t, row-major order, 256 buckets per row
srcInputSource data tile, uint16_t or uint32_t, row-major order
idxInputCascade filtering index tile, uint8_t, shape varies with byte and source dtype (see below)
events...InputWait events (WaitEvents), implicit TSYNC before the instruction

Tile Size and Data Types

Assume that the source valid shape is R×CR \times C:

TileDtypeValid ShapeLayoutDescription
dstuint32_tR×256R \times 256RowMajor256 bucket counts per row
srcuint16_t or uint32_tR×CR \times CRowMajorData to be counted
idx (uint16 source)uint8_tR×1R \times 1ColMajor (DN)1 matching byte (high bits) per row
idx (uint32 source)uint8_t(3−k)×C(3-k) \times CRowMajor1 filter byte broadcast per row; 0 rows when k=3k=3 (unused)

The physical row count of idx must be aligned to 32-byte blocks (PTO_CEIL(rows · sizeof(uint8_t), 32)); the uint16 mode requires idx to use the DN layout (BLayout::ColMajor + SLayout::NoneBox) with exactly 1 column.

Supported Input Dtypes

Source DtypeDestination Dtypeidx DtypeAllowed byteDescription
U16 (uint16_t)U32U8BYTE_0, BYTE_1Only the low/high byte can be extracted.
U32 (uint32_t)U32U8BYTE_0…BYTE_3All four bytes are available, paired with 0–3 rows of idx.

dst must be uint32_t, idx must be uint8_t, and src is restricted to uint16_t/uint32_t; other combinations are intercepted by static_assert in the implementation.

Implementation Notes

THISTOGRAM executes on the vector pipe (PIPE_V):

  1. Byte extraction: Deinterleaves the source elements into per-byte vectors — uint16 uses DINTLV_B8 (splitting out MSB/LSB), and uint32 uses DINTLV_B16 + vdintlv (splitting out 4 bytes).
  2. Cascade filtering: Uses vcmp_eq to generate the predicate "processed high-order byte == idx", cascading AND per byte (the first pass MSB has no filtering).
  3. Byte histogram: Uses the hardware chistv2 to count the selected bytes under the filtering predicate, internally using N0/N1 dual banks and odd/even split accumulation; finally writes back 256 uint32 buckets per row (INTLV_B32 interleaved storage). The dual banks and odd/even split are implementation details; the logical result is 256 counts per row.

Constraints

ConstraintScopeReason
dst is uint32_t and row-major orderAll targets256-bucket count width and storage layout
src ∈ {uint16_t, uint32_t} and row-major orderAll targetsByte extraction path
idx is uint8_tAll targetsFilter byte word width
uint16 source: idx is DN (ColMajor + NoneBox) with 1 columnAscend 950PR/Ascend 950DT/Kirin9030/CPUSingle byte/row broadcast match
uint32 source: idx row-major order, rows =3−k=3-k, columns == source columnsAscend 950PR/Ascend 950DT/Kirin9030/CPUIndex rows required for cascade filtering
uint16 source allows only BYTE_0 / BYTE_1All targetsuint16 has only 2 bytes
dst 256 buckets per rowAll targetsByte value space 0–255

Examples

// uint16 source: histogram the high-order byte (MSB) of each element (first pass of radix sort).
THISTOGRAM<HistByte::BYTE_1>(dstTile, srcTile, idxTile);

// uint16 source: histogram the low-order byte (LSB), counting only elements whose high-order byte == idx (second pass).
THISTOGRAM<HistByte::BYTE_0>(dstTile, srcTile, idxTile);

Typical tile declaration (uint16 mode, source valid shape R×CR\times C):

using TileDataSrc = Tile<TileType::Vec, uint16_t, R, alignedC,        BLayout::RowMajor>;
using TileDataDst = Tile<TileType::Vec, uint32_t, R, 256,             BLayout::RowMajor>;
using TileDataIdx = Tile<TileType::Vec, uint8_t,  alignedIdxBytes, 1, BLayout::ColMajor>;

For complete ST examples, see tests/npu/a5/src/st/testcase/thistogram/ (A5), tests/npu/kirin9030/src/st/testcase/thistogram/ (Kirin9030), tests/npu/kirinX90/src/st/testcase/thistogram/ (KirinX90), and tests/cpu/st/testcase/thistogram/ (CPU reference implementation).