TMOV
Instruction Diagram
Introduction
Moves/Copies between tiles, optionally selecting an implementation-defined conversion mode through template parameters and overloads.
TMOV is used for:
- Vec -> Vec move
- Mat -> Left/Right/Bias/Scaling/Scale(Microscaling) move (target-dependent)
- Acc -> Mat/Vec move (target-dependent)
Mathematical Semantics
Copies or converts elements from src to dst within the valid region. The specific conversion depends on the selected mode and target.
For the copy-only case:
ND → NZ (Repacking Data for Cube Unit)
The Cube Unit consumes operands in the NZ (Normal-ZigZag) fractal format: a tile is divided into C0 × C0 fractals, and each fractal is stored with BLayout = ColMajor ("N"—column-major outer block) and SLayout = RowMajor ("Z"—row-major within the fractal). TMOV(dstNZ, src) repacks a RowMajor Vec/Mat tile (NoneBox) into the NZ layout. No tmp is required.
| Operand (GM/L1 Side) | BLayout | SLayout | Meaning |
|---|---|---|---|
| Left (A, NT) | ColMajor | RowMajor | Standard NZ |
| Right (B, NT) | RowMajor | ColMajor | Transposed NZ |
The CompactMode::RowPlusOne destination (Rows = Vec_S0 + 1) is the standard practice for avoiding UB bank conflicts on vsstb scatter.
X → ZZ (Microscaling Exponent Repacking)
For MXFP8/MXFP4 matrix multiplication, each group of E8M0 exponents must be delivered to the Cube scale operand in ZZ format: a [16,2] block (32 bytes = 16 columns × 2 row groups), linearized as one block per Cube fractal column. Two variants:
ND → ZZ (grp_axis = 1, default): the source exponents are ND-grouped (axis-1).
Where , , , and .
DN → ZZ (grp_axis = 0): the source exponents are DN-grouped (axis-0). Equivalent to ND→ZZ after transposition:
Where , , , , and .
For a fixed , the 32 bytes of the ZZ block come from two contiguous 16-byte source segments ( and ); therefore DN→ZZ uses contiguous loads + vintlv (more efficient than the vgather2 of ND→ZZ).
Role of the tmp Tile
Only the X→ZZ conversion accepts the tmp operand (3-parameter overload). ND→ZZ uses it as the vgather2 index buffer; DN→ZZ accepts it for API consistency but does not access it (vsstb scatter requires no scratch). ND→NZ has no tmp.
Assembly Syntax
The PTO AS design recommends splitting TMOV into a set of operations:
%left = tmov.m2l %mat : !pto.tile<...> -> !pto.tile<...>
%right = tmov.m2r %mat : !pto.tile<...> -> !pto.tile<...>
%bias = tmov.m2b %mat : !pto.tile<...> -> !pto.tile<...>
%scale = tmov.m2s %mat : !pto.tile<...> -> !pto.tile<...>
%vec = tmov.a2v %acc : !pto.tile<...> -> !pto.tile<...>
%v1 = tmov.v2v %v0 : !pto.tile<...> -> !pto.tile<...>AS Level 1 (SSA)
%dst = pto.tmov.s2d %src : !pto.tile<...> -> !pto.tile<...>AS Level 2 (DPS)
pto.tmov ins(%src : !pto.tile_buf<...>) outs(%dst : !pto.tile_buf<...>)C++ Built-in APIs
Declared in include/pto/common/pto_instr.hpp and include/pto/common/constants.hpp:
The public include header is
<pto/pto-inst.hpp>, and the internal declarations are located inpto/common/pto_instr.hpp.
template <typename DstTileData, typename SrcTileData, typename... WaitEvents>
PTO_INST RecordEvent TMOV(DstTileData &dst, SrcTileData &src, WaitEvents &... events);
template <typename DstTileData, typename SrcTileData, ReluPreMode reluMode, typename... WaitEvents>
PTO_INST RecordEvent TMOV(DstTileData &dst, SrcTileData &src, WaitEvents &... events);
template <typename DstTileData, typename SrcTileData, AccToVecMode mode, ReluPreMode reluMode = ReluPreMode::NoRelu,
typename... WaitEvents>
PTO_INST RecordEvent TMOV(DstTileData &dst, SrcTileData &src, WaitEvents &... events);
template <typename DstTileData, typename SrcTileData, typename FpTileData, AccToVecMode mode,
ReluPreMode reluMode = ReluPreMode::NoRelu, typename... WaitEvents>
PTO_INST RecordEvent TMOV(DstTileData &dst, SrcTileData &src, FpTileData &fp, WaitEvents &... events);
template <typename DstTileData, typename SrcTileData, ReluPreMode reluMode = ReluPreMode::NoRelu,
typename... WaitEvents>
PTO_INST RecordEvent TMOV(DstTileData &dst, SrcTileData &src, uint64_t preQuantScalar, WaitEvents &... events);
template <typename DstTileData, typename SrcTileData, AccToVecMode mode, ReluPreMode reluMode = ReluPreMode::NoRelu,
typename... WaitEvents>
PTO_INST RecordEvent TMOV(DstTileData &dst, SrcTileData &src, uint64_t preQuantScalar, WaitEvents &... events);ND → NZ / X → ZZ Overload
// ND -> NZ (2 parameters, no tmp).
template <typename DstTileData, typename SrcTileData, typename... WaitEvents>
PTO_INST RecordEvent TMOV(DstTileData &dst, SrcTileData &src, WaitEvents &...events);
// X -> ZZ (3 parameters, with tmp). grp_axis=1 (default) = ND->ZZ; grp_axis=0 = DN->ZZ.
template <typename DstTileData, typename SrcTileData, typename TmpTileData, typename... WaitEvents,
std::enable_if_t<is_tile_data_v<TmpTileData>, int> = 0>
PTO_INST RecordEvent TMOV(DstTileData &dst, SrcTileData &src, TmpTileData &tmp, WaitEvents &...events);
template <int grp_axis, typename DstTileData, typename SrcTileData, typename TmpTileData, typename... WaitEvents,
std::enable_if_t<is_tile_data_v<TmpTileData>, int> = 0>
PTO_INST RecordEvent TMOV(DstTileData &dst, SrcTileData &src, TmpTileData &tmp, WaitEvents &...events);| Overload | grp_axis | Conversion | tmp Used |
|---|---|---|---|
TMOV(dst, src) | — | ND → NZ | No |
TMOV(dst, src, tmp) | 1 (default) | ND → ZZ | Yes (vgather2 index buffer) |
TMOV<0>(dst, src, tmp) | 0 | DN → ZZ | No (accepted only for API consistency) |
Constraints
General Constraints/Checks
TMOVhas the following overload families:- Pure move:
TMOV(dst, src) - relu form:
TMOV<..., reluMode>(dst, src) - Accumulator-to-vector form:
TMOV<..., mode, reluMode>(dst, src) - Vector quantization form:
TMOV<..., FpTileData, mode, reluMode>(dst, src, fp) - Scalar quantization form:
TMOV<..., reluMode>(dst, src, preQuantScalar)andTMOV<..., mode, reluMode>(dst, src, preQuantScalar)
- Pure move:
reluModetakes the value ofReluPreMode::{NoRelu, NormalRelu}.modetakes the value ofAccToVecMode::{SingleModeVec0, SingleModeVec1, DualModeSplitM, DualModeSplitN}.
Implementation Check for Atlas A2/A3 Training Products/Atlas A2/A3 Inference Products
- Shapes must match:
SrcTileData::Rows == DstTileData::RowsandSrcTileData::Cols == DstTileData::Cols. - The supported tile type pairs are restricted at compile time to:
TileType::Mat -> TileType::Left/Right/Bias/ScalingTileType::Vec -> TileType::VecTileType::Acc -> TileType::Mat
- For
TileType::Mat -> TileType::Bias:- The supported source/destination dtype pairs are
int32_t -> int32_t,float -> float, andhalf -> float. - The source row count must be
1. SrcTileData::Cols * sizeof(SrcType)must be 64-byte aligned.
- The supported source/destination dtype pairs are
- For
TileType::Mat -> TileType::Scaling:- The destination dtype must be equal to the source dtype and be
uint64_torint64_t - The source row count must be
1. SrcTileData::Cols * sizeof(SrcType)must be 128-byte aligned.
- The destination dtype must be equal to the source dtype and be
- For
TileType::Acc -> TileType::Mat:- Additionally performs the
CheckTMovAccToMat<...>compile-time check - The pure/relu form uses the pre-quantization mode for type conversion derived by
GetCastPreQuantMode<SrcDType, DstDType>(). - The scalar quantization form uses
GetScalarPreQuantMode<SrcDType, DstDType>(). - The vector quantization form requires the
FpTileDataoperandFpTileData::Loc == TileType::Scaling, and usesGetVectorPreQuantMode<SrcDType, DstDType>().
- Additionally performs the
Ascend 950PR/Ascend 950DT Implementation Check
CommonCheck()requires:- The destination/source dtype must be the same.
- The supported element types are
int8_t,hifloat8_t,float8_e5m2_t,float8_e4m3_t,half,bfloat16_t,float,float4_e2m1x2_t, andfloat4_e1m2x2_t. - The source layout must satisfy one of the following:
(SrcTileData::SFractal == SLayout::ColMajor && SrcTileData::isRowMajor)(SrcTileData::SFractal == SLayout::RowMajor && !SrcTileData::isRowMajor)SrcTileData::isRowMajor
CommonCheckMX()is used for the MX path and requires the source/destination dtype to be consistent and to supportfloat8_e8m0_t.- The supported paths include:
TileType::Mat -> TileType::Left/Right/Bias/Scaling/ScaleLeft/ScaleRightTileType::Vec -> TileType::Vec/TileType::MatTileType::Acc -> TileType::Vec/TileType::Mat- Specific internal path variants such as
ND -> ZZare handled by the Ascend 950PR/Ascend 950DT implementation
- For
TileType::Mat -> TileType::Bias:- The supported dtype pairs are
int32_t -> int32_t,float -> float,half -> float, andbfloat16_t -> float. - The source row count must be
1. DstTileData::Cols * sizeof(DstType)must be 64-byte aligned.- The bias table occupies
DstTileData::Cols * sizeof(DstType)and must not exceed4096bytes.
- The supported dtype pairs are
- For
TileType::Mat -> TileType::Scaling:- The source row count must be
1. DstTileData::Cols * sizeof(DstType)must be 128-byte aligned.- The fixpipe buffer occupancy
DstTileData::Cols * sizeof(DstType)must not exceed4096bytes.
- The source row count must be
- For
TileType::Acc -> TileType::Vec:- Set
modetoSingleModeVec0,SingleModeVec1,DualModeSplitM, orDualModeSplitN. - The dual-destination mode requires
QuantMode_t::NoQuant. - The dual-destination mode does not support the
nz2dnpath. - For 32-bit destination types (
float/int32_t), when usingDualModeSplitN,ValidCol(before splitting) must be a multiple of 32. - The destination stride must be non-zero and
dstStride * sizeof(dstType)must be a multiple of 32 bytes.
- Set
- For
TileType::Acc -> TileType::Mat:- The destination stride must be non-zero, and
dstStride * sizeof(dstType)must be a multiple of 32 bytes. - The relu/scalar quantization/vector quantization forms are supported through the corresponding overloads.
- The destination stride must be non-zero, and
Examples
ND → NZ (Data) — (128, 256) BF16
// Source: 128 rows × 256 columns BF16 RowMajor Vec Tile (ND).
// Destination: NZ fractal Mat Tile of the Cube Unit (Left operand).
constexpr uint32_t R = 128, C = 256;
using SrcT = Tile<TileType::Vec, bfloat16_t, R, C, BLayout::RowMajor, R, C, SLayout::NoneBox>;
using DstT = Tile<TileType::Mat, bfloat16_t, R, C, BLayout::ColMajor, R, C, SLayout::RowMajor>;
SrcT src; DstT dst;
TMOV(dst, src); // ND -> NZ, no tmp.tmp for ND→NZ: None — the 2-parameter overload is repacked in place via vsstb.
ND → ZZ (Exponent) — tmp Size Derivation
Given a quantized input shape (group size ), the ND-grouped E8M0 exponent tile shape is:
| Quantity | Value |
|---|---|
| Exponent row count | (one exponent row per input row) |
| Exponent column count | (one exponent per 32-element column group) |
| Row block count | |
| Block pair count |
The tmp buffer holds the vgather2 B16 index buffer used by GenerateB8IndicesZZToUB:
- 16 B16 lanes of head space + gather indices + 16 B16 lanes of tail space.
tmpdtype =uint8_t(E8M0), shape1 × ⌈tmpBytes⌉.
Example: , → exponent tile , , :
DN → ZZ (Exponent) — tmp
The DN grouping exponent shape is , where . TMOV<0> accepts the tmp operand for API consistency but does not access it (the vsstb scatter requires no scratch). Any tile with a non-zero size satisfies the signature.
// DN grouping e8 exponent (M̂×N) -> ZZ fractal scale Tile.
TMOV<0>(e8ZzTile, e8DnTile, tmpTile); // grp_axis=0 = DN->ZZ; tmp unused.Usage in MX Quantization (Reference)
After TQUANT generates the quantized data + E8M0 exponents, two TMOV instructions repack them for use by the Cube Unit. See TQUANT.md / TQUANT_DN.md for the complete pipeline.
// MXFP8 DN pipeline: quantize a 128×256 BF16 tile, then repack for Cube.
constexpr uint32_t M = 128, N = 256, G = 32, Mhat = M / G; // Mhat = 4
// Tiles
using SrcT = Tile<TileType::Vec, bfloat16_t, M, N, BLayout::RowMajor>;
using Fp8T = Tile<TileType::Vec, int8_t, M, N, BLayout::RowMajor>;
using E8DnT = Tile<TileType::Vec, uint8_t, Mhat, N, BLayout::RowMajor>; // 4×256
using E8ZzT = Tile<TileType::Mat, uint8_t, N, Mhat, BLayout::ColMajor, N, Mhat, SLayout::RowMajor>;
using Fp8NzT = Tile<TileType::Mat, int8_t, M, N, BLayout::ColMajor, M, N, SLayout::RowMajor>;
// 1. Quantize (DN grouping).
TQUANT<0, MxQuantAlg::OcpMxFp8E4M3>(fp8Tile, srcTile, &e8DnTile, &maxTile, &scalingTile);
// 2. Repack data ND->NZ (2 parameters, no tmp).
TMOV(fp8NzTile, fp8Tile);
// 3. Repack exponents DN->ZZ (3 parameters, tmp accepted but unused).
TMOV<0>(e8ZzTile, e8DnTile, tmpTile);Automatic
#include <pto/pto-inst.hpp>
using namespace pto;
void example_auto() {
using TileT = Tile<TileType::Vec, float, 16, 16>;
TileT src, dst;
TMOV(dst, src);
}Manual
#include <pto/pto-inst.hpp>
using namespace pto;
void example_manual() {
using SrcT = Tile<TileType::Mat, float, 16, 16, BLayout::RowMajor, 16, 16, SLayout::ColMajor>;
using DstT = TileLeft<float, 16, 16>;
SrcT mat;
DstT left;
TASSIGN(mat, 0x1000);
TASSIGN(left, 0x2000);
TMOV(left, mat);
}ASM Examples
Automatic Mode
# Automatic mode: resource placement and scheduling are managed by the compiler/runtime.
%dst = pto.tmov.s2d %src : !pto.tile<...> -> !pto.tile<...>Manual Mode
# Manual mode: bind resources explicitly before issuing the instruction.
# pto.tassign %arg0, @tile(0x1000)
# pto.tassign %arg1, @tile(0x2000)
%dst = pto.tmov.s2d %src : !pto.tile<...> -> !pto.tile<...>PTO Assembly Form
%dst = pto.tmov.s2d %src : !pto.tile<...> -> !pto.tile<...>
# AS Level 2 (DPS)
pto.tmov ins(%src : !pto.tile_buf<...>) outs(%dst : !pto.tile_buf<...>)