Line data Source code
1 : /**
2 : * Copyright (c) 2025-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 aclnn_foreach_sub_list_v2.cpp
13 : * \brief
14 : */
15 :
16 : #include "aclnn_foreach_sub_list_v2.h"
17 : #include "foreach_sub_list_v2.h"
18 : #include "aclnn_kernels/contiguous.h"
19 : #include "op_api/op_api_def_nn.h"
20 : #include "op_api/aclnn_util.h"
21 : #include "aclnn_kernels/common/op_error_check.h"
22 : #include "opdev/op_dfx.h"
23 : #include "opdev/make_op_executor.h"
24 : #include "opdev/platform.h"
25 :
26 : using namespace op;
27 :
28 : #ifdef __cplusplus
29 : extern "C" {
30 : #endif
31 :
32 : static const std::initializer_list<ge::DataType> ASCEND910BC_TENSOR_DTYPE_DTYPE_SUPPORT_LIST = {
33 : ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_INT32, ge::DT_INT16, ge::DT_INT8, ge::DT_UINT8};
34 :
35 : static const std::initializer_list<ge::DataType> FOREACH_SCALAR_FLOAT_SUPPORT_LIST = {ge::DT_FLOAT, ge::DT_DOUBLE};
36 :
37 : static const std::initializer_list<ge::DataType> FOREACH_SCALAR_FLOAT16_SUPPORT_LIST = {ge::DT_FLOAT16, ge::DT_DOUBLE};
38 :
39 : static const std::initializer_list<ge::DataType> FOREACH_SCALAR_INT_SUPPORT_LIST = {ge::DT_INT32, ge::DT_INT64};
40 :
41 : static const std::initializer_list<ge::DataType> EMPTY_LIST = {};
42 :
43 : static inline ge::DataType GetAlphaTensorDtype(ge::DataType selfDtype)
44 : {
45 : if (selfDtype == ge::DT_BF16) {
46 : return ge::DT_FLOAT;
47 : }
48 : if (selfDtype == ge::DT_INT16 || selfDtype == ge::DT_INT8 || selfDtype == ge::DT_UINT8) {
49 : return ge::DT_INT32;
50 : }
51 : return selfDtype;
52 : }
53 :
54 : static inline bool CheckNotNull(const aclTensorList* self, const aclTensorList* x2, const aclScalar* scalar,
55 : const aclTensorList* out)
56 : {
57 : OP_CHECK_NULL(self, return false);
58 : OP_CHECK_NULL(x2, return false);
59 : OP_CHECK_NULL(scalar, return false);
60 : OP_CHECK_NULL(out, return false);
61 : return true;
62 : }
63 :
64 : static inline bool CheckFormat(const aclTensorList* self, const aclTensorList* x2, const aclTensorList* out)
65 : {
66 : for (uint64_t i = 0; i < self->Size(); i++) {
67 : // self格式不能是私有格式
68 : if (IsPrivateFormat((*self)[i]->GetStorageFormat()) || IsPrivateFormat((*x2)[i]->GetStorageFormat()) ||
69 : IsPrivateFormat((*out)[i]->GetStorageFormat())) {
70 : OP_LOGE(ACLNN_ERR_PARAM_INVALID, "Format only support ND, NCHW, NHWC, HWCN, NDHWC, NCDHW.");
71 : return false;
72 : }
73 : }
74 : return true;
75 : }
76 :
77 : static const std::initializer_list<ge::DataType>& GetDtypeSupportList()
78 : {
79 : auto curArch = GetCurrentPlatformInfo().GetCurNpuArch();
80 : if (curArch == NpuArch::DAV_2201 || Ops::NN::AclnnUtil::IsRegbase(curArch)) {
81 : return ASCEND910BC_TENSOR_DTYPE_DTYPE_SUPPORT_LIST;
82 : } else {
83 : OP_LOGE(ACLNN_ERR_RUNTIME_ERROR, "support for %s is not implemented",
84 : op::ToString(GetCurrentPlatformInfo().GetSocVersion()).GetString());
85 : return EMPTY_LIST;
86 : }
87 : }
88 :
89 : static inline bool CheckDtypeValid(const aclTensorList* self, const aclTensorList* x2, const aclScalar* scalar,
90 : const aclTensorList* out)
91 : {
92 : const auto& dtypeSupportList = GetDtypeSupportList();
93 : if (dtypeSupportList.size() == 0) {
94 : OP_LOGE(ACLNN_ERR_PARAM_INVALID, "support for %s is not implemented",
95 : op::ToString(GetCurrentPlatformInfo().GetSocVersion()).GetString());
96 : return false;
97 : }
98 : if (self->Size() == 0) {
99 : return true;
100 : }
101 :
102 : // checkself input dtype, and check the releation of input and out
103 : auto selfDtyte = (*self)[0]->GetDataType();
104 : OP_CHECK_DTYPE_NOT_SUPPORT((*self)[0], dtypeSupportList, return false);
105 :
106 : // check the releation of self and scalar
107 : if (selfDtyte == ge::DT_BF16 || selfDtyte == ge::DT_FLOAT) {
108 : OP_CHECK_DTYPE_NOT_SUPPORT(scalar, FOREACH_SCALAR_FLOAT_SUPPORT_LIST, return false);
109 : } else if (selfDtyte == ge::DT_FLOAT16) {
110 : OP_CHECK_DTYPE_NOT_SUPPORT(scalar, FOREACH_SCALAR_FLOAT16_SUPPORT_LIST, return false);
111 : } else if (selfDtyte == ge::DT_INT32 || selfDtyte == ge::DT_INT16 || selfDtyte == ge::DT_INT8 ||
112 : selfDtyte == ge::DT_UINT8) {
113 : OP_CHECK_DTYPE_NOT_SUPPORT(scalar, FOREACH_SCALAR_INT_SUPPORT_LIST, return false);
114 : }
115 :
116 : for (uint64_t i = 0; i < self->Size(); i++) {
117 : OP_CHECK_DTYPE_NOT_MATCH((*self)[i], selfDtyte, return false);
118 : }
119 :
120 : for (uint64_t i = 0; i < x2->Size(); i++) {
121 : OP_CHECK_DTYPE_NOT_MATCH((*x2)[i], selfDtyte, return false);
122 : }
123 :
124 : for (uint64_t i = 0; i < out->Size(); i++) {
125 : OP_CHECK_DTYPE_NOT_MATCH((*out)[i], selfDtyte, return false);
126 : }
127 : return true;
128 : }
129 :
130 : static inline bool CheckShape(const aclTensorList* self, const aclTensorList* x2, const aclTensorList* out)
131 : {
132 : // tensorlist size检查
133 : if (self->Size() != x2->Size()) {
134 : OP_LOGE(ACLNN_ERR_PARAM_INVALID, "Tensor lists must have the same number of tensors, got %lu and %lu",
135 : self->Size(), x2->Size());
136 : return false;
137 : }
138 :
139 : // tensorlist中tensor shape一致性检查
140 : for (uint64_t i = 0; i < self->Size(); i++) {
141 : OP_CHECK_SHAPE_NOT_EQUAL((*self)[i], (*x2)[i], return false);
142 : }
143 :
144 : // tensor 维度检查
145 : for (uint64_t i = 0; i < self->Size(); i++) {
146 : OP_CHECK_MAX_DIM((*self)[i], MAX_SUPPORT_DIMS_NUMS, return false);
147 : }
148 :
149 : // self和out的shape必须一致
150 : for (uint64_t i = 0; i < self->Size(); i++) {
151 : OP_CHECK_SHAPE_NOT_EQUAL((*self)[i], (*out)[i], return false);
152 : }
153 : return true;
154 : }
155 :
156 : static inline aclnnStatus CheckParams(const aclTensorList* self, const aclTensorList* x2, const aclScalar* scalar,
157 : const aclTensorList* out)
158 : {
159 : // 1. 检查参数是否为空指针
160 : CHECK_RET(CheckNotNull(self, x2, scalar, out), ACLNN_ERR_PARAM_NULLPTR);
161 :
162 : // Check every entry in tensor lists is not null, to avoid null pointer
163 : // dereference in CheckDtypeValid/CheckShape/CheckFormat.
164 22 : for (uint64_t i = 0; i < self->Size(); i++) {
165 11 : if ((*self)[i] == nullptr) {
166 0 : OP_LOGE(ACLNN_ERR_PARAM_INVALID, "self[%lu] is null.", i);
167 0 : return ACLNN_ERR_PARAM_INVALID;
168 : }
169 : }
170 22 : for (uint64_t i = 0; i < x2->Size(); i++) {
171 11 : if ((*x2)[i] == nullptr) {
172 0 : OP_LOGE(ACLNN_ERR_PARAM_INVALID, "x2[%lu] is null.", i);
173 0 : return ACLNN_ERR_PARAM_INVALID;
174 : }
175 : }
176 22 : for (uint64_t i = 0; i < out->Size(); i++) {
177 11 : if ((*out)[i] == nullptr) {
178 0 : OP_LOGE(ACLNN_ERR_PARAM_INVALID, "out[%lu] is null.", i);
179 0 : return ACLNN_ERR_PARAM_INVALID;
180 : }
181 : }
182 :
183 : // 2. 检查输入的数据类型是否在API支持的数据类型范围之内,需要根据api定义校验
184 : CHECK_RET(CheckDtypeValid(self, x2, scalar, out), ACLNN_ERR_PARAM_INVALID);
185 : // 3. 检查shape是否满足约束
186 : CHECK_RET(CheckShape(self, x2, out), ACLNN_ERR_PARAM_INVALID);
187 : // 4. 检查Format是否满足约束
188 : CHECK_RET(CheckFormat(self, x2, out), ACLNN_ERR_PARAM_INVALID);
189 : return ACLNN_SUCCESS;
190 : }
191 :
192 : static aclnnStatus ExecForeachSubListV2GetWorkspaceSize(const aclTensorList* x1, const aclTensorList* x2,
193 : const aclScalar* scalar, const aclTensorList* out,
194 : uint64_t* workspaceSize, aclOpExecutor** executor)
195 : {
196 : // 固定写法,创建OpExecutor
197 : auto uniqueExecutor = CREATE_EXECUTOR();
198 : CHECK_RET(uniqueExecutor.get() != nullptr, ACLNN_ERR_INNER_CREATE_EXECUTOR);
199 :
200 : // 固定写法,参数检查
201 : auto ret = CheckParams(x1, x2, scalar, out);
202 : CHECK_RET(ret == ACLNN_SUCCESS, ret);
203 :
204 : // 空Tensorlist处理
205 : if (x1->Size() == 0 || x2->Size() == 0) {
206 : *workspaceSize = 0;
207 : uniqueExecutor.ReleaseTo(executor);
208 : return ACLNN_SUCCESS;
209 : }
210 :
211 : // self如果非连续,需要转连续
212 : std::vector<const aclTensor*> tensorsVecX1;
213 : for (size_t i = 0; i < x1->Size(); ++i) {
214 : auto secondContiguous = l0op::Contiguous((*x1)[i], uniqueExecutor.get());
215 : CHECK_RET(secondContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR);
216 : tensorsVecX1.push_back(secondContiguous);
217 : }
218 : auto contiguousTensorsX1 = uniqueExecutor.get()->AllocTensorList(tensorsVecX1.data(), tensorsVecX1.size());
219 : CHECK_RET(contiguousTensorsX1 != nullptr, ACLNN_ERR_INNER_NULLPTR);
220 :
221 : std::vector<const aclTensor*> tensorsVecX2;
222 : for (size_t i = 0; i < x2->Size(); ++i) {
223 : auto secondContiguous = l0op::Contiguous((*x2)[i], uniqueExecutor.get());
224 : CHECK_RET(secondContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR);
225 : tensorsVecX2.push_back(secondContiguous);
226 : }
227 : auto contiguousTensorsX2 = uniqueExecutor.get()->AllocTensorList(tensorsVecX2.data(), tensorsVecX2.size());
228 : CHECK_RET(contiguousTensorsX2 != nullptr, ACLNN_ERR_INNER_NULLPTR);
229 :
230 : // sclar to tensor
231 : const aclTensor* otherTensor = uniqueExecutor.get()->ConvertToTensor(scalar,
232 : GetAlphaTensorDtype((*x1)[0]->GetDataType()));
233 :
234 : // 调用l0算子ForeachSubListV2进行计算
235 : auto result = l0op::ForeachSubListV2(contiguousTensorsX1, contiguousTensorsX2, otherTensor, out,
236 : uniqueExecutor.get());
237 : CHECK_RET(result != nullptr, ACLNN_ERR_INNER_NULLPTR);
238 :
239 : // 固定写法,获取计算过程中需要使用的workspace大小
240 : *workspaceSize = uniqueExecutor->GetWorkspaceSize();
241 : uniqueExecutor.ReleaseTo(executor);
242 : return ACLNN_SUCCESS;
243 : }
244 :
245 : aclnnStatus aclnnForeachSubListV2GetWorkspaceSize(const aclTensorList* x1, const aclTensorList* x2,
246 : const aclScalar* alpha, aclTensorList* out, uint64_t* workspaceSize,
247 : aclOpExecutor** executor)
248 : {
249 : L2_DFX_PHASE_1(aclnnForeachSubListV2, DFX_IN(x1, x2, alpha), DFX_OUT(out));
250 : return ExecForeachSubListV2GetWorkspaceSize(x1, x2, alpha, out, workspaceSize, executor);
251 : }
252 :
253 : aclnnStatus aclnnForeachSubListV2(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor,
254 : const aclrtStream stream)
255 : {
256 : L2_DFX_PHASE_2(aclnnForeachSubListV2);
257 : return CommonOpExecutorRun(workspace, workspaceSize, executor, stream);
258 : }
259 :
260 : #ifdef __cplusplus
261 : }
262 : #endif
|