name: ascendc-operator-code-gen description: Generate the complete AscendC operator code implementation from the design document and complete framework adaptation. TRIGGER when: the design document is complete and you need to generate op_host/op_kernel code, register it with the PyTorch framework, and compile and test. Keywords: code generation, op_host, op_kernel, tiling, kernel, framework adaptation, operator registration.
AscendC Operator Code Generation and Framework AdaptationGenerate op_host + op_kernel code from the design document, register it with the PyTorch framework, and call ascendc-operator-compile-debug skill to complete compilation, installation, and accuracy testing.
Prerequisites: Design document ascend-kernel/csrc/ops/<op_name>/design.md is ready, and the directory structure has been created.
Workflow Overview
读取设计文档 → 加载 reference → 选择模板 → 生成 op_host + op_kernel
→ 框架适配 (ops.h + register.cpp + csrc/CMakeLists.txt)
→ 调用 ascendc-operator-compile-debug skill (编译 + 安装 + 测试)
Phase 1: Load Reference Documents
MANDATORY — READ BEFORE CODING: Read references/GUIDE.md, and load the corresponding reference file according to the operator type in the design document.Never skip this step.
Phase 2: Read the Design Document
From ascend-kernel/csrc/ops/<op_name>/design.md extract:
| Extraction Item | Design Document Section | Purpose |
|---|---|---|
| Function signature + supported data types | Prototype design | op_host function prototype, kernel template parameters |
| Operator type | Tiling split → Step 1 | Select template (elementwise / row) |
| UB allocation table | UB space allocation | Derive bufferCoefficient, InitBuffer size |
| Computation pseudocode | Kernel implementation | Compute function logic |
Phase 3: Select a Template and Generate Code
Select the corresponding template according to the operator type, copy it to the project directory, and then modify it.
Template Selection
| Operator Type | op_host Template | op_kernel Template |
|---|---|---|
| Elementwise (ReLU, GELU, Add...) | templates/elementwise_op_host.cpp |
templates/elementwise_op_kernel.cpp |
| Row processing (LayerNorm, Softmax...) | templates/row_op_host.cpp |
templates/row_op_kernel.cpp |
| Index-PerRow (IndexSelect, shared 1D index) | templates/index_op_host.cpp |
templates/index_op_kernel.cpp |
| Index-PerElem (Gather, Scatter, per-row index) | templates/index_op_per_elem_host.cpp |
templates/index_op_per_elem_kernel.cpp |
| Sort (Sort, TopK...) | templates/sort_op_host.cpp |
templates/sort_op_kernel.cpp |
| Pool (MaxPool, AvgPool...) | templates/pool_ndhwc_op_host.cpp |
templates/pool_ndhwc_op_kernel.cpp |
MANDATORY: Read the complete contents of the corresponding template file, copy it to the target path, and then modify it.
Generation Steps
- Read the complete contents of the selected op_host and op_kernel templates
- Write the contents to
ascend-kernel/csrc/ops/<op_name>/op_host/<op_name>.cppandop_kernel/<op_name>.cpp(overwriting the skeleton placeholder files) - Replace all placeholders (
<op_name>,<OpName>, etc.) - Modify according to the design document:
- op_host: function signature, input validation,
bufferCoefficient, EXEC_KERNEL_CMD parameters - op_kernel: Init's GM parameters, InitBuffer size, Compute computation logic
- op_host: function signature, input validation,
FP16/FP32 template branch
The Kernel template uses template <typename T> generics + if constexpr branches to handle different data types:
if constexpr (sizeof(T) == sizeof(float)) {
// float32 直接计算
} else {
// fp16/bf16: Cast 升精度 → fp32 计算 → Cast 降精度
}
The AscendC compiler supports C++17 if constexpr, which can be safely used for template branches. If compatibility with older compilers is needed, you can also use sizeof(T) == 4 plain if (the compiler will optimize away dead branches).
Key Points for Template Modification
Hardware Parameter Retrieval (built into the template)
The template uses platform APIs to dynamically retrieve hardware parameters, no longer hardcoded:
#include "tiling/platform/platform_ascendc.h"
auto ascendc_platform = platform_ascendc::PlatformAscendCManager::GetInstance();
int64_t coreNum = static_cast<int64_t>(ascendc_platform->GetCoreNumAiv());
uint64_t ubSize;
ascendc_platform->GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSize);
// 需要 workspace 时:
// uint64_t sysWorkspaceSize = ascendc_platform->GetLibApiWorkSpaceSize();
bufferCoefficient Derivation
Derived from the UB allocation table in the design document—the total size of all Buffers = bufferCoefficient * tileLength:
- Sum the "total size" column of all buffers in the UB allocation table
- The result has the form
tileLength * N, thenbufferCoefficient = N - When different dtypes have different coefficients, use the
if (dtypeSize == 2) {...} else {...}branch
FP16/BF16 precision promotion
Both FP16 and BF16 must be promoted to FP32 for computation and then converted back. The template already marks the insertion positions for the precision-promotion code; just uncomment and fill in the computation logic.
GM ↔ UB transfer (DataCopyPad)
Production code must use DataCopyPad(do not use DataCopy for GM↔UB transfers):
// CopyIn: GM → UB
AscendC::DataCopyExtParams copyInParams{1,
static_cast<uint32_t>(curTileLength * sizeof(T)), 0, 0, 0};
AscendC::DataCopyPadExtParams<T> padParams{false, 0, 0, static_cast<T>(0)};
AscendC::DataCopyPad(xLocal, xGm[progress * tileLength], copyInParams, padParams);
// CopyOut: UB → GM
AscendC::DataCopyExtParams copyOutParams{1,
static_cast<uint32_t>(curTileLength * sizeof(T)), 0, 0, 0};
AscendC::DataCopyPad(yGm[progress * tileLength], yLocal, copyOutParams);
Note: template files may use DataCopy to simplify examples; production code should replace it with DataCopyPad.
ReduceSum/ReduceMax precautions
Reduction operations may modify the source tensor, so a backup must be made first:
AscendC::Adds(backup, src, 0.0f, len); // 备份
AscendC::ReduceSum<float, true>(result, backup, sharedTmp, dimLen);
Stage 4: Framework adaptation
4.1 Update csrc/ops.h
In namespace ascend_kernel , add the function declaration:
<返回类型> <op_name>(<参数列表>);
4.2 Update csrc/register.cpp
In TORCH_LIBRARY_FRAGMENT(npu, m) , add the schema:
m.def("<op_name>(<schema_参数>) -> <返回类型>");
In TORCH_LIBRARY_IMPL(npu, PrivateUse1, m) , add the implementation:
m.impl("<op_name>", TORCH_FN(ascend_kernel::<op_name>));
Schema syntax quick reference:
| C++ type | Schema type | Example |
|---|---|---|
const at::Tensor & |
Tensor |
Tensor self |
at::IntArrayRef |
int[] |
int[] kernel_size |
int64_t |
int |
int dim=-1 |
double |
float |
float eps=1e-5 |
bool |
bool |
bool flag=False |
c10::optional<at::Tensor> |
Tensor? |
Tensor? weight=None |
c10::optional<int64_t> |
int? |
int? divisor=None |
4.3 Update csrc/CMakeLists.txt
In FILE(GLOB OP_SRCS ...) , add the host source file:
${PROJECT_OP_SRC_BASE}/ops/<op_name>/op_host/<op_name>.cpp
In ascendc_library(no_workspace_kernel STATIC ...) , add the kernel source file:
${PROJECT_OP_SRC_BASE}/ops/<op_name>/op_kernel/<op_name>.cpp
Stage 5: Build, install, and test
MANDATORY — execute according to the ascendc-operator-compile-debug skill process:
- Load the environment: source the CANN environment variables, activate the conda environment
- Ensure
build.shhas execute permission:chmod +x build.sh - Build the project:
bash build.sh - Verify the build succeeded:
ls output/ascend_kernel*.whl - Install the whl package:
pip install output/ascend_kernel*.whl --force-reinstall --no-deps - Generate the test file
tests/test_<op_name>.py - Run the functional test:
python tests/test_<op_name>.py - Run the precision test:
pytest tests/test_<op_name>.py -v - If build/test fails, enter the troubleshooting loop (at most 3 times)
- When the precision test fails: if allclose does not pass, the output deviates, or it is all zeros or NaN,MUST read and follow the
ascendc-operator-precision-debugskill process to troubleshoot the root cause (error analysis → code review → experimental isolation → instrumentation localization → fix verification)
Practical experience: source the environment variables before every shell command, otherwise the build tools or Python packages will not be found.
Post-generation checklist
op_host
- namespace
ascend_kernel,includetorch_kernel_helper.h+tiling/platform/platform_ascendc.h+aclrtlaunch_<op_name>.h - use platform APIs to obtain coreNum and ubSize (do not hardcode)
- bufferCoefficient is consistent with the UB allocation table in the design document
- all parameters of EXEC_KERNEL_CMD are lvalues
- [row processing] padding/unpadding is correct
op_kernel
- include
kernel_operator.h,BUFFER_NUM = 2 - Init whole-core/tail-core offsets are correct, and InitBuffer sizes are consistent with the UB allocation table
- Process tail tile alignment (alignedTailLen)
- AllocTensor/FreeTensor are paired, EnQue/DeQue are paired
- FP16/BF16 must be promoted to FP32 for computation
- Back up the source data before ReduceSum
Framework adaptation
- ops.h declarations match the op_host function signatures
- register.cpp schema parameter types/default values are correct
- csrc/CMakeLists.txt has the host and kernel source files added
Anti-pattern list
- NEVER To let FP16/BF16 directly participate in complex mathematical calculations (Mul/Div/Exp/Tanh, etc.), you must first Cast to FP32
- NEVER Pass rvalues (temporary objects, literals, expression results) in EXEC_KERNEL_CMD
- NEVER Use the bool parameter type in a kernel; use int64_t instead
- NEVER For GM↔UB transfers, use DataCopy; you must use DataCopyPad
- NEVER Directly reuse the source tensor after ReduceSum/ReduceMax (the reduction may modify the source data)
- NEVER Use in a kernel
std::min/max/abs/sqrt/expand other standard library functions - NEVER Pass repeatTime > 255 to a high-dimensional slicing API (uint8_t will silently truncate to 0)
- NEVER Modify
cmake/orcsrc/utils/files under - NEVER Omit default values in the schema in register.cpp (e.g.,
int dimshould be written asint dim=-1) - NEVER Hardcode the number of cores or UB size; it must be obtained through the platform API
- NEVER Make the dst of ReduceMax/Sum point to the same memory block as tmpBuffer