MGATHER
Instruction Diagram
Introduction
MGATHER reads data from a GM GlobalTensor into a UB destination tile through a UB index tile. The operation mode is explicitly selected through the Coalesce template parameter:
Coalesce::Row(default) — gathers an entire row fromtable[idx[r], :]intodst[r, :]. The index tile is in 1-D form ([1, R]row-major;[R, 1]column-major is also supported on Ascend 950PR/Ascend 950DT).R = 1is allowed.Coalesce::Elem— gathers elements from the 1Dtableintodst[R, C]throughidx[R, C]. The index tile must have the same valid shape as the destination. The degenerate(1, 1)case is allowed.
Out-of-bounds handling is selected through the GatherOOB template parameter. MGATHER has no atomic or conflict policy: each destination slot has a unique source index, so no conflict occurs.
The destination can also be an L1/cube TileType::Mat tile (NZ layout) (indexes are provided in GM tensor form). This GM → L1 path (supporting Coalesce::Row and Coalesce::Elem, applicable to Atlas A2/A3 training products/Atlas A2/A3 inference products and Ascend 950PR/Ascend 950DT) is described in the GM → L1 Gather (TileType::Mat Destination) section below.
Summary of dispatch by destination:
- CPU simulator — A pure C++ reference implementation. It traverses
validRow * validColand readstable[idx[i, j]](Elem semantics). The CPU simulator has no separate row coalesce path. - VEC-CORE for Atlas A2/A3 training products/Atlas A2/A3 inference products — Scalar pipe-driven single-thread/MTE2 traversal. In row mode, each row issues one wide
copy_gm_to_ubuf_align_b*DMA throughtablePtr + safeIdx * tableRowStride; in Elem mode, a scalar GM→UB copy is performed per element. Tile pairing is supported between ND-GM and ND-UB and between NZ-GM and NZ-UB. - Ascend 950PR/Ascend 950DT SIMT — performs SIMT launch through
cce::async_invokewithdim3{32, 32}(1024 threads). The row mode uses warp-parallel channels for reading; the Elem mode maps each channel to one element. The row kernel treats the GM table as packed ND (row stride =validCols);MGatherCheckenforcesGlobalTable::staticShape[4] == TileDst::ValidColat compile time. Ascend 950PR/Ascend 950DT do not support the NZ block stride layout. The degenerate Elem(1, 1)case bypasses the SIMT launch and runsMGatherScalarImplon the AIV vector core.
Mathematical Semantics
Row Coalesce (Coalesce::Row)
Destination dst[R, C], index idx[1, R] (also idx[R, 1] on Ascend 950PR/Ascend 950DT), and table table[TableRows, C]:
Element Coalesce (Coalesce::Elem)
Destination dst[R, C], index idx[R, C] (with the same valid shape as dst), and a 1D table of length TableSize:
Out-of-Bounds Behavior
enum class GatherOOB : uint8_t {
Undefined = 0, // No bounds check; the caller guarantees that the index is valid.
Clamp = 1, // Clamp the index to capacity - 1.
Wrap = 2, // Index modulo capacity.
Zero = 3 // OOB returns zero; in-bounds indexes load normally.
};Assembly Syntax
Synchronous form:
%dst = mgather %mem, %idx : !pto.memref<...>, !pto.tile<...> -> !pto.tile<...>AS Level 1 (SSA)
%dst = pto.mgather %mem, %idx : (!pto.partition_tensor_view<MxNxdtype>, pto.tile<...>)
-> !pto.tile<loc, dtype, rows, cols, blayout, slayout, fractal, pad>AS Level 2 (DPS)
pto.mgather ins(%mem, %idx : !pto.partition_tensor_view<MxNxdtype>, !pto.tile_buf<...>) outs(%dst : !pto.tile_buf<...>)C++ Built-in APIs
Declared in include/pto/common/pto_instr.hpp:
The public include header is
<pto/pto-inst.hpp>, and the internal declaration is located inpto/common/pto_instr.hpp.
CPU Reference Form
template <typename TileDst, typename GlobalData, typename TileInd, typename... WaitEvents>
PTO_INST RecordEvent MGATHER(TileDst &dst, GlobalData &src, TileInd &indexes, WaitEvents &... events);Atlas A2/A3 Training Product and Atlas A2/A3 Inference Product Forms
template <Coalesce CMode = Coalesce::Row,
GatherOOB Oob = GatherOOB::Undefined,
typename TileDst, typename GlobalTable, typename TileIdx,
typename... WaitEvents>
PTO_INST RecordEvent MGATHER(TileDst& dst, GlobalTable& table, TileIdx& idx,
WaitEvents&... events);Ascend 950PR/Ascend 950DT Form
template <Coalesce CMode = Coalesce::Row,
GatherOOB Mode = GatherOOB::Undefined,
typename TileDst, typename GlobalData, typename TileInd,
typename... WaitEvents>
PTO_INST RecordEvent MGATHER(TileDst& dst, GlobalData& table, TileInd& idx,
WaitEvents&... events);Parameters (NPU Form)
dst— UB destination tile (TileType::Vec); shape[R, C].table— source GMGlobalTensor.GlobalTensor::DTypemust be__gm__ T, matching the destination element type.idx— UB index tile (TileType::Vec).CMode—Coalescevalue (RoworElem). As the first template parameter, the operation mode is always explicitly specified at the call site.Oob/Mode—GatherOOBvalue, used for out-of-bounds handling.
Enum
enum class Coalesce : uint8_t {
Row = 0, // dst[r, :] = table[idx[r], :] (1-D index of length R)
Elem = 1 // dst[i, j] = table[idx[i, j]] (idx shape == dst shape)
};
enum class GatherOOB : uint8_t {
Undefined = 0,
Clamp = 1,
Wrap = 2,
Zero = 3
};Constraints
Tile Constraints (CPU)
Supported data types:
- The
dst/srcelement type must be one of the following:int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,half,bfloat16_t,float. - On the AI Core destination (when the CPU simulator is compiled with
__CCE_AICORE__),float8_e4m3_tandfloat8_e5m2_tare also supported. - The
indexeselement type must beint32_toruint32_t.
Tile and memory types:
dstmust be a vector tile (TileType::Vec).indexesmust be a vector tile (TileType::Vec).dstandindexesmust use the row-major layout (BLayout::RowMajor + SLayout::NoneBox).srcmust be aGlobalTensorlocated in GM.srcmust useLayout::ND.
Shape constraints:
dst.Rows == indexes.Rows.- The shape of
indexesmust be[1, N](row-wise gather) or[N, M](element-wise gather). - The row width of
dstmust satisfy 32-byte alignment, that is,dst.Cols * sizeof(T)must be a multiple of 32. - The static shape of
srcmust satisfyShape<1, 1, 1, TableRows, RowWidth>.
Tile Constraints (Atlas A2/A3 Training Products/Atlas A2/A3 Inference Products)
Supported data types:
- The element type of
dst/srcmust be one of the following:int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,half, andbfloat16_t.float8_e4m3_t,float8_e5m2_t, andhifloat8_tare not supported on Atlas A2/A3 training products/Atlas A2/A3 inference products. - The element type of
indexesmust beint32_toruint32_t.
Tile and memory types:
dstmust be a vector tile (TileDst::Loc == TileType::Vec).indexesmust be a vector tile (TileIdx::Loc == TileType::Vec).- The index tile is always
BLayout::RowMajor + SLayout::NoneBox(ND), regardless of the table layout. srcmust be aGlobalTensorlocated in GM;GlobalTable::DType == __gm__ T.- The bulk + sub layout of the destination tile must exactly match the table layout:
GlobalTable::layout == Layout::ND⇒TileDstisBLayout::RowMajor + SLayout::NoneBox.GlobalTable::layout == Layout::NZ⇒TileDstisBLayout::ColMajor + SLayout::RowMajor + SFractalSize == TileConfig::fractalABSize(= 512Byte).
Shape constraints:
- The padded
TileDst::Cols * sizeof(T)must be 32-byte aligned in both layouts.ValidRow/ValidColare not subject to this rule. - For
Coalesce::Row:TileIdx::ValidRow == 1andTileIdx::ValidCol == TileDst::ValidRow. - For
Coalesce::Elem:TileIdx::ValidRow == TileDst::ValidRowandTileIdx::ValidCol == TileDst::ValidCol. - Both modes require
TileDst::ValidRow >= 1andTileDst::ValidCol >= 1. - The NZ table additionally requires
GlobalTable::staticShape[3] == FRACTAL_NZ_ROW(= 16),GlobalTable::staticShape[4] == C0_SIZE_BYTE / sizeof(T)(= 32 bytes/element width),TileDst::Cols % (C0_SIZE_BYTE / sizeof(T)) == 0, andTileDst::Rows % FRACTAL_NZ_ROW == 0.
Tile Constraints (Ascend 950PR/Ascend 950DT)
Supported data types:
- The element type of
dst/srcmust be one of the following:int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,half,bfloat16_t, andfloat. In__CCE_AICORE__builds,hifloat8_t,float8_e4m3_t, andfloat8_e5m2_tare also included. - The element type of
indexesmust beint32_toruint32_t.
Tile and memory types:
dstmust be a vector tile (TileDst::Loc == TileType::Vec).indexesmust be a vector tile (TileIdx::Loc == TileType::Vec).- The SIMT kernel is unaware of the layout of UB tiles: every UB read/write is achieved through
tile_offset_2d<TileX>(r, c), soTileDstcan beBLayout::RowMajororBLayout::ColMajor(withSLayout::NoneBox). srcmust be aGlobalTensorlocated in GM;GlobalTable::DType == __gm__ T.- GM table layout:
Layout::NDonly. The Ascend 950PR/Ascend 950DT SIMT kernel addresses GM as a flat row-major buffer, with the row stride hardcoded tovalidCols;MGatherCheckenforcesGlobalTable::staticShape[4] == TileDst::ValidCol, so the table cannot have any inter-row padding.
Shape constraints:
- The padded
TileDst::Cols * sizeof(T)(RowMajor) orTileDst::Rows * sizeof(T)(ColMajor) must be 32-byte aligned. - For
Coalesce::Row: the valid shape of the index tile is[1, R](BLayout::RowMajor) or[R, 1](BLayout::ColMajor). - For
Coalesce::Elem:TileIdx::ValidRow == TileDst::ValidRowandTileIdx::ValidCol == TileDst::ValidCol. TheBLayoutofTileIdxis independent ofTileDst. - Both modes require
TileDst::ValidRow >= 1andTileDst::ValidCol >= 1. In Elem mode, the degenerate(1, 1)shape bypasses SIMT launch.
Dynamic Runtime Shapes (Atlas A2/A3 Training Products/Atlas A2/A3 Inference Products and Ascend 950PR/Ascend 950DT)
MGATHER accepts both compile-time and runtime dynamic shapes:
Tile<…, RowMask, ColMask>withRowMask == -1and/orColMask == -1stores the runtime valid range in the tile object; the implementation reads it throughdst.GetValidRow()/dst.GetValidCol().- In
Shape<S0, S1, S2, S3, S4>/Stride<…>, one or more-1entries are constructed with runtime dimensions; the implementation reads them throughtable.GetShape(GlobalTensorDim::DIM_*).
The static assertions in MGatherCheck are gated by if constexpr (DIM > 0) and take effect only for compile-time known dimensions.
Example:
constexpr auto kPadCols = 16;
using DstTileT = Tile<TileType::Vec, float, 1, kPadCols, BLayout::RowMajor, -1, -1>;
using IdxTileT = Tile<TileType::Vec, int32_t, 1, kPadCols, BLayout::RowMajor, -1, -1>;
using TableShape = Shape<1, 1, 1, -1, -1>;
using TableStride = Stride<1, 1, 1, -1, -1>;
int64_t validCols = 9, d3 = 3, d4 = 10, srcStride3 = 10;
TableShape tableShape(d3, d4);
TableStride tableStride(srcStride3, (int64_t)1);
GlobalTensor<float, TableShape, TableStride> tableGM(srcGm, tableShape, tableStride);
DstTileT dstTile(1, validCols);
IdxTileT idxTile(1, validCols);
TASSIGN(dstTile, dstUbOffsetBytes);
TASSIGN(idxTile, idxUbOffsetBytes);
MGATHER<Coalesce::Elem, GatherOOB::Undefined>(dstTile, tableGM, idxTile);Mode Matching
On Atlas A2/A3 training products/Atlas A2/A3 inference products and Ascend 950PR/Ascend 950DT, the mode is explicit and is not automatically detected. The static assertions in MGatherCheck validate the provided tile shapes based on the selected Coalesce value:
A2/A3:
Coalesce::Row : Idx.ValidRow == 1 && Idx.ValidCol == Dst.ValidRow
Coalesce::Elem : Idx.ValidRow == Dst.ValidRow && Idx.ValidCol == Dst.ValidCol
A5:
Coalesce::Row : (Idx.ValidRow == 1 && Idx.ValidCol == Dst.ValidRow) ||
(Idx.ValidRow == Dst.ValidRow && Idx.ValidCol == 1)
Coalesce::Elem : (Idx.ValidRow == Dst.ValidRow) && (Idx.ValidCol == Dst.ValidCol)Layout Support
| Tile/Tensor | CPU | Atlas A2/A3 Training Products/Atlas A2/A3 Inference Products | Ascend 950PR/Ascend 950DT |
|---|---|---|---|
TileDst (UB) —ND | BLayout::RowMajor + SLayout::NoneBox only | BLayout::RowMajor + SLayout::NoneBox | BLayout::RowMajor or ColMajor, SLayout::NoneBox |
TileDst (UB) —NZ | Not supported | BLayout::ColMajor + SLayout::RowMajor + SFractalSize == 512 | Not supported |
TileIdx (UB) —Row | Row-major (Cols must be equal to 1) | [1, R] BLayout::RowMajor + SLayout::NoneBox | [1, R] RowMajor or [R, 1] ColMajor |
TileIdx (UB) —Elem | BLayout::RowMajor + SLayout::NoneBox | [R, C] BLayout::RowMajor + SLayout::NoneBox | Any BLayout, independent of TileDst |
GlobalTable (GM) —ND | Layout::ND only | Layout::ND (linear contiguous addressing) | Layout::ND only; The row mode hardcodes tableRowStride = validCols. |
GlobalTable (GM) —NZ | Not supported | Layout::NZ; 5-D Shape<B, BCols, BRows, 16, C0> | Not supported |
NZ Layout (Atlas A2/A3 Training Products/Atlas A2/A3 Inference Products)
When GlobalTable::layout == Layout::NZ and TileDst is a matching BLayout::ColMajor + SLayout::RowMajor + SFractalSize = 512 tile, MGATHER (Atlas A2/A3 training products/Atlas A2/A3 inference products) runs a dedicated NZ path (MGatherRowNzImpl, MGatherElemNzImpl).
- Constants.
kC0 = C0_SIZE_BYTE / sizeof(T);kFRow = FRACTAL_NZ_ROW = 16. Each fractal block iskFRow × kC0elements (= 512 bytes). - Logical shape. Logical rows =
gShape2 * kFRow. Logical columns =gShape0 * gShape1 * kC0. OOB in row mode is limited/modulo by the number of logical rows; in Elem mode, it is limited/modulo by the total number of elements. - Row mode. For each logical row
r, the kernel will mapidx[r]to(srcBlockRow, srcRowInBlock)andrto(dstBlockRow, dstRowInBlock), and then issue one multi-burst MTE2 transfer for each outer batch. WhenOob == GatherOOB::Zero, the kernel will prefill the entire tile withT(0)before the DMA loop and skip the DMA for OOB rows. - Elem mode. For each
(r, c), the kernel will mapidxto(logicalRow, logicalCol) = (idx / nLogicalCols, idx % nLogicalCols), and then convert it to an NZ physical offset throughMGatherNZGmOffset. The traversal order is block column → row → column within block, ensuring that consecutive writes always target consecutive 32-byte UB blocks.
Pipe/Synchronization Model
Atlas A2/A3 Training Products/Atlas A2/A3 Inference Products — Explicit Pipe Handshake
The caller does not need to insert any additional barriers. It only needs to use the standard set_flag(PIPE_MTE2, PIPE_V)/wait_flag(PIPE_MTE2, PIPE_V) pair following TLOAD before MGATHER to bring the index tile to a clean state on the vector pipe.
| Stage | Pipe Transition | Protected Content |
|---|---|---|
| Pre-amble (Row ND + Row NZ) | V→S, MTE3→S flag chain | Makes the index tile visible to scalar reads, and flushes any pending MTE3 writes that may overlap with UB before the scalar loop starts. |
| Pre-amble (Elem ND + Elem NZ) | V→S, MTE3→S, MTE2→S flag chain | Same as row, with the additional MTE2→S flag flushing any in-flight MTE2 burst before the scalar loop reads idxPtr. |
| Row post-amble (ND + NZ) | S→MTE2, MTE2→V, MTE2→MTE3, S→V, S→MTE3 flag chain | Ensures the scalar→MTE2 race is resolved, then drains the MTE2 DMA before downstream consumers touch the destination tile. |
| Elem post-amble (ND + NZ) | S→V, S→MTE2, S→MTE3 flag chain | Makes scalar UB writes visible to V, MTE2, and MTE3. |
Ascend 950PR/Ascend 950DT — SIMT Launch and V↔S Handshake
The Ascend 950PR/Ascend 950DT implementation hides almost the entire pipe model behind cce::async_invoke. The only explicit kernel-side handshake is in the scalar fallback path (MGatherScalarImpl, used for Elem (1, 1)).
| Stage | Pipe Transition | Protected Content |
|---|---|---|
| Leading (scalar fallback) | set_flag(PIPE_V, PIPE_S)/wait_flag(PIPE_V, PIPE_S) | Makes the index tile visible to the scalar pipe before the single-element gather. |
| Post (scalar fallback) | set_flag(PIPE_S, PIPE_V)/wait_flag(PIPE_S, PIPE_V) | Releases the scalar UB writes to downstream vector operations. |
UB Memory Budget
Atlas A2/A3 Training Products/Atlas A2/A3 Inference Products
The AIV vector core has the standard CANN 192 KB UB layout. MGATHER does not allocate any UB scratch from within the kernel — the only UB consumers are the caller-allocated destination tile and index tile.
Ascend 950PR/Ascend 950DT
The Ascend 950PR/Ascend 950DT SIMT kernel runs on the AIV vector core. All user tiles must fit into the AIV's 256 KB UB, plus two fixed runtime reservations: an 8 KB reserved region and the data cache (at least 32 KB). Therefore, the maximum available size is:
max dynUBufSize = 256 KB - 8 KB - 32 KB - static_memory
= 216 KB - static_memoryWhen using TASSIGN to manually place tiles, the compiler detects static_memory ≈ 0 and can use the full 216 KB as dynUBufSize. When the working set exceeds 128 KB under the default budget, declare it explicitly through kernel_name<<<numBlocks, dynUBufSize, stream>>>(args...).
Examples
Automatic
#include <pto/pto-inst.hpp>
using namespace pto;
void example_auto() {
using DstT = Tile<TileType::Vec, float, 16, 16>;
using IdxT = Tile<TileType::Vec, int32_t, 16, 16>;
DstT dst;
IdxT idx;
// src is a GlobalTensor in GM.
MGATHER(dst, src, idx);
}Manual
#include <pto/pto-inst.hpp>
using namespace pto;
void example_manual() {
using DstT = Tile<TileType::Vec, float, 16, 16>;
using IdxT = Tile<TileType::Vec, int32_t, 16, 16>;
DstT dst;
IdxT idx;
// src is a GlobalTensor in GM.
TASSIGN(dst, 0x1000);
TASSIGN(idx, 0x2000);
MGATHER(dst, src, idx);
}Row Coalesce — Embedding Lookup (Atlas A2/A3 Training Products/Atlas A2/A3 Inference Products or Ascend 950PR/Ascend 950DT)
#include <pto/pto-inst.hpp>
using namespace pto;
template <typename T, int R, int C, int TableRows>
AICORE void example_embedding_lookup(__gm__ T* tablePtr, __gm__ int32_t* idxPtr, __gm__ T* outPtr)
{
using DstTile = Tile<TileType::Vec, T, R, C, BLayout::RowMajor, R, C>;
using IdxTile = Tile<TileType::Vec, int32_t, 1, R, BLayout::RowMajor, 1, R>;
using TableShape = Shape<1, 1, 1, TableRows, C>;
using TableStride = Stride<1, 1, 1, C, 1>;
using TableTensor = GlobalTensor<T, TableShape, TableStride>;
using IdxShape = Shape<1, 1, 1, 1, R>;
using IdxStride = Stride<1, 1, 1, R, 1>;
using IdxTensor = GlobalTensor<int32_t, IdxShape, IdxStride>;
TableTensor tableGM(tablePtr);
IdxTensor idxGM(idxPtr);
DstTile dst; TASSIGN(dst, 0x0000);
IdxTile idx; TASSIGN(idx, 0x1000);
TLOAD(idx, idxGM);
set_flag(PIPE_MTE2, PIPE_V, EVENT_ID0);
wait_flag(PIPE_MTE2, PIPE_V, EVENT_ID0);
MGATHER<Coalesce::Row, GatherOOB::Clamp>(dst, tableGM, idx);
}Element Coalesce — 2-D Random Access
AICORE void example_elem_2d(__gm__ float* tablePtr, __gm__ int32_t* idxPtr)
{
constexpr int R = 8, C = 32, TableSize = 256;
using DstTile = Tile<TileType::Vec, float, R, C, BLayout::RowMajor, R, C>;
using IdxTile = Tile<TileType::Vec, int32_t, R, C, BLayout::RowMajor, R, C>;
using TableShape = Shape<1, 1, 1, 1, TableSize>;
using TableStride = Stride<1, 1, 1, TableSize, 1>;
using TableTensor = GlobalTensor<float, TableShape, TableStride>;
TableTensor tableGM(tablePtr);
DstTile dst; TASSIGN(dst, 0x0000);
IdxTile idx; TASSIGN(idx, 0x0800);
MGATHER<Coalesce::Elem, GatherOOB::Wrap>(dst, tableGM, idx);
}Row Coalesce — [R, 1] ColMajor Index (Ascend 950PR/Ascend 950DT Only)
AICORE void example_row_colidx(__gm__ half* tablePtr, __gm__ int32_t* idxPtr)
{
constexpr int R = 8, C = 64, TableRows = 64;
using DstTile = Tile<TileType::Vec, half, R, C, BLayout::RowMajor, R, C>;
using IdxTile = Tile<TileType::Vec, int32_t, R, 1, BLayout::ColMajor, R, 1>;
using TableShape = Shape<1, 1, 1, TableRows, C>;
using TableStride = Stride<1, 1, 1, C, 1>;
using TableTensor = GlobalTensor<half, TableShape, TableStride>;
using IdxShape = Shape<1, 1, 1, R, 1>;
using IdxStride = Stride<1, 1, 1, 1, 1>;
using IdxTensor = GlobalTensor<int32_t, IdxShape, IdxStride, Layout::DN>;
TableTensor tableGM(tablePtr);
IdxTensor idxGM(idxPtr);
DstTile dst; TASSIGN(dst, 0x0000);
IdxTile idx; TASSIGN(idx, 0x1000);
TLOAD(idx, idxGM);
set_flag(PIPE_MTE2, PIPE_V, EVENT_ID0);
wait_flag(PIPE_MTE2, PIPE_V, EVENT_ID0);
MGATHER<Coalesce::Row, GatherOOB::Undefined>(dst, tableGM, idx);
}Element Coalesce — (1, 1) Degenerate Case
AICORE void example_scalar(__gm__ float* tablePtr, __gm__ int32_t* idxPtr)
{
constexpr int TableSize = 32;
using DstTile = Tile<TileType::Vec, float, 1, 8, BLayout::RowMajor, 1, 1>;
using IdxTile = Tile<TileType::Vec, int32_t, 1, 8, BLayout::RowMajor, 1, 1>;
using TableShape = Shape<1, 1, 1, 1, TableSize>;
using TableStride = Stride<1, 1, 1, TableSize, 1>;
using TableTensor = GlobalTensor<float, TableShape, TableStride>;
TableTensor tableGM(tablePtr);
DstTile dst; TASSIGN(dst, 0x0000);
IdxTile idx; TASSIGN(idx, 0x0080);
MGATHER<Coalesce::Elem>(dst, tableGM, idx);
}ASM Examples
Automatic Mode
# Automatic mode: the compiler/runtime is responsible for resource placement and scheduling.
%dst = pto.mgather %mem, %idx : (!pto.partition_tensor_view<MxNxdtype>, pto.tile<...>)Manual Mode
# Manual mode: first explicitly bind the resources, then issue the instruction.
# Optional (when the instruction contains tile operands):
# pto.tassign %arg0, @tile(0x1000)
# pto.tassign %arg1, @tile(0x2000)
%dst = pto.mgather %mem, %idx : (!pto.partition_tensor_view<MxNxdtype>, pto.tile<...>)PTO Assembly Form
%dst = mgather %mem, %idx : !pto.memref<...>, !pto.tile<...> -> !pto.tile<...>
# AS Level 2 (DPS)
pto.mgather ins(%mem, %idx : !pto.partition_tensor_view<MxNxdtype>, !pto.tile_buf<...>) outs(%dst : !pto.tile_buf<...>)In addition to the GM → UB gather described above, MGATHER also supports directly gathering index-selected data into an L1/cube TileType::Mat tile (NZ fractal layout). When TileDst::Loc == TileType::Mat, the GM → L1 path is automatically selected.
Index Source — GM
The GM → L1 variant provides the indexes as a GM GlobalTensor (int32_t/uint32_t). The row mode reuses the three-parameter form; the Elem mode requires a fourth parameter scratch (GM workspace):
// Row — three-parameter form
template <Coalesce CMode = Coalesce::Row, GatherOOB Oob = GatherOOB::Undefined,
typename MatTileDst, typename GlobalTable, typename GlobalIdx>
PTO_INST RecordEvent MGATHER(MatTileDst& dst, GlobalTable& table, GlobalIdx& idx);
// Elem — four-parameter form (with GM scratch)
template <Coalesce CMode = Coalesce::Elem, GatherOOB Oob = GatherOOB::Undefined,
typename MatTileDst, typename GlobalTable, typename GlobalIdx, typename GlobalScratch>
PTO_INST RecordEvent MGATHER(MatTileDst& dst, GlobalTable& table, GlobalIdx& idx,
GlobalScratch& scratch);Constraints (MGatherCheckGm2L1, Atlas A2/A3 Training Products/Atlas A2/A3 Inference Products and Ascend 950PR/Ascend 950DT)
- Dtypes. Atlas A2/A3 training products/Atlas A2/A3 inference products:
int8/uint8/int16/uint16/int32/uint32/half/bfloat16/float. Ascend 950PR/Ascend 950DT additionally supportshifloat8/float8_e4m3/float8_e5m2. The row mode further requiressizeof(T) <= 4. - Index.
idxmust be a GMGlobalTensorof typeint32_toruint32_t. - Table.
GlobalTable::DType == __gm__ T,GlobalTable::layout == Layout::ND. - Scratch (Elem only).
GlobalScratch::DType == __gm__ T. - Destination.
TileDst::Loc == TileType::Mat, NZ form (!isRowMajor && SFractal == SLayout::RowMajor && SFractalSize == 512),TileDst::Cols % (C0_SIZE_BYTE / sizeof(T)) == 0,TileDst::Rows % FRACTAL_NZ_ROW (16) == 0.
Example — Row Gather to L1 NZ Tile
template <typename T, int R, int C, int TableRows>
AICORE void example_gm2l1_row(__gm__ T* tablePtr, __gm__ int32_t* idxPtr)
{
using TableShape = Shape<1, 1, 1, TableRows, C>;
using TableStride = Stride<1, 1, 1, C, 1>;
using IdxShape = Shape<1, 1, 1, 1, R>;
using IdxStride = Stride<1, 1, 1, R, 1>;
GlobalTensor<T, TableShape, TableStride, Layout::ND> tableGM(tablePtr);
GlobalTensor<int32_t, IdxShape, IdxStride, Layout::ND> idxGM(idxPtr);
using DstTile = Tile<TileType::Mat, T, R, C, BLayout::ColMajor, R, C, SLayout::RowMajor, 512>;
DstTile dst; TASSIGN(dst, 0x0);
MGATHER<Coalesce::Row, GatherOOB::Clamp>(dst, tableGM, idxGM); // GM (ND) -> L1 (NZ)
}Example — Elem Gather to L1 NZ Tile (with GM Scratch)
template <typename T, int R, int C, int TableSize>
AICORE void example_gm2l1_elem(__gm__ T* tablePtr, __gm__ int32_t* idxPtr, __gm__ T* scratchPtr)
{
using TableShape = Shape<1, 1, 1, 1, TableSize>;
using TableStride = Stride<1, 1, 1, TableSize, 1>;
using IdxShape = Shape<1, 1, 1, R, C>;
using IdxStride = Stride<1, 1, 1, C, 1>;
using ScratchShape = Shape<1, 1, 1, 1, R * C>;
using ScratchStride= Stride<1, 1, 1, R * C, 1>;
GlobalTensor<T, TableShape, TableStride, Layout::ND> tableGM(tablePtr);
GlobalTensor<int32_t, IdxShape, IdxStride, Layout::ND> idxGM(idxPtr);
GlobalTensor<T, ScratchShape, ScratchStride, Layout::ND> scratchGM(scratchPtr);
using DstTile = Tile<TileType::Mat, T, R, C, BLayout::ColMajor, R, C, SLayout::RowMajor, 512>;
DstTile dst; TASSIGN(dst, 0x0);
MGATHER<Coalesce::Elem, GatherOOB::Zero>(dst, tableGM, idxGM, scratchGM);
}Ascend 950PR/Ascend 950DT Only — SIMT Executor for Elem GM → L1 (GatherExec::Simt)
On Ascend 950PR/Ascend 950DT, the Elem GM → L1 path has two executors, selected through the third template parameter GatherExec:
enum class GatherExec : uint8_t { Scalar = 0, Simt = 1 };GatherExec::Scalar(default for the four-parameter form) — the cube core traverses the indexes through scalar loads.GatherExec::Simt— the AIV vector core parallelizes the gather through a SIMT kernel.
template <Coalesce CMode, GatherOOB Oob, GatherExec Exec, typename MatTileDst,
typename GlobalTable, typename GlobalIdx, typename GlobalScratch>
PTO_INST RecordEvent MGATHER(MatTileDst& dst, GlobalTable& table, GlobalIdx& idx,
GlobalScratch& scratch);template <typename T, int R, int C, int TableSize>
AICORE void example_gm2l1_elem_simt(__gm__ T* tablePtr, __gm__ int32_t* idxPtr, __gm__ T* scratchPtr)
{
using TableShape = Shape<1, 1, 1, 1, TableSize>;
using TableStride = Stride<1, 1, 1, TableSize, 1>;
using IdxShape = Shape<1, 1, 1, R, C>;
using IdxStride = Stride<1, 1, 1, C, 1>;
using ScratchShape = Shape<1, 1, 1, 1, R * C>;
using ScratchStride= Stride<1, 1, 1, R * C, 1>;
GlobalTensor<T, TableShape, TableStride, Layout::ND> tableGM(tablePtr);
GlobalTensor<int32_t, IdxShape, IdxStride, Layout::ND> idxGM(idxPtr);
GlobalTensor<T, ScratchShape, ScratchStride, Layout::ND> scratchGM(scratchPtr);
using DstTile = Tile<TileType::Mat, T, R, C, BLayout::ColMajor, R, C, SLayout::RowMajor, 512>;
DstTile dst; TASSIGN(dst, 0x0);
MGATHER<Coalesce::Elem, GatherOOB::Zero, GatherExec::Simt>(dst, tableGM, idxGM, scratchGM);
}Performance Considerations
Atlas A2/A3 Training Products/Atlas A2/A3 Inference Products
- Row vs. Elem. Row coalesce achieves the best gathering bandwidth. Elem coalesce issues one scalar GM read + UB write per active channel.
- Sequential scalar loop (Elem). Atlas A2/A3 training products/Atlas A2/A3 inference products traverse
validRow * validColchannels sequentially in a single thread. - DMA cost (Row). ND: one
copy_gm_to_ubuf_align_b*call per row. NZ: one call per (logical row, batch) pair. - OOB cost.
Undefined: free;Clamp/Wrap: add one arithmetic remap per channel;Zero: writeT(0)inline (Elem) or pre-zero the tile and skip the DMA (Row) of OOB rows.
Ascend 950PR/Ascend 950DT
- Shape-adaptive launch. The SIMT grid size is determined by the resolved
validRows/validCols. - OOB policy cost.
Undefined: zero overhead.Clamp/Wrap: one arithmetic remap per channel.Zero: one comparison and select per channel. - No thread branching for mode/OOB. All decisions are
if constexpr. - Row vs. Elem bandwidth. Row coalesce achieves the best gathering bandwidth; Elem coalesce performs one scalar GM load per channel.