已合并
Add Ascend950 Basic SYRK example #1195
void_ptr创建于 8月28日
Add Ascend950 Basic SYRK example #1195
已合并
void_ptr创建于 8月28日
共 18 个文件变更+566-0
@@ -0,0 +1,14 @@
1+# -----------------------------------------------------------------------------------------------------------
2+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+# CANN Open Software License Agreement Version 2.0 (the "License").
5+# Please refer to the License for details. You may not use this file except in compliance with the License.
6+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+# See LICENSE in the root of the software repository for the full text of the License.
9+# -----------------------------------------------------------------------------------------------------------
10+ 
11+set_source_files_properties(basic_syrk_tla.cpp PROPERTIES LANGUAGE ASC)
longjihui
longjihuilongjihui8月28日

需要补充一下对应ci case到tests/test_example.py,我看optest子PR里也没有该动作

likedislike
void_ptr
29 天前 评论:
12+catlass_example_add_executable(82_ascend950_basic_syrk cube basic_syrk_tla.cpp)
13+target_compile_definitions(82_ascend950_basic_syrk PRIVATE L2_CACHE_HINT)
14+target_link_libraries(82_ascend950_basic_syrk PRIVATE m)
@@ -0,0 +1,91 @@
1+# Ascend950 Basic SYRK Example Readme
2+ 
3+## 代码组织
4+ 
5+```text
6+├── 82_ascend950_basic_syrk
7+│ ├── CMakeLists.txt # CMake 编译文件
8+│ ├── README.md
9+│ └── basic_syrk_tla.cpp # 主文件(host 数据生成、kernel 调度、精度校验)
10+```
11+ 
12+## 使用示例
13+ 
14+1. 编译样例(Ascend950 需指定 `CATLASS_ARCH=3510`),可参考[快速入门](../../docs/zh/1_Practice/01_quick_start.md#编译执行):
15+ 
16+ ```bash
17+ bash scripts/build.sh -DCATLASS_ARCH=3510 82_ascend950_basic_syrk
18+ ```
19+ 
20+2. 切换到可执行文件目录并运行。测试数据随机生成,尺寸由命令行传入:
21+ 
22+ ```bash
23+ cd output/bin
24+ # 可执行文件名 | m | k | deviceId(可选,默认0)
25+ ./82_ascend950_basic_syrk 1024 1024 0
26+ ```
27+ 
28+ - `1024`:矩阵 m 轴($X$ 行数 / $Y$ 边长)
29+ - `1024`:k 轴($X$ 列数)
30+ - `0`:Device ID
31+ 
32+3. 执行成功输出:
33+ 
34+ ```text
35+ Compare success.
36+ ```
37+ 
38+## 功能说明
39+ 
40+- 算子功能:完成对称秩更新(SYRK),对输入矩阵 $X$ 做自乘,得到对称结果矩阵 $Y$。
41+- 计算公式:
42+ 
43+ $$
44+ Y = X \cdot X^{T}
45+ $$
46+ 
47+ 其中 $X$ 形如 `(M, K)`,$Y$ 形如 `(M, M)` 且满足 $Y = Y^{T}$。
48+ 
49+- 本样例面向 Ascend950,在 Basic Matmul 路径上复用 L1/L0 pingpong,并通过 swizzle 调度只计算下三角 Block,再利用 nz2nd / nz2dn 双写补全上三角。
50+ 
51+## 参数说明
52+ 
53+本样例使用 `SyrkOptions`(定义于 `examples/common/options.hpp`),命令行参数为 `m k [device_id]`:
54+ 
55+| 参数名 | 描述 | 约束 |
56+| ---------- | ----------------------------------------- | ---------------------------- |
57+| `m` | 输入矩阵 $X$ 的行数(也即输出 $Y$ 的边长) | 正整数 |
58+| `k` | 输入矩阵 $X$ 的列数 | 正整数 |
59+| `deviceId` | 使用的 NPU 卡 ID(默认 0) | 在设备 NPU 有效范围内 |
60+ 
61+`SyrkOptions::Parse` 会将 problem shape 设为 `(m, m, k)`。
62+ 
63+## 算子支持范围
64+ 
65+| 参数 | 输入 / 输出 | 数据类型 | 维度 | 数据排布 |
66+| --- | --- | --- | --- | --- |
67+| X | 输入 | `bfloat16`,`float16` | `[M, K]` | `layout::RowMajor` |
68+| Y | 输出 | `bfloat16`,`float16` | `[M, M]` | `layout::RowMajor` |
69+ 
70+### 关键模板参数(本样例当前固定值)
longjihui
longjihuilongjihui8月28日

使用optest+atk进行测试维护时会参考泛化的dtype和layout,所以这里建议写明整个样例内可泛化的范围,不只是说明示例代码内的dtype/layout取值。

likedislike
void_ptr
27 天前 评论:
71+ 
72+| 模板参数 | 说明 | 本样例取值 |
73+| ---------- | ------------------ | --------------------- |
74+| `ElementX` | 输入矩阵数据类型 | `bfloat16_t` |
75+| `ElementY` | 输出矩阵数据类型 | `bfloat16_t` |
76+| `LayoutX` | 输入 $X$ 排布 | `layout::RowMajor` |
77+| `LayoutXt` | $X^{T}$ 视图排布 | `layout::ColumnMajor` |
78+| `LayoutY` | 输出 $Y$ 排布 | `layout::RowMajor` |
79+ 
80+## 实现方案
81+ 
82+算子整体沿用 Ascend950 Basic Matmul,并采用 `GemmIdentityBlockSwizzle` 调度。由于 $Y$ 为对称阵,只需计算下三角(含对角)基本块,规则如下:
83+ 
84+1. `blockCoord.m() < blockCoord.n()` 时,跳过该基本块。
85+2. `blockCoord.m() == blockCoord.n()` 时,计算后仅用 nz2nd 写入对角位置一次。
86+3. `blockCoord.m() > blockCoord.n()` 时,计算后双写:
87+ 
88+ - 使用 nz2nd 写入 `(blockCoord.m(), blockCoord.n())`;
89+ - 使用 nz2dn(转置)写入 `(blockCoord.n(), blockCoord.m())`。
90+ 
91+L0C→GM 双写路径使用 `M_FIX` 同步,不启用 unitFlag(一次 mmad 的 unitFlag 只能配对一次 Fixpipe)。
@@ -0,0 +1,137 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
L
Llh796327 天前

提供一下对应的atk交付件合入记录

likedislike
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#ifndef K_MAX_SHAPE_DIM
12+#define K_MAX_SHAPE_DIM 0
13+#endif
14+ 
15+#include "catlass/gemm/kernel/basic_syrk_tla.hpp"
16+ 
17+#include "catlass/arch/arch.hpp"
18+#include "catlass/catlass.hpp"
19+#include "catlass/gemm/block/block_mmad_syrk_tla.hpp"
20+#include "catlass/gemm/block/block_swizzle.hpp"
21+#include "catlass/gemm/device/device_gemm.hpp"
22+#include "catlass/layout/layout.hpp"
23+#include "catlass/status.hpp"
24+#include "tla/layout.hpp"
25+ 
26+#include "golden.hpp"
27+#include "helper.hpp"
28+ 
29+using namespace Catlass;
30+using namespace tla;
31+ 
32+using Options = SyrkOptions;
33+ 
34+static void Run(const Options& options)
35+{
36+ aclrtStream stream{nullptr};
37+ 
38+ ACL_CHECK(aclInit(nullptr));
39+ ACL_CHECK(aclrtSetDevice(options.deviceId));
40+ ACL_CHECK(aclrtCreateStream(&stream));
41+ 
42+ // Y = X * X^T, X: [M, K], Y: [M, M]
43+ uint32_t m = options.problemShape.m();
44+ uint32_t k = options.problemShape.k();
45+ 
46+ using ElementX = bfloat16_t;
47+ using ElementY = bfloat16_t;
48+ 
49+ // Host-side tags for golden only; device layouts are fixed inside BlockMmadSyrkTla.
50+ using LayoutTagX = layout::RowMajor;
51+ using LayoutTagXt = layout::ColumnMajor;
52+ using LayoutTagY = layout::RowMajor;
53+ 
54+ LayoutTagX tagX = LayoutTagX::MakeLayout<ElementX>(m, k);
55+ LayoutTagXt tagXt = LayoutTagXt::MakeLayout<ElementX>(k, m);
56+ LayoutTagY tagY = LayoutTagY::MakeLayout<ElementY>(m, m);
57+ 
58+ size_t lenX = tagX.Capacity();
59+ size_t lenY = tagY.Capacity();
60+ 
61+ size_t sizeX = lenX * sizeof(ElementX);
62+ size_t sizeY = lenY * sizeof(ElementY);
63+ 
64+ std::vector<bfloat16> hostX(lenX);
65+ golden::FillRandomData<bfloat16>(hostX, -5.0f, 5.0f);
66+ 
67+ uint8_t* deviceX{nullptr};
68+ ACL_CHECK(aclrtMalloc(reinterpret_cast<void**>(&deviceX), sizeX, ACL_MEM_MALLOC_HUGE_FIRST));
69+ ACL_CHECK(aclrtMemcpy(deviceX, sizeX, hostX.data(), sizeX, ACL_MEMCPY_HOST_TO_DEVICE));
70+ 
71+ uint8_t* deviceY{nullptr};
72+ ACL_CHECK(aclrtMalloc(reinterpret_cast<void**>(&deviceY), sizeY, ACL_MEM_MALLOC_HUGE_FIRST));
73+ 
74+ uint8_t* deviceWorkspace{nullptr};
75+ 
76+ auto aicCoreNum = platform_ascendc::PlatformAscendCManager::GetInstance()->GetCoreNumAic();
77+ 
78+ using L1TileShape = Shape<Int<256>, Int<256>, Int<128>>;
longjihui
longjihuilongjihui8月28日

在block组件里没看到相关约束,但这个算子的设计是不是有l1m=l1n的约束,同时根据l0c大小,l1/l0的m/n应该只能固定256?

likedislike
void_ptr
27 天前 评论:
longjihui
longjihui
26 天前 评论:
void_ptr
26 天前 评论:
longjihui
longjihui
26 天前 评论:
79+ using L0TileShape = Shape<Int<256>, Int<256>, Int<64>>;
80+ 
81+ // Layout tags are fixed by BlockMmadSyrkTla; use the default TileCopy.
82+ using BlockMmad = Gemm::Block::BlockMmadSyrkTla<L1TileShape, L0TileShape, ElementX, ElementY>;
83+ using BlockEpilogue = void;
84+ 
85+ uint32_t taskNum = CeilDiv(m, tla::get<0>(L1TileShape{})) * CeilDiv(m, tla::get<1>(L1TileShape{}));
86+ uint32_t aicCoreUsed = min(aicCoreNum, taskNum);
87+ 
88+ // Swizzle offset is 3 and direction is 1.
89+ using BlockScheduler = typename Gemm::Block::GemmIdentityBlockSwizzle<3, 1>;
90+ using MatmulKernel = Gemm::Kernel::BasicSyrkTla<BlockMmad, BlockEpilogue, BlockScheduler>;
91+ using MatmulAdapter = Gemm::Device::DeviceGemm<MatmulKernel>;
92+ 
93+ MatmulKernel::Arguments arguments{options.problemShape, deviceX, deviceY};
94+ 
95+ MatmulAdapter matmulOp;
96+ matmulOp.CanImplement(arguments);
97+ size_t sizeWorkspace = matmulOp.GetWorkspaceSize(arguments);
98+ if (sizeWorkspace > 0) {
99+ ACL_CHECK(aclrtMalloc(reinterpret_cast<void**>(&deviceWorkspace), sizeWorkspace, ACL_MEM_MALLOC_HUGE_FIRST));
100+ }
101+ matmulOp.Initialize(arguments, deviceWorkspace);
102+ matmulOp(stream, aicCoreUsed);
103+ ACL_CHECK(aclrtSynchronizeStream(stream));
104+ 
105+ std::vector<bfloat16> hostY(lenY);
106+ ACL_CHECK(aclrtMemcpy(hostY.data(), sizeY, deviceY, sizeY, ACL_MEMCPY_DEVICE_TO_HOST));
107+ 
108+ std::vector<float> hostGolden(lenY);
109+ golden::ComputeMatmul(options.problemShape, hostX, tagX, hostX, tagXt, hostGolden, tagY);
110+ 
111+ std::vector<uint64_t> errorIndices = golden::CompareData(hostY, hostGolden, k);
112+ if (errorIndices.empty()) {
113+ std::cout << "Compare success." << std::endl;
114+ } else {
115+ std::cerr << "Compare failed. Error count: " << errorIndices.size() << std::endl;
116+ }
117+ 
118+ ACL_CHECK(aclrtFree(deviceX));
119+ ACL_CHECK(aclrtFree(deviceY));
120+ if (sizeWorkspace > 0) {
121+ ACL_CHECK(aclrtFree(deviceWorkspace));
122+ }
123+ 
124+ ACL_CHECK(aclrtDestroyStream(stream));
125+ ACL_CHECK(aclrtResetDevice(options.deviceId));
126+ ACL_CHECK(aclFinalize());
127+}
128+ 
129+int main(int argc, const char** argv)
130+{
131+ Options options;
132+ if (options.Parse(argc, argv) != 0) {
133+ return -1;
134+ }
135+ Run(options);
136+ return 0;
137+}
@@ -179,6 +179,7 @@ set(EXAMPLE_ASCEND950
179 74_ascend950_weight_quant_a8w4_grouped_mx_matmul179 74_ascend950_weight_quant_a8w4_grouped_mx_matmul
180 80_ascend950_grouped_matmul_slice_m_gelu180 80_ascend950_grouped_matmul_slice_m_gelu
181 81_ascend950_rain_fusion_attention181 81_ascend950_rain_fusion_attention
182+ 82_ascend950_basic_syrk
182)183)
183 184 
184if(CATLASS_ARCH STREQUAL "2201")185if(CATLASS_ARCH STREQUAL "2201")
@@ -202,4 +202,43 @@ struct TrmmOptions {
202 }202 }
203};203};
204 204 
205+/**
206+ * @struct SyrkOptions
207+ * @brief Options structuture for syrk examples.
208+ * @brief Arguments: `example_name m k [device_id]`
209+ */
210+struct SyrkOptions {
211+ const std::string HELPER = "m k [device_id]";
212+ 
213+ Catlass::GemmCoord problemShape{128, 128, 128};
214+ int32_t deviceId{0};
215+ 
216+ SyrkOptions() = default;
217+ 
218+ int Parse(int argc, const char** argv)
219+ {
220+ enum class ArgsIndex
221+ {
222+ M_INDEX = 1,
223+ K_INDEX,
224+ DEVICE_ID_INDEX,
225+ ARGS_MAX
226+ };
227+ 
228+ if (argc > static_cast<uint32_t>(ArgsIndex::ARGS_MAX) ||
229+ argc < static_cast<uint32_t>(ArgsIndex::DEVICE_ID_INDEX)) {
230+ std::cerr << TOSTRING(CATLASS_EXAMPLE_NAME) << " " << HELPER << std::endl;
231+ return -1;
232+ }
233+ 
234+ problemShape.m() = std::atoi(argv[static_cast<uint32_t>(ArgsIndex::M_INDEX)]);
235+ problemShape.n() = std::atoi(argv[static_cast<uint32_t>(ArgsIndex::M_INDEX)]);
236+ problemShape.k() = std::atoi(argv[static_cast<uint32_t>(ArgsIndex::K_INDEX)]);
237+ if (argc == static_cast<uint32_t>(ArgsIndex::ARGS_MAX)) {
238+ deviceId = std::atoi(argv[static_cast<uint32_t>(ArgsIndex::DEVICE_ID_INDEX)]);
239+ }
240+ return 0;
241+ }
242+};
243+ 
205#endif244#endif
@@ -83,6 +83,7 @@ Python API (`torch_catlass.ops.*`)
83- [x] 67_ascend950_batched_matmul (Ascend950)83- [x] 67_ascend950_batched_matmul (Ascend950)
84- [x] 73_ascend950_matmul_full_loadA (Ascend950)84- [x] 73_ascend950_matmul_full_loadA (Ascend950)
85- [x] 74_ascend950_weight_quant_a8w4_grouped_mx_matmul (Ascend950)85- [x] 74_ascend950_weight_quant_a8w4_grouped_mx_matmul (Ascend950)
86+- [x] 82_ascend950_basic_syrk (Ascend950)
86 87 
87### 暂未接入88### 暂未接入
88 89 
@@ -516,6 +516,15 @@ void Ascend950MatmulFullLoadA(
516 */516 */
517void Symm(const uint32_t blockNum, aclrtStream stream, const TParams& tParams, const SymmParams& params);517void Symm(const uint32_t blockNum, aclrtStream stream, const TParams& tParams, const SymmParams& params);
518 518 
519+/**
520+ * @brief JIT interface for example 82_ascend950_basic_syrk.
521+ *
522+ * Y = X * X^T. ``params.m`` must equal ``params.n``; only ``inputAddr[0]`` (X) and
523+ * ``outputAddr[0]`` (Y) are consumed.
524+ */
525+void Ascend950BasicSyrk(
526+ const uint32_t blockNum, aclrtStream stream, const TParams& tParams, const MatmulParams& params);
527+ 
519/**528/**
520 * @brief Reserved JIT interface for example 74_ascend950_weight_quant_a8w4_grouped_mx_matmul.529 * @brief Reserved JIT interface for example 74_ascend950_weight_quant_a8w4_grouped_mx_matmul.
521 *530 *
@@ -0,0 +1,15 @@
1+# -----------------------------------------------------------------------------------------------------------
2+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+# CANN Open Software License Agreement Version 2.0 (the "License").
5+# Please refer to the License for details. You may not use this file except in compliance with the License.
6+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+# See LICENSE in the root of the software repository for the full text of the License.
9+# -----------------------------------------------------------------------------------------------------------
10+ 
11+add_kernel(NAME ascend950_basic_syrk
12+ NPU_ARCH_LIST 3510
13+ KERNEL_TYPE jit
14+ ${CMAKE_CURRENT_SOURCE_DIR}/ascend950_basic_syrk.cpp
15+ TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/ascend950_basic_syrk_impl.cpp)
@@ -0,0 +1,37 @@
1+/**
2+ * This program is free software, you can redistribute it and/or modify.
3+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+ * This file is a part of the CANN Open Software.
5+ * Licensed under CANN Open Software License Agreement Version 2.0 (the "License").
6+ * Please refer to the License for details. You may not use this file except in compliance with the License.
7+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
8+ * BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. See LICENSE in the root of
9+ * the software repository for the full text of the License.
10+ */
11+ 
12+#include "catlass_kernel.h"
13+#include "jit_compiler.h"
14+#include "jit_macro_generator.h"
15+ 
16+namespace CatlassKernel {
17+ 
18+/**
19+ * @brief example 82_ascend950_basic_syrk: Resolve and launch JIT BasicSyrkTla.
20+ *
21+ * Y = X * X^T. Runtime params reuse MatmulParams with m == n (output is square).
22+ */
23+extern "C" void Ascend950BasicSyrk(
24+ const uint32_t blockNum, aclrtStream stream, const TParams& tParams, const MatmulParams& params)
25+{
26+ auto macros = JitMacroGenerator<TParams>::generate("ascend950_basic_syrk", tParams);
27+ // Match example: GemmIdentityBlockSwizzle<3, 1>
28+ macros["CATLASS_JIT_BLOCK_SCHEDULER"] = "31";
29+ auto* entry =
30+ JitCompiler::instance().getKernel("ascend950_basic_syrk_impl.cpp", macros, JitKernelType::AIC);
31+ if (entry) {
32+ entry(blockNum, stream, &params);
33+ }
34+ aclrtSynchronizeStream(stream);
35+}
36+ 
37+} // namespace CatlassKernel
@@ -0,0 +1,65 @@
1+/**
2+ * This program is free software, you can redistribute it and/or modify.
3+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+ * This file is a part of the CANN Open Software.
5+ * Licensed under CANN Open Software License Agreement Version 2.0 (the "License").
6+ * Please refer to the License for details. You may not use this file except in compliance with the License.
7+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
8+ * BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. See LICENSE in the root of
9+ * the software repository for the full text of the License.
10+ */
11+ 
12+#ifndef K_MAX_SHAPE_DIM
13+#define K_MAX_SHAPE_DIM 0
14+#endif
15+ 
16+#include "catlass/arch/arch.hpp"
17+#include "catlass/catlass.hpp"
18+#include "catlass/gemm/block/block_mmad_syrk_tla.hpp"
19+#include "catlass/gemm/block/block_swizzle.hpp"
20+#include "catlass/gemm/kernel/basic_syrk_tla.hpp"
21+#include "catlass/gemm_coord.hpp"
22+#include "catlass/layout/layout.hpp"
23+#include "tla/layout.hpp"
24+#include "tla/tensor.hpp"
25+ 
26+#include "../common/common.h"
27+#include "catlass_kernel.h"
28+#include "common/kernel_runner.h"
29+ 
30+// Device/kernel types are fixed by BlockMmadSyrkTla; only element dtypes are JIT-configurable.
31+#ifndef CATLASS_JIT_ELEMENT_A
32+#define CATLASS_JIT_ELEMENT_A bfloat16_t
33+#endif
34+#ifndef CATLASS_JIT_ELEMENT_C
35+#define CATLASS_JIT_ELEMENT_C bfloat16_t
36+#endif
37+ 
38+using namespace Catlass;
39+using namespace tla;
40+ 
41+using ElementX = CATLASS_JIT_ELEMENT_A;
42+using ElementY = CATLASS_JIT_ELEMENT_C;
43+ 
44+using L1TileShape = tuple<C<256>, C<256>, C<128>>;
45+using L0TileShape = tuple<C<256>, C<256>, C<64>>;
46+ 
47+using BlockMmad = Gemm::Block::BlockMmadSyrkTla<L1TileShape, L0TileShape, ElementX, ElementY>;
48+using BlockEpilogue = void;
49+ 
50+#ifndef CATLASS_JIT_BLOCK_SCHEDULER
51+#define CATLASS_JIT_BLOCK_SCHEDULER 31
52+#endif
53+using BlockScheduler = typename Gemm::Block::GemmIdentityBlockSwizzle<
54+ (CATLASS_JIT_BLOCK_SCHEDULER / 10), (CATLASS_JIT_BLOCK_SCHEDULER % 10)>;
55+ 
56+using MatmulKernel = Gemm::Kernel::BasicSyrkTla<BlockMmad, BlockEpilogue, BlockScheduler>;
57+ 
58+extern "C" void run(uint32_t blockNum, aclrtStream stream, const CatlassKernel::MatmulParams* params)
59+{
60+ typename MatmulKernel::Arguments arguments{
61+ GemmCoord{params->m, params->n, params->k},
62+ params->inputAddr[0],
63+ params->outputAddr[0]};
64+ Catlass::RunKernel<MatmulKernel>(arguments, stream, blockNum);
65+}
@@ -283,6 +283,7 @@ add_subdirectory(77_planar_complex_matmul)
283add_subdirectory(78_matrix_inverse)283add_subdirectory(78_matrix_inverse)
284add_subdirectory(80_grouped_matmul_slice_m_gelu)284add_subdirectory(80_grouped_matmul_slice_m_gelu)
285add_subdirectory(81_ascend950_rain_fusion_attention)285add_subdirectory(81_ascend950_rain_fusion_attention)
286+add_subdirectory(82_ascend950_basic_syrk)
286 287 
287if(_JIT_KERNEL_ENTRIES)288if(_JIT_KERNEL_ENTRIES)
288 set(_JIT_TARGET catlass_kernel_jit)289 set(_JIT_TARGET catlass_kernel_jit)
@@ -49,6 +49,7 @@
49#include "template/a8w4_mx_matmul.h"49#include "template/a8w4_mx_matmul.h"
50#include "template/a8w4_grouped_mx_matmul.h"50#include "template/a8w4_grouped_mx_matmul.h"
51#include "template/svd_quant_matmul.h"51#include "template/svd_quant_matmul.h"
52+#include "template/basic_syrk.h"
52#include "template/trmm.h"53#include "template/trmm.h"
53#include "template/conv_bias.h"54#include "template/conv_bias.h"
54#include "template/symm.h"55#include "template/symm.h"
@@ -429,4 +430,9 @@ REGISTER_TORCH_FUNC(planar_complex_matmul);
429static auto& matrix_inverse = MatrixInverseOp::Run;430static auto& matrix_inverse = MatrixInverseOp::Run;
430REGISTER_TORCH_FUNC(matrix_inverse);431REGISTER_TORCH_FUNC(matrix_inverse);
431 432 
433+// ── example 82_ascend950_basic_syrk ──
434+using Ascend950BasicSyrkOp = BasicSyrkLike<CatlassKernel::Ascend950BasicSyrk>;
435+static auto& ascend950_basic_syrk = Ascend950BasicSyrkOp::Run;
436+REGISTER_TORCH_FUNC(ascend950_basic_syrk);
437+ 
432} // namespace CatlassKernelWrapper438} // namespace CatlassKernelWrapper
@@ -0,0 +1,78 @@
1+/**
2+ * This program is free software, you can redistribute it and/or modify.
3+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+ * This file is a part of the CANN Open Software.
5+ * Licensed under CANN Open Software License Agreement Version 2.0 (the "License").
6+ * Please refer to the License for details. You may not use this file except in compliance with the License.
7+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
8+ * BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. See LICENSE in the root of
9+ * the software repository for the full text of the License.
10+ */
11+ 
12+#ifndef OPTEST_BASIC_SYRK_H
13+#define OPTEST_BASIC_SYRK_H
14+ 
15+#include <torch/torch.h>
16+#include <tiling/platform/platform_ascendc.h>
17+ 
18+#include "catlass_kernel_jit.h"
19+#include "common/run_npu_func.h"
20+#include "torch_utils.h"
21+#include "type_utils.hpp"
22+ 
23+namespace CatlassKernelWrapper {
24+ 
25+using BasicSyrkKernelFn =
26+ void (*)(const uint32_t, aclrtStream, const CatlassKernel::TParams&, const CatlassKernel::MatmulParams&);
27+ 
28+/**
29+ * @brief Torch adapter for Y = X * X^T (example 82_ascend950_basic_syrk).
30+ *
31+ * Reuses MatmulParams with m == n; only inputAddr[0] (X) and outputAddr[0] (Y) are used.
32+ */
33+template <BasicSyrkKernelFn KernelFunc>
34+struct BasicSyrkLike {
35+ using OutputType = at::Tensor;
36+ 
37+ static OutputType Run(const at::Tensor& matX, const c10::ScalarType& outDType)
38+ {
39+ TORCH_CHECK(matX.dim() == 2, "ascend950_basic_syrk expects a 2-D input X of shape (M, K)");
40+ TORCH_CHECK(
41+ matX.scalar_type() == at::kBFloat16 || matX.scalar_type() == at::kHalf,
42+ "ascend950_basic_syrk currently supports bfloat16 / float16 inputs");
43+ TORCH_CHECK(
44+ outDType == at::kBFloat16 || outDType == at::kHalf,
45+ "ascend950_basic_syrk currently supports bfloat16 / float16 output");
46+ 
47+ CatlassKernel::TParams tParams;
48+ CatlassKernel::MatmulParams params;
49+ 
50+ // ELEMENT_A → X, ELEMENT_C → Y (layouts are fixed inside BlockMmadSyrkTla).
51+ tParams.element["A"] = TorchDtypeToAclDtype(matX.scalar_type());
52+ tParams.element["C"] = TorchDtypeToAclDtype(outDType);
53+ tParams.transpose["A"] = false;
54+ tParams.transpose["C"] = false;
55+ tParams.useNz["A"] = false;
56+ tParams.useNz["C"] = false;
57+ 
58+ params.m = static_cast<uint32_t>(matX.size(0));
59+ params.k = static_cast<uint32_t>(matX.size(1));
60+ params.n = params.m; // Y is [M, M]
61+ 
62+ params.inputAddr.resize(1);
63+ params.inputAddr[0] = static_cast<uint8_t*>(const_cast<void*>(matX.storage().data()));
64+ 
65+ OutputType output = GetOutputTensor({params.m, params.n}, AclDtypeToTorchDtype(tParams.elem("C")));
66+ params.outputAddr.resize(1);
67+ params.outputAddr[0] = static_cast<uint8_t*>(const_cast<void*>(output.storage().data()));
68+ 
69+ aclrtStream stream = c10_npu::getCurrentNPUStream().stream(false);
70+ uint32_t aicCoreNum = platform_ascendc::PlatformAscendCManager::GetInstance()->GetCoreNumAic();
71+ RUN_NPU_FUNC(KernelFunc, aicCoreNum, stream, tParams, params);
72+ return output;
73+ }
74+};
75+ 
76+} // namespace CatlassKernelWrapper
77+ 
78+#endif // OPTEST_BASIC_SYRK_H
@@ -0,0 +1,35 @@
1+# This program is free software, you can redistribute it and/or modify.
2+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+# This file is a part of the CANN Open Software.
4+# Licensed under CANN Open Software License Agreement Version 2.0 (the "License").
5+# Please refer to the License for details. You may not use this file except in compliance with the License.
6+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
7+# BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. See LICENSE in the root of
8+# the software repository for the full text of the License.
9+ 
10+import pytest
11+import torch
12+import torch_npu
13+import torch_catlass
14+ 
15+from common import only_on_3510
16+ 
17+ 
18+@only_on_3510
19+@pytest.mark.parametrize("dtype", [torch.bfloat16, torch.float16], ids=["bf16", "fp16"])
20+@pytest.mark.parametrize("m,k", [(256, 256), (512, 128), (300, 200)])
21+def test_ascend950_basic_syrk(m, k, dtype):
22+ """Compare CATLASS Ascend950 Basic SYRK against torch.matmul(X, X.T)."""
23+ x = torch.randn(m, k, dtype=dtype, device="npu")
24+ 
25+ result = torch_catlass.ascend950_basic_syrk(x)
26+ expected = torch.matmul(x.float(), x.float().T).to(dtype)
27+ 
28+ assert result.shape == (m, m)
29+ assert result.dtype == dtype
30+ assert result.device.type == "npu"
31+ assert torch.allclose(result.float(), expected.float(), rtol=1e-2, atol=1e-2)
32+ 
33+ 
34+if __name__ == "__main__":
35+ pytest.main([__file__, "-v", "-s"])
@@ -85,6 +85,7 @@ __all__ = [
85 "ascend950_rain_fusion_attention",85 "ascend950_rain_fusion_attention",
86 "clear_jit_cache",86 "clear_jit_cache",
87 "symm",87 "symm",
88+ "ascend950_basic_syrk",
88 "__version__",89 "__version__",
89 "__catlass_version__",90 "__catlass_version__",
90]91]
@@ -119,6 +119,7 @@ from .w4a8_matmul import w4a8_matmul # example 32
119from .w8a16_matmul import w8a16_matmul # example 30119from .w8a16_matmul import w8a16_matmul # example 30
120from .grouped_matmul_slice_m_gelu import grouped_matmul_slice_m_gelu # example 80120from .grouped_matmul_slice_m_gelu import grouped_matmul_slice_m_gelu # example 80
121from .matrix_inverse import matrix_inverse # example 78121from .matrix_inverse import matrix_inverse # example 78
122+from .ascend950_basic_syrk import ascend950_basic_syrk # example 82
122 123 
123__all__ = [124__all__ = [
124 "basic_matmul", # example 00125 "basic_matmul", # example 00
@@ -206,4 +207,5 @@ __all__ = [
206 "planar_complex_matmul", # example 77207 "planar_complex_matmul", # example 77
207 "grouped_matmul_slice_m_gelu", # example 80208 "grouped_matmul_slice_m_gelu", # example 80
208 "matrix_inverse", # example 78209 "matrix_inverse", # example 78
210+ "ascend950_basic_syrk", # example 82
209]211]
@@ -0,0 +1,33 @@
1+import torch
2+from torch import Tensor
3+ 
4+ 
5+def ascend950_basic_syrk(
6+ matX: Tensor,
7+ outDType: str | torch.dtype | None = None,
8+) -> Tensor:
9+ """Run CATLASS Ascend950 Basic SYRK: ``Y = X @ X.T``.
10+ 
11+ Source: example 82_ascend950_basic_syrk.
12+ 
13+ Args:
14+ matX: Input matrix ``X`` with shape ``(M, K)`` on NPU.
15+ outDType: Output dtype. Defaults to ``matX.dtype``. Accepted strings
16+ include ``float16`` / ``fp16`` and ``bfloat16`` / ``bf16``.
17+ 
18+ Returns:
19+ Output tensor ``Y`` with shape ``(M, M)`` on the active NPU device.
20+ """
21+ if outDType is None:
22+ outDType = matX.dtype
23+ if isinstance(outDType, str):
24+ dtype_lower = outDType.lower()
25+ if dtype_lower in ("bf16", "bfloat16"):
26+ outDType = torch.bfloat16
27+ elif dtype_lower in ("fp16", "float16"):
28+ outDType = torch.float16
29+ else:
30+ outDType = getattr(torch, dtype_lower, None)
31+ if outDType is None:
32+ raise ValueError(f"{outDType} is not a data type of torch")
33+ return torch.ops.catlass.ascend950_basic_syrk(matX, outDType)
@@ -845,6 +845,7 @@ normal_cases_3510 = [
845 "64_ascend950_matmul_evg_add_ub 256 512 1024 0",845 "64_ascend950_matmul_evg_add_ub 256 512 1024 0",
846 "68_ascend950_multi_core_splitk_matmul 256 512 1024 0",846 "68_ascend950_multi_core_splitk_matmul 256 512 1024 0",
847 "69_ascend950_tail_multi_core_splitk_matmul 256 512 1024 0",847 "69_ascend950_tail_multi_core_splitk_matmul 256 512 1024 0",
848+ "82_ascend950_basic_syrk 1024 1024 0",
848]849]
849 850 
850 851