已合并
Feat:add aclblasLtMatmulAlgo init/configSetAttr/configGetAttr func #248
Feat:add aclblasLtMatmulAlgo init/configSetAttr/configGetAttr func #248
已合并
wangzitao创建于 7月2日
59 个文件变更+3843-2354
@@ -86,10 +86,17 @@ set_source_files_properties(
86 PROPERTIES LANGUAGE ASC86 PROPERTIES LANGUAGE ASC
87)87)
88 88 
89+# Device kernels compile with the ASC frontend; all host code (api / internal / engine / *_host.cpp)
90+# compiles as plain C++. The classification is produced by blasLt/CMakeLists.txt, so no per-file
91+# language whitelist is maintained here.
89set_source_files_properties(92set_source_files_properties(
90- ${ALL_BLASLT_SRC_FILES}93+ ${ALL_BLASLT_KERNEL_SRC_FILES}
91 PROPERTIES LANGUAGE ASC94 PROPERTIES LANGUAGE ASC
92)95)
96+set_source_files_properties(
97+ ${ALL_BLASLT_HOST_SRC_FILES}
98+ PROPERTIES LANGUAGE CXX
99+)
93 100 
94set(_OPS_BLAS_ASC_ROOT "${ASCEND_CANN_PACKAGE_PATH}/${CMAKE_SYSTEM_PROCESSOR}-linux/asc")101set(_OPS_BLAS_ASC_ROOT "${ASCEND_CANN_PACKAGE_PATH}/${CMAKE_SYSTEM_PROCESSOR}-linux/asc")
95target_include_directories(${OPS_BLASLT} PRIVATE102target_include_directories(${OPS_BLASLT} PRIVATE
@@ -112,22 +119,21 @@ if(EXISTS "${_OPS_BLAS_ASC_ROOT}/include")
112 )119 )
113endif()120endif()
114 121 
122+# Private include roots for the refactored blasLt layout. internal/ headers are never installed;
123+# each operator's arch dir exposes its co-located host/kernel/tiling headers.
115target_include_directories(${OPS_BLASLT} PRIVATE124target_include_directories(${OPS_BLASLT} PRIVATE
116- ${CMAKE_CURRENT_LIST_DIR}/blasLt/include125+ ${CMAKE_CURRENT_LIST_DIR}/blasLt/internal/include
117- ${CMAKE_CURRENT_LIST_DIR}/blasLt/include/kernel126+ ${CMAKE_CURRENT_LIST_DIR}/blasLt/common/helper
118- ${CMAKE_CURRENT_LIST_DIR}/blasLt/include/host127+ ${CMAKE_CURRENT_LIST_DIR}/blasLt/matmul/engine
119- ${CMAKE_CURRENT_LIST_DIR}/blasLt/utils128+ ${CMAKE_CURRENT_LIST_DIR}/blasLt/matmul/common
129+ ${CMAKE_CURRENT_LIST_DIR}/blasLt/matmul/fp32/arch35
130+ ${CMAKE_CURRENT_LIST_DIR}/blasLt/matmul/mxfp8/arch35
131+ ${CMAKE_CURRENT_LIST_DIR}/blasLt/matmul/mxfp4/arch35
132+ ${CMAKE_CURRENT_LIST_DIR}/blasLt/matmul/epilogue/arch35
133+ ${CMAKE_CURRENT_LIST_DIR}/blasLt/matrix_transform/engine
134+ ${CMAKE_CURRENT_LIST_DIR}/blasLt/matrix_transform/arch35
120)135)
121 136 
122-if(ENABLE_BLASLT_MXFP8)
123- set_source_files_properties(
124- ${CMAKE_CURRENT_LIST_DIR}/blasLt/matmul_mxfp8/arch35/matmul_mxfp8_host.cpp
125- ${CMAKE_CURRENT_LIST_DIR}/blasLt/matmul_mxfp4/arch35/matmul_mxfp4_host.cpp
126- PROPERTIES LANGUAGE CXX)
127-endif()
128-set_source_files_properties(
129- ${CMAKE_CURRENT_LIST_DIR}/blasLt/epilogue/arch35/epilogue_alpha_beta_host.cpp
130- PROPERTIES LANGUAGE CXX)
131target_compile_definitions(${OPS_BLASLT} PRIVATE137target_compile_definitions(${OPS_BLASLT} PRIVATE
132 ASC_DEVKIT_MAJOR=${ASC_DEVKIT_MAJOR}138 ASC_DEVKIT_MAJOR=${ASC_DEVKIT_MAJOR}
133 ASC_DEVKIT_MINOR=${ASC_DEVKIT_MINOR})139 ASC_DEVKIT_MINOR=${ASC_DEVKIT_MINOR})
@@ -10,52 +10,54 @@
10 10 
11cmake_minimum_required(VERSION 3.16)11cmake_minimum_required(VERSION 3.16)
12 12 
13-# 收集当前目录下的基础源文件13+# Collect every blasLt source, then classify by role so the parent scope can assign compile
14-file(GLOB BASE_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)14+# languages without maintaining a per-file whitelist:
15+# *_kernel.cpp -> ASC (device kernels)
16+# api / internal / engine / *_host.cpp -> CXX (pure host code)
17+# Architecture-specific operator code lives under <op>/<variant>/<archXX>/; only the arch dirs
18+# matching the current SOC (SOC_ARCH_DIRS) are compiled.
19+file(GLOB_RECURSE BLASLT_ALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
15 20 
16-# 自动收集所有算子目录的源文件(支持子目录中的架构特定目录)21+set(BLASLT_HOST_SRCS "")
17-set(OP_SRC_FILES "")22+set(BLASLT_KERNEL_SRCS "")
18-file(GLOB children RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)23+ 
19-foreach(child ${children})24+foreach(src_file ${BLASLT_ALL_SRCS})
20- if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${child})25+ # Skip sources under an architecture directory that does not match the current SOC.
21- # 递归收集非架构特定源码,避免与 archXX 目录重复收集26+ set(is_arch_specific FALSE)
22- file(GLOB_RECURSE dir_srcs ${CMAKE_CURRENT_SOURCE_DIR}/${child}/*.cpp)27+ foreach(arch_dir ${ARCH_SPECIFIC_DIRS})
23- foreach(src_file ${dir_srcs})28+ if(src_file MATCHES "/${arch_dir}/")
24- set(is_arch_specific FALSE)29+ set(is_arch_specific TRUE)
25- foreach(arch_dir ${ARCH_SPECIFIC_DIRS})30+ endif()
26- if(src_file MATCHES "/${arch_dir}/")31+ endforeach()
27- set(is_arch_specific TRUE)32+ set(arch_selected FALSE)
28- break()33+ foreach(arch_dir ${SOC_ARCH_DIRS})
29- endif()34+ if(src_file MATCHES "/${arch_dir}/")
30- endforeach()35+ set(arch_selected TRUE)
31- if(NOT is_arch_specific)36+ endif()
32- list(APPEND OP_SRC_FILES ${src_file})37+ endforeach()
33- endif()38+ 
34- endforeach()39+ if(is_arch_specific AND NOT arch_selected)
40+ # architecture directory for another SOC: do not compile it here
41+ elseif(src_file MATCHES "_kernel\\.cpp$")
42+ list(APPEND BLASLT_KERNEL_SRCS ${src_file})
43+ else()
44+ list(APPEND BLASLT_HOST_SRCS ${src_file})
35 endif()45 endif()
36endforeach()46endforeach()
37 47 
38-# 收集子目录中的架构特定源文件48+# MXFP8/MXFP4 require asc-devkit >= 9.1 (ENABLE_BLASLT_MXFP8). Exclude the whole precision subtrees
39-set(ARCH_SRC_FILES "")49+# rather than filtering individual files.
40-foreach(arch_dir ${SOC_ARCH_DIRS})
41- file(GLOB_RECURSE arch_dir_srcs
42- ${CMAKE_CURRENT_SOURCE_DIR}/*/${arch_dir}/*.cpp
43- )
44- list(APPEND ARCH_SRC_FILES ${arch_dir_srcs})
45-endforeach()
46- 
47-# MXFP8 需要 asc-devkit >= 9.1(ASC_DEVKIT_MAJOR >= 9 && ASC_DEVKIT_MINOR > 0)
48if(NOT ENABLE_BLASLT_MXFP8)50if(NOT ENABLE_BLASLT_MXFP8)
49- list(FILTER ARCH_SRC_FILES EXCLUDE REGEX "/matmul_mxfp8/")51+ list(FILTER BLASLT_HOST_SRCS EXCLUDE REGEX "/mxfp8/")
50- list(FILTER ARCH_SRC_FILES EXCLUDE REGEX "/matmul_mxfp4/")52+ list(FILTER BLASLT_HOST_SRCS EXCLUDE REGEX "/mxfp4/")
53+ list(FILTER BLASLT_KERNEL_SRCS EXCLUDE REGEX "/mxfp8/")
54+ list(FILTER BLASLT_KERNEL_SRCS EXCLUDE REGEX "/mxfp4/")
51 message(55 message(
52 STATUS56 STATUS
53 "Skipping blasLt MXFP8/MXFP4 (requires asc-devkit >= 9.1, current ${ASC_DEVKIT_MAJOR}.${ASC_DEVKIT_MINOR})")57 "Skipping blasLt MXFP8/MXFP4 (requires asc-devkit >= 9.1, current ${ASC_DEVKIT_MAJOR}.${ASC_DEVKIT_MINOR})")
54endif()58endif()
55 59 
56-set(ALL_BLASLT_SRC_FILES60+# Exported to the parent scope; the top-level CMakeLists assigns LANGUAGE per list.
57- ${BASE_SRC_FILES}61+set(ALL_BLASLT_SRC_FILES ${BLASLT_HOST_SRCS} ${BLASLT_KERNEL_SRCS} PARENT_SCOPE)
58- ${OP_SRC_FILES}62+set(ALL_BLASLT_HOST_SRC_FILES ${BLASLT_HOST_SRCS} PARENT_SCOPE)
59- ${ARCH_SRC_FILES}63+set(ALL_BLASLT_KERNEL_SRC_FILES ${BLASLT_KERNEL_SRCS} PARENT_SCOPE)
60- PARENT_SCOPE
61-)
@@ -1,334 +1,265 @@
1-## aclblasLtMatmul 接口实现1+# LtMatmul算子
2 2 
3-## 概述3+## 算子概述
4 4 
5-BLAS Lt 矩阵乘法(`aclblasLtMatmul`接口实现与精度测试5+LtMatmul 算子实现了 BLAS Lt 通用矩阵乘法,核心运算为 D = alpha * op(A) * op(B) + beta * C。其中 A、B 为输入矩阵,C 为累加矩阵,D 为输出矩阵,alpha 和 beta 为标量,op(A)/op(B) 支持不转置N和转置(T)。当前实现支持 FP32、MXFP8(E4M3FN)、MXFP4(E2M1)三种输入类型组合,输出支持 FP32 和 BF16
6 6 
7-`aclblasLtMatmul` 实现了通用矩阵乘法运算,对应的数学表达式7+数学表达式:
8 8 
9```9```
10D = alpha * op(A) * op(B) + beta * C10D = alpha * op(A) * op(B) + beta * C
11```11```
12 12 
13-其中 A、B 为输入矩阵,C 为累加矩阵,D 为输出矩阵,alpha 和 beta 为标量,op(A)/op(B) 支持不转置(N)和转置(T)。当前实现支持 FP32、MXFP8(E4M3FN)、MXFP4(E2M1)三种输入类型组合,输出支持 FP32 和 BF16。13+包含以下接口:
14 14 
15-## 产品支持情况15+| 接口名 | 功能简述 |
16+|--------|---------|
17+| aclblasLtMatmul | 通用矩阵乘法(支持 FP32 / MXFP8 / MXFP4 路径) |
16 18 
17-| 产品 | 是否支持 |19+## 算子执行接口
18-| :----------------------------------------------------------- |:-------:|20+ 
19-| <term>Ascend 950PR/Ascend 950DT</term> | ✓ |21+### aclblasLtMatmul
20-| <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term> | ✗ |22+ 
21-| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | ✗ |23+#### 产品支持情况
24+ 
25+- Ascend 950PR / Ascend 950DT:支持
26+- Atlas A3 训练系列产品 / Atlas A3 推理系列产品:不支持
27+- Atlas A2 训练系列产品 / Atlas A2 推理系列产品:不支持
22 28 
23> MXFP8/MXFP4 量化路径依赖 CANN asc-devkit >= 9.1(`ASC_DEVKIT_MAJOR >= 9 && ASC_DEVKIT_MINOR >= 1`)。29> MXFP8/MXFP4 量化路径依赖 CANN asc-devkit >= 9.1(`ASC_DEVKIT_MAJOR >= 9 && ASC_DEVKIT_MINOR >= 1`)。
24 30 
25-## 目录结构介绍31+#### 函数原型
26 32 
27-接口实现位于 `blasLt/`33+```cpp
28- 34+aclblasStatus_t aclblasLtMatmul(aclblasLtHandle_t lightHandle, aclblasLtMatmulDesc_t computeDesc, const void* alpha, const void* A, aclblasLtMatrixLayout_t Adesc, const void* B, aclblasLtMatrixLayout_t Bdesc, const void* beta, const void* C, aclblasLtMatrixLayout_t Cdesc, void* D, aclblasLtMatrixLayout_t Ddesc, const aclblasLtMatmulAlgo_t* algo, void* workspace, size_t workspaceSizeInBytes, aclrtStream stream)
29-```
30-blasLt/
31-├── aclblasLt.cpp // aclBLASLt 库入口,含 aclblasLtMatmul 路由
32-├── matmul_fp32/arch35/
33-│ ├── matmul_fp32_host.cpp // FP32 Host 侧 Tiling
34-│ └── matmul_fp32_kernel.cpp // FP32 Kernel 侧实现
35-├── matmul_mxfp8/arch35/
36-│ ├── matmul_mxfp8_host.cpp // MXFP8 Host 侧 Tiling
37-│ └── matmul_mxfp8_kernel.cpp // MXFP8 Kernel 侧实现
38-├── matmul_mxfp4/arch35/
39-│ ├── matmul_mxfp4_host.cpp // MXFP4 Host 侧 Tiling
40-│ └── matmul_mxfp4_kernel.cpp // MXFP4 Kernel 侧实现
41-└── utils/
42- └── kernel_utils.h // shared kernel helpers
43```35```
44 36 
45-测试代码位于 `test/blasLtMatmul/`:37+#### 参数说明
46 38 
39+| 参数名 | 输入/输出 | 参数类型 | 说明 |
40+|--------|----------|---------|------|
41+| lightHandle | 输入 | aclblasLtHandle_t | aclBLASLt 库上下文句柄,由 `aclblasLtCreate` 创建,不可为 NULL,否则返回 `ACLBLAS_STATUS_NOT_INITIALIZED`,Host 内存 |
42+| computeDesc | 输入 | aclblasLtMatmulDesc_t | 矩阵乘法描述符,设置 transA/transB、epilogue、scale 指针等属性,不可为 NULL,Host 内存 |
43+| alpha | 输入 | const void*(FP32) | 用于乘法的 float 标量指针,不可为 NULL,Host 内存 |
44+| A | 输入 | const void* | 输入矩阵 A,数据类型由 Adesc 指定,m>0 且 n>0 时不可为 NULL,Device 内存 |
45+| Adesc | 输入 | aclblasLtMatrixLayout_t | 矩阵 A 的 layout 描述符(rows/cols/ld/order/dtype),Host 内存 |
46+| B | 输入 | const void* | 输入矩阵 B,数据类型由 Bdesc 指定,m>0 且 n>0 时不可为 NULL,Device 内存 |
47+| Bdesc | 输入 | aclblasLtMatrixLayout_t | 矩阵 B 的 layout 描述符,Host 内存 |
48+| beta | 输入 | const void*(FP32) | 用于累加的 float 标量指针,不可为 NULL,beta=0 时 C 可不参与计算,Host 内存 |
49+| C | 输入 | const void* | 累加矩阵 C,beta=0 时可为 NULL,Device 内存 |
50+| Cdesc | 输入 | aclblasLtMatrixLayout_t | 矩阵 C 的 layout 描述符,Host 内存 |
51+| D | 输出 | void* | 输出矩阵 D,维度 m x n,m>0 且 n>0 时不可为 NULL,Device 内存 |
52+| Ddesc | 输入 | aclblasLtMatrixLayout_t | 矩阵 D 的 layout 描述符,指定输出数据类型(FP32 或 BF16),Host 内存 |
53+| algo | 输入 | const aclblasLtMatmulAlgo_t* | 算法描述符,可为 NULL(使用默认算法),Host 内存 |
54+| workspace | 输入 | void* | 工作空间内存,可为 NULL,非 NULL 时需 16B 对齐,Device 内存 |
55+| workspaceSizeInBytes | 输入 | size_t | 工作空间大小(字节),Host 内存 |
56+| stream | 输入 | aclrtStream | AscendCL 执行流,Host 内存 |
57+ 
58+#### 约束说明
59+ 
60+- M、N、K >= 0;M=0 或 N=0 时为空操作,直接返回 `ACLBLAS_STATUS_SUCCESS`
61+- dtypeA / dtypeB 须为同类型组合:FP32×FP32、MXFP8×MXFP8、MXFP4×MXFP4;其他组合返回 `ACLBLAS_STATUS_NOT_SUPPORTED`
62+- dtypeC 当前固定为 FP32
63+- dtypeD 支持 FP32 或 BF16;MXFP8/MXFP4 路径支持 FP32 或 BF16 输出,FP32 路径输出 FP32
64+- computeType 当前仅支持 `ACLBLAS_COMPUTE_32F`
65+- transA / transB 支持 N、T,对应 `ACLBLAS_OP_N`(不转置)、`ACLBLAS_OP_T`(转置)
66+- MXFP8/MXFP4 路径要求 K 为 32 的整数倍,否则返回 `ACLBLAS_STATUS_INVALID_VALUE`
67+- order 当前仅支持 `ACLBLASLT_ORDER_ROW`;lda / ldb / ldc / ldd 须 >= 矩阵物理列数;MXFP4 的 ld 为逻辑元素 leading dim(2 个 FP4 元素打包为 1 字节)
68+- epilogue 当前仅支持 `ACLBLASLT_EPILOGUE_DEFAULT`
69+- MXFP8/MXFP4 路径须通过 computeDesc 设置 scaleA / scaleB(`ACLBLASLT_MATMUL_DESC_A/B_SCALE_POINTER`),E8M0 格式,按 K 方向每 32 元素一组,不可为 NULL
70+- algo 可为 NULL,使用默认算法
71+- workspace 非 NULL 时需 16B 对齐;algo 非 NULL 时 workspaceSizeInBytes 须 >= algo->max_workspace_bytes
72+ 
73+#### 调用示例
74+ 
75+示例代码如下,仅供参考,具体编译和执行过程请参考[编译与运行样例](../../docs/zh/develop/compile_and_run_example.md)。使用 BLASLt 接口时,需在 CMakeLists.txt 中额外链接 `libops_blasLt.so`
76+ 
77+```cpp
78+#include <cstdio>
79+#include <memory>
80+#include <vector>
81+ 
82+#include "acl/acl.h"
83+#include "cann_ops_blasLt.h"
84+ 
85+#define CHECK_RET(cond, return_expr) \
86+ do { \
87+ if (!(cond)) { \
88+ return_expr; \
89+ } \
90+ } while (0)
91+ 
92+#define LOG_PRINT(message, ...) \
93+ do { \
94+ printf(message, ##__VA_ARGS__); \
95+ } while (0)
96+ 
97+class AclContext {
98+public:
99+ explicit AclContext(int32_t deviceId) : deviceId_(deviceId) {}
100+ 
101+ ~AclContext()
102+ {
103+ if (stream_ != nullptr) {
104+ aclrtDestroyStream(stream_);
105+ stream_ = nullptr;
106+ }
107+ if (deviceSet_) {
108+ aclrtResetDevice(deviceId_);
109+ deviceSet_ = false;
110+ }
111+ if (aclInited_) {
112+ aclFinalize();
113+ aclInited_ = false;
114+ }
115+ }
116+ 
117+ int Init()
118+ {
119+ auto ret = aclInit(nullptr);
120+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
121+ aclInited_ = true;
122+ 
123+ ret = aclrtSetDevice(deviceId_);
124+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
125+ deviceSet_ = true;
126+ 
127+ ret = aclrtCreateStream(&stream_);
128+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
129+ return ACL_SUCCESS;
130+ }
131+ 
132+ aclrtStream Stream() const { return stream_; }
133+ 
134+private:
135+ int32_t deviceId_;
136+ aclrtStream stream_ = nullptr;
137+ bool aclInited_ = false;
138+ bool deviceSet_ = false;
139+};
140+ 
141+struct AclMemDeleter {
142+ void operator()(void* p) const { aclrtFree(p); }
143+};
144+struct LtHandleDeleter {
145+ void operator()(aclblasLtHandle_t h) const { aclblasLtDestroy(h); }
146+};
147+struct LtDescDeleter {
148+ void operator()(aclblasLtMatmulDesc_t d) const { aclblasLtMatmulDescDestroy(d); }
149+};
150+struct LtLayoutDeleter {
151+ void operator()(aclblasLtMatrixLayout_t d) const { aclblasLtMatrixLayoutDestroy(d); }
152+};
153+ 
154+int aclblasLtMatmulTest(AclContext& ctx)
155+{
156+ aclrtStream stream = ctx.Stream();
157+ 
158+ // 1. 创建 aclBLASLt 句柄与描述符
159+ aclblasLtHandle_t rawHandle = nullptr;
160+ auto blasRet = aclblasLtCreate(&rawHandle);
161+ CHECK_RET(blasRet == ACLBLAS_STATUS_SUCCESS, LOG_PRINT("aclblasLtCreate failed. ERROR: %d\n", blasRet);
162+ return blasRet);
163+ std::unique_ptr<std::remove_pointer<aclblasLtHandle_t>::type, LtHandleDeleter> handlePtr(rawHandle);
164+ 
165+ aclblasLtMatmulDesc_t rawDesc = nullptr;
166+ blasRet = aclblasLtMatmulDescCreate(&rawDesc, ACLBLAS_COMPUTE_32F, ACL_FLOAT);
167+ CHECK_RET(blasRet == ACLBLAS_STATUS_SUCCESS, LOG_PRINT("aclblasLtMatmulDescCreate failed. ERROR: %d\n", blasRet);
168+ return blasRet);
169+ std::unique_ptr<std::remove_pointer<aclblasLtMatmulDesc_t>::type, LtDescDeleter> descPtr(rawDesc);
170+ 
171+ const uint64_t m = 16, n = 16, k = 16;
172+ aclblasLtMatrixLayout_t rawAdesc = nullptr;
173+ aclblasLtMatrixLayout_t rawBdesc = nullptr;
174+ aclblasLtMatrixLayout_t rawCdesc = nullptr;
175+ aclblasLtMatrixLayout_t rawDdesc = nullptr;
176+ int32_t order = ACLBLASLT_ORDER_ROW;
177+ 
178+ blasRet = aclblasLtMatrixLayoutCreate(&rawAdesc, ACL_FLOAT, m, k, k);
179+ CHECK_RET(blasRet == ACLBLAS_STATUS_SUCCESS, LOG_PRINT("MatrixLayoutCreate A failed. ERROR: %d\n", blasRet);
180+ return blasRet);
181+ blasRet = aclblasLtMatrixLayoutCreate(&rawBdesc, ACL_FLOAT, k, n, n);
182+ CHECK_RET(blasRet == ACLBLAS_STATUS_SUCCESS, LOG_PRINT("MatrixLayoutCreate B failed. ERROR: %d\n", blasRet);
183+ return blasRet);
184+ blasRet = aclblasLtMatrixLayoutCreate(&rawCdesc, ACL_FLOAT, m, n, n);
185+ CHECK_RET(blasRet == ACLBLAS_STATUS_SUCCESS, LOG_PRINT("MatrixLayoutCreate C failed. ERROR: %d\n", blasRet);
186+ return blasRet);
187+ blasRet = aclblasLtMatrixLayoutCreate(&rawDdesc, ACL_FLOAT, m, n, n);
188+ CHECK_RET(blasRet == ACLBLAS_STATUS_SUCCESS, LOG_PRINT("MatrixLayoutCreate D failed. ERROR: %d\n", blasRet);
189+ return blasRet);
190+ 
191+ std::unique_ptr<std::remove_pointer<aclblasLtMatrixLayout_t>::type, LtLayoutDeleter> aDescPtr(rawAdesc);
192+ std::unique_ptr<std::remove_pointer<aclblasLtMatrixLayout_t>::type, LtLayoutDeleter> bDescPtr(rawBdesc);
193+ std::unique_ptr<std::remove_pointer<aclblasLtMatrixLayout_t>::type, LtLayoutDeleter> cDescPtr(rawCdesc);
194+ std::unique_ptr<std::remove_pointer<aclblasLtMatrixLayout_t>::type, LtLayoutDeleter> dDescPtr(rawDdesc);
195+ 
196+ aclblasLtMatrixLayoutSetAttribute(aDescPtr.get(), ACLBLASLT_MATRIX_LAYOUT_ORDER, &order, sizeof(order));
197+ aclblasLtMatrixLayoutSetAttribute(bDescPtr.get(), ACLBLASLT_MATRIX_LAYOUT_ORDER, &order, sizeof(order));
198+ aclblasLtMatrixLayoutSetAttribute(cDescPtr.get(), ACLBLASLT_MATRIX_LAYOUT_ORDER, &order, sizeof(order));
199+ aclblasLtMatrixLayoutSetAttribute(dDescPtr.get(), ACLBLASLT_MATRIX_LAYOUT_ORDER, &order, sizeof(order));
200+ 
201+ // 2. 准备 Host 数据
202+ float alpha = 1.0f;
203+ float beta = 0.0f;
204+ std::vector<float> hA(m * k, 1.0f);
205+ std::vector<float> hB(k * n, 1.0f);
206+ 
207+ // 3. 申请 Device 内存并拷贝数据
208+ void* rawA = nullptr;
209+ void* rawB = nullptr;
210+ void* rawD = nullptr;
211+ size_t aBytes = hA.size() * sizeof(float);
212+ size_t bBytes = hB.size() * sizeof(float);
213+ size_t dBytes = m * n * sizeof(float);
214+ 
215+ auto aclRet = aclrtMalloc(&rawA, aBytes, ACL_MEM_MALLOC_HUGE_FIRST);
216+ CHECK_RET(aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMalloc for A failed. ERROR: %d\n", aclRet); return aclRet);
217+ aclRet = aclrtMalloc(&rawB, bBytes, ACL_MEM_MALLOC_HUGE_FIRST);
218+ CHECK_RET(aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMalloc for B failed. ERROR: %d\n", aclRet); return aclRet);
219+ aclRet = aclrtMalloc(&rawD, dBytes, ACL_MEM_MALLOC_HUGE_FIRST);
220+ CHECK_RET(aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMalloc for D failed. ERROR: %d\n", aclRet); return aclRet);
221+ 
222+ std::unique_ptr<void, AclMemDeleter> aDevicePtr(rawA);
223+ std::unique_ptr<void, AclMemDeleter> bDevicePtr(rawB);
224+ std::unique_ptr<void, AclMemDeleter> dDevicePtr(rawD);
225+ 
226+ aclRet = aclrtMemcpy(aDevicePtr.get(), aBytes, hA.data(), aBytes, ACL_MEMCPY_HOST_TO_DEVICE);
227+ CHECK_RET(aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy for A failed. ERROR: %d\n", aclRet); return aclRet);
228+ aclRet = aclrtMemcpy(bDevicePtr.get(), bBytes, hB.data(), bBytes, ACL_MEMCPY_HOST_TO_DEVICE);
229+ CHECK_RET(aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy for B failed. ERROR: %d\n", aclRet); return aclRet);
230+ 
231+ // 4. 调用 aclblasLtMatmul:D = alpha * A * B + beta * C(beta=0,C 可为 nullptr)
232+ blasRet = aclblasLtMatmul(
233+ handlePtr.get(), descPtr.get(), &alpha,
234+ aDevicePtr.get(), aDescPtr.get(), bDevicePtr.get(), bDescPtr.get(),
235+ &beta, nullptr, cDescPtr.get(),
236+ dDevicePtr.get(), dDescPtr.get(),
237+ nullptr, nullptr, 0, stream);
238+ CHECK_RET(blasRet == ACLBLAS_STATUS_SUCCESS, LOG_PRINT("aclblasLtMatmul failed. ERROR: %d\n", blasRet);
239+ return blasRet);
240+ 
241+ // 5. 同步等待任务执行结束
242+ aclRet = aclrtSynchronizeStream(stream);
243+ CHECK_RET(aclRet == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", aclRet); return aclRet);
244+ 
245+ // 6. 将结果从 Device 拷贝回 Host 并打印
246+ std::vector<float> hD(m * n, 0.0f);
247+ aclRet = aclrtMemcpy(hD.data(), dBytes, dDevicePtr.get(), dBytes, ACL_MEMCPY_DEVICE_TO_HOST);
248+ CHECK_RET(aclRet == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", aclRet);
249+ return aclRet);
250+ LOG_PRINT("D[0] = %f (expected %f)\n", hD[0], static_cast<float>(k));
251+ 
252+ return ACL_SUCCESS;
253+}
254+ 
255+int main()
256+{
257+ AclContext ctx(0);
258+ auto ret = ctx.Init();
259+ CHECK_RET(ret == ACL_SUCCESS, return ret);
260+ 
261+ ret = aclblasLtMatmulTest(ctx);
262+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclblasLtMatmulTest failed. ERROR: %d\n", ret); return ret);
263+ return 0;
264+}
47```265```
48-test/blasLtMatmul/
49-├── CMakeLists.txt // 编译工程文件
50-├── blasLtMatmul_param.h // 参数结构体(继承 BlasTestParamBase)
51-├── blasLtMatmul_golden.h // CPU golden(封装 aclblasLtMatmul CPU 参考)
52-└── arch35/
53- ├── blasLtMatmul_npu_wrapper.h // NPU wrapper(封装 aclrtMalloc/H2D/kernel/D2H/free)
54- ├── blasLtMatmul_test.cpp // 精度测试(GTest 入口)
55- └── blasLtMatmul_test.csv // 精度测试用例表
56-```
57- 
58-## 接口描述
59- 
60-- 接口功能:
61- 执行矩阵乘法 D = alpha * op(A) * op(B) + beta * C。支持 FP32 全精度路径,以及 MXFP8/MXFP4 量化输入路径(需配合 scale factor)。
62- 
63-- 对应接口为:
64- 
65-```
66-aclblasStatus_t aclblasLtMatmul(
67- aclblasLtHandle_t lightHandle,
68- aclblasLtMatmulDesc_t computeDesc,
69- const void* alpha,
70- const void* A,
71- aclblasLtMatrixLayout_t Adesc,
72- const void* B,
73- aclblasLtMatrixLayout_t Bdesc,
74- const void* beta,
75- const void* C,
76- aclblasLtMatrixLayout_t Cdesc,
77- void* D,
78- aclblasLtMatrixLayout_t Ddesc,
79- const aclblasLtMatmulAlgo_t* algo,
80- void* workspace,
81- size_t workspaceSizeInBytes,
82- aclrtStream stream);
83-```
84- 
85-<table>
86- <tr>
87- <td rowspan="1" align="center">参数</td>
88- <td colspan="4" align="center">aclblasLtMatmul 参数说明</td>
89- </tr>
90- <tr>
91- <td rowspan="18" align="center">参数列表</td>
92- <td align="center">Param.</td>
93- <td align="center">Memory</td>
94- <td align="center">in/out</td>
95- <td align="center">含义</td>
96- </tr>
97- <tr>
98- <td align="center">lightHandle</td>
99- <td align="center"></td>
100- <td align="center">in</td>
101- <td align="center">aclBLASLt 库上下文句柄,由 aclblasLtCreate 创建。不可为 NULL,否则返回 ACLBLAS_STATUS_NOT_INITIALIZED。</td>
102- </tr>
103- <tr>
104- <td align="center">computeDesc</td>
105- <td align="center"></td>
106- <td align="center">in</td>
107- <td align="center">矩阵乘法描述符,设置 transA/transB、epilogue、scale 指针等属性。不可为 NULL。</td>
108- </tr>
109- <tr>
110- <td align="center">alpha</td>
111- <td align="center">host</td>
112- <td align="center">in</td>
113- <td align="center">用于乘法的 float 标量。不可为 NULL。</td>
114- </tr>
115- <tr>
116- <td align="center">A</td>
117- <td align="center">device</td>
118- <td align="center">in</td>
119- <td align="center">输入矩阵 A,数据类型由 Adesc 指定。不可为 NULL(m&gt;0 且 n&gt;0 时)。</td>
120- </tr>
121- <tr>
122- <td align="center">Adesc</td>
123- <td align="center"></td>
124- <td align="center">in</td>
125- <td align="center">矩阵 A 的 layout 描述符(rows/cols/ld/order/dtype)。</td>
126- </tr>
127- <tr>
128- <td align="center">B</td>
129- <td align="center">device</td>
130- <td align="center">in</td>
131- <td align="center">输入矩阵 B,数据类型由 Bdesc 指定。不可为 NULL(m&gt;0 且 n&gt;0 时)。</td>
132- </tr>
133- <tr>
134- <td align="center">Bdesc</td>
135- <td align="center"></td>
136- <td align="center">in</td>
137- <td align="center">矩阵 B 的 layout 描述符。</td>
138- </tr>
139- <tr>
140- <td align="center">beta</td>
141- <td align="center">host</td>
142- <td align="center">in</td>
143- <td align="center">用于累加的 float 标量。不可为 NULL。beta=0 时 C 可不参与计算。</td>
144- </tr>
145- <tr>
146- <td align="center">C</td>
147- <td align="center">device</td>
148- <td align="center">in</td>
149- <td align="center">累加矩阵 C。beta=0 时可为 NULL。当前测试覆盖 C=NULL 场景。</td>
150- </tr>
151- <tr>
152- <td align="center">Cdesc</td>
153- <td align="center"></td>
154- <td align="center">in</td>
155- <td align="center">矩阵 C 的 layout 描述符。</td>
156- </tr>
157- <tr>
158- <td align="center">D</td>
159- <td align="center">device</td>
160- <td align="center">out</td>
161- <td align="center">输出矩阵 D,维度 m x n。不可为 NULL(m&gt;0 且 n&gt;0 时)。</td>
162- </tr>
163- <tr>
164- <td align="center">Ddesc</td>
165- <td align="center"></td>
166- <td align="center">in</td>
167- <td align="center">矩阵 D 的 layout 描述符,指定输出数据类型(FP32 或 BF16)。</td>
168- </tr>
169- <tr>
170- <td align="center">algo</td>
171- <td align="center"></td>
172- <td align="center">in</td>
173- <td align="center">算法描述符,可为 NULL(使用默认算法)。</td>
174- </tr>
175- <tr>
176- <td align="center">workspace</td>
177- <td align="center">device</td>
178- <td align="center">in</td>
179- <td align="center">工作空间内存,可为 NULL。非 NULL 时需 16B 对齐。</td>
180- </tr>
181- <tr>
182- <td align="center">workspaceSizeInBytes</td>
183- <td align="center"></td>
184- <td align="center">in</td>
185- <td align="center">工作空间大小(字节)。</td>
186- </tr>
187- <tr>
188- <td align="center">stream</td>
189- <td align="center"></td>
190- <td align="center">in</td>
191- <td align="center">AscendCL 执行流。</td>
192- </tr>
193-</table>
194- 
195-### 当前支持的入参/出参范围
196- 
197-<table>
198- <tr>
199- <td align="center">参数项</td>
200- <td align="center">支持范围</td>
201- <td align="center">说明</td>
202- </tr>
203- <tr>
204- <td align="center">dtypeA / dtypeB</td>
205- <td align="center">FP32;MXFP8_E4M3FN;MXFP4_E2M1</td>
206- <td align="center">A/B 须为同类型组合:FP32×FP32、MXFP8×MXFP8、MXFP4×MXFP4。其他组合返回 ACLBLAS_STATUS_NOT_SUPPORTED。</td>
207- </tr>
208- <tr>
209- <td align="center">dtypeC</td>
210- <td align="center">FP32</td>
211- <td align="center">累加矩阵类型,当前固定为 FP32。</td>
212- </tr>
213- <tr>
214- <td align="center">dtypeD</td>
215- <td align="center">FP32;BF16</td>
216- <td align="center">MXFP8/MXFP4 路径支持 FP32 或 BF16 输出;FP32 路径输出 FP32。</td>
217- </tr>
218- <tr>
219- <td align="center">computeType</td>
220- <td align="center">ACLBLAS_COMPUTE_32F</td>
221- <td align="center">所有已支持路径均使用 32F 计算精度。</td>
222- </tr>
223- <tr>
224- <td align="center">transA / transB</td>
225- <td align="center">N、T</td>
226- <td align="center">对应 ACLBLAS_OP_N(不转置)、ACLBLAS_OP_T(转置)。</td>
227- </tr>
228- <tr>
229- <td align="center">M / N / K</td>
230- <td align="center">M,N,K ≥ 0</td>
231- <td align="center">M=0 或 N=0 时为空操作,直接返回 SUCCESS。MXFP8/MXFP4 路径要求 K 为 32 的整数倍,否则返回 ACLBLAS_STATUS_INVALID_VALUE。</td>
232- </tr>
233- <tr>
234- <td align="center">lda / ldb / ldc / ldd</td>
235- <td align="center">ld ≥ 物理列数</td>
236- <td align="center">行主序(ACLBLASLT_ORDER_ROW)存储,ld 为 leading dimension,须 ≥ 矩阵物理列数。MXFP4 的 ld 为逻辑元素 leading dim(2 个 FP4 元素打包为 1 字节)。</td>
237- </tr>
238- <tr>
239- <td align="center">alpha / beta</td>
240- <td align="center">float</td>
241- <td align="center">当前测试覆盖 alpha=1.0、beta=0.0。beta=0 时 C 可为 NULL。</td>
242- </tr>
243- <tr>
244- <td align="center">epilogue</td>
245- <td align="center">ACLBLASLT_EPILOGUE_DEFAULT</td>
246- <td align="center">当前仅支持默认 epilogue。</td>
247- </tr>
248- <tr>
249- <td align="center">scaleA / scaleB</td>
250- <td align="center">MXFP8/MXFP4 必填</td>
251- <td align="center">通过 computeDesc 的 ACLBLASLT_MATMUL_DESC_A/B_SCALE_POINTER 设置,E8M0 格式,按 K 方向每 32 元素一组。不可为 NULL。</td>
252- </tr>
253- <tr>
254- <td align="center">algo</td>
255- <td align="center">default / NULL</td>
256- <td align="center">可为 NULL,使用默认算法。</td>
257- </tr>
258- <tr>
259- <td align="center">order</td>
260- <td align="center">ACLBLASLT_ORDER_ROW</td>
261- <td align="center">当前实现使用行主序。</td>
262- </tr>
263-</table>
264- 
265-- 算子规格:
266- <table>
267- <tr><td rowspan="1" align="center">算子类型(OpType)</td><td colspan="6" align="center">aclblasLtMatmul</td></tr>
268- <tr><td rowspan="7" align="center">算子输入</td><td align="center">name</td><td align="center">shape</td><td align="center">data type</td><td align="center">format</td></tr>
269- <tr><td align="center">A</td><td align="center">M×K(或转置后 K×M)</td><td align="center">FP32 / MXFP8 / MXFP4</td><td align="center">ND</td></tr>
270- <tr><td align="center">B</td><td align="center">K×N(或转置后 N×K)</td><td align="center">FP32 / MXFP8 / MXFP4</td><td align="center">ND</td></tr>
271- <tr><td align="center">C</td><td align="center">M×N</td><td align="center">FP32</td><td align="center">ND</td></tr>
272- <tr><td align="center">scaleA</td><td align="center">按 K 分组</td><td align="center">E8M0 (uint8)</td><td align="center">ND</td></tr>
273- <tr><td align="center">scaleB</td><td align="center">按 K 分组</td><td align="center">E8M0 (uint8)</td><td align="center">ND</td></tr>
274- <tr><td align="center">alpha/beta</td><td align="center">1</td><td align="center">float</td><td align="center">ND</td></tr>
275- <tr><td rowspan="1" align="center">算子输出</td><td align="center">D</td><td align="center">M×N</td><td align="center">FP32 / BF16</td><td align="center">ND</td></tr>
276- <tr><td rowspan="1" align="center">核函数名</td><td colspan="6" align="center">MatmulFp32Kernel / matmul_mxfp8_kernel_do / ltmatmul_mxfp4_kernel_do</td></tr>
277- </table>
278- 
279-- 算子实现:
280- Host 侧根据 A/B 数据类型路由至 FP32、MXFP8 或 MXFP4 对应的 Tiling 与 Kernel 实现。MXFP 路径在 Kernel 内完成量化矩阵乘加,输出经 epilogue 处理写入 D。
281- 
282-- 调用实现:
283- 通过 aclBLASLt 标准 API 调用,内部使用内核调用符 `<<<>>>` 启动 NPU 核函数。
284- 
285-## 测试用例覆盖
286- 
287-| 分组 | 用例数 | 覆盖场景 |
288-|------|--------|----------|
289-| L0 FP32 基础 | 7 | 小/中/大规模 NN、TN/NT/TT 转置、algo=nullptr |
290-| L0 MXFP8 | 6 | K=32 基础/大规模、四种转置、C=null |
291-| L0 MXFP4 | 7 | K=32 基础/大规模、四种转置、C=null、FP32 输出 |
292-| L0 异常入参 | 4 | handle/desc/alpha/A 为 NULL |
293-| L0 边界 | 6 | M=0/N=0 空操作、K 非 32 倍数非法、algo=nullptr |
294-| L1 FP32 扩展 | 11 | 矩形矩阵、非方阵转置、瘦矩阵 M128×N32×K128 |
295-| L1 MXFP8 扩展 | 22 | 多种 K 规模、矩形/奇数维度、scale 全零、K 非法值 |
296-| L1 MXFP4 扩展 | 17 | 多种 K 规模、矩形/转置、scale 全零、K 非法值 |
297-| TEST_F 固定用例 | 4 | NullHandle、NullComputeDesc、NullAlpha、NullA |
298- 
299-## 编译运行
300- 
301-在本样例根目录下执行如下步骤,编译并执行测试。
302- 
303-- 配置环境变量
304- 请根据当前环境上 CANN 开发套件包的安装方式,选择对应配置环境变量的命令。
305- - 默认路径,root 用户安装 CANN 软件包
306- ```bash
307- source /usr/local/Ascend/cann/set_env.sh
308- ```
309- 
310- - 默认路径,非 root 用户安装 CANN 软件包
311- ```bash
312- source $HOME/Ascend/cann/set_env.sh
313- ```
314- 
315- - 指定路径 install_path,安装 CANN 软件包
316- ```bash
317- source ${install_path}/cann/set_env.sh
318- ```
319- 
320-- 样例执行
321- ```bash
322- bash build.sh --ops=blasLtMatmul --soc=ascend950 --run
323- ```
324- 
325- 其中 `--soc`**可选**参数,用于指定目标硬件平台(与上文「产品支持情况」对应)。按实际硬件选用:
326- 
327- | 产品 | `--soc` 取值 |
328- |------|----------------|
329- | Ascend 950PR / Ascend 950DT | `ascend950` |
330- 
331- 执行结果如下,说明精度对比成功。
332- ```bash
333- [PASS] blasLtMatmul_test
334- ```
@@ -1,1593 +0,0 @@
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-#include "cann_ops_blasLt.h"
12- 
13-#include <acl/acl.h>
14-#include <algorithm>
15-#include <cmath>
16-#include <cstdlib>
17-#include <cstring>
18-#include <list>
19-#include <mutex>
20-#include <new>
21-#include <unordered_map>
22-#include <vector>
23- 
24-#include "host_utils.h"
25-#include "matmul_get_tiling.h"
26-#include "matmul_kernel.h"
27-#include "matmul_mxfp4_host.h"
28-#include "matrix_transform_acl_impl.h"
29- 
30-#include <cstdint>
31- 
32-namespace {
33- 
34-constexpr int ACLBLASLT_VERSION_MAJOR = 1;
35-constexpr int ACLBLASLT_VERSION_MINOR = 0;
36-constexpr int ACLBLASLT_VERSION_PATCH = 0;
37- 
38-constexpr uint32_t ACLBLASLT_HANDLE_MAGIC = 0xACBA1234;
39-constexpr uint32_t ACLBLASLT_LAYOUT_MAGIC = 0xACBB1234;
40-constexpr uint32_t ACLBLASLT_DESC_MAGIC = 0xACBC1234;
41-constexpr uint32_t ACLBLASLT_ALGO_MAGIC = 0xACBD1234;
42- 
43-constexpr size_t DEFAULT_WORKSPACE_SIZE = 32 * 1024 * 1024;
44-constexpr size_t L1_SIZE = 512 * 1024;
45-constexpr size_t L0_SIZE = 256;
46-constexpr uint32_t DEFAULT_AI_CORES = 8;
47-constexpr double DEFAULT_PEAK_TFLOPS = 140.0;
48-constexpr double DEFAULT_PEAK_GBPS = 900.0;
49- 
50-struct AtlasA2 {
51- static constexpr uint32_t BIAS_SIZE = 1024;
52- static constexpr uint32_t FIXBUF_SIZE = 7 * 1024;
53- static constexpr uint32_t UB_SIZE = 192 * 1024;
54- static constexpr uint32_t L1_SIZE = 512 * 1024;
55- static constexpr uint32_t L0A_SIZE = 64 * 1024;
56- static constexpr uint32_t L0B_SIZE = 64 * 1024;
57- static constexpr uint32_t L0C_SIZE = 128 * 1024;
58-};
59- 
60-struct Ascend950 {
61- static constexpr uint32_t BIAS_SIZE = 4 * 1024;
62- static constexpr uint32_t FIXBUF_SIZE = 16 * 1024;
63- static constexpr uint32_t UB_SIZE = 248 * 1024;
64- static constexpr uint32_t L1_SIZE = 512 * 1024;
65- static constexpr uint32_t L0A_SIZE = 64 * 1024;
66- static constexpr uint32_t L0B_SIZE = 64 * 1024;
67- static constexpr uint32_t L0C_SIZE = 256 * 1024;
68-};
69- 
70-enum DispatchPolicyType : uint8_t
71-{
72- DISPATCH_POLICY_MMAD_SYNC = 0,
73- DISPATCH_POLICY_MMAD_PINGPONG = 1,
74- DISPATCH_POLICY_MMAD_MULTI_STAGE = 2,
75-};
76- 
77-struct AlgoKey {
78- uint64_t m = 0;
79- uint64_t n = 0;
80- uint64_t k = 0;
81- aclDataType aType = ACL_FLOAT;
82- aclDataType bType = ACL_DT_UNDEFINED;
83- aclDataType cType = ACL_DT_UNDEFINED;
84- aclDataType dType = ACL_DT_UNDEFINED;
85- aclblasComputeType_t computeType = ACLBLAS_COMPUTE_32F;
86- aclblasLtEpilogue_t epilogue = ACLBLASLT_EPILOGUE_DEFAULT;
87- bool transA = false;
88- bool transB = false;
89- 
90- bool operator==(const AlgoKey& other) const
91- {
92- return m == other.m && n == other.n && k == other.k && aType == other.aType && bType == other.bType &&
93- cType == other.cType && dType == other.dType && computeType == other.computeType &&
94- epilogue == other.epilogue && transA == other.transA && transB == other.transB;
95- }
96-};
97- 
98-struct AlgoKeyHasher {
99- size_t operator()(const AlgoKey& x) const
100- {
101- size_t h = 1469598103934665603ull;
102- auto mix = [&](uint64_t v) { h ^= static_cast<size_t>(v + 0x9e3779b97f4a7c15ull + (h << 6) + (h >> 2)); };
103- mix(x.m);
104- mix(x.n);
105- mix(x.k);
106- mix(static_cast<uint64_t>(x.aType));
107- mix(static_cast<uint64_t>(x.bType));
108- mix(static_cast<uint64_t>(x.cType));
109- mix(static_cast<uint64_t>(x.dType));
110- mix(static_cast<uint64_t>(x.computeType));
111- mix(static_cast<uint64_t>(x.epilogue));
112- mix(static_cast<uint64_t>(x.transA));
113- mix(static_cast<uint64_t>(x.transB));
114- return h;
115- }
116-};
117- 
118-struct CacheEntry {
119- aclblasLtMatmulAlgo_t algo;
120- std::list<AlgoKey>::iterator lruIter;
121-};
122- 
123-struct aclblasLtHandle {
124- uint32_t magic = ACLBLASLT_HANDLE_MAGIC;
125- bool initialized = false;
126- // version info
127- int versionMajor = ACLBLASLT_VERSION_MAJOR;
128- int versionMinor = ACLBLASLT_VERSION_MINOR;
129- // AscendCL runtime
130- aclrtContext context = nullptr;
131- aclrtStream defaultStream = nullptr;
132- int32_t deviceId = 0;
133- // workspace
134- void* internalWorkspace = nullptr;
135- size_t workspaceSize = 0;
136- // thread safety
137- std::mutex* mutex = nullptr;
138- // soc spec
139- int npuArch = 0;
140- size_t maxSharedMemory = 0;
141- // algo cache
142- std::unordered_map<AlgoKey, CacheEntry, AlgoKeyHasher>* algoCache = nullptr;
143- size_t algoCacheMaxSize = 128;
144- std::list<AlgoKey>* lruList = nullptr;
145-};
146- 
147-struct aclblasLtMatrixLayoutImpl {
148- uint32_t magic;
149- aclDataType type;
150- uint64_t rows;
151- uint64_t cols;
152- int64_t ld;
153- aclblasLtOrder_t order = ACLBLASLT_ORDER_COL;
154- int32_t batchCount = 1;
155- int64_t stridedBatchOffset = 0;
156-};
157-static_assert(
158- sizeof(aclblasLtMatrixLayoutImpl) <= sizeof(aclblasLtMatrixLayoutOpaque_t),
159- "Impl of aclblasLtMatrixLayout must fit in capsule!");
160- 
161-struct aclblasLtMatmulDescImpl {
162- uint32_t magic;
163- aclblasComputeType_t computeType;
164- aclDataType scaleType;
165- aclblasOperation_t transA = ACLBLAS_OP_N;
166- aclblasOperation_t transB = ACLBLAS_OP_N;
167- aclblasLtEpilogue_t epilogue = ACLBLASLT_EPILOGUE_DEFAULT;
168- const void* bias = nullptr;
169- aclDataType biasDataType = ACL_DT_UNDEFINED;
170- const void* scaleA = nullptr;
171- const void* scaleB = nullptr;
172-};
173- 
174-constexpr size_t kBiasPtrStorageBytes = sizeof(void*);
175-static_assert(
176- sizeof(aclblasLtMatmulDescImpl) <= sizeof(aclblasLtMatmulDescOpaque_t),
177- "Impl of aclblasLtMatmulDesc must fit in capsule!");
178- 
179-struct aclblasLtMatmulPreferenceImpl {
180- uint32_t magic;
181- uint32_t searchMode = 0;
182- size_t maxWorkspaceBytes = DEFAULT_WORKSPACE_SIZE;
183- int32_t maxResults = 3;
184- bool allowMixedPrecision = true;
185- bool allowSplitK = true;
186- // tiling
187- uint32_t preferredL0M = 0;
188- uint32_t preferredL0N = 0;
189- uint32_t preferredL0K = 0;
190- // Scheduling
191- bool preferPingpong = false;
192- bool preferDoubleBuffer = false;
193- float minEfficiency = 0.5f;
194-};
195-static_assert(
196- sizeof(aclblasLtMatmulPreferenceImpl) <= sizeof(aclblasLtMatmulPreferenceOpaque_t),
197- "Impl of aclblasLtMatmulPreference must fit in capsule!");
198- 
199-struct AscendHardwareCaps {
200- uint32_t numAICores = DEFAULT_AI_CORES;
201- uint32_t l0CubeSize = L0_SIZE;
202- size_t l1BufferSize = L1_SIZE;
203- double memoryBandwidthGBps = DEFAULT_PEAK_GBPS;
204- double peakTFlops = DEFAULT_PEAK_TFLOPS;
205- double bandwidthBoundThreshold = 32.0;
206-};
207- 
208-struct AlgoCandidate {
209- uint32_t algoId = 0;
210- uint32_t l1TileM = 128;
211- uint32_t l1TileN = 128;
212- uint32_t l1TileK = 128;
213- uint32_t l0TileM = 64;
214- uint32_t l0TileN = 64;
215- uint32_t l0TileK = 64;
216- DispatchPolicyType policy = DISPATCH_POLICY_MMAD_SYNC;
217- uint32_t numBuffers = 1;
218- uint32_t splitKFactor = 1;
219- size_t workspaceSize = 0;
220- double peakPerformance = DEFAULT_PEAK_TFLOPS;
221-};
222- 
223-struct ScoredResult {
224- AlgoCandidate cand;
225- double estimatedTimeMs = 0.0;
226- double totalScore = 0.0;
227- bool isEfficient = true;
228-};
229- 
230-struct PackedAlgo {
231- uint32_t magic;
232- uint32_t algoId;
233- uint16_t l1mDiv16;
234- uint16_t l1nDiv16;
235- uint8_t policy;
236- uint8_t numBuffers;
237- uint8_t splitK;
238- uint8_t flags;
239-};
240-static_assert(sizeof(PackedAlgo) == 16, "PackedAlgo must fit algo.data");
241- 
242-static uint32_t GenerateAlgoId(
243- DispatchPolicyType policy, uint32_t l1m, uint32_t l1n, uint32_t l1k, uint32_t splitKFactor)
244-{
245- return (static_cast<uint32_t>(policy) << 28) ^ (l1m << 16) ^ (l1n << 8) ^ (l1k << 2) ^ splitKFactor;
246-}
247- 
248-static aclblasLtMatmulAlgo_t BuildAlgoFromCandidate(const AlgoCandidate& cand)
249-{
250- aclblasLtMatmulAlgo_t out{};
251- PackedAlgo packed{};
252- packed.magic = ACLBLASLT_ALGO_MAGIC;
253- packed.algoId = cand.algoId;
254- packed.l1mDiv16 = static_cast<uint16_t>(cand.l1TileM / 16);
255- packed.l1nDiv16 = static_cast<uint16_t>(cand.l1TileN / 16);
256- packed.policy = static_cast<uint8_t>(cand.policy);
257- packed.numBuffers = static_cast<uint8_t>(cand.numBuffers);
258- packed.splitK = static_cast<uint8_t>(cand.splitKFactor);
259- (void)MemcpySSucceeds(out.data, sizeof(out.data), &packed, sizeof(packed));
260- out.max_workspace_bytes = cand.workspaceSize;
261- return out;
262-}
263- 
264-static bool DecodeAlgo(const aclblasLtMatmulAlgo_t& algo, PackedAlgo* packed)
265-{
266- if (packed == nullptr) {
267- return false;
268- }
269- if (!MemcpySSucceeds(packed, sizeof(PackedAlgo), algo.data, sizeof(PackedAlgo))) {
270- return false;
271- }
272- return packed->magic == ACLBLASLT_ALGO_MAGIC;
273-}
274- 
275-static void GetAscendHardwareCaps(int32_t, AscendHardwareCaps* caps)
276-{
277- if (caps == nullptr) {
278- return;
279- }
280- // 当前仓库保持轻量默认能力值,后续可对接真实设备查询。
281- caps->numAICores = DEFAULT_AI_CORES;
282- caps->l0CubeSize = L0_SIZE;
283- caps->l1BufferSize = L1_SIZE;
284- caps->memoryBandwidthGBps = DEFAULT_PEAK_GBPS;
285- caps->peakTFlops = DEFAULT_PEAK_TFLOPS;
286- caps->bandwidthBoundThreshold = 32.0;
287-}
288- 
289-static void SelectL1TileShape(
290- uint64_t m, uint64_t n, uint64_t, uint32_t numAICores, uint32_t prefL0M, uint32_t prefL0N, uint32_t prefL0K,
291- uint32_t* l1M, uint32_t* l1N, uint32_t* l1K)
292-{
293- const uint32_t candidates[][3] = {
294- {128, 256, 256}, {256, 128, 256}, {256, 256, 128}, {128, 128, 128}, {256, 256, 64}};
295- 
296- float bestScore = -1.0f;
297- const uint32_t* best = candidates[0];
298- 
299- for (const auto& cand : candidates) {
300- uint32_t cm = cand[0];
301- uint32_t cn = cand[1];
302- uint32_t ck = cand[2];
303- 
304- uint32_t tilesM = CeilDiv<uint32_t>(m, cm);
305- uint32_t tilesN = CeilDiv<uint32_t>(n, cn);
306- uint32_t totalTiles = tilesM * tilesN;
307- 
308- float balanceScore =
309- 1.0f - static_cast<float>(totalTiles % std::max(1u, numAICores)) / std::max(1u, numAICores);
310- size_t l1Usage = static_cast<size_t>(cm) * ck * sizeof(float) + static_cast<size_t>(cn) * ck * sizeof(float);
311- float l1Util = static_cast<float>(l1Usage) / static_cast<float>(L1_SIZE);
312- 
313- float l0Match = 0.0f;
314- if (prefL0M > 0 && cm % prefL0M == 0) {
315- l0Match += 0.3f;
316- }
317- if (prefL0N > 0 && cn % prefL0N == 0) {
318- l0Match += 0.3f;
319- }
320- if (prefL0K > 0 && ck % prefL0K == 0) {
321- l0Match += 0.4f;
322- }
323- 
324- float score = balanceScore * 0.4f + std::min(l1Util, 1.0f) * 0.3f + l0Match * 0.3f;
325- if (score > bestScore) {
326- bestScore = score;
327- best = cand;
328- }
329- }
330- 
331- *l1M = best[0];
332- *l1N = best[1];
333- *l1K = best[2];
334-}
335- 
336-static void SelectL0TileShape(
337- uint32_t l1M, uint32_t l1N, uint32_t l1K, size_t, size_t, aclDataType, aclDataType, uint32_t* l0M, uint32_t* l0N,
338- uint32_t* l0K)
339-{
340- *l0K = std::min(64u, l1K);
341- *l0M = std::min(128u, l1M);
342- *l0N = std::min(256u, l1N);
343- 
344- while (*l0M > 16 && (l1M % *l0M != 0)) {
345- --(*l0M);
346- }
347- while (*l0N > 16 && (l1N % *l0N != 0)) {
348- --(*l0N);
349- }
350-}
351- 
352-static uint32_t SelectSplitKForAscend(uint32_t l1LoopsK, uint32_t numAICores)
353-{
354- if (numAICores == 0) {
355- return 1;
356- }
357- uint32_t candidate = std::min(l1LoopsK, numAICores);
358- return std::max(1u, candidate);
359-}
360- 
361-static size_t CalculateWorkspaceForAscend(uint64_t m, uint64_t n, uint32_t splitKFactor, aclblasLtEpilogue_t epilogue)
362-{
363- size_t workspace = 0;
364- if (splitKFactor > 1) {
365- workspace +=
366- static_cast<size_t>(splitKFactor) * static_cast<size_t>(m) * static_cast<size_t>(n) * sizeof(float);
367- }
368- 
369- switch (epilogue) {
370- case ACLBLASLT_EPILOGUE_BIAS:
371- case ACLBLASLT_EPILOGUE_RELU_BIAS:
372- case ACLBLASLT_EPILOGUE_GELU_BIAS:
373- workspace += static_cast<size_t>(m) * sizeof(float);
374- break;
375- case ACLBLASLT_EPILOGUE_GELU:
376- case ACLBLASLT_EPILOGUE_RELU:
377- workspace += 64 * 1024;
378- break;
379- default:
380- break;
381- }
382- return workspace;
383-}
384- 
385-static bool CheckHandleValid(const aclblasLtHandle* h)
386-{
387- return h != nullptr && h->magic == ACLBLASLT_HANDLE_MAGIC && h->initialized && h->algoCache != nullptr &&
388- h->lruList != nullptr;
389-}
390- 
391-static bool BuildGemmShape(
392- const aclblasLtMatmulDescImpl* desc, const aclblasLtMatrixLayoutImpl* A, const aclblasLtMatrixLayoutImpl* B,
393- const aclblasLtMatrixLayoutImpl* D, uint64_t* m, uint64_t* n, uint64_t* k)
394-{
395- if (desc == nullptr || A == nullptr || B == nullptr || D == nullptr || m == nullptr || n == nullptr ||
396- k == nullptr) {
397- return false;
398- }
399- const bool transA = (desc->transA != ACLBLAS_OP_N);
400- const bool transB = (desc->transB != ACLBLAS_OP_N);
401- 
402- const uint64_t mA = transA ? A->cols : A->rows;
403- const uint64_t kA = transA ? A->rows : A->cols;
404- const uint64_t kB = transB ? B->cols : B->rows;
405- const uint64_t nB = transB ? B->rows : B->cols;
406- 
407- if (mA != D->rows || kA != kB || nB != D->cols) {
408- return false;
409- }
410- 
411- *m = mA;
412- *n = nB;
413- *k = kA;
414- return true;
415-}
416- 
417-// Pack a transform descriptor impl back into its capsule and zero-pad the remainder.
418-aclblasStatus_t MatPackTransformImpl(void* capsule, size_t capsuleBytes, const void* impl, size_t implBytes)
419-{
420- aclblasStatus_t copyStatus = CheckedMemcpyS(capsule, capsuleBytes, impl, implBytes);
421- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
422- return copyStatus;
423- }
424- if (capsuleBytes > implBytes) {
425- copyStatus = CheckedMemsetS(
426- reinterpret_cast<char*>(capsule) + implBytes, capsuleBytes - implBytes, capsuleBytes - implBytes);
427- }
428- return copyStatus;
429-}
430- 
431-// Pack the transform-relevant fields of a matrix layout capsule into the plain parameter struct
432-// consumed by the transform operator unit.
433-MatTransformLayout MatPackTransformLayout(const aclblasLtMatrixLayoutImpl* layout)
434-{
435- MatTransformLayout packed;
436- packed.type = layout->type;
437- packed.rows = layout->rows;
438- packed.cols = layout->cols;
439- packed.ld = layout->ld;
440- packed.order = layout->order;
441- packed.batchCount = layout->batchCount;
442- return packed;
443-}
444- 
445-} // namespace
446- 
447-extern "C" {
448- 
449-aclblasStatus_t aclblasLtGetVersion(size_t* version)
450-{
451- if (version == nullptr) {
452- return ACLBLAS_STATUS_INVALID_VALUE;
453- }
454- 
455- *version = (static_cast<size_t>(ACLBLASLT_VERSION_MAJOR) << 24) |
456- (static_cast<size_t>(ACLBLASLT_VERSION_MINOR) << 16) | static_cast<size_t>(ACLBLASLT_VERSION_PATCH);
457- return ACLBLAS_STATUS_SUCCESS;
458-}
459- 
460-aclblasStatus_t aclblasLtGetProperty(aclblasLtPropertyType_t type, int* value)
461-{
462- if (value == nullptr) {
463- return ACLBLAS_STATUS_INVALID_VALUE;
464- }
465- 
466- switch (type) {
467- case ACLBLASLT_PROPERTY_MAJOR_VERSION:
468- *value = ACLBLASLT_VERSION_MAJOR;
469- return ACLBLAS_STATUS_SUCCESS;
470- case ACLBLASLT_PROPERTY_MINOR_VERSION:
471- *value = ACLBLASLT_VERSION_MINOR;
472- return ACLBLAS_STATUS_SUCCESS;
473- case ACLBLASLT_PROPERTY_PATCH_LEVEL:
474- *value = ACLBLASLT_VERSION_PATCH;
475- return ACLBLAS_STATUS_SUCCESS;
476- default:
477- return ACLBLAS_STATUS_INVALID_VALUE;
478- }
479-}
480- 
481-aclblasStatus_t aclblasLtCreate(aclblasLtHandle_t* lightHandle)
482-{
483- if (lightHandle == nullptr) {
484- return ACLBLAS_STATUS_INVALID_VALUE;
485- }
486- 
487- aclblasLtHandle* h = nullptr;
488- auto st = AllocHandle(&h);
489- if (st != ACLBLAS_STATUS_SUCCESS) {
490- return st;
491- }
492- 
493- int32_t deviceId = 0;
494- aclError aclRet = aclrtGetDevice(&deviceId);
495- if (aclRet != ACL_SUCCESS) {
496- delete h;
497- return ACLBLAS_STATUS_NOT_INITIALIZED;
498- }
499- 
500- aclrtContext currentCtx = nullptr;
501- aclRet = aclrtGetCurrentContext(&currentCtx);
502- if (aclRet != ACL_SUCCESS || currentCtx == nullptr) {
503- delete h;
504- return ACLBLAS_STATUS_NOT_INITIALIZED;
505- }
506- 
507- h->deviceId = deviceId;
508- h->context = currentCtx;
509- h->defaultStream = nullptr;
510- h->workspaceSize = DEFAULT_WORKSPACE_SIZE;
511- h->internalWorkspace = std::malloc(h->workspaceSize);
512- if (h->internalWorkspace == nullptr) {
513- delete h;
514- return ACLBLAS_STATUS_ALLOC_FAILED;
515- }
516- 
517- h->mutex = new (std::nothrow) std::mutex();
518- h->algoCache = new (std::nothrow) std::unordered_map<AlgoKey, CacheEntry, AlgoKeyHasher>();
519- h->lruList = new (std::nothrow) std::list<AlgoKey>();
520- if (h->mutex == nullptr || h->algoCache == nullptr || h->lruList == nullptr) {
521- delete h->mutex;
522- delete h->algoCache;
523- delete h->lruList;
524- std::free(h->internalWorkspace);
525- delete h;
526- return ACLBLAS_STATUS_ALLOC_FAILED;
527- }
528- 
529- h->npuArch = 2;
530- h->maxSharedMemory = L1_SIZE;
531- h->initialized = true;
532- *lightHandle = reinterpret_cast<aclblasLtHandle_t>(h);
533- return ACLBLAS_STATUS_SUCCESS;
534-}
535- 
536-aclblasStatus_t aclblasLtDestroy(const aclblasLtHandle_t lightHandle)
537-{
538- if (lightHandle == nullptr) {
539- return ACLBLAS_STATUS_INVALID_VALUE;
540- }
541- 
542- auto* h = reinterpret_cast<aclblasLtHandle*>(lightHandle);
543- if (h->magic != ACLBLASLT_HANDLE_MAGIC) {
544- return ACLBLAS_STATUS_INVALID_VALUE;
545- }
546- 
547- h->initialized = false;
548- delete h->mutex;
549- delete h->algoCache;
550- delete h->lruList;
551- std::free(h->internalWorkspace);
552- h->mutex = nullptr;
553- h->algoCache = nullptr;
554- h->lruList = nullptr;
555- h->internalWorkspace = nullptr;
556- h->workspaceSize = 0;
557- return FreeHandle(h);
558-}
559- 
560-aclblasStatus_t aclblasLtMatrixLayoutCreate(
561- aclblasLtMatrixLayout_t* layout, aclDataType type, uint64_t rows, uint64_t cols, int64_t ld)
562-{
563- // 1. 参数校验(BLAS/cuBLAS 允许 m=0 或 n=0 的空矩阵,仅拒绝非法 ld)
564- if (layout == nullptr || ld < 0) {
565- return ACLBLAS_STATUS_INVALID_VALUE;
566- }
567- *layout = nullptr;
568- // 2. 堆上分配胶囊
569- auto* capsule = new (std::nothrow) aclblasLtMatrixLayoutOpaque_t();
570- if (capsule == nullptr) {
571- return ACLBLAS_STATUS_ALLOC_FAILED;
572- }
573- // 3. 栈上创建Impl,初始化后拷贝进胶囊
574- aclblasLtMatrixLayoutImpl impl;
575- impl.magic = ACLBLASLT_LAYOUT_MAGIC;
576- impl.type = type;
577- impl.rows = rows;
578- impl.cols = cols;
579- impl.ld = (ld == 0) ? static_cast<int64_t>(rows) : ld;
580- // 4. Impl → 胶囊
581- static_assert(sizeof(impl) <= sizeof(*capsule), "aclblasLtMatrixLayoutImpl too large, not fit in capsule!");
582- aclblasStatus_t copyStatus = CheckedMemcpyS(capsule, sizeof(*capsule), &impl, sizeof(impl));
583- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
584- delete capsule;
585- return copyStatus;
586- }
587- if (sizeof(*capsule) > sizeof(impl)) {
588- copyStatus = CheckedMemsetS(
589- reinterpret_cast<char*>(capsule) + sizeof(impl), sizeof(*capsule) - sizeof(impl),
590- sizeof(*capsule) - sizeof(impl));
591- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
592- delete capsule;
593- return copyStatus;
594- }
595- }
596- // 5. 返回胶囊指针
597- *layout = capsule;
598- return ACLBLAS_STATUS_SUCCESS;
599-}
600- 
601-aclblasStatus_t aclblasLtMatrixLayoutDestroy(const aclblasLtMatrixLayout_t layout)
602-{
603- if (layout == nullptr) {
604- return ACLBLAS_STATUS_INVALID_VALUE;
605- }
606- 
607- auto* capsule = reinterpret_cast<aclblasLtMatrixLayoutOpaque_t*>(layout);
608- delete capsule;
609- 
610- return ACLBLAS_STATUS_SUCCESS;
611-}
612- 
613-aclblasStatus_t aclblasLtMatrixLayoutSetAttribute(
614- aclblasLtMatrixLayout_t layout, aclblasLtMatrixLayoutAttribute_t attr, const void* buf, size_t sizeInBytes)
615-{
616- if (layout == nullptr || buf == nullptr) {
617- return ACLBLAS_STATUS_INVALID_VALUE;
618- }
619- 
620- // 解包到栈上
621- aclblasLtMatrixLayoutImpl impl;
622- aclblasStatus_t copyStatus = CheckedMemcpyS(&impl, sizeof(impl), layout, sizeof(impl));
623- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
624- return copyStatus;
625- }
626- 
627- switch (attr) {
628- case ACLBLASLT_MATRIX_LAYOUT_TYPE:
629- if (sizeInBytes != sizeof(impl.type)) {
630- return ACLBLAS_STATUS_INVALID_VALUE;
631- }
632- impl.type = *reinterpret_cast<const aclDataType*>(buf);
633- break;
634- 
635- case ACLBLASLT_MATRIX_LAYOUT_ROWS:
636- if (sizeInBytes != sizeof(impl.rows)) {
637- return ACLBLAS_STATUS_INVALID_VALUE;
638- }
639- impl.rows = *reinterpret_cast<const uint64_t*>(buf);
640- break;
641- 
642- case ACLBLASLT_MATRIX_LAYOUT_COLS:
643- if (sizeInBytes != sizeof(impl.cols)) {
644- return ACLBLAS_STATUS_INVALID_VALUE;
645- }
646- impl.cols = *reinterpret_cast<const uint64_t*>(buf);
647- break;
648- 
649- case ACLBLASLT_MATRIX_LAYOUT_LD:
650- if (sizeInBytes != sizeof(impl.ld)) {
651- return ACLBLAS_STATUS_INVALID_VALUE;
652- }
653- impl.ld = *reinterpret_cast<const int64_t*>(buf);
654- break;
655- 
656- case ACLBLASLT_MATRIX_LAYOUT_ORDER:
657- if (sizeInBytes != sizeof(impl.order)) {
658- return ACLBLAS_STATUS_INVALID_VALUE;
659- }
660- impl.order = *reinterpret_cast<const aclblasLtOrder_t*>(buf);
661- break;
662- 
663- case ACLBLASLT_MATRIX_LAYOUT_BATCH_COUNT:
664- if (sizeInBytes != sizeof(impl.batchCount)) {
665- return ACLBLAS_STATUS_INVALID_VALUE;
666- }
667- impl.batchCount = *reinterpret_cast<const int32_t*>(buf);
668- break;
669- 
670- case ACLBLASLT_MATRIX_LAYOUT_STRIDED_BATCH_OFFSET:
671- if (sizeInBytes != sizeof(impl.stridedBatchOffset)) {
672- return ACLBLAS_STATUS_INVALID_VALUE;
673- }
674- impl.stridedBatchOffset = *reinterpret_cast<const int64_t*>(buf);
675- break;
676- 
677- default:
678- return ACLBLAS_STATUS_INVALID_VALUE;
679- }
680- 
681- // 压缩回堆上
682- copyStatus = CheckedMemcpyS(layout, sizeof(*layout), &impl, sizeof(impl));
683- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
684- return copyStatus;
685- }
686- if (sizeof(*layout) > sizeof(impl)) {
687- copyStatus = CheckedMemsetS(
688- reinterpret_cast<char*>(layout) + sizeof(impl), sizeof(*layout) - sizeof(impl),
689- sizeof(*layout) - sizeof(impl));
690- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
691- return copyStatus;
692- }
693- }
694- 
695- return ACLBLAS_STATUS_SUCCESS;
696-}
697- 
698-aclblasStatus_t aclblasLtMatrixLayoutGetAttribute(
699- const aclblasLtMatrixLayout_t layout, aclblasLtMatrixLayoutAttribute_t attr, void* buf, size_t sizeInBytes,
700- size_t* sizeWritten)
701-{
702- if (layout == nullptr || buf == nullptr) {
703- return ACLBLAS_STATUS_INVALID_VALUE;
704- }
705- 
706- aclblasLtMatrixLayoutImpl impl;
707- static_assert(sizeof(impl) <= sizeof(*layout), "aclblasLtMatrixLayoutImpl too large for capsule");
708- aclblasStatus_t copyStatus = CheckedMemcpyS(&impl, sizeof(impl), layout, sizeof(impl));
709- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
710- return copyStatus;
711- }
712- 
713- size_t actualSize = 0;
714- 
715- switch (attr) {
716- case ACLBLASLT_MATRIX_LAYOUT_TYPE:
717- actualSize = sizeof(impl.type);
718- if (sizeInBytes < actualSize) {
719- return ACLBLAS_STATUS_INVALID_VALUE;
720- }
721- *reinterpret_cast<aclDataType*>(buf) = impl.type;
722- break;
723- 
724- case ACLBLASLT_MATRIX_LAYOUT_ROWS:
725- actualSize = sizeof(impl.rows);
726- if (sizeInBytes < actualSize) {
727- return ACLBLAS_STATUS_INVALID_VALUE;
728- }
729- *reinterpret_cast<uint64_t*>(buf) = impl.rows;
730- break;
731- 
732- case ACLBLASLT_MATRIX_LAYOUT_COLS:
733- actualSize = sizeof(impl.cols);
734- if (sizeInBytes < actualSize) {
735- return ACLBLAS_STATUS_INVALID_VALUE;
736- }
737- *reinterpret_cast<uint64_t*>(buf) = impl.cols;
738- break;
739- 
740- case ACLBLASLT_MATRIX_LAYOUT_LD:
741- actualSize = sizeof(impl.ld);
742- if (sizeInBytes < actualSize) {
743- return ACLBLAS_STATUS_INVALID_VALUE;
744- }
745- *reinterpret_cast<int64_t*>(buf) = impl.ld;
746- break;
747- 
748- case ACLBLASLT_MATRIX_LAYOUT_ORDER:
749- actualSize = sizeof(impl.order);
750- if (sizeInBytes < actualSize) {
751- return ACLBLAS_STATUS_INVALID_VALUE;
752- }
753- *reinterpret_cast<aclblasLtOrder_t*>(buf) = impl.order;
754- break;
755- 
756- case ACLBLASLT_MATRIX_LAYOUT_BATCH_COUNT:
757- actualSize = sizeof(impl.batchCount);
758- if (sizeInBytes < actualSize) {
759- return ACLBLAS_STATUS_INVALID_VALUE;
760- }
761- *reinterpret_cast<int32_t*>(buf) = impl.batchCount;
762- break;
763- 
764- case ACLBLASLT_MATRIX_LAYOUT_STRIDED_BATCH_OFFSET:
765- actualSize = sizeof(impl.stridedBatchOffset);
766- if (sizeInBytes < actualSize) {
767- return ACLBLAS_STATUS_INVALID_VALUE;
768- }
769- *reinterpret_cast<int64_t*>(buf) = impl.stridedBatchOffset;
770- break;
771- 
772- default:
773- return ACLBLAS_STATUS_INVALID_VALUE;
774- }
775- 
776- if (sizeWritten != nullptr) {
777- *sizeWritten = actualSize;
778- }
779- 
780- return ACLBLAS_STATUS_SUCCESS;
781-}
782- 
783-aclblasStatus_t aclblasLtMatmulDescCreate(
784- aclblasLtMatmulDesc_t* desc, aclblasComputeType_t computeType, aclDataType scaleType)
785-{
786- if (desc == nullptr) {
787- return ACLBLAS_STATUS_INVALID_VALUE;
788- }
789- *desc = nullptr;
790- 
791- auto* capsule = new (std::nothrow) aclblasLtMatmulDescOpaque_t();
792- if (capsule == nullptr) {
793- return ACLBLAS_STATUS_ALLOC_FAILED;
794- }
795- 
796- aclblasLtMatmulDescImpl impl;
797- impl.magic = ACLBLASLT_DESC_MAGIC;
798- impl.computeType = computeType;
799- impl.scaleType = scaleType;
800- 
801- static_assert(sizeof(impl) <= sizeof(*capsule), "aclblasLtMatmulDescImpl too large, not fit in capsule!");
802- aclblasStatus_t copyStatus = CheckedMemcpyS(capsule, sizeof(*capsule), &impl, sizeof(impl));
803- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
804- delete capsule;
805- return copyStatus;
806- }
807- if (sizeof(*capsule) > sizeof(impl)) {
808- copyStatus = CheckedMemsetS(
809- reinterpret_cast<char*>(capsule) + sizeof(impl), sizeof(*capsule) - sizeof(impl),
810- sizeof(*capsule) - sizeof(impl));
811- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
812- delete capsule;
813- return copyStatus;
814- }
815- }
816- 
817- *desc = capsule;
818- return ACLBLAS_STATUS_SUCCESS;
819-}
820- 
821-aclblasStatus_t aclblasLtMatmulDescDestroy(const aclblasLtMatmulDesc_t desc)
822-{
823- if (desc == nullptr) {
824- return ACLBLAS_STATUS_INVALID_VALUE;
825- }
826- 
827- auto* capsule = reinterpret_cast<aclblasLtMatmulDescOpaque_t*>(desc);
828- delete capsule;
829- return ACLBLAS_STATUS_SUCCESS;
830-}
831- 
832-aclblasStatus_t aclblasLtMatmulDescSetAttribute(
833- aclblasLtMatmulDesc_t desc, aclblasLtMatmulDescAttribute_t attr, const void* buf, size_t sizeInBytes)
834-{
835- if (desc == nullptr || buf == nullptr) {
836- return ACLBLAS_STATUS_INVALID_VALUE;
837- }
838- 
839- aclblasLtMatmulDescImpl impl;
840- aclblasStatus_t copyStatus = CheckedMemcpyS(&impl, sizeof(impl), desc, sizeof(impl));
841- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
842- return copyStatus;
843- }
844- 
845- switch (attr) {
846- case ACLBLASLT_MATMUL_DESC_EPILOGUE: {
847- if (sizeInBytes != sizeof(aclblasLtEpilogue_t) && sizeInBytes != sizeof(uint32_t)) {
848- return ACLBLAS_STATUS_INVALID_VALUE;
849- }
850- uint32_t v = 0;
851- copyStatus = CheckedMemcpyS(&v, sizeof(v), buf, sizeof(uint32_t));
852- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
853- return copyStatus;
854- }
855- impl.epilogue = static_cast<aclblasLtEpilogue_t>(v);
856- break;
857- }
858- case ACLBLASLT_MATMUL_DESC_BIAS_POINTER: {
859- if (sizeInBytes != kBiasPtrStorageBytes) {
860- return ACLBLAS_STATUS_INVALID_VALUE;
861- }
862- void* biasPtr = nullptr;
863- copyStatus = CheckedMemcpyS(&biasPtr, kBiasPtrStorageBytes, buf, kBiasPtrStorageBytes);
864- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
865- return copyStatus;
866- }
867- impl.bias = biasPtr;
868- break;
869- }
870- case ACLBLASLT_MATMUL_DESC_TRANSA: {
871- if (sizeInBytes != sizeof(int32_t)) {
872- return ACLBLAS_STATUS_INVALID_VALUE;
873- }
874- int32_t v = 0;
875- copyStatus = CheckedMemcpyS(&v, sizeof(v), buf, sizeof(int32_t));
876- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
877- return copyStatus;
878- }
879- impl.transA = static_cast<aclblasOperation_t>(v);
880- break;
881- }
882- case ACLBLASLT_MATMUL_DESC_TRANSB: {
883- if (sizeInBytes != sizeof(int32_t)) {
884- return ACLBLAS_STATUS_INVALID_VALUE;
885- }
886- int32_t v = 0;
887- copyStatus = CheckedMemcpyS(&v, sizeof(v), buf, sizeof(int32_t));
888- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
889- return copyStatus;
890- }
891- impl.transB = static_cast<aclblasOperation_t>(v);
892- break;
893- }
894- case ACLBLASLT_MATMUL_DESC_BIAS_DATA_TYPE: {
895- if (sizeInBytes != sizeof(int32_t)) {
896- return ACLBLAS_STATUS_INVALID_VALUE;
897- }
898- int32_t v = 0;
899- copyStatus = CheckedMemcpyS(&v, sizeof(v), buf, sizeof(int32_t));
900- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
901- return copyStatus;
902- }
903- impl.biasDataType = static_cast<aclDataType>(v);
904- break;
905- }
906- case ACLBLASLT_MATMUL_DESC_A_SCALE_POINTER: {
907- if (sizeInBytes != sizeof(void*)) {
908- return ACLBLAS_STATUS_INVALID_VALUE;
909- }
910- void* scalePtr = nullptr;
911- copyStatus = CheckedMemcpyS(&scalePtr, sizeof(void*), buf, sizeof(void*));
912- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
913- return copyStatus;
914- }
915- impl.scaleA = scalePtr;
916- break;
917- }
918- case ACLBLASLT_MATMUL_DESC_B_SCALE_POINTER: {
919- if (sizeInBytes != sizeof(void*)) {
920- return ACLBLAS_STATUS_INVALID_VALUE;
921- }
922- void* scalePtr = nullptr;
923- copyStatus = CheckedMemcpyS(&scalePtr, sizeof(void*), buf, sizeof(void*));
924- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
925- return copyStatus;
926- }
927- impl.scaleB = scalePtr;
928- break;
929- }
930- case ACLBLASLT_MATMUL_DESC_A_SCALE_MODE:
931- case ACLBLASLT_MATMUL_DESC_B_SCALE_MODE:
932- break;
933- default:
934- return ACLBLAS_STATUS_NOT_SUPPORTED;
935- }
936- 
937- copyStatus = CheckedMemcpyS(desc, sizeof(*desc), &impl, sizeof(impl));
938- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
939- return copyStatus;
940- }
941- if (sizeof(*desc) > sizeof(impl)) {
942- copyStatus = CheckedMemsetS(
943- reinterpret_cast<char*>(desc) + sizeof(impl), sizeof(*desc) - sizeof(impl),
944- sizeof(*desc) - sizeof(impl));
945- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
946- return copyStatus;
947- }
948- }
949- 
950- return ACLBLAS_STATUS_SUCCESS;
951-}
952- 
953-aclblasStatus_t aclblasLtMatmulDescGetAttribute(
954- aclblasLtMatmulDesc_t desc, aclblasLtMatmulDescAttribute_t attr, void* buf, size_t sizeInBytes, size_t* sizeWritten)
955-{
956- if (desc == nullptr || buf == nullptr) {
957- return ACLBLAS_STATUS_INVALID_VALUE;
958- }
959- 
960- aclblasLtMatmulDescImpl impl;
961- aclblasStatus_t copyStatus = CheckedMemcpyS(&impl, sizeof(impl), desc, sizeof(impl));
962- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
963- return copyStatus;
964- }
965- 
966- size_t requiredSize = 0;
967- const void* srcPtr = nullptr;
968- 
969- switch (attr) {
970- case ACLBLASLT_MATMUL_DESC_EPILOGUE:
971- requiredSize = sizeof(impl.epilogue);
972- srcPtr = &impl.epilogue;
973- break;
974- 
975- case ACLBLASLT_MATMUL_DESC_BIAS_POINTER:
976- requiredSize = kBiasPtrStorageBytes;
977- srcPtr = &impl.bias;
978- break;
979- 
980- case ACLBLASLT_MATMUL_DESC_TRANSA:
981- requiredSize = sizeof(impl.transA);
982- srcPtr = &impl.transA;
983- break;
984- 
985- case ACLBLASLT_MATMUL_DESC_TRANSB:
986- requiredSize = sizeof(impl.transB);
987- srcPtr = &impl.transB;
988- break;
989- 
990- case ACLBLASLT_MATMUL_DESC_BIAS_DATA_TYPE:
991- requiredSize = sizeof(impl.biasDataType);
992- srcPtr = &impl.biasDataType;
993- break;
994- 
995- case ACLBLASLT_MATMUL_DESC_A_SCALE_POINTER:
996- requiredSize = sizeof(impl.scaleA);
997- srcPtr = &impl.scaleA;
998- break;
999- 
1000- case ACLBLASLT_MATMUL_DESC_B_SCALE_POINTER:
1001- requiredSize = sizeof(impl.scaleB);
1002- srcPtr = &impl.scaleB;
1003- break;
1004- 
1005- default:
1006- return ACLBLAS_STATUS_NOT_SUPPORTED;
1007- }
1008- 
1009- // 检查用户缓冲区大小
1010- if (sizeInBytes < requiredSize) {
1011- if (sizeWritten != nullptr) {
1012- *sizeWritten = requiredSize;
1013- }
1014- return ACLBLAS_STATUS_INVALID_VALUE;
1015- }
1016- 
1017- copyStatus = CheckedMemcpyS(buf, sizeInBytes, srcPtr, requiredSize);
1018- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1019- return copyStatus;
1020- }
1021- 
1022- if (sizeWritten != nullptr) {
1023- *sizeWritten = requiredSize;
1024- }
1025- 
1026- return ACLBLAS_STATUS_SUCCESS;
1027-}
1028- 
1029-aclblasStatus_t aclblasLtMatmulPreferenceCreate(aclblasLtMatmulPreference_t* pref)
1030-{
1031- if (pref == nullptr) {
1032- return ACLBLAS_STATUS_INVALID_VALUE;
1033- }
1034- 
1035- *pref = nullptr;
1036- auto* capsule = new (std::nothrow) aclblasLtMatmulPreferenceOpaque_t();
1037- if (capsule == nullptr) {
1038- return ACLBLAS_STATUS_ALLOC_FAILED;
1039- }
1040- aclblasStatus_t copyStatus = CheckedMemsetS(capsule, sizeof(*capsule), sizeof(*capsule));
1041- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1042- delete capsule;
1043- return copyStatus;
1044- }
1045- 
1046- aclblasLtMatmulPreferenceImpl impl;
1047- copyStatus = CheckedMemcpyS(capsule, sizeof(*capsule), &impl, sizeof(impl));
1048- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1049- delete capsule;
1050- return copyStatus;
1051- }
1052- if (sizeof(*capsule) > sizeof(impl)) {
1053- copyStatus = CheckedMemsetS(
1054- reinterpret_cast<char*>(capsule) + sizeof(impl), sizeof(*capsule) - sizeof(impl),
1055- sizeof(*capsule) - sizeof(impl));
1056- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1057- delete capsule;
1058- return copyStatus;
1059- }
1060- }
1061- 
1062- *pref = capsule;
1063- return ACLBLAS_STATUS_SUCCESS;
1064-}
1065- 
1066-aclblasStatus_t aclblasLtMatmulPreferenceDestroy(const aclblasLtMatmulPreference_t pref)
1067-{
1068- if (pref == nullptr) {
1069- return ACLBLAS_STATUS_INVALID_VALUE;
1070- }
1071- 
1072- auto* capsule = reinterpret_cast<aclblasLtMatmulPreferenceOpaque_t*>(pref);
1073- delete capsule;
1074- return ACLBLAS_STATUS_SUCCESS;
1075-}
1076- 
1077-aclblasStatus_t aclblasLtMatmulPreferenceSetAttribute(
1078- aclblasLtMatmulPreference_t pref, aclblasLtMatmulPreferenceAttribute_t attr, const void* buf, size_t sizeInBytes)
1079-{
1080- if (pref == nullptr || buf == nullptr) {
1081- return ACLBLAS_STATUS_INVALID_VALUE;
1082- }
1083- 
1084- aclblasLtMatmulPreferenceImpl impl;
1085- aclblasStatus_t copyStatus = CheckedMemcpyS(&impl, sizeof(impl), pref, sizeof(impl));
1086- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1087- return copyStatus;
1088- }
1089- 
1090- switch (attr) {
1091- case ACLBLASLT_MATMUL_PREF_SEARCH_MODE: {
1092- if (sizeInBytes != sizeof(uint32_t)) {
1093- return ACLBLAS_STATUS_INVALID_VALUE;
1094- }
1095- uint32_t v = 0;
1096- copyStatus = CheckedMemcpyS(&v, sizeof(v), buf, sizeof(v));
1097- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1098- return copyStatus;
1099- }
1100- // 0=heuristic, 1=exhaustive, 2=fast
1101- if (v > 2) {
1102- return ACLBLAS_STATUS_INVALID_VALUE;
1103- }
1104- impl.searchMode = v;
1105- break;
1106- }
1107- 
1108- case ACLBLASLT_MATMUL_PREF_MAX_WORKSPACE_BYTES: {
1109- if (sizeInBytes != sizeof(size_t) && sizeInBytes != sizeof(uint64_t)) {
1110- return ACLBLAS_STATUS_INVALID_VALUE;
1111- }
1112- size_t v = 0;
1113- const size_t copyBytes = std::min(sizeInBytes, sizeof(v));
1114- copyStatus = CheckedMemcpyS(&v, sizeof(v), buf, copyBytes);
1115- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1116- return copyStatus;
1117- }
1118- if (v > INT64_MAX) {
1119- return ACLBLAS_STATUS_INVALID_VALUE;
1120- }
1121- impl.maxWorkspaceBytes = v;
1122- break;
1123- }
1124- 
1125- default:
1126- return ACLBLAS_STATUS_NOT_SUPPORTED;
1127- }
1128- 
1129- copyStatus = CheckedMemcpyS(pref, sizeof(*pref), &impl, sizeof(impl));
1130- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1131- return copyStatus;
1132- }
1133- 
1134- return ACLBLAS_STATUS_SUCCESS;
1135-}
1136- 
1137-aclblasStatus_t aclblasLtMatmulPreferenceGetAttribute(
1138- aclblasLtMatmulPreference_t pref, aclblasLtMatmulPreferenceAttribute_t attr, void* buf, size_t sizeInBytes,
1139- size_t* sizeWritten)
1140-{
1141- if (pref == nullptr || buf == nullptr) {
1142- return ACLBLAS_STATUS_INVALID_VALUE;
1143- }
1144- 
1145- aclblasLtMatmulPreferenceImpl impl;
1146- aclblasStatus_t copyStatus = CheckedMemcpyS(&impl, sizeof(impl), pref, sizeof(impl));
1147- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1148- return copyStatus;
1149- }
1150- 
1151- size_t requiredSize = 0;
1152- const void* srcPtr = nullptr;
1153- 
1154- switch (attr) {
1155- case ACLBLASLT_MATMUL_PREF_SEARCH_MODE:
1156- requiredSize = sizeof(impl.searchMode);
1157- srcPtr = &impl.searchMode;
1158- break;
1159- 
1160- case ACLBLASLT_MATMUL_PREF_MAX_WORKSPACE_BYTES:
1161- requiredSize = sizeof(impl.maxWorkspaceBytes);
1162- srcPtr = &impl.maxWorkspaceBytes;
1163- break;
1164- 
1165- default:
1166- return ACLBLAS_STATUS_NOT_SUPPORTED;
1167- }
1168- 
1169- if (sizeInBytes < requiredSize) {
1170- if (sizeWritten != nullptr) {
1171- *sizeWritten = requiredSize;
1172- }
1173- return ACLBLAS_STATUS_INVALID_VALUE;
1174- }
1175- 
1176- copyStatus = CheckedMemcpyS(buf, sizeInBytes, srcPtr, requiredSize);
1177- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1178- return copyStatus;
1179- }
1180- 
1181- if (sizeWritten != nullptr) {
1182- *sizeWritten = requiredSize;
1183- }
1184- 
1185- return ACLBLAS_STATUS_SUCCESS;
1186-}
1187- 
1188-aclblasStatus_t aclblasLtMatmulAlgoGetHeuristic(
1189- aclblasLtHandle_t lightHandle, aclblasLtMatmulDesc_t computeDesc, aclblasLtMatrixLayout_t Adesc,
1190- aclblasLtMatrixLayout_t Bdesc, aclblasLtMatrixLayout_t Cdesc, aclblasLtMatrixLayout_t Ddesc,
1191- aclblasLtMatmulPreference_t preference, int requestedAlgoCount,
1192- aclblasLtMatmulHeuristicResult_t heuristicResultsArray[], int* returnAlgoCount)
1193-{
1194- // Validate input parameters
1195- if (returnAlgoCount == nullptr) {
1196- return ACLBLAS_STATUS_INVALID_VALUE;
1197- }
1198- *returnAlgoCount = 0;
1199- 
1200- if (requestedAlgoCount <= 0 || heuristicResultsArray == nullptr) {
1201- return ACLBLAS_STATUS_INVALID_VALUE;
1202- }
1203- 
1204- if (lightHandle == nullptr || computeDesc == nullptr) {
1205- return ACLBLAS_STATUS_INVALID_VALUE;
1206- }
1207- 
1208- if (Adesc == nullptr || Bdesc == nullptr || Cdesc == nullptr || Ddesc == nullptr) {
1209- return ACLBLAS_STATUS_INVALID_VALUE;
1210- }
1211- 
1212- // Get workspace size from preference
1213- size_t maxWorkspace = 0;
1214- if (preference != nullptr) {
1215- auto* p = reinterpret_cast<aclblasLtMatmulPreferenceImpl*>(preference);
1216- maxWorkspace = p->maxWorkspaceBytes;
1217- }
1218- 
1219- // Get matrix dimensions
1220- auto* A = reinterpret_cast<aclblasLtMatrixLayoutImpl*>(Adesc);
1221- auto* B = reinterpret_cast<aclblasLtMatrixLayoutImpl*>(Bdesc);
1222- auto* D = reinterpret_cast<aclblasLtMatrixLayoutImpl*>(Ddesc);
1223- auto* desc = reinterpret_cast<aclblasLtMatmulDescImpl*>(computeDesc);
1224- 
1225- // Validate dimensions for GEMM: D = A * B + C
1226- // A: m x k, B: k x n, C/D: m x n
1227- uint64_t m = D->rows;
1228- uint64_t n = D->cols;
1229- uint64_t k = (desc->transA == ACLBLAS_OP_N) ? A->cols : A->rows;
1230- 
1231- // Basic validation
1232- if (!CheckComputeTypeCompatibility(desc->computeType, A->type, B->type)) {
1233- heuristicResultsArray[0].state = ACLBLAS_STATUS_INVALID_VALUE;
1234- return ACLBLAS_STATUS_INVALID_VALUE;
1235- }
1236- 
1237- // Fill heuristic result
1238- heuristicResultsArray[0].algo.max_workspace_bytes = maxWorkspace;
1239- heuristicResultsArray[0].workspaceSize = maxWorkspace;
1240- heuristicResultsArray[0].state = ACLBLAS_STATUS_SUCCESS;
1241- heuristicResultsArray[0].wavesCount = 1.0f;
1242- if (CheckedMemsetS(
1243- heuristicResultsArray[0].reserved, sizeof(heuristicResultsArray[0].reserved),
1244- sizeof(heuristicResultsArray[0].reserved)) != ACLBLAS_STATUS_SUCCESS) {
1245- return ACLBLAS_STATUS_INTERNAL_ERROR;
1246- }
1247- 
1248- *returnAlgoCount = 1;
1249- return ACLBLAS_STATUS_SUCCESS;
1250-}
1251- 
1252-aclblasStatus_t aclblasLtMatmul(
1253- aclblasLtHandle_t lightHandle, aclblasLtMatmulDesc_t computeDesc, const void* alpha, const void* A,
1254- aclblasLtMatrixLayout_t Adesc, const void* B, aclblasLtMatrixLayout_t Bdesc, const void* beta, const void* C,
1255- aclblasLtMatrixLayout_t Cdesc, void* D, aclblasLtMatrixLayout_t Ddesc, const aclblasLtMatmulAlgo_t* algo,
1256- void* workspace, size_t workspaceSizeInBytes, aclrtStream stream)
1257-{
1258- // Validate lightHandle
1259- if (lightHandle == nullptr) {
1260- return ACLBLAS_STATUS_NOT_INITIALIZED;
1261- }
1262- 
1263- // Validate descriptors
1264- if (computeDesc == nullptr || Adesc == nullptr || Bdesc == nullptr || Cdesc == nullptr || Ddesc == nullptr) {
1265- return ACLBLAS_STATUS_INVALID_VALUE;
1266- }
1267- 
1268- // Validate pointers
1269- if (alpha == nullptr || beta == nullptr) {
1270- return ACLBLAS_STATUS_INVALID_VALUE;
1271- }
1272- 
1273- // Get layout info
1274- auto* ALayout = reinterpret_cast<aclblasLtMatrixLayoutImpl*>(Adesc);
1275- auto* BLayout = reinterpret_cast<aclblasLtMatrixLayoutImpl*>(Bdesc);
1276- auto* CLayout = reinterpret_cast<aclblasLtMatrixLayoutImpl*>(Cdesc);
1277- auto* DLayout = reinterpret_cast<aclblasLtMatrixLayoutImpl*>(Ddesc);
1278- auto* desc = reinterpret_cast<aclblasLtMatmulDescImpl*>(computeDesc);
1279- 
1280- // Get dimensions
1281- uint64_t m = DLayout->rows;
1282- uint64_t n = DLayout->cols;
1283- uint64_t k = 0;
1284- 
1285- // Determine k based on transpose operations
1286- if (desc->transA == ACLBLAS_OP_N) {
1287- k = ALayout->cols;
1288- } else {
1289- k = ALayout->rows;
1290- }
1291- 
1292- // BLAS/cuBLAS convention: m=0 or n=0 is a no-op, succeed without touching matrices.
1293- if (m == 0U || n == 0U) {
1294- return ACLBLAS_STATUS_SUCCESS;
1295- }
1296- 
1297- if (A == nullptr || B == nullptr || D == nullptr) {
1298- return ACLBLAS_STATUS_INVALID_VALUE;
1299- }
1300- 
1301- // Validate workspace alignment (must be 16B aligned)
1302- if (workspace != nullptr && (reinterpret_cast<uintptr_t>(workspace) & 0xF) != 0) {
1303- return ACLBLAS_STATUS_INVALID_VALUE;
1304- }
1305- 
1306- // Validate workspace size
1307- if (algo != nullptr && workspaceSizeInBytes < algo->max_workspace_bytes) {
1308- return ACLBLAS_STATUS_INVALID_VALUE;
1309- }
1310- 
1311- // Actual GEMM implementation using ltmatmul routing
1312- auto* handleImpl = reinterpret_cast<aclblasLtHandle*>(lightHandle);
1313- int32_t deviceId = handleImpl->deviceId;
1314- int64_t cubeCoreNum = 0;
1315- aclError aclRet = aclrtGetDeviceInfo(deviceId, ACL_DEV_ATTR_CUBE_CORE_NUM, &cubeCoreNum);
1316- if (aclRet != ACL_SUCCESS || cubeCoreNum <= 0) {
1317- cubeCoreNum = 8; // Fallback minimum
1318- }
1319- uint32_t numBlocks = static_cast<uint32_t>(cubeCoreNum);
1320- 
1321- float alphaValue = *(reinterpret_cast<const float*>(alpha));
1322- float betaValue = *(reinterpret_cast<const float*>(beta));
1323- bool needEpilogue = (alphaValue != 1.0f || betaValue != 0.0f);
1324- bool cOverlap = (C == D) && needEpilogue;
1325- void* dRawAddr = needEpilogue && cOverlap ? workspace : D;
1326- 
1327- aclDataType dtypeA = ALayout->type;
1328- aclDataType dtypeB = BLayout->type;
1329- aclDataType dtypeD = DLayout->type;
1330- bool transA = (desc->transA != ACLBLAS_OP_N);
1331- bool transB = (desc->transB != ACLBLAS_OP_N);
1332- 
1333- if ((IsMxfp8Type(dtypeA) || IsMxfp4Type(dtypeA)) && (k % 32 != 0)) {
1334- return ACLBLAS_STATUS_INVALID_VALUE;
1335- }
1336- 
1337- // ===== Step 1: MMAD Kernel =====
1338- if (dtypeA == ACL_FLOAT && dtypeB == ACL_FLOAT) {
1339- MatmulFp32TilingData fp32Tiling;
1340- matmul_fp32_get_tiling(
1341- m, n, k, transA, transB, static_cast<uint32_t>(ALayout->ld), static_cast<uint32_t>(BLayout->ld), numBlocks,
1342- fp32Tiling);
1343- matmul_fp32_kernel_do(
1344- static_cast<uint8_t*>(const_cast<void*>(A)),
1345- static_cast<uint8_t*>(const_cast<void*>(B)),
1346- static_cast<uint8_t*>(dRawAddr),
1347- fp32Tiling, numBlocks, stream);
1348- } else if (IsMxfp8Type(dtypeA) && IsMxfp8Type(dtypeB)) {
1349- void* scaleA = const_cast<void*>(desc->scaleA);
1350- void* scaleB = const_cast<void*>(desc->scaleB);
1351- if (scaleA == nullptr || scaleB == nullptr) {
1352- return ACLBLAS_STATUS_INVALID_VALUE;
1353- }
1354- QuantMatmulTilingData mxfp8Tiling;
1355- matmul_mxfp8_get_tiling(m, n, k, transA, transB, numBlocks, mxfp8Tiling);
1356- matmul_mxfp8_kernel_do(
1357- static_cast<uint8_t*>(const_cast<void*>(A)),
1358- static_cast<uint8_t*>(const_cast<void*>(B)),
1359- static_cast<uint8_t*>(scaleA),
1360- static_cast<uint8_t*>(scaleB),
1361- static_cast<uint8_t*>(dRawAddr),
1362- mxfp8Tiling, transA, transB, stream);
1363- } else if (IsMxfp4Type(dtypeA) && IsMxfp4Type(dtypeB)) {
1364- void* scaleA = const_cast<void*>(desc->scaleA);
1365- void* scaleB = const_cast<void*>(desc->scaleB);
1366- if (scaleA == nullptr || scaleB == nullptr) {
1367- return ACLBLAS_STATUS_INVALID_VALUE;
1368- }
1369- QuantMatmulTilingData mxfp4Tiling;
1370- matmul_mxfp4_get_tiling(m, n, k, transA, transB, numBlocks, mxfp4Tiling);
1371- ltmatmul_mxfp4_kernel_do(
1372- static_cast<uint8_t*>(const_cast<void*>(A)),
1373- static_cast<uint8_t*>(const_cast<void*>(B)),
1374- static_cast<uint8_t*>(scaleA),
1375- static_cast<uint8_t*>(scaleB),
1376- static_cast<uint8_t*>(dRawAddr),
1377- mxfp4Tiling, dtypeA, dtypeB, dtypeD, transA, transB, stream);
1378- } else {
1379- return ACLBLAS_STATUS_NOT_SUPPORTED;
1380- }
1381- 
1382- // ===== Step 2: Epilogue alpha*D_raw + beta*C =====
1383- if (needEpilogue) {
1384- if (betaValue != 0.0f && C == nullptr) {
1385- return ACLBLAS_STATUS_INVALID_VALUE;
1386- }
1387- 
1388- aclDataType dtypeC = CLayout->type;
1389- aclDataType dtypeDRaw =
1390- (dtypeA == ACL_FLOAT && dtypeB == ACL_FLOAT) ? ACL_FLOAT : dtypeD;
1391- const uint32_t ldc = static_cast<uint32_t>(CLayout->ld > 0 ? CLayout->ld : n);
1392- const uint32_t ldd = static_cast<uint32_t>(DLayout->ld > 0 ? DLayout->ld : n);
1393- const uint32_t lddRaw = (dRawAddr == D) ? ldd : static_cast<uint32_t>(n);
1394- 
1395- if (cOverlap) {
1396- const size_t dRawElemSize = (dtypeDRaw == ACL_BF16) ? sizeof(uint16_t) : sizeof(float);
1397- const size_t requiredWorkspace = static_cast<size_t>(m) * static_cast<size_t>(n) * dRawElemSize;
1398- if (workspace == nullptr || workspaceSizeInBytes < requiredWorkspace) {
1399- return ACLBLAS_STATUS_INVALID_VALUE;
1400- }
1401- }
1402- 
1403- epilogue_alpha_beta_do(
1404- static_cast<uint8_t*>(dRawAddr),
1405- betaValue != 0.0f ? static_cast<uint8_t*>(const_cast<void*>(C)) : nullptr,
1406- static_cast<uint8_t*>(D),
1407- static_cast<uint32_t>(m), static_cast<uint32_t>(n),
1408- ldc, ldd, lddRaw,
1409- alphaValue, betaValue,
1410- dtypeC, dtypeDRaw, dtypeD,
1411- stream);
1412- }
1413- 
1414- return ACLBLAS_STATUS_SUCCESS;
1415-}
1416- 
1417-aclblasStatus_t aclblasLtMatrixTransformDescCreate(
1418- aclblasLtMatrixTransformDesc_t* transformDesc, aclDataType scaleType)
1419-{
1420- if (transformDesc == nullptr) {
1421- return ACLBLAS_STATUS_INVALID_VALUE;
1422- }
1423- *transformDesc = nullptr;
1424- 
1425- auto* capsule = new (std::nothrow) aclblasLtMatrixTransformDescOpaque_t();
1426- if (capsule == nullptr) {
1427- return ACLBLAS_STATUS_ALLOC_FAILED;
1428- }
1429- 
1430- aclblasLtMatrixTransformDescImpl impl;
1431- impl.magic = ACLBLASLT_TRANSFORM_DESC_MAGIC;
1432- impl.scaleType = scaleType;
1433- 
1434- aclblasStatus_t copyStatus = MatPackTransformImpl(capsule, sizeof(*capsule), &impl, sizeof(impl));
1435- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1436- delete capsule;
1437- return copyStatus;
1438- }
1439- 
1440- *transformDesc = capsule;
1441- return ACLBLAS_STATUS_SUCCESS;
1442-}
1443- 
1444-aclblasStatus_t aclblasLtMatrixTransformDescDestroy(const aclblasLtMatrixTransformDesc_t transformDesc)
1445-{
1446- if (transformDesc == nullptr) {
1447- return ACLBLAS_STATUS_INVALID_VALUE;
1448- }
1449- auto* capsule = reinterpret_cast<aclblasLtMatrixTransformDescOpaque_t*>(transformDesc);
1450- delete capsule;
1451- return ACLBLAS_STATUS_SUCCESS;
1452-}
1453- 
1454-aclblasStatus_t aclblasLtMatrixTransformDescSetAttribute(
1455- aclblasLtMatrixTransformDesc_t transformDesc, aclblasLtMatrixTransformDescAttribute_t attr, const void* buf,
1456- size_t sizeInBytes)
1457-{
1458- if (transformDesc == nullptr || buf == nullptr) {
1459- return ACLBLAS_STATUS_INVALID_VALUE;
1460- }
1461- 
1462- aclblasLtMatrixTransformDescImpl impl;
1463- aclblasStatus_t copyStatus = CheckedMemcpyS(&impl, sizeof(impl), transformDesc, sizeof(impl));
1464- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1465- return copyStatus;
1466- }
1467- if (impl.magic != ACLBLASLT_TRANSFORM_DESC_MAGIC) {
1468- return ACLBLAS_STATUS_INVALID_VALUE; // corrupted / foreign descriptor
1469- }
1470- if (sizeInBytes != sizeof(int32_t)) {
1471- return ACLBLAS_STATUS_INVALID_VALUE;
1472- }
1473- int32_t v = 0;
1474- copyStatus = CheckedMemcpyS(&v, sizeof(v), buf, sizeof(int32_t));
1475- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1476- return copyStatus;
1477- }
1478- 
1479- switch (attr) {
1480- case ACLBLASLT_MATRIX_TRANSFORM_DESC_SCALE_TYPE:
1481- impl.scaleType = static_cast<aclDataType>(v);
1482- break;
1483- case ACLBLASLT_MATRIX_TRANSFORM_DESC_POINTER_MODE:
1484- impl.pointerMode = v;
1485- break;
1486- case ACLBLASLT_MATRIX_TRANSFORM_DESC_TRANSA:
1487- impl.transA = static_cast<aclblasOperation_t>(v);
1488- break;
1489- case ACLBLASLT_MATRIX_TRANSFORM_DESC_TRANSB:
1490- impl.transB = static_cast<aclblasOperation_t>(v);
1491- break;
1492- default:
1493- return ACLBLAS_STATUS_NOT_SUPPORTED;
1494- }
1495- 
1496- return MatPackTransformImpl(transformDesc, sizeof(*transformDesc), &impl, sizeof(impl));
1497-}
1498- 
1499-aclblasStatus_t aclblasLtMatrixTransformDescGetAttribute(
1500- aclblasLtMatrixTransformDesc_t transformDesc, aclblasLtMatrixTransformDescAttribute_t attr, void* buf,
1501- size_t sizeInBytes, size_t* sizeWritten)
1502-{
1503- if (transformDesc == nullptr || buf == nullptr) {
1504- return ACLBLAS_STATUS_INVALID_VALUE;
1505- }
1506- 
1507- aclblasLtMatrixTransformDescImpl impl;
1508- aclblasStatus_t copyStatus = CheckedMemcpyS(&impl, sizeof(impl), transformDesc, sizeof(impl));
1509- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1510- return copyStatus;
1511- }
1512- if (impl.magic != ACLBLASLT_TRANSFORM_DESC_MAGIC) {
1513- return ACLBLAS_STATUS_INVALID_VALUE; // corrupted / foreign descriptor
1514- }
1515- 
1516- const void* srcPtr = nullptr;
1517- switch (attr) {
1518- case ACLBLASLT_MATRIX_TRANSFORM_DESC_SCALE_TYPE:
1519- srcPtr = &impl.scaleType;
1520- break;
1521- case ACLBLASLT_MATRIX_TRANSFORM_DESC_POINTER_MODE:
1522- srcPtr = &impl.pointerMode;
1523- break;
1524- case ACLBLASLT_MATRIX_TRANSFORM_DESC_TRANSA:
1525- srcPtr = &impl.transA;
1526- break;
1527- case ACLBLASLT_MATRIX_TRANSFORM_DESC_TRANSB:
1528- srcPtr = &impl.transB;
1529- break;
1530- default:
1531- return ACLBLAS_STATUS_NOT_SUPPORTED;
1532- }
1533- 
1534- const size_t requiredSize = sizeof(int32_t);
1535- if (sizeInBytes < requiredSize) {
1536- if (sizeWritten != nullptr) {
1537- *sizeWritten = requiredSize;
1538- }
1539- return ACLBLAS_STATUS_INVALID_VALUE;
1540- }
1541- copyStatus = CheckedMemcpyS(buf, sizeInBytes, srcPtr, requiredSize);
1542- if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
1543- return copyStatus;
1544- }
1545- if (sizeWritten != nullptr) {
1546- *sizeWritten = requiredSize;
1547- }
1548- return ACLBLAS_STATUS_SUCCESS;
1549-}
1550- 
1551-aclblasStatus_t aclblasLtMatrixTransform(
1552- aclblasLtHandle_t lightHandle, aclblasLtMatrixTransformDesc_t transformDesc, const void* alpha, const void* A,
1553- aclblasLtMatrixLayout_t Adesc, const void* beta, const void* B, aclblasLtMatrixLayout_t Bdesc, void* C,
1554- aclblasLtMatrixLayout_t Cdesc, aclrtStream stream)
1555-{
1556- if (lightHandle == nullptr) {
1557- return ACLBLAS_STATUS_NOT_INITIALIZED;
1558- }
1559- if (transformDesc == nullptr || Adesc == nullptr || Cdesc == nullptr) {
1560- return ACLBLAS_STATUS_INVALID_VALUE;
1561- }
1562- if (alpha == nullptr) {
1563- return ACLBLAS_STATUS_INVALID_VALUE;
1564- }
1565- 
1566- auto* desc = reinterpret_cast<aclblasLtMatrixTransformDescImpl*>(transformDesc);
1567- auto* ALayout = reinterpret_cast<aclblasLtMatrixLayoutImpl*>(Adesc);
1568- auto* CLayout = reinterpret_cast<aclblasLtMatrixLayoutImpl*>(Cdesc);
1569- if (desc->magic != ACLBLASLT_TRANSFORM_DESC_MAGIC || ALayout->magic != ACLBLASLT_LAYOUT_MAGIC ||
1570- CLayout->magic != ACLBLASLT_LAYOUT_MAGIC) {
1571- return ACLBLAS_STATUS_INVALID_VALUE; // corrupted / foreign descriptor or layout
1572- }
1573- 
1574- auto* handleImpl = reinterpret_cast<aclblasLtHandle*>(lightHandle);
1575- 
1576- const uint64_t rows = CLayout->rows;
1577- const uint64_t cols = CLayout->cols;
1578- if (rows == 0U || cols == 0U) {
1579- return ACLBLAS_STATUS_SUCCESS; // empty matrix, no-op (checked before B presence)
1580- }
1581- 
1582- const MatTransformLayout aPacked = MatPackTransformLayout(ALayout);
1583- const MatTransformLayout cPacked = MatPackTransformLayout(CLayout);
1584- auto* BLayout = reinterpret_cast<aclblasLtMatrixLayoutImpl*>(Bdesc);
1585- const MatTransformLayout bPacked = (BLayout != nullptr) ? MatPackTransformLayout(BLayout) : MatTransformLayout{};
1586- const bool bLayoutValid = (BLayout != nullptr) && BLayout->magic == ACLBLASLT_LAYOUT_MAGIC;
1587- 
1588- return MatTransformLaunch(
1589- handleImpl->deviceId, desc, alpha, A, &aPacked, beta, B, (BLayout != nullptr) ? &bPacked : nullptr,
1590- bLayoutValid, C, &cPacked, rows, cols, stream);
1591-}
1592- 
1593-} // extern "C"
@@ -0,0 +1,110 @@
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+/*!
12+ * \file aclblaslt_handle.cpp
13+ * \brief Public C API: handle lifecycle (create / destroy).
14+ */
15+ 
16+#include "cann_ops_blasLt.h"
17+ 
18+#include "aclblaslt_handle_impl.h"
19+#include "aclblaslt_layout_impl.h"
20+#include "host_utils.h"
21+ 
22+#include <acl/acl.h>
23+#include <cstdlib>
24+#include <list>
25+#include <mutex>
26+#include <new>
27+#include <unordered_map>
28+ 
29+extern "C" {
30+ 
31+aclblasStatus_t aclblasLtCreate(aclblasLtHandle_t* lightHandle)
32+{
33+ if (lightHandle == nullptr) {
34+ return ACLBLAS_STATUS_INVALID_VALUE;
35+ }
36+ 
37+ aclblasLtHandle* h = nullptr;
38+ auto st = AllocHandle(&h);
39+ if (st != ACLBLAS_STATUS_SUCCESS) {
40+ return st;
41+ }
42+ 
43+ int32_t deviceId = 0;
44+ aclError aclRet = aclrtGetDevice(&deviceId);
45+ if (aclRet != ACL_SUCCESS) {
46+ delete h;
47+ return ACLBLAS_STATUS_NOT_INITIALIZED;
48+ }
49+ 
50+ aclrtContext currentCtx = nullptr;
51+ aclRet = aclrtGetCurrentContext(&currentCtx);
52+ if (aclRet != ACL_SUCCESS || currentCtx == nullptr) {
53+ delete h;
54+ return ACLBLAS_STATUS_NOT_INITIALIZED;
55+ }
56+ 
57+ h->deviceId = deviceId;
58+ h->context = currentCtx;
59+ h->defaultStream = nullptr;
60+ h->workspaceSize = DEFAULT_WORKSPACE_SIZE;
61+ h->internalWorkspace = std::malloc(h->workspaceSize);
62+ if (h->internalWorkspace == nullptr) {
63+ delete h;
64+ return ACLBLAS_STATUS_ALLOC_FAILED;
65+ }
66+ 
67+ h->mutex = new (std::nothrow) std::mutex();
68+ h->algoCache = new (std::nothrow) std::unordered_map<AlgoKey, CacheEntry, AlgoKeyHasher>();
69+ h->lruList = new (std::nothrow) std::list<AlgoKey>();
70+ if (h->mutex == nullptr || h->algoCache == nullptr || h->lruList == nullptr) {
71+ delete h->mutex;
72+ delete h->algoCache;
73+ delete h->lruList;
74+ std::free(h->internalWorkspace);
75+ delete h;
76+ return ACLBLAS_STATUS_ALLOC_FAILED;
77+ }
78+ 
79+ h->npuArch = 2;
80+ h->maxSharedMemory = L1_SIZE;
81+ h->initialized = true;
82+ *lightHandle = reinterpret_cast<aclblasLtHandle_t>(h);
83+ return ACLBLAS_STATUS_SUCCESS;
84+}
85+ 
86+aclblasStatus_t aclblasLtDestroy(const aclblasLtHandle_t lightHandle)
87+{
88+ if (lightHandle == nullptr) {
89+ return ACLBLAS_STATUS_INVALID_VALUE;
90+ }
91+ 
92+ auto* h = reinterpret_cast<aclblasLtHandle*>(lightHandle);
93+ if (h->magic != ACLBLASLT_HANDLE_MAGIC) {
94+ return ACLBLAS_STATUS_INVALID_VALUE;
95+ }
96+ 
97+ h->initialized = false;
98+ delete h->mutex;
99+ delete h->algoCache;
100+ delete h->lruList;
101+ std::free(h->internalWorkspace);
102+ h->mutex = nullptr;
103+ h->algoCache = nullptr;
104+ h->lruList = nullptr;
105+ h->internalWorkspace = nullptr;
106+ h->workspaceSize = 0;
107+ return FreeHandle(h);
108+}
109+ 
110+} // extern "C"
@@ -0,0 +1,195 @@
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+/*!
12+ * \file aclblaslt_layout.cpp
13+ * \brief Public C API: matrix layout descriptor CRUD.
14+ */
15+ 
16+#include "cann_ops_blasLt.h"
17+ 
18+#include "aclblaslt_layout_impl.h"
19+#include "host_utils.h"
20+ 
21+#include <cstdint>
22+#include <new>
23+ 
24+namespace {
25+ 
26+// Validate that the caller buffer is exactly sizeof(T), then load it into field. Collapsing the
27+// per-attribute size check into one helper keeps the Set/Get switches free of nested branches.
28+template <typename T>
29+inline aclblasStatus_t LoadLayoutField(const void* buf, size_t sizeInBytes, T& field)
30+{
31+ if (sizeInBytes != sizeof(T)) {
32+ return ACLBLAS_STATUS_INVALID_VALUE;
33+ }
34+ field = *reinterpret_cast<const T*>(buf);
35+ return ACLBLAS_STATUS_SUCCESS;
36+}
37+ 
38+template <typename T>
39+inline aclblasStatus_t StoreLayoutField(void* buf, size_t sizeInBytes, const T& field, size_t& actualSize)
40+{
41+ actualSize = sizeof(T);
42+ if (sizeInBytes < actualSize) {
43+ return ACLBLAS_STATUS_INVALID_VALUE;
44+ }
45+ *reinterpret_cast<T*>(buf) = field;
46+ return ACLBLAS_STATUS_SUCCESS;
47+}
48+ 
49+aclblasStatus_t ApplyLayoutSetAttr(
50+ aclblasLtMatrixLayoutImpl& impl, aclblasLtMatrixLayoutAttribute_t attr, const void* buf, size_t sizeInBytes)
51+{
52+ switch (attr) {
53+ case ACLBLASLT_MATRIX_LAYOUT_TYPE:
54+ return LoadLayoutField(buf, sizeInBytes, impl.type);
55+ case ACLBLASLT_MATRIX_LAYOUT_ROWS:
56+ return LoadLayoutField(buf, sizeInBytes, impl.rows);
57+ case ACLBLASLT_MATRIX_LAYOUT_COLS:
58+ return LoadLayoutField(buf, sizeInBytes, impl.cols);
59+ case ACLBLASLT_MATRIX_LAYOUT_LD:
60+ return LoadLayoutField(buf, sizeInBytes, impl.ld);
61+ case ACLBLASLT_MATRIX_LAYOUT_ORDER:
62+ return LoadLayoutField(buf, sizeInBytes, impl.order);
63+ case ACLBLASLT_MATRIX_LAYOUT_BATCH_COUNT:
64+ return LoadLayoutField(buf, sizeInBytes, impl.batchCount);
65+ case ACLBLASLT_MATRIX_LAYOUT_STRIDED_BATCH_OFFSET:
66+ return LoadLayoutField(buf, sizeInBytes, impl.stridedBatchOffset);
67+ default:
68+ return ACLBLAS_STATUS_INVALID_VALUE;
69+ }
70+}
71+ 
72+aclblasStatus_t ReadLayoutGetAttr(
73+ const aclblasLtMatrixLayoutImpl& impl, aclblasLtMatrixLayoutAttribute_t attr, void* buf, size_t sizeInBytes,
74+ size_t& actualSize)
75+{
76+ switch (attr) {
77+ case ACLBLASLT_MATRIX_LAYOUT_TYPE:
78+ return StoreLayoutField(buf, sizeInBytes, impl.type, actualSize);
79+ case ACLBLASLT_MATRIX_LAYOUT_ROWS:
80+ return StoreLayoutField(buf, sizeInBytes, impl.rows, actualSize);
81+ case ACLBLASLT_MATRIX_LAYOUT_COLS:
82+ return StoreLayoutField(buf, sizeInBytes, impl.cols, actualSize);
83+ case ACLBLASLT_MATRIX_LAYOUT_LD:
84+ return StoreLayoutField(buf, sizeInBytes, impl.ld, actualSize);
85+ case ACLBLASLT_MATRIX_LAYOUT_ORDER:
86+ return StoreLayoutField(buf, sizeInBytes, impl.order, actualSize);
87+ case ACLBLASLT_MATRIX_LAYOUT_BATCH_COUNT:
88+ return StoreLayoutField(buf, sizeInBytes, impl.batchCount, actualSize);
89+ case ACLBLASLT_MATRIX_LAYOUT_STRIDED_BATCH_OFFSET:
90+ return StoreLayoutField(buf, sizeInBytes, impl.stridedBatchOffset, actualSize);
91+ default:
92+ return ACLBLAS_STATUS_INVALID_VALUE;
93+ }
94+}
95+ 
96+} // namespace
97+ 
98+extern "C" {
99+ 
100+aclblasStatus_t aclblasLtMatrixLayoutCreate(
101+ aclblasLtMatrixLayout_t* layout, aclDataType type, uint64_t rows, uint64_t cols, int64_t ld)
102+{
103+ // Step 1: validate inputs. BLAS/cuBLAS permit empty matrices (m=0 or n=0); reject invalid ld only.
104+ if (layout == nullptr || ld < 0) {
105+ return ACLBLAS_STATUS_INVALID_VALUE;
106+ }
107+ *layout = nullptr;
108+ // Step 2: allocate the opaque capsule on the heap.
109+ auto* capsule = new (std::nothrow) aclblasLtMatrixLayoutOpaque_t();
110+ if (capsule == nullptr) {
111+ return ACLBLAS_STATUS_ALLOC_FAILED;
112+ }
113+ // Step 3: build the impl on the stack and copy it into the capsule.
114+ aclblasLtMatrixLayoutImpl impl;
115+ impl.magic = ACLBLASLT_LAYOUT_MAGIC;
116+ impl.type = type;
117+ impl.rows = rows;
118+ impl.cols = cols;
119+ impl.ld = (ld == 0) ? static_cast<int64_t>(rows) : ld;
120+ // Step 4: copy impl into capsule and zero the remaining bytes.
121+ static_assert(sizeof(impl) <= sizeof(*capsule), "aclblasLtMatrixLayoutImpl too large, not fit in capsule!");
122+ aclblasStatus_t copyStatus = PackImplIntoCapsule(capsule, sizeof(*capsule), &impl, sizeof(impl));
123+ if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
124+ delete capsule;
125+ return copyStatus;
126+ }
127+ // Step 5: return the opaque layout handle.
128+ *layout = capsule;
129+ return ACLBLAS_STATUS_SUCCESS;
130+}
131+ 
132+aclblasStatus_t aclblasLtMatrixLayoutDestroy(const aclblasLtMatrixLayout_t layout)
133+{
134+ if (layout == nullptr) {
135+ return ACLBLAS_STATUS_INVALID_VALUE;
136+ }
137+ 
138+ auto* capsule = reinterpret_cast<aclblasLtMatrixLayoutOpaque_t*>(layout);
139+ delete capsule;
140+ 
141+ return ACLBLAS_STATUS_SUCCESS;
142+}
143+ 
144+aclblasStatus_t aclblasLtMatrixLayoutSetAttribute(
145+ aclblasLtMatrixLayout_t layout, aclblasLtMatrixLayoutAttribute_t attr, const void* buf, size_t sizeInBytes)
146+{
147+ if (layout == nullptr || buf == nullptr) {
148+ return ACLBLAS_STATUS_INVALID_VALUE;
149+ }
150+ 
151+ // Unpack the opaque capsule into a stack-side impl for mutation.
152+ aclblasLtMatrixLayoutImpl impl;
153+ aclblasStatus_t copyStatus = CheckedMemcpyS(&impl, sizeof(impl), layout, sizeof(impl));
154+ if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
155+ return copyStatus;
156+ }
157+ 
158+ const aclblasStatus_t setStatus = ApplyLayoutSetAttr(impl, attr, buf, sizeInBytes);
159+ if (setStatus != ACLBLAS_STATUS_SUCCESS) {
160+ return setStatus;
161+ }
162+ 
163+ // Pack the updated impl back into the opaque capsule.
164+ return PackImplIntoCapsule(layout, sizeof(*layout), &impl, sizeof(impl));
165+}
166+ 
167+aclblasStatus_t aclblasLtMatrixLayoutGetAttribute(
168+ const aclblasLtMatrixLayout_t layout, aclblasLtMatrixLayoutAttribute_t attr, void* buf, size_t sizeInBytes,
169+ size_t* sizeWritten)
170+{
171+ if (layout == nullptr || buf == nullptr) {
172+ return ACLBLAS_STATUS_INVALID_VALUE;
173+ }
174+ 
175+ aclblasLtMatrixLayoutImpl impl;
176+ static_assert(sizeof(impl) <= sizeof(*layout), "aclblasLtMatrixLayoutImpl too large for capsule");
177+ aclblasStatus_t copyStatus = CheckedMemcpyS(&impl, sizeof(impl), layout, sizeof(impl));
178+ if (copyStatus != ACLBLAS_STATUS_SUCCESS) {
179+ return copyStatus;
180+ }
181+ 
182+ size_t actualSize = 0;
183+ const aclblasStatus_t getStatus = ReadLayoutGetAttr(impl, attr, buf, sizeInBytes, actualSize);
184+ if (getStatus != ACLBLAS_STATUS_SUCCESS) {
185+ return getStatus;
186+ }
187+ 
188+ if (sizeWritten != nullptr) {
189+ *sizeWritten = actualSize;
190+ }
191+ 
192+ return ACLBLAS_STATUS_SUCCESS;
193+}
194+ 
195+} // extern "C"
@@ -0,0 +1,92 @@
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+/*!
12+ * \file aclblaslt_matmul.cpp
13+ * \brief Public C API: aclblasLtMatmul — validates inputs, builds the MatmulProblem, dispatches to
14+ * the matmul engine.
15+ */
16+ 
17+#include "cann_ops_blasLt.h"
18+ 
19+#include "aclblaslt_handle_impl.h"
20+#include "aclblaslt_layout_impl.h"
21+#include "aclblaslt_matmul_problem.h"
22+#include "matmul_engine.h"
23+ 
24+#include <acl/acl.h>
25+#include <cstdint>
26+ 
27+extern "C" {
28+ 
29+aclblasStatus_t aclblasLtMatmul(
30+ aclblasLtHandle_t lightHandle, aclblasLtMatmulDesc_t computeDesc, const void* alpha, const void* A,
31+ aclblasLtMatrixLayout_t Adesc, const void* B, aclblasLtMatrixLayout_t Bdesc, const void* beta, const void* C,
32+ aclblasLtMatrixLayout_t Cdesc, void* D, aclblasLtMatrixLayout_t Ddesc, const aclblasLtMatmulAlgo_t* algo,
33+ void* workspace, size_t workspaceSizeInBytes, aclrtStream stream)
34+{
35+ // Validate lightHandle
36+ if (lightHandle == nullptr) {
37+ return ACLBLAS_STATUS_NOT_INITIALIZED;
38+ }
39+ 
40+ // Validate descriptors
41+ if (computeDesc == nullptr || Adesc == nullptr || Bdesc == nullptr || Cdesc == nullptr || Ddesc == nullptr) {
42+ return ACLBLAS_STATUS_INVALID_VALUE;
43+ }
44+ 
45+ // Validate pointers
46+ if (alpha == nullptr || beta == nullptr) {
47+ return ACLBLAS_STATUS_INVALID_VALUE;
48+ }
49+ 
50+ // Get layout info
51+ auto* ALayout = reinterpret_cast<aclblasLtMatrixLayoutImpl*>(Adesc);
52+ auto* BLayout = reinterpret_cast<aclblasLtMatrixLayoutImpl*>(Bdesc);
53+ auto* CLayout = reinterpret_cast<aclblasLtMatrixLayoutImpl*>(Cdesc);
54+ auto* DLayout = reinterpret_cast<aclblasLtMatrixLayoutImpl*>(Ddesc);
55+ auto* desc = reinterpret_cast<aclblasLtMatmulDescImpl*>(computeDesc);
56+ 
57+ // Get dimensions
58+ uint64_t m = DLayout->rows;
59+ uint64_t n = DLayout->cols;
60+ uint64_t k = (desc->transA == ACLBLAS_OP_N) ? ALayout->cols : ALayout->rows;
61+ 
62+ // BLAS/cuBLAS convention: m=0 or n=0 is a no-op, succeed without touching matrices.
63+ if (m == 0U || n == 0U) {
64+ return ACLBLAS_STATUS_SUCCESS;
65+ }
66+ 
67+ if (A == nullptr || B == nullptr || D == nullptr) {
68+ return ACLBLAS_STATUS_INVALID_VALUE;
69+ }
70+ 
71+ // Validate workspace alignment (must be 16B aligned)
72+ if (workspace != nullptr && (reinterpret_cast<uintptr_t>(workspace) & 0xF) != 0) {
73+ return ACLBLAS_STATUS_INVALID_VALUE;
74+ }
75+ 
76+ // Validate workspace size
77+ if (algo != nullptr && workspaceSizeInBytes < algo->max_workspace_bytes) {
78+ return ACLBLAS_STATUS_INVALID_VALUE;
79+ }
80+ 
81+ auto* handleImpl = reinterpret_cast<aclblasLtHandle*>(lightHandle);
82+ const uint32_t numBlocks = QueryCubeCoreNum(handleImpl->deviceId);
83+ 
84+ MatmulProblem problem;
85+ BuildMatmulProblem(
86+ desc, ALayout, BLayout, CLayout, DLayout, m, n, k, alpha, beta, A, B, C, D, algo, workspace,
87+ workspaceSizeInBytes, numBlocks, stream, problem);
88+ 
89+ return MatmulLaunch(problem);
90+}
91+ 
92+} // extern "C"
RblasLt/utils/host_utils.hblasLt/common/helper/host_utils.h+36-0
RblasLt/utils/integral_constant.hblasLt/matmul/common/integral_constant.h+0-0
RblasLt/include/host/quant_matmul_mx_tiling_swat_host.hblasLt/matmul/common/quant_matmul_mx_tiling_swat_host.h+1-1
RblasLt/include/host/matmul_tiling_data.hblasLt/matmul/common/quant_matmul_tiling_data.h+2-20
RblasLt/epilogue/arch35/epilogue_alpha_beta_host.cppblasLt/matmul/epilogue/arch35/epilogue_alpha_beta_host.cpp+2-1
RblasLt/include/kernel/matmul_kernel.hblasLt/matmul/epilogue/arch35/epilogue_alpha_beta_host.h+2-25
RblasLt/epilogue/arch35/epilogue_alpha_beta_kernel.cppblasLt/matmul/epilogue/arch35/epilogue_alpha_beta_kernel.cpp+1-0
RblasLt/include/host/epilogue_alpha_beta_tiling_data.hblasLt/matmul/epilogue/arch35/epilogue_alpha_beta_tiling_data.h+0-0
RblasLt/matmul_fp32/arch35/matmul_fp32_host.cppblasLt/matmul/fp32/arch35/matmul_fp32_host.cpp+2-2
RblasLt/include/host/matmul_get_tiling.hblasLt/matmul/fp32/arch35/matmul_fp32_host.h+2-11
RblasLt/matmul_fp32/arch35/matmul_fp32_kernel.cppblasLt/matmul/fp32/arch35/matmul_fp32_kernel.cpp+4-4
RblasLt/utils/kernel_utils.hblasLt/matmul/fp32/arch35/matmul_fp32_kernel_utils.h+2-2
RblasLt/matmul_mxfp4/arch35/matmul_mxfp4_host.cppblasLt/matmul/mxfp4/arch35/matmul_mxfp4_host.cpp+3-4
RblasLt/include/host/matmul_mxfp4_host.hblasLt/matmul/mxfp4/arch35/matmul_mxfp4_host.h+7-2
RblasLt/matmul_mxfp4/arch35/matmul_mxfp4_kernel.cppblasLt/matmul/mxfp4/arch35/matmul_mxfp4_kernel.cpp+2-1
RblasLt/matmul_mxfp8/arch35/matmul_mxfp8_host.cppblasLt/matmul/mxfp8/arch35/matmul_mxfp8_host.cpp+2-2
RblasLt/matmul_mxfp8/arch35/matmul_mxfp8_kernel.cppblasLt/matmul/mxfp8/arch35/matmul_mxfp8_kernel.cpp+3-2
RblasLt/include/host/matrix_transform_get_tiling.hblasLt/matrix_transform/arch35/matrix_transform_get_tiling.h+0-0
RblasLt/matrixtransform/arch35/matrix_transform_host.cppblasLt/matrix_transform/arch35/matrix_transform_host.cpp+1-1
RblasLt/matrixtransform/arch35/matrix_transform_kernel.cppblasLt/matrix_transform/arch35/matrix_transform_kernel.cpp+0-0
RblasLt/include/kernel/matrix_transform_kernel.hblasLt/matrix_transform/arch35/matrix_transform_kernel.h+0-0
RblasLt/include/host/matrix_transform_perm_table.hblasLt/matrix_transform/arch35/matrix_transform_perm_table.h+0-0
RblasLt/include/host/matrix_transform_tiling_data.hblasLt/matrix_transform/arch35/matrix_transform_tiling_data.h+0-0
RblasLt/include/host/matrix_transform_acl_impl.hblasLt/matrix_transform/engine/matrix_transform_engine.h+1-1