CANN PTO-ISA TFILLPAD 指令详解:Tile 编译时填充值的复制与边界物化
【免费下载链接】pto-isaParallel Tile Operation (PTO) is a virtual instruction set architecture designed by Ascend CANN, focusing on tile-level operations. This repository offers high-performance, cross-platform tile operations across Ascend platforms.项目地址: https://gitcode.com/cann/pto-isa
TFILLPAD 是 CANN PTO-ISA(Parallel Tile Operation)虚拟指令集中的核心数据搬移指令,负责将源 Tile 复制到目标 Tile,并在有效区域之外使用编译时确定的填充值(如PadValue::Min/PadValue::Max)完成填充。本文围绕 docs/isa/TFILLPAD_zh.md 展开,结合 include/pto 下的真实实现与测试用例,讲解其数学语义、PadValue 填充值机制、三种操作模式(Normal/InPlace/Expand)、汇编语法与 C++ 内建接口。读完本文,你将掌握如何在算子开发中利用 TFILLPAD 将动态有效区域"物化"为完整的静态 Tile 形状,为后续指令提供确定性的输入边界。
指令概述:为什么需要 TFILLPAD
在 PTO 的 Tile 抽象中,Tile 具有静态形状(由模板参数Rows/Cols决定)与运行时有效区域(由GetValidRow()/GetValidCol()决定)两层概念。当算子从全局内存加载数据时,实际有效数据往往小于 Tile 的静态形状(例如 Load 到的行数/列数不足),此时若直接对完整 Tile 做运算,填充区域的取值是未定义的。
TFILLPAD 解决的就是这个问题:它将源 Tile 复制到目标 Tile,并用编译时选定的填充值确定性物化有效区域之外的元素。其价值在于:
- 使后续指令能够在完整的静态 Tile 形状上安全运算,无需分支判断有效边界;
- 填充值是编译期常量,可配合
PadValue::Max/PadValue::Min(如+inf/-inf)用于后续的归约、极值、TPAD等对边界值敏感的运算; - 所有后端(CPU 模拟、A2/A3、A5)共享同一套 PadValue 位模式映射,保证行为跨平台一致。
数学语义
设VR = src.GetValidRow(),VC = src.GetValidCol()。对目标 Tile 的每个元素(i, j),TFILLPAD 定义如下:
dst(i, j) = src(i, j) if i < VR 且 j < VC dst(i, j) = pad 其他情况即:有效区域内的元素原样复制,有效区域外的元素写入pad。pad由目标 Tile 的TileDataDst::PadVal与元素类型共同决定。例如浮点类型支持时使用+inf/-inf,否则使用std::numeric_limits<T>::max()/min()。该语义在 CPU 模拟实现 include/pto/cpu/TFillPad.hpp 中被逐元素验证:
for (std::size_t j = 0; j < TileDataDst::Cols; ++j) { if (i < validSrcRow && j < validSrcCol) { dst.SetElement(i, j, src.GetElement(i, j)); } else { dst.SetElement(i, j, padVal); } }填充值机制:PadValue 与 PadValueMap
PadValue 枚举
填充值通过PadValue枚举指定,其底层类型为uint64_t(见 include/pto/common/type.hpp),标准值占用低位,自定义值通过高位携带位模式:
enum class PadValue : uint64_t { Null = 0, Zero = 1, Max = 2, Min = 3, // CustomBase marks the start of custom values (bit 32 set) CustomBase = 0x100000000ULL, };内建 PadValue 映射表(Vec 类型)
PadValue::Zero/Min/Max对应与类型相关的位模式,实现在 include/pto/common/constants.hpp 的PadValueMap<DType, PadVal>特化中(如float的Min为0xff800000、Max为0x7f800000)。各类型映射关系如下:
| DType | Zero | Min | Max | 说明 |
|---|---|---|---|---|
float/half/bfloat16_t | 0 | -inf | +inf | IEEE 无穷 |
| 整数类型 | 0 | 类型最小值 | 类型最大值 | 如INT32_MIN/INT32_MAX |
float8_e4m3_t | 0x00 | 0xFE | 0x7E | 无 inf,取有限极值 |
float8_e5m2_t | 0x00 | 0xFC | 0x7C | 支持±inf |
hifloat8_t | 0x00 | 0xEF | 0x6F | HiF8±inf(S1101111),仅 A5 |
float4_e2m1x2_t | 0x00 | 0xFF | 0x77 | 两个 nibble 均为-6/+6 |
float4_e1m2x2_t | 0x00 | 0xFF | 0x77 | 两个 nibble 均为有限极值 |
注意:float8_e8m0_t没有内建的 Zero/Min/Max 语法糖,需使用PadValueCustom;fp8/fp4 低精度映射表仅用于 A5 后端(CPU 模拟对 fp8/fp4 亦支持),hifloat8_t仅限 A5。
自定义填充值:PadValueCustom
当内建值不满足需求时,可用PadValueCustom()(定义于 include/pto/common/constants.hpp)在编译期把任意数值的位模式编码进PadValue:
#include <pto/common/constants.hpp> constexpr PadValue PadCustomNeg1 = PadValueCustom(-1.0f); // float -1.0 constexpr PadValue PadCustomNeg1_Half = PadValueCustom16(0xBC00); // fp16 -1.0编码规则为:[63:32]存放数值的位模式,[31:0]标记CustomBase(0x100000000)。例如-1.0f的位模式为0xBF800000,编码后为0xBF80000000000001ULL。辅助函数isCustomPadValue()与getCustomPadBits()负责检测与解码,CPU 模拟与 A2/A3、A5 后端均通过统一的GetPadValue<TileData>()读取位模式(见 include/pto/common/constants.hpp 中的编码/解码static_assert验证)。
汇编语法
TFILLPAD 在 PTO 汇编中有多个层级的表现形式。
同步形式(概念性):
%dst = tfillpad %src : !pto.tile<...> -> !pto.tile<...>AS Level 1(SSA)
%dst = pto.tfillpad %src : !pto.tile<...> -> !pto.tile<...>AS Level 2(DPS)
pto.tfillpad ins(%src : !pto.tile_buf<...>) outs(%dst : !pto.tile_buf<...>)变体指令在 SSA/DPS 层面对应pto.tfillpad_expand与pto.tfillpad_inplace,详细定义见 docs/isa/TFILLPAD_EXPAND_zh.md 与 docs/isa/TFILLPAD_INPLACE_zh.md。
C++ 内建接口
TFILLPAD 的 C++ 内建接口声明于include/pto/common/pto_instr.hpp(经公共头 include/pto/pto-inst.hpp 引入),实现在各后端头文件中:
template <typename TileData, PadValue PadVal = PadValue::Zero, typename... WaitEvents> PTO_INST RecordEvent TFILLPAD(TileData &dst, TileData &src, WaitEvents &... events); template <typename DstTileData, typename SrcTileData, typename... WaitEvents> PTO_INST RecordEvent TFILLPAD(DstTileData &dst, SrcTileData &src, WaitEvents &... events);两条重载的分工(对应 include/pto/common/pto_instr.hpp 的实现):
- 第一条为Mat 类型重载,
TileData::Loc == TileType::Mat,通过std::enable_if_t的 SFINAE 约束触发,默认PadVal = PadValue::Zero; - 第二条为Vec 类型重载,
Dst/SrcTileData::Loc == TileType::Vec,通过模板参数TFillPadMode mode选择变体。
此外还有两个兼容别名:
template <typename DstTileData, typename SrcTileData, typename... WaitEvents> PTO_INST RecordEvent TFILLPAD_INPLACE(DstTileData &dst, SrcTileData &src, WaitEvents &... events); template <typename DstTileData, typename SrcTileData, typename... WaitEvents> PTO_INST RecordEvent TFILLPAD_EXPAND(DstTileData &dst, SrcTileData &src, WaitEvents &... events);它们分别等价于TFILLPAD<TFillPadMode::InPlace>(...)与TFILLPAD<TFillPadMode::Expand>(...)(见 include/pto/common/pto_instr.hpp)。
三种操作模式
TFillPadMode枚举(定义于 include/pto/common/type.hpp)控制 Vec 重载的变体选择:
| 模式 | 语义 | 形状约束 |
|---|---|---|
TFillPadMode::Normal | 标准复制 + 填充 | 目标与源静态形状一致 |
TFillPadMode::InPlace | 原地填充(源与目标别名同一存储) | 目标与源静态形状一致 |
TFillPadMode::Expand | 扩展填充,允许目标大于源 | 目标 Rows/Cols 分别不小于源 |
约束条件
使用 TFILLPAD 需满足以下约束(来自 docs/isa/TFILLPAD_zh.md 及 CPU 实现中的static_assert,见 include/pto/cpu/TFillPad.hpp):
- Vec 类型重载:
TileDataDst::PadVal != PadValue::Null,即目标 Tile 必须显式指定填充值(CPU 实现对应断言 "TFillPad, dst vecTile pad value can't be Null!")。 - 数据类型:
sizeof(TileDataDst::DType) == sizeof(TileDataSrc::DType),且元素大小必须是 1、2 或 4 字节。打包的fp4x2按 1 字节 DType 处理(每元素两个 nibble);在 A5 上fp4x2的ValidCol/Cols按 nibble 计数(与 TLOAD/TSTORE/TCVT 一致),填充长度为ceil(Cols/2)个打包字节。 - TFILLPAD(Normal):
TileDataDst::Rows/Cols必须匹配TileDataSrc::Rows/Cols。 - TFILLPAD_EXPAND:
TileDataDst::Rows >= TileDataSrc::Rows且TileDataDst::Cols >= TileDataSrc::Cols。 - Mat 类型重载:当
TileData::TileType为Mat时,布局必须满足!TileData::isRowMajor && TileData::SLayout::RowMajor,且PadVal必须为PadValue::Zero或PadValue::Null。该 Mat 重载与首条 Vec 重载(要求PadVal != PadValue::Null)分属不同 SFINAE 重载,二者并不矛盾。
CPU 实现的约束更明确:TFILLPAD_IMPL中通过static_assert校验了 PadVal、DType 大小相等以及 1/2/4 字节限制,并在TFILLPAD_INPLACE_IMPL/TFILLPAD_EXPAND_IMPL中分别断言形状相等或目标不小于源(include/pto/cpu/TFillPad.hpp)。
编程示例
Vec 类型示例:Min 填充
以下示例将16×16的源 Vec Tile 复制到同为16×16、但携带PadValue::Min填充值的目标 Tile:
#include <pto/pto-inst.hpp> using namespace pto; void example1() { using SrcT = Tile<TileType::Vec, float, 16, 16>; using DstT = Tile<TileType::Vec, float, 16, 16, BLayout::RowMajor, 16, 16, SLayout::NoneBox, TileConfig::fractalABSize, PadValue::Min>; SrcT src; DstT dst; TFILLPAD(dst, src); }Mat 类型示例:自填充
Mat 重载支持对同一个 Tile 原地调用(dst与src为同一对象),ColMajor布局且SLayout::RowMajor:
void example2() { using TileMatData = Tile<TileType::Mat, float, 16, 256, BLayout::ColMajor, 1, 224, SLayout::RowMajor, 512>; TileMatData matTile; TFILLPAD(matTile, matTile); }自定义填充值与 Expand 模式组合
参考 CPU 测试 tests/cpu/st/testcase/tfillpad/tfillpad_kernel.cpp 的用例 10:源 Tile 有效数据为128×64,目标 Tile 静态形状128×128,用-1.0f填充剩余列:
#include <pto/pto-inst.hpp> #include <pto/common/constants.hpp> using namespace pto; constexpr PadValue PadCustomNeg1 = PadValueCustom(-1.0f); void example3() { using SrcT = Tile<TileType::Vec, float, 128, 128, BLayout::RowMajor, -1, -1, SLayout::NoneBox, 512, PadValue::Null>; using DstT = Tile<TileType::Vec, float, 128, 128, BLayout::RowMajor, -1, -1, SLayout::NoneBox, 512, PadCustomNeg1>; SrcT src(128, 64); // 运行时有效区域 128×64 DstT dst(128, 128); // dst[0:128, 0:64] = src 数据 // dst[0:128, 64:128] = -1.0f(填充) TFILLPAD(dst, src); }汇编示例(ASM)
自动模式
自动模式下由编译器/运行时负责资源放置与调度,指令只需描述数据流:
# 自动模式:由编译器/运行时负责资源放置与调度。 %dst = pto.tfillpad %src : !pto.tile<...> -> !pto.tile<...>手动模式
手动模式下需先用pto.tassign显式绑定 Tile 资源(地址),再发射指令:
# 手动模式:先显式绑定资源,再发射指令。 # 可选(当该指令包含 tile 操作数时): # pto.tassign %arg0, @tile(0x1000) # pto.tassign %arg1, @tile(0x2000) %dst = pto.tfillpad %src : !pto.tile<...> -> !pto.tile<...>PTO 汇编形式
%dst = pto.tfillpad %src : !pto.tile<...> -> !pto.tile<...> # AS Level 2 (DPS) pto.tfillpad ins(%src : !pto.tile_buf<...>) outs(%dst : !pto.tile_buf<...>)源码级实现剖析
CPU 模拟后端
include/pto/cpu/TFillPad.hpp 提供了完整的 CPU 参考实现,核心流程为:
- 通过
GetPadValue<TileDataDst>()解码 PadValue 位模式,再按sizeof(DType)(4/2/1 字节)用std::bit_cast还原为元素值;标准bfloat16_t走std::numeric_limits的infinity()回退路径; - 使用
cpu::parallel_for_1d并行遍历目标 Tile 全部行,逐元素判断i < validSrcRow && j < validSrcCol,命中则dst.SetElement(i, j, src.GetElement(i, j)),否则写入padVal; TFILLPAD_IMPL的多个重载分别承载同类型(带 PadVal 参数)、Normal(形状相等断言)、InPlace(形状相等断言)与 Expand(目标不小于源断言)四种情况。
NPU 后端与跨平台一致性
A2/A3 与 A5 分别有独立的实现文件(include/pto/npu/a2a3/TFillPad.hpp、include/pto/npu/a5/TFillPad.hpp)。A5 实现中填充值通过pset_b32/pset_b16/pset_b8等掩码寄存器指令按元素宽度写入(见 include/pto/npu/a5/TFillPad.hpp 的PSetTyped辅助函数)。由于所有后端共用PadValueMap与GetPadValue()解码路径,自定义位模式在 CPU 模拟、A2/A3、A5 上行为一致。
测试验证
TFILLPAD 拥有完整的 CPU 单测覆盖,位于 tests/cpu/st/testcase/tfillpad:
- tfillpad_kernel.cpp 提供 18 个测试用例的 kernel 与 golden 生成逻辑,覆盖 float/uint16/int8/uint8/bfloat16/half 类型,
PadValue::Max/Min/Zero与自定义-1.0f/fp16-1.0填充,以及 InPlace(用例 5)、Expand(用例 8、9)变体; - README.md 以表格形式汇总了各用例的全局形状、Tile 形状与填充值设置,可作为参数选型的速查表;
- golden 计算逻辑(
get_input_golden_case)同样按isCustomPadValue→ 位解码、has_infinity→±inf、否则min/max的顺序复现语义,形成对实现的双向校验。
总结
TFILLPAD 是 PTO-ISA 中"确定性物化边界"的关键指令:它把运行时动态的有效区域"补齐"为编译期已知的静态 Tile 形状,并通过PadValue机制(内建 Zero/Min/Max、低精度类型映射表、PadValueCustom自定义位模式)保证填充值跨后端一致。理解其数学语义、三种操作模式(Normal/InPlace/Expand)、PadValue 约束与汇编形式,是在 PTO 算子中正确处理边界填充、写出可移植高性能 kernel 的前提。更多相关指令可参考 docs/isa 目录下的完整指令手册,以及 docs/coding/tutorials 中的编程教程。
【免费下载链接】pto-isaParallel Tile Operation (PTO) is a virtual instruction set architecture designed by Ascend CANN, focusing on tile-level operations. This repository offers high-performance, cross-platform tile operations across Ascend platforms.项目地址: https://gitcode.com/cann/pto-isa
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考