已合并
新增Mul算子AICPU实现 #4111
liu-wei创建于 7月16日
新增Mul算子AICPU实现 #4111
已合并
liu-wei创建于 7月16日
6 个文件变更+1143-1
@@ -1273,7 +1273,7 @@
1273 <td>√</td>1273 <td>√</td>
1274 <td>√</td>1274 <td>√</td>
1275 <td>√</td>1275 <td>√</td>
1276- <td>AI Core</td>1276+ <td>AI Core/AI CPU</td>
1277 <td>返回两个张量元素之间的乘积结果组成的新张量。</td>1277 <td>返回两个张量元素之间的乘积结果组成的新张量。</td>
1278 </tr>1278 </tr>
1279 <tr>1279 <tr>
@@ -0,0 +1,309 @@
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 test_geir_mul.cpp
13+ * \brief
14+ */
15+ 
16+#include <complex>
17+#include <ctime>
18+#include <cstdio>
19+#include <iostream>
20+#include <map>
21+#include <stdint.h>
22+#include <string>
23+#include <string.h>
24+#include <vector>
25+ 
26+#include "assert.h"
27+ 
28+#include "ge_api.h"
29+#include "ge_api_types.h"
30+#include "ge_error_codes.h"
31+#include "ge_ir_build.h"
32+#include "graph.h"
33+#include "graph/operator.h"
34+#include "graph/operator_reg.h"
35+#include "tensor.h"
36+#include "types.h"
37+ 
38+#include "../op_graph/mul_proto.h"
39+ 
40+#define FAILED -1
41+#define SUCCESS 0
42+ 
43+namespace ge {
44+REG_OP(Data).INPUT(x, TensorType::ALL()).OUTPUT(y, TensorType::ALL()).ATTR(index, Int, 0).OP_END_FACTORY_REG(Data)
45+} // namespace ge
46+ 
47+using namespace ge;
48+using std::map;
49+using std::string;
50+using std::vector;
51+ 
52+string GetTime()
53+{
54+ time_t timep;
55+ time(&timep);
56+ char tmp[64];
57+ strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S,000", localtime(&timep));
58+ return tmp;
59+}
60+ 
61+uint32_t GetDataTypeSize(DataType dt)
62+{
63+ if (dt == ge::DT_FLOAT) {
64+ return 4;
65+ } else if (dt == ge::DT_FLOAT16) {
66+ return 2;
67+ } else if (dt == ge::DT_DOUBLE) {
68+ return 8;
69+ } else if (dt == ge::DT_COMPLEX64) {
70+ return 8;
71+ } else if (dt == ge::DT_COMPLEX128) {
72+ return 16;
73+ } else if (dt == ge::DT_INT32) {
74+ return 4;
75+ } else if (dt == ge::DT_INT64) {
76+ return 8;
77+ }
78+ return 0;
79+}
80+ 
81+const char* DataTypeToString(DataType dt)
82+{
83+ switch (dt) {
84+ case DT_FLOAT:
85+ return "DT_FLOAT";
86+ case DT_FLOAT16:
87+ return "DT_FLOAT16";
88+ case DT_DOUBLE:
89+ return "DT_DOUBLE";
90+ case DT_COMPLEX64:
91+ return "DT_COMPLEX64";
92+ case DT_COMPLEX128:
93+ return "DT_COMPLEX128";
94+ case DT_INT32:
95+ return "DT_INT32";
96+ case DT_INT64:
97+ return "DT_INT64";
98+ default:
99+ return "DTYPE(unknown)";
100+ }
101+}
102+ 
103+template <typename T>
104+int32_t GenTensorData(const vector<int64_t>& shapes, Tensor& input_tensor, TensorDesc& input_tensor_desc,
105+ const vector<T>& values)
106+{
107+ input_tensor_desc.SetRealDimCnt(shapes.size());
108+ size_t size = 1;
109+ for (uint32_t i = 0; i < shapes.size(); i++) {
110+ size *= shapes[i];
111+ }
112+ if (size != values.size()) {
113+ return FAILED;
114+ }
115+ 
116+ size_t data_len = size * sizeof(T);
117+ T* p_data = new (std::nothrow) T[size];
118+ if (p_data == nullptr) {
119+ return FAILED;
120+ }
121+ for (size_t i = 0; i < size; ++i) {
122+ p_data[i] = values[i];
123+ }
124+ input_tensor = Tensor(input_tensor_desc, reinterpret_cast<uint8_t*>(p_data), data_len);
125+ delete[] p_data;
126+ return SUCCESS;
127+}
128+ 
129+int CreateOppInGraph(DataType input_dtype, std::vector<ge::Tensor>& input, std::vector<Operator>& inputs,
130+ std::vector<Operator>& outputs, Graph& graph)
131+{
132+ Status ret = SUCCESS;
133+ auto mul_op = op::Mul("mul_op");
134+ 
135+ std::vector<int64_t> x1_shape = {2, 3};
136+ std::vector<int64_t> x2_shape = {2, 3};
137+ 
138+ std::vector<std::complex<double>> x1_data = {{1.0, 0.0}, {2.0, 1.0}, {3.0, 0.0},
139+ {4.0, -1.0}, {5.0, 0.0}, {6.0, 2.0}};
140+ std::vector<std::complex<double>> x2_data = {{1.0, 1.0}, {1.0, 0.0}, {2.0, 0.0},
141+ {1.0, -1.0}, {0.0, 1.0}, {1.0, 1.0}};
142+ 
143+ vector<int64_t> placeholder1_shape = x1_shape;
144+ auto placeholder1 = op::Data("placeholder1").set_attr_index(0);
145+ TensorDesc placeholder1_desc = TensorDesc(ge::Shape(placeholder1_shape), FORMAT_ND, input_dtype);
146+ placeholder1_desc.SetPlacement(ge::kPlacementHost);
147+ placeholder1_desc.SetFormat(FORMAT_ND);
148+ Tensor tensor_placeholder1;
149+ ret = GenTensorData(placeholder1_shape, tensor_placeholder1, placeholder1_desc, x1_data);
150+ if (ret != SUCCESS) {
151+ printf("%s - ERROR - [XIR]: Generate x1 data failed\n", GetTime().c_str());
152+ return FAILED;
153+ }
154+ placeholder1.update_input_desc_x(placeholder1_desc);
155+ placeholder1.update_output_desc_y(placeholder1_desc);
156+ input.push_back(tensor_placeholder1);
157+ graph.AddOp(placeholder1);
158+ mul_op.set_input_x1(placeholder1);
159+ inputs.push_back(placeholder1);
160+ 
161+ vector<int64_t> placeholder2_shape = x2_shape;
162+ auto placeholder2 = op::Data("placeholder2").set_attr_index(1);
163+ TensorDesc placeholder2_desc = TensorDesc(ge::Shape(placeholder2_shape), FORMAT_ND, input_dtype);
164+ placeholder2_desc.SetPlacement(ge::kPlacementHost);
165+ placeholder2_desc.SetFormat(FORMAT_ND);
166+ Tensor tensor_placeholder2;
167+ ret = GenTensorData(placeholder2_shape, tensor_placeholder2, placeholder2_desc, x2_data);
168+ if (ret != SUCCESS) {
169+ printf("%s - ERROR - [XIR]: Generate x2 data failed\n", GetTime().c_str());
170+ return FAILED;
171+ }
172+ placeholder2.update_input_desc_x(placeholder2_desc);
173+ placeholder2.update_output_desc_y(placeholder2_desc);
174+ input.push_back(tensor_placeholder2);
175+ graph.AddOp(placeholder2);
176+ mul_op.set_input_x2(placeholder2);
177+ inputs.push_back(placeholder2);
178+ 
179+ TensorDesc output_desc = TensorDesc(ge::Shape(x1_shape), FORMAT_ND, input_dtype);
180+ output_desc.SetPlacement(ge::kPlacementHost);
181+ output_desc.SetFormat(FORMAT_ND);
182+ mul_op.update_output_desc_y(output_desc);
183+ 
184+ outputs.push_back(mul_op);
185+ return SUCCESS;
186+}
187+ 
188+int main(int argc, char* argv[])
189+{
190+ const char* graph_name = "tc_ge_irrun_test";
191+ Graph graph(graph_name);
192+ std::vector<ge::Tensor> input;
193+ 
194+ printf("%s - INFO - [XIR]: Start to initialize ge using ge global options\n", GetTime().c_str());
195+ std::map<AscendString, AscendString> global_options = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}};
196+ Status ret = ge::GEInitialize(global_options);
197+ if (ret != SUCCESS) {
198+ printf("%s - INFO - [XIR]: Initialize ge using ge global options failed.ret = %d\n", GetTime().c_str(), ret);
199+ return FAILED;
200+ }
201+ printf("%s - INFO - [XIR]: Initialize ge using ge global options success\n", GetTime().c_str());
202+ 
203+ std::vector<Operator> inputs{};
204+ std::vector<Operator> outputs{};
205+ 
206+ if (argc > 1) {
207+ std::cout << argv[1] << std::endl;
208+ }
209+ 
210+ DataType input_dtype = DT_COMPLEX128;
211+ 
212+ ret = CreateOppInGraph(input_dtype, input, inputs, outputs, graph);
213+ if (ret != SUCCESS) {
214+ printf("%s - ERROR - [XIR]: Create ir session using build options failed\n", GetTime().c_str());
215+ GEFinalize();
216+ return FAILED;
217+ }
218+ 
219+ if (!inputs.empty() && !outputs.empty()) {
220+ graph.SetInputs(inputs).SetOutputs(outputs);
221+ }
222+ 
223+ std::map<AscendString, AscendString> build_options = {};
224+ printf("%s - INFO - [XIR]: Start to create ir session using build options\n", GetTime().c_str());
225+ ge::Session* session = new Session(build_options);
226+ if (session == nullptr) {
227+ printf("%s - ERROR - [XIR]: Create ir session using build options failed\n", GetTime().c_str());
228+ GEFinalize();
229+ return FAILED;
230+ }
231+ printf("%s - INFO - [XIR]: Create ir session using build options success\n", GetTime().c_str());
232+ printf("%s - INFO - [XIR]: Start to add compute graph to ir session\n", GetTime().c_str());
233+ 
234+ std::map<AscendString, AscendString> graph_options = {};
235+ uint32_t graph_id = 0;
236+ ret = session->AddGraph(graph_id, graph, graph_options);
237+ if (ret != SUCCESS) {
238+ printf("%s - ERROR - [XIR]: Session add ir compute graph failed\n", GetTime().c_str());
239+ delete session;
240+ GEFinalize();
241+ return FAILED;
242+ }
243+ 
244+ printf("%s - INFO - [XIR]: Session add ir compute graph to ir session success\n", GetTime().c_str());
245+ printf("%s - INFO - [XIR]: dump graph to txt\n", GetTime().c_str());
246+ std::string file_path = "./dump";
247+ aclgrphDumpGraph(graph, file_path.c_str(), file_path.length());
248+ printf("%s - INFO - [XIR]: Start to run ir compute graph\n", GetTime().c_str());
249+ 
250+ std::vector<ge::Tensor> output;
251+ ret = session->RunGraph(graph_id, input, output);
252+ if (ret != SUCCESS) {
253+ printf("%s - INFO - [XIR]: Run graph failed\n", GetTime().c_str());
254+ delete session;
255+ GEFinalize();
256+ return FAILED;
257+ }
258+ printf("%s - INFO - [XIR]: Session run ir compute graph success\n", GetTime().c_str());
259+ 
260+ if (!output.empty()) {
261+ uint8_t* output_data = output[0].GetData();
262+ int64_t output_shape_size = output[0].GetTensorDesc().GetShape().GetShapeSize();
263+ DataType output_dtype = output[0].GetTensorDesc().GetDataType();
264+ printf("%s - INFO - [XIR]: Output dtype: %s, shape size: %ld\n", GetTime().c_str(),
265+ DataTypeToString(output_dtype), output_shape_size);
266+ std::complex<double>* result = reinterpret_cast<std::complex<double>*>(output_data);
267+ 
268+ std::complex<double> x1_vals[6] = {{1.0, 0.0}, {2.0, 1.0}, {3.0, 0.0}, {4.0, -1.0}, {5.0, 0.0}, {6.0, 2.0}};
269+ std::complex<double> x2_vals[6] = {{1.0, 1.0}, {1.0, 0.0}, {2.0, 0.0}, {1.0, -1.0}, {0.0, 1.0}, {1.0, 1.0}};
270+ bool match = true;
271+ for (int64_t i = 0; i < output_shape_size; ++i) {
272+ std::complex<double> expected = x1_vals[i] * x2_vals[i];
273+ printf("result[%ld] = (%.6f, %.6f), expected = (%.6f, %.6f)\n", i, result[i].real(), result[i].imag(),
274+ expected.real(), expected.imag());
275+ if (std::abs(result[i] - expected) > 1e-6) {
276+ printf("MISMATCH at[%ld]\n", i);
277+ match = false;
278+ }
279+ }
280+ if (match) {
281+ printf("%s - INFO - [XIR]: Output verification PASSED\n", GetTime().c_str());
282+ } else {
283+ printf("%s - ERROR - [XIR]: Output verification FAILED\n", GetTime().c_str());
284+ }
285+ }
286+ 
287+ delete session;
288+ 
289+ printf("%s - INFO - [XIR]: Start to finalize ir graph session\n", GetTime().c_str());
290+ ret = ge::GEFinalize();
291+ if (ret != SUCCESS) {
292+ printf("%s - INFO - [XIR]: Finalize ir graph session failed\n", GetTime().c_str());
293+ return FAILED;
294+ }
295+ printf("%s - INFO - [XIR]: Finalize ir graph session success\n", GetTime().c_str());
296+ 
297+ ge::AscendString err_msg_asc = ge::GEGetErrorMsgV2();
298+ std::string err_msg = err_msg_asc.GetString();
299+ if (!err_msg.empty()) {
300+ printf("Error message: %s\n", err_msg.c_str());
301+ }
302+ ge::AscendString warn_msg_asc = ge::GEGetWarningMsgV2();
303+ std::string warn_msg = warn_msg_asc.GetString();
304+ if (!warn_msg.empty()) {
305+ printf("Warning message: %s\n", warn_msg.c_str());
306+ }
307+ 
308+ return SUCCESS;
309+}
@@ -0,0 +1,487 @@
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 "mul_aicpu.h"
12+ 
13+#include <algorithm>
14+#include <unordered_map>
15+#include <functional>
16+#include "cpu_kernel_utils.h"
17+#include "cpu_types.h"
18+#include "utils/eigen_tensor.h"
19+#include "utils/kernel_util.h"
20+ 
21+namespace {
22+const char* const kMul = "Mul";
23+constexpr uint32_t kInputNum = 2;
24+constexpr uint32_t kOutputNum = 1;
25+constexpr int64_t kParallelDataNum = 6 * 1024;
26+constexpr int64_t kParallelDataNumMid = 33 * 1024;
27+constexpr int64_t kParallelDataNumSameShape = 7 * 1024;
28+} // namespace
29+ 
30+namespace aicpu {
31+uint32_t MulCpuKernel::MulSameTypeCompute(const CpuKernelContext& ctx)
32+{
33+ auto data_type = static_cast<DataType>(ctx.Input(kFirstInputIndex)->GetDataType());
34+ switch (data_type) {
35+ case DT_FLOAT16:
36+ return MulCompute<Eigen::half>(ctx);
37+ case DT_BFLOAT16:
38+ return MulCompute<Eigen::bfloat16>(ctx);
39+ case DT_FLOAT:
40+ return MulCompute<float>(ctx);
41+ case DT_DOUBLE:
42+ return MulCompute<double>(ctx);
43+ case DT_INT8:
44+ return MulCompute<int8_t>(ctx);
45+ case DT_INT16:
46+ return MulCompute<int16_t>(ctx);
47+ case DT_INT32:
48+ return MulCompute<int32_t>(ctx);
49+ case DT_INT64:
50+ return MulCompute<int64_t>(ctx);
51+ case DT_UINT8:
52+ return MulCompute<uint8_t>(ctx);
53+ case DT_UINT16:
54+ return MulCompute<uint16_t>(ctx);
55+ case DT_UINT32:
56+ return MulCompute<uint32_t>(ctx);
57+ case DT_UINT64:
58+ return MulCompute<uint64_t>(ctx);
59+ case DT_COMPLEX64:
60+ return MulCompute<std::complex<float>>(ctx);
61+ case DT_COMPLEX128:
62+ return MulCompute<std::complex<double>>(ctx);
63+ default:
64+ KERNEL_LOG_ERROR("[%s] Data type of input is not support, input data type is [%s].",
65+ ctx.GetOpType().c_str(), DTypeStr(data_type).c_str());
66+ return KERNEL_STATUS_PARAM_INVALID;
67+ }
68+}
69+ 
70+template <typename T>
71+uint32_t MulCpuKernel::MulCompute(const CpuKernelContext& ctx)
72+{
73+ BCalcInfo calc_info;
74+ calc_info.input_0 = ctx.Input(kFirstInputIndex);
75+ calc_info.input_1 = ctx.Input(kSecondInputIndex);
76+ calc_info.output = ctx.Output(kFirstOutputIndex);
77+ KERNEL_CHECK_NULLPTR(calc_info.input_0->GetData(), KERNEL_STATUS_PARAM_INVALID, "[%s] Get input 0 data failed",
78+ ctx.GetOpType().c_str())
79+ KERNEL_CHECK_NULLPTR(calc_info.input_1->GetData(), KERNEL_STATUS_PARAM_INVALID, "[%s] Get input 1 data failed",
80+ ctx.GetOpType().c_str())
81+ KERNEL_CHECK_NULLPTR(calc_info.output->GetData(), KERNEL_STATUS_PARAM_INVALID, "[%s] Get output data failed",
82+ ctx.GetOpType().c_str())
83+ KERNEL_LOG_INFO("[%s] Input[0] data size is [%lu], input[1] data size is [%lu], output data size is [%lu].",
84+ ctx.GetOpType().c_str(), calc_info.input_0->GetDataSize(), calc_info.input_1->GetDataSize(),
85+ calc_info.output->GetDataSize());
86+ 
87+ Bcast bcast;
88+ if (bcast.GenerateBcastInfo(calc_info) != KERNEL_STATUS_OK) {
89+ KERNEL_LOG_ERROR("[%s] Generate broadcast info failed.", ctx.GetOpType().c_str());
90+ return KERNEL_STATUS_PARAM_INVALID;
91+ }
92+ bcast.GetBcastVec(calc_info);
93+ return MulDispatch<T>(calc_info);
94+}
95+ 
96+template <typename T>
97+uint32_t MulCpuKernel::MulDispatch(BCalcInfo& calc_info)
98+{
99+ int32_t rank = static_cast<int32_t>(calc_info.shape_out.size());
100+ switch (rank) {
101+ case 0: {
102+ T v0 = *(reinterpret_cast<const T*>(calc_info.input_0->GetData()));
103+ T v1 = *(reinterpret_cast<const T*>(calc_info.input_1->GetData()));
104+ T* value_out = reinterpret_cast<T*>(calc_info.output->GetData());
105+ *(value_out) = v0 * v1;
106+ return KERNEL_STATUS_OK;
107+ }
108+ case kRank1:
109+ return MulCalculateWithAlignedCheck<kRank1, T>(calc_info);
110+ case kRank2:
111+ return MulCalculateWithAlignedCheck<kRank2, T>(calc_info);
112+ case kRank3:
113+ return MulCalculateWithAlignedCheck<kRank3, T>(calc_info);
114+ case kRank4:
115+ return MulCalculateWithAlignedCheck<kRank4, T>(calc_info);
116+ case kRank5:
117+ return MulCalculateWithAlignedCheck<kRank5, T>(calc_info);
118+ case kRank6:
119+ return MulCalculateWithAlignedCheck<kRank6, T>(calc_info);
120+ case kRank7:
121+ return MulCalculateWithAlignedCheck<kRank7, T>(calc_info);
122+ case kRank8:
123+ return MulCalculateWithAlignedCheck<kRank8, T>(calc_info);
124+ default:
125+ KERNEL_LOG_ERROR("Rank of output should less than 8 but get [%zu].", calc_info.shape_out.size());
126+ return KERNEL_STATUS_PARAM_INVALID;
127+ }
128+}
129+ 
130+template <int32_t RANK, typename T>
131+uint32_t MulCpuKernel::MulCalculateWithAlignedCheck(BCalcInfo& calc_info)
132+{
133+ if (AlignedCheck(calc_info)) {
134+ return MulCalculate<RANK, T, Eigen::Aligned>(calc_info);
135+ }
136+ return MulCalculate<RANK, T, Eigen::Unaligned>(calc_info);
137+}
138+ 
139+bool MulCpuKernel::AlignedCheck(const BCalcInfo& calc_info) const
140+{
141+ return AddrAlignedCheck(calc_info.input_0->GetData()) && AddrAlignedCheck(calc_info.input_1->GetData()) &&
142+ AddrAlignedCheck(calc_info.output->GetData());
143+}
144+ 
145+template <int32_t RANK, typename T, int32_t OPTION>
146+uint32_t MulCpuKernel::MulCalculate(BCalcInfo& calc_info)
147+{
148+ Eigen::TensorMap<Eigen::Tensor<T, 1>, OPTION> input0(static_cast<T*>(calc_info.input_0->GetData()),
149+ calc_info.input_0->GetTensorShape()->NumElements());
150+ Eigen::TensorMap<Eigen::Tensor<T, 1>, OPTION> input1(static_cast<T*>(calc_info.input_1->GetData()),
151+ calc_info.input_1->GetTensorShape()->NumElements());
152+ Eigen::TensorMap<Eigen::Tensor<T, 1>, OPTION> output(static_cast<T*>(calc_info.output->GetData()),
153+ calc_info.output->GetTensorShape()->NumElements());
154+ auto input_shape_0 = calc_info.input_0->GetTensorShape()->GetDimSizes();
155+ auto input_shape_1 = calc_info.input_1->GetTensorShape()->GetDimSizes();
156+ if (input_shape_0.empty()) {
157+ T v0 = *(reinterpret_cast<const T*>(calc_info.input_0->GetData()));
158+ output = v0 * input1;
159+ return KERNEL_STATUS_OK;
160+ }
161+ if (input_shape_1.empty()) {
162+ T v1 = *(reinterpret_cast<const T*>(calc_info.input_1->GetData()));
163+ output = input0 * v1;
164+ return KERNEL_STATUS_OK;
165+ }
166+ 
167+ Eigen::DSizes<Eigen::DenseIndex, RANK> reshape_0;
168+ Eigen::DSizes<Eigen::DenseIndex, RANK> reshape_1;
169+ Eigen::DSizes<Eigen::DenseIndex, RANK> shape_out;
170+ Eigen::array<Eigen::DenseIndex, RANK> bcast_0;
171+ Eigen::array<Eigen::DenseIndex, RANK> bcast_1;
172+ for (int32_t i = 0; i < RANK; i++) {
173+ reshape_0[(RANK - i) - 1] = calc_info.reshape_0[i];
174+ reshape_1[(RANK - i) - 1] = calc_info.reshape_1[i];
175+ shape_out[(RANK - i) - 1] = calc_info.shape_out[i];
176+ bcast_0[(RANK - i) - 1] = calc_info.bcast_0[i];
177+ bcast_1[(RANK - i) - 1] = calc_info.bcast_1[i];
178+ }
179+ if (input_shape_0 == input_shape_1) {
180+ output.reshape(shape_out) = input0.reshape(reshape_0) * input1.reshape(reshape_1);
181+ } else {
182+ output.reshape(shape_out) = input0.reshape(reshape_0).broadcast(bcast_0) *
183+ input1.reshape(reshape_1).broadcast(bcast_1);
184+ }
185+ return KERNEL_STATUS_OK;
186+}
187+ 
188+int64_t GetMulParallelCoreNum(const CpuKernelContext& ctx, int64_t data_num)
189+{
190+ uint32_t min_core_num = 1;
191+ int64_t max_core_num = std::max(static_cast<int64_t>(min_core_num),
192+ static_cast<int64_t>(aicpu::CpuKernelUtils::GetCPUNum(ctx)) - kResvCpuNum);
193+ if (data_num <= kParallelDataNumMid) {
194+ max_core_num = std::min(max_core_num, static_cast<int64_t>(4));
195+ }
196+ if (max_core_num > data_num) {
197+ max_core_num = data_num;
198+ }
199+ if (max_core_num < 1) {
200+ max_core_num = 1;
201+ }
202+ return max_core_num;
203+}
204+ 
205+template <typename TIn1, typename TIn2, typename TOut>
206+typename std::enable_if<std::is_same<TIn1, TOut>::value, void>::type inline MulImpl(TIn1 a, TIn2 b, TOut& output)
207+{
208+ output = a * static_cast<TIn1>(b);
209+}
210+ 
211+template <typename TIn1, typename TIn2, typename TOut>
212+typename std::enable_if<std::is_same<TIn2, TOut>::value, void>::type inline MulImpl(TIn1 a, TIn2 b, TOut& output)
213+{
214+ output = static_cast<TIn2>(a) * b;
215+}
216+ 
217+template <typename TIn1, typename TIn2, typename TOut>
218+typename std::enable_if<!std::is_same<TIn1, TOut>::value && !std::is_same<TIn2, TOut>::value,
219+ void>::type inline MulImpl(TIn1 a, TIn2 b, TOut& output)
220+{
221+ output = static_cast<TOut>(a) * static_cast<TOut>(b);
222+}
223+ 
224+template <typename TIn1, typename TIn2, typename TOut>
225+uint32_t BcastCompute(const CpuKernelContext& ctx, const Bcast& bcast)
226+{
227+ auto in0 = reinterpret_cast<TIn1*>(ctx.Input(0)->GetData());
228+ auto in1 = reinterpret_cast<TIn2*>(ctx.Input(1)->GetData());
229+ auto out = reinterpret_cast<TOut*>(ctx.Output(0)->GetData());
230+ int64_t data_num = ctx.Output(0)->NumElements();
231+ if (data_num >= kParallelDataNum) {
232+ int64_t max_core_num = GetMulParallelCoreNum(ctx, data_num);
233+ if (max_core_num == 0) {
234+ KERNEL_LOG_ERROR("Mul max_core_num is zero, division by zero.");
235+ return KERNEL_STATUS_PARAM_INVALID;
236+ }
237+ auto sharder_mul = [&](int64_t start, int64_t end) {
238+ for (int64_t i = start; i < end; ++i) {
239+ MulImpl(*(in0 + bcast.GetBroadcastXIndex(i)), *(in1 + bcast.GetBroadcastYIndex(i)), *(out + i));
240+ }
241+ };
242+ KERNEL_HANDLE_ERROR(CpuKernelUtils::ParallelFor(ctx, data_num, data_num / max_core_num, sharder_mul),
243+ "Mul Compute failed.")
244+ } else {
245+ for (int64_t i = 0; i < data_num; ++i) {
246+ MulImpl(*(in0 + bcast.GetBroadcastXIndex(i)), *(in1 + bcast.GetBroadcastYIndex(i)), *(out + i));
247+ }
248+ }
249+ return KERNEL_STATUS_OK;
250+}
251+ 
252+template <typename TIn1, typename TIn2, typename TOut>
253+void SpecialCompute(BcastShapeType type, int64_t start, int64_t end, CpuKernelContext& ctx)
254+{
255+ auto in1 = reinterpret_cast<TIn1*>(ctx.Input(0)->GetData());
256+ auto in2 = reinterpret_cast<TIn2*>(ctx.Input(1)->GetData());
257+ auto output = reinterpret_cast<TOut*>(ctx.Output(0)->GetData());
258+ switch (type) {
259+ case BcastShapeType::SAME_SHAPE:
260+ for (int64_t i = start; i < end; ++i) {
261+ MulImpl(*(in1 + i), *(in2 + i), *(output + i));
262+ }
263+ break;
264+ case BcastShapeType::X_ONE_ELEMENT:
265+ for (int64_t i = start; i < end; ++i) {
266+ MulImpl(*in1, *(in2 + i), *(output + i));
267+ }
268+ break;
269+ case BcastShapeType::Y_ONE_ELEMENT:
270+ for (int64_t i = start; i < end; ++i) {
271+ MulImpl(*(in1 + i), *in2, *(output + i));
272+ }
273+ break;
274+ default:
275+ KERNEL_LOG_WARN("Invalid type [%d]", static_cast<int32_t>(type));
276+ break;
277+ }
278+}
279+ 
280+template <typename TIn1, typename TIn2, typename TOut>
281+uint32_t NoBcastCompute(CpuKernelContext& ctx)
282+{
283+ int64_t element_num_in0 = ctx.Input(0)->NumElements();
284+ int64_t element_num_in1 = ctx.Input(1)->NumElements();
285+ int64_t data_num = ctx.Output(0)->NumElements();
286+ BcastShapeType type = (element_num_in0 == element_num_in1 ?
287+ BcastShapeType::SAME_SHAPE :
288+ (element_num_in0 == 1 ? BcastShapeType::X_ONE_ELEMENT : BcastShapeType::Y_ONE_ELEMENT));
289+ if (data_num >= kParallelDataNumSameShape) {
290+ int64_t max_core_num = GetMulParallelCoreNum(ctx, data_num);
291+ if (max_core_num == 0) {
292+ KERNEL_LOG_ERROR("Mul max_core_num is zero, division by zero.");
293+ return KERNEL_STATUS_PARAM_INVALID;
294+ }
295+ auto sharder_mul = [&](int64_t start, int64_t end) { SpecialCompute<TIn1, TIn2, TOut>(type, start, end, ctx); };
296+ KERNEL_HANDLE_ERROR(CpuKernelUtils::ParallelFor(ctx, data_num, data_num / max_core_num, sharder_mul),
297+ "Mul Compute failed.")
298+ } else {
299+ SpecialCompute<TIn1, TIn2, TOut>(type, 0, data_num, ctx);
300+ }
301+ return KERNEL_STATUS_OK;
302+}
303+ 
304+template <typename TIn1, typename TIn2, typename TOut>
305+uint32_t MulDiffTypeCompute(CpuKernelContext& ctx)
306+{
307+ Tensor* tensor_in0 = ctx.Input(0);
308+ auto shape_in0 = tensor_in0->GetTensorShape()->GetDimSizes();
309+ Tensor* tensor_in1 = ctx.Input(1);
310+ auto shape_in1 = tensor_in1->GetTensorShape()->GetDimSizes();
311+ 
312+ bool no_need_bcast = (shape_in0 == shape_in1) || (tensor_in0->NumElements() == 1) ||
313+ (tensor_in1->NumElements() == 1);
314+ if (no_need_bcast) {
315+ return NoBcastCompute<TIn1, TIn2, TOut>(ctx);
316+ }
317+ 
318+ Bcast bcast(shape_in0, shape_in1);
319+ if (!bcast.IsValid()) {
320+ KERNEL_LOG_ERROR("[%s] broadcast failed.", ctx.GetOpType().c_str());
321+ return KERNEL_STATUS_PARAM_INVALID;
322+ }
323+ return BcastCompute<TIn1, TIn2, TOut>(ctx, bcast);
324+}
325+ 
326+static const std::unordered_map<int32_t, std::unordered_map<int32_t, std::function<uint32_t(CpuKernelContext&)>>>&
327+GetMulDiffTypeCalls()
328+{
329+ static const std::unordered_map<int32_t, std::unordered_map<int32_t, std::function<uint32_t(CpuKernelContext&)>>>
330+ kcalls = {
331+ {DT_UINT8,
332+ {{DT_INT8, MulDiffTypeCompute<uint8_t, int8_t, int16_t>},
333+ {DT_INT16, MulDiffTypeCompute<uint8_t, int16_t, int16_t>},
334+ {DT_INT32, MulDiffTypeCompute<uint8_t, int32_t, int32_t>},
335+ {DT_INT64, MulDiffTypeCompute<uint8_t, int64_t, int64_t>},
336+ {DT_BFLOAT16, MulDiffTypeCompute<uint8_t, Eigen::bfloat16, Eigen::bfloat16>},
337+ {DT_FLOAT16, MulDiffTypeCompute<uint8_t, Eigen::half, Eigen::half>},
338+ {DT_FLOAT, MulDiffTypeCompute<uint8_t, float, float>},
339+ {DT_DOUBLE, MulDiffTypeCompute<uint8_t, double, double>},
340+ {DT_COMPLEX64, MulDiffTypeCompute<uint8_t, std::complex<float>, std::complex<float>>},
341+ {DT_COMPLEX128, MulDiffTypeCompute<uint8_t, std::complex<double>, std::complex<double>>}}},
342+ {DT_INT8,
343+ {{DT_INT16, MulDiffTypeCompute<int8_t, int16_t, int16_t>},
344+ {DT_INT32, MulDiffTypeCompute<int8_t, int32_t, int32_t>},
345+ {DT_INT64, MulDiffTypeCompute<int8_t, int64_t, int64_t>},
346+ {DT_BFLOAT16, MulDiffTypeCompute<int8_t, Eigen::bfloat16, Eigen::bfloat16>},
347+ {DT_FLOAT16, MulDiffTypeCompute<int8_t, Eigen::half, Eigen::half>},
348+ {DT_FLOAT, MulDiffTypeCompute<int8_t, float, float>},
349+ {DT_DOUBLE, MulDiffTypeCompute<int8_t, double, double>},
350+ {DT_UINT8, MulDiffTypeCompute<int8_t, uint8_t, int16_t>},
351+ {DT_COMPLEX64, MulDiffTypeCompute<int8_t, std::complex<float>, std::complex<float>>},
352+ {DT_COMPLEX128, MulDiffTypeCompute<int8_t, std::complex<double>, std::complex<double>>}}},
353+ {DT_INT16,
354+ {{DT_INT8, MulDiffTypeCompute<int16_t, int8_t, int16_t>},
355+ {DT_INT32, MulDiffTypeCompute<int16_t, int32_t, int32_t>},
356+ {DT_INT64, MulDiffTypeCompute<int16_t, int64_t, int64_t>},
357+ {DT_BFLOAT16, MulDiffTypeCompute<int16_t, Eigen::bfloat16, Eigen::bfloat16>},
358+ {DT_FLOAT16, MulDiffTypeCompute<int16_t, Eigen::half, Eigen::half>},
359+ {DT_FLOAT, MulDiffTypeCompute<int16_t, float, float>},
360+ {DT_DOUBLE, MulDiffTypeCompute<int16_t, double, double>},
361+ {DT_UINT8, MulDiffTypeCompute<int16_t, uint8_t, int16_t>},
362+ {DT_COMPLEX64, MulDiffTypeCompute<int16_t, std::complex<float>, std::complex<float>>},
363+ {DT_COMPLEX128, MulDiffTypeCompute<int16_t, std::complex<double>, std::complex<double>>}}},
364+ {DT_INT32,
365+ {{DT_INT8, MulDiffTypeCompute<int32_t, int8_t, int32_t>},
366+ {DT_INT16, MulDiffTypeCompute<int32_t, int16_t, int32_t>},
367+ {DT_INT64, MulDiffTypeCompute<int32_t, int64_t, int64_t>},
368+ {DT_BFLOAT16, MulDiffTypeCompute<int32_t, Eigen::bfloat16, Eigen::bfloat16>},
369+ {DT_FLOAT16, MulDiffTypeCompute<int32_t, Eigen::half, Eigen::half>},
370+ {DT_FLOAT, MulDiffTypeCompute<int32_t, float, float>},
371+ {DT_DOUBLE, MulDiffTypeCompute<int32_t, double, double>},
372+ {DT_UINT8, MulDiffTypeCompute<int32_t, uint8_t, int32_t>},
373+ {DT_COMPLEX64, MulDiffTypeCompute<int32_t, std::complex<float>, std::complex<float>>},
374+ {DT_COMPLEX128, MulDiffTypeCompute<int32_t, std::complex<double>, std::complex<double>>}}},
375+ {DT_INT64,
376+ {{DT_INT8, MulDiffTypeCompute<int64_t, int8_t, int64_t>},
377+ {DT_INT16, MulDiffTypeCompute<int64_t, int16_t, int64_t>},
378+ {DT_INT32, MulDiffTypeCompute<int64_t, int32_t, int64_t>},
379+ {DT_BFLOAT16, MulDiffTypeCompute<int64_t, Eigen::bfloat16, Eigen::bfloat16>},
380+ {DT_FLOAT16, MulDiffTypeCompute<int64_t, Eigen::half, Eigen::half>},
381+ {DT_FLOAT, MulDiffTypeCompute<int64_t, float, float>},
382+ {DT_DOUBLE, MulDiffTypeCompute<int64_t, double, double>},
383+ {DT_UINT8, MulDiffTypeCompute<int64_t, uint8_t, int64_t>},
384+ {DT_COMPLEX64, MulDiffTypeCompute<int64_t, std::complex<float>, std::complex<float>>},
385+ {DT_COMPLEX128, MulDiffTypeCompute<int64_t, std::complex<double>, std::complex<double>>}}},
386+ {DT_BFLOAT16,
387+ {{DT_INT8, MulDiffTypeCompute<Eigen::bfloat16, int8_t, Eigen::bfloat16>},
388+ {DT_INT16, MulDiffTypeCompute<Eigen::bfloat16, int16_t, Eigen::bfloat16>},
389+ {DT_INT32, MulDiffTypeCompute<Eigen::bfloat16, int32_t, Eigen::bfloat16>},
390+ {DT_INT64, MulDiffTypeCompute<Eigen::bfloat16, int64_t, Eigen::bfloat16>},
391+ {DT_FLOAT16, MulDiffTypeCompute<Eigen::bfloat16, Eigen::half, float>},
392+ {DT_FLOAT, MulDiffTypeCompute<Eigen::bfloat16, float, float>},
393+ {DT_DOUBLE, MulDiffTypeCompute<Eigen::bfloat16, double, double>},
394+ {DT_UINT8, MulDiffTypeCompute<Eigen::bfloat16, uint8_t, Eigen::bfloat16>},
395+ {DT_COMPLEX64, MulDiffTypeCompute<Eigen::bfloat16, std::complex<float>, std::complex<float>>},
396+ {DT_COMPLEX128, MulDiffTypeCompute<Eigen::bfloat16, std::complex<double>, std::complex<double>>}}},
397+ {DT_FLOAT16,
398+ {{DT_INT8, MulDiffTypeCompute<Eigen::half, int8_t, Eigen::half>},
399+ {DT_INT16, MulDiffTypeCompute<Eigen::half, int16_t, Eigen::half>},
400+ {DT_INT32, MulDiffTypeCompute<Eigen::half, int32_t, Eigen::half>},
401+ {DT_INT64, MulDiffTypeCompute<Eigen::half, int64_t, Eigen::half>},
402+ {DT_FLOAT, MulDiffTypeCompute<Eigen::half, float, float>},
403+ {DT_BFLOAT16, MulDiffTypeCompute<Eigen::half, Eigen::bfloat16, float>},
404+ {DT_DOUBLE, MulDiffTypeCompute<Eigen::half, double, double>},
405+ {DT_UINT8, MulDiffTypeCompute<Eigen::half, uint8_t, Eigen::half>},
406+ {DT_COMPLEX64, MulDiffTypeCompute<Eigen::half, std::complex<float>, std::complex<float>>},
407+ {DT_COMPLEX128, MulDiffTypeCompute<Eigen::half, std::complex<double>, std::complex<double>>}}},
408+ {DT_FLOAT,
409+ {{DT_INT8, MulDiffTypeCompute<float, int8_t, float>},
410+ {DT_INT16, MulDiffTypeCompute<float, int16_t, float>},
411+ {DT_INT32, MulDiffTypeCompute<float, int32_t, float>},
412+ {DT_INT64, MulDiffTypeCompute<float, int64_t, float>},
413+ {DT_BFLOAT16, MulDiffTypeCompute<float, Eigen::bfloat16, float>},
414+ {DT_FLOAT16, MulDiffTypeCompute<float, Eigen::half, float>},
415+ {DT_DOUBLE, MulDiffTypeCompute<float, double, double>},
416+ {DT_UINT8, MulDiffTypeCompute<float, uint8_t, float>},
417+ {DT_COMPLEX64, MulDiffTypeCompute<float, std::complex<float>, std::complex<float>>},
418+ {DT_COMPLEX128, MulDiffTypeCompute<float, std::complex<double>, std::complex<double>>}}},
419+ {DT_DOUBLE,
420+ {{DT_INT8, MulDiffTypeCompute<double, int8_t, double>},
421+ {DT_INT16, MulDiffTypeCompute<double, int16_t, double>},
422+ {DT_INT32, MulDiffTypeCompute<double, int32_t, double>},
423+ {DT_INT64, MulDiffTypeCompute<double, int64_t, double>},
424+ {DT_BFLOAT16, MulDiffTypeCompute<double, Eigen::bfloat16, double>},
425+ {DT_FLOAT16, MulDiffTypeCompute<double, Eigen::half, double>},
426+ {DT_FLOAT, MulDiffTypeCompute<double, float, double>},
427+ {DT_UINT8, MulDiffTypeCompute<double, uint8_t, double>},
428+ {DT_COMPLEX64, MulDiffTypeCompute<double, std::complex<float>, std::complex<double>>},
429+ {DT_COMPLEX128, MulDiffTypeCompute<double, std::complex<double>, std::complex<double>>}}},
430+ {DT_COMPLEX64,
431+ {{DT_INT8, MulDiffTypeCompute<std::complex<float>, int8_t, std::complex<float>>},
432+ {DT_INT16, MulDiffTypeCompute<std::complex<float>, int16_t, std::complex<float>>},
433+ {DT_INT32, MulDiffTypeCompute<std::complex<float>, int32_t, std::complex<float>>},
434+ {DT_INT64, MulDiffTypeCompute<std::complex<float>, int64_t, std::complex<float>>},
435+ {DT_BFLOAT16, MulDiffTypeCompute<std::complex<float>, Eigen::bfloat16, std::complex<float>>},
436+ {DT_FLOAT16, MulDiffTypeCompute<std::complex<float>, Eigen::half, std::complex<float>>},
437+ {DT_FLOAT, MulDiffTypeCompute<std::complex<float>, float, std::complex<float>>},
438+ {DT_DOUBLE, MulDiffTypeCompute<std::complex<float>, double, std::complex<double>>},
439+ {DT_UINT8, MulDiffTypeCompute<std::complex<float>, uint8_t, std::complex<float>>},
440+ {DT_COMPLEX128, MulDiffTypeCompute<std::complex<float>, std::complex<double>, std::complex<double>>}}},
441+ {DT_COMPLEX128,
442+ {{DT_INT8, MulDiffTypeCompute<std::complex<double>, int8_t, std::complex<double>>},
443+ {DT_INT16, MulDiffTypeCompute<std::complex<double>, int16_t, std::complex<double>>},
444+ {DT_INT32, MulDiffTypeCompute<std::complex<double>, int32_t, std::complex<double>>},
445+ {DT_INT64, MulDiffTypeCompute<std::complex<double>, int64_t, std::complex<double>>},
446+ {DT_BFLOAT16, MulDiffTypeCompute<std::complex<double>, Eigen::bfloat16, std::complex<double>>},
447+ {DT_FLOAT16, MulDiffTypeCompute<std::complex<double>, Eigen::half, std::complex<double>>},
448+ {DT_FLOAT, MulDiffTypeCompute<std::complex<double>, float, std::complex<double>>},
449+ {DT_DOUBLE, MulDiffTypeCompute<std::complex<double>, double, std::complex<double>>},
450+ {DT_UINT8, MulDiffTypeCompute<std::complex<double>, uint8_t, std::complex<double>>},
451+ {DT_COMPLEX64, MulDiffTypeCompute<std::complex<double>, std::complex<float>, std::complex<double>>}}}};
452+ return kcalls;
453+}
454+ 
455+uint32_t MulCpuKernel::Compute(CpuKernelContext& ctx)
456+{
457+ if (NormalCheck(ctx, kInputNum, kOutputNum) != KERNEL_STATUS_OK) {
458+ return KERNEL_STATUS_PARAM_INVALID;
459+ }
460+ Tensor* input0 = ctx.Input(kFirstInputIndex);
461+ Tensor* input1 = ctx.Input(kSecondInputIndex);
462+ if ((input0->GetDataSize() == 0) || (input1->GetDataSize() == 0)) {
463+ KERNEL_LOG_INFO("[%s] Input is empty tensor.", ctx.GetOpType().c_str());
464+ return KERNEL_STATUS_OK;
465+ }
466+ 
467+ auto dtype_in1 = ctx.Input(kFirstInputIndex)->GetDataType();
468+ auto dtype_in2 = ctx.Input(kSecondInputIndex)->GetDataType();
469+ auto dtype_out = ctx.Output(kFirstOutputIndex)->GetDataType();
470+ KERNEL_LOG_DEBUG("Mul kernel get input1 dtype[%s], input2 dtype[%s], output dtype[%s].",
471+ DTypeStr(dtype_in1).c_str(), DTypeStr(dtype_in2).c_str(), DTypeStr(dtype_out).c_str());
472+ if (dtype_in1 == dtype_in2) {
473+ return MulSameTypeCompute(ctx);
474+ }
475+ 
476+ const auto& func_map = GetMulDiffTypeCalls().find(dtype_in1);
477+ if (func_map != GetMulDiffTypeCalls().end()) {
478+ const auto& funcs = func_map->second.find(dtype_in2);
479+ if (funcs != func_map->second.end()) {
480+ return (funcs->second)(ctx);
481+ }
482+ }
483+ return KERNEL_STATUS_PARAM_INVALID;
484+}
485+ 
486+REGISTER_CPU_KERNEL(kMul, MulCpuKernel);
487+} // namespace aicpu
@@ -0,0 +1,46 @@
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+#ifndef OPS_MATH_MATH_MUL_OP_KERNEL_AICPU_MUL_AICPU_H_
12+#define OPS_MATH_MATH_MUL_OP_KERNEL_AICPU_MUL_AICPU_H_
13+ 
14+#define EIGEN_USE_THREADS
15+#define EIGEN_USE_SIMPLE_THREAD_POOL
16+ 
17+#include "cpu_kernel.h"
18+#include "utils/bcast.h"
19+ 
20+namespace aicpu {
21+class MulCpuKernel : public CpuKernel {
22+public:
23+ MulCpuKernel() = default;
24+ ~MulCpuKernel() override = default;
25+ uint32_t Compute(CpuKernelContext& ctx) override;
26+ 
27+private:
28+ template <typename T>
29+ uint32_t MulCompute(const CpuKernelContext& ctx);
30+ 
31+ template <typename T>
32+ uint32_t MulDispatch(BCalcInfo& calc_info);
33+ 
34+ bool AlignedCheck(const BCalcInfo& calc_info) const;
35+ 
36+ template <int32_t RANK, typename T>
37+ uint32_t MulCalculateWithAlignedCheck(BCalcInfo& calc_info);
38+ 
39+ template <int32_t RANK, typename T, int32_t OPTION>
40+ uint32_t MulCalculate(BCalcInfo& calc_info);
41+ 
42+ uint32_t MulSameTypeCompute(const CpuKernelContext& ctx);
43+};
44+} // namespace aicpu
45+ 
46+#endif // OPS_MATH_MATH_MUL_OP_KERNEL_AICPU_MUL_AICPU_H_
@@ -0,0 +1,33 @@
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 "register/op_def_registry.h"
12+#include "../../../common/inc/aicpu/aicpu_op_def.h"
13+ 
14+namespace ops {
15+class Mul : public OpDef {
16+public:
17+ explicit Mul(const char* name) : OpDef(name)
18+ {
19+ const std::vector<ge::DataType> data_types = {ge::DT_INT8, ge::DT_INT16, ge::DT_UINT16, ge::DT_UINT8,
20+ ge::DT_INT32, ge::DT_INT64, ge::DT_UINT32, ge::DT_UINT64,
21+ ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_DOUBLE,
22+ ge::DT_COMPLEX64, ge::DT_COMPLEX128};
23+ this->Input("x1").ParamType(REQUIRED).DataType(data_types);
24+ this->Input("x2").ParamType(REQUIRED).DataType(data_types);
25+ this->Output("y").ParamType(REQUIRED).DataType(data_types);
26+ 
27+ ApplyMathAicpuDefaultCfg(*this);
28+ this->AICPU().ExtendCfgInfo(OP_INFO_OPS_FLAG.c_str(), OPEN_OPS_FLAG.c_str());
29+ }
30+};
31+ 
32+OP_ADD(Mul);
33+} // namespace ops
@@ -0,0 +1,267 @@
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 <complex>
12+#include <memory>
13+#include <numeric>
14+#include <vector>
15+ 
16+#include "gtest/gtest.h"
17+#ifndef private
18+#define private public
19+#define protected public
20+#endif
21+#include "utils/aicpu_test_utils.h"
22+#include "cpu_kernel_utils.h"
23+#include "node_def_builder.h"
24+#undef private
25+#undef protected
26+#include "Eigen/Core"
27+ 
28+using namespace std;
29+using namespace aicpu;
30+ 
31+class TEST_MUL_AICPU_UT : public testing::Test {};
32+ 
33+auto CreateMulNodeDef(const vector<vector<int64_t>>& shapes, const vector<DataType>& data_types,
34+ const vector<void*>& datas) -> decltype(CpuKernelUtils::CreateNodeDef())
35+{
36+ auto node_def = CpuKernelUtils::CreateNodeDef();
37+ NodeDefBuilder(node_def.get(), "Mul", "Mul")
38+ .Input({"x1", data_types[0], shapes[0], datas[0]})
39+ .Input({"x2", data_types[1], shapes[1], datas[1]})
40+ .Output({"y", data_types[2], shapes[2], datas[2]});
41+ return node_def;
42+}
43+ 
44+template <typename T>
45+void RunMulKernel(const vector<vector<int64_t>>& shapes, const vector<DataType>& data_types, const vector<T>& input1,
46+ const vector<T>& input2, const vector<T>& expect_output, uint32_t expect_status = KERNEL_STATUS_OK)
47+{
48+ auto calc_size = [](const vector<int64_t>& shape) -> uint64_t {
49+ return shape.empty() ? 1 : accumulate(shape.begin(), shape.end(), 1LL, multiplies<int64_t>());
50+ };
51+ 
52+ const uint64_t in0_size = calc_size(shapes[0]);
53+ const uint64_t in1_size = calc_size(shapes[1]);
54+ const uint64_t out_size = calc_size(shapes[2]);
55+ 
56+ auto x1_data = make_unique<T[]>(in0_size);
57+ auto x2_data = make_unique<T[]>(in1_size);
58+ auto output_data = make_unique<T[]>(out_size);
59+ 
60+ for (uint64_t i = 0; i < in0_size; ++i) {
61+ x1_data[i] = input1[i];
62+ }
63+ for (uint64_t i = 0; i < in1_size; ++i) {
64+ x2_data[i] = input2[i];
65+ }
66+ for (uint64_t i = 0; i < out_size; ++i) {
67+ output_data[i] = T();
68+ }
69+ 
70+ vector<void*> datas = {static_cast<void*>(x1_data.get()), static_cast<void*>(x2_data.get()),
71+ static_cast<void*>(output_data.get())};
72+ auto node_def = CreateMulNodeDef(shapes, data_types, datas);
73+ RUN_KERNEL(node_def, HOST, expect_status);
74+ 
75+ if (expect_status == KERNEL_STATUS_OK) {
76+ auto expect = make_unique<T[]>(out_size);
77+ for (uint64_t i = 0; i < out_size; ++i) {
78+ expect[i] = expect_output[i];
79+ }
80+ EXPECT_TRUE(CompareResult(output_data.get(), expect.get(), out_size));
81+ }
82+}
83+ 
84+TEST_F(TEST_MUL_AICPU_UT, FLOAT_SAME_SHAPE_SUCC)
85+{
86+ vector<DataType> data_types = {DT_FLOAT, DT_FLOAT, DT_FLOAT};
87+ vector<vector<int64_t>> shapes = {{2, 3}, {2, 3}, {2, 3}};
88+ vector<float> x1 = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f};
89+ vector<float> x2 = {6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 1.0f};
90+ vector<float> expect = {6.0f, 10.0f, 12.0f, 12.0f, 10.0f, 6.0f};
91+ RunMulKernel(shapes, data_types, x1, x2, expect);
92+}
93+ 
94+TEST_F(TEST_MUL_AICPU_UT, INT32_SAME_SHAPE_SUCC)
95+{
96+ vector<DataType> data_types = {DT_INT32, DT_INT32, DT_INT32};
97+ vector<vector<int64_t>> shapes = {{2, 3}, {2, 3}, {2, 3}};
98+ vector<int32_t> x1 = {1, 2, 3, 4, 5, 6};
99+ vector<int32_t> x2 = {6, 5, 4, 3, 2, 1};
100+ vector<int32_t> expect = {6, 10, 12, 12, 10, 6};
101+ RunMulKernel(shapes, data_types, x1, x2, expect);
102+}
103+ 
104+TEST_F(TEST_MUL_AICPU_UT, DOUBLE_SAME_SHAPE_SUCC)
105+{
106+ vector<DataType> data_types = {DT_DOUBLE, DT_DOUBLE, DT_DOUBLE};
107+ vector<vector<int64_t>> shapes = {{2, 3}, {2, 3}, {2, 3}};
108+ vector<double> x1 = {1.5, 2.5, 3.5, 4.5, 5.5, 6.5};
109+ vector<double> x2 = {2.0, 2.0, 2.0, 2.0, 2.0, 2.0};
110+ vector<double> expect = {3.0, 5.0, 7.0, 9.0, 11.0, 13.0};
111+ RunMulKernel(shapes, data_types, x1, x2, expect);
112+}
113+ 
114+TEST_F(TEST_MUL_AICPU_UT, FLOAT16_SAME_SHAPE_SUCC)
115+{
116+ vector<DataType> data_types = {DT_FLOAT16, DT_FLOAT16, DT_FLOAT16};
117+ vector<vector<int64_t>> shapes = {{2, 3}, {2, 3}, {2, 3}};
118+ vector<Eigen::half> x1 = {Eigen::half(1.0), Eigen::half(2.0), Eigen::half(3.0),
119+ Eigen::half(4.0), Eigen::half(5.0), Eigen::half(6.0)};
120+ vector<Eigen::half> x2 = {Eigen::half(6.0), Eigen::half(5.0), Eigen::half(4.0),
121+ Eigen::half(3.0), Eigen::half(2.0), Eigen::half(1.0)};
122+ vector<Eigen::half> expect = {Eigen::half(6.0), Eigen::half(10.0), Eigen::half(12.0),
123+ Eigen::half(12.0), Eigen::half(10.0), Eigen::half(6.0)};
124+ RunMulKernel(shapes, data_types, x1, x2, expect);
125+}
126+ 
127+TEST_F(TEST_MUL_AICPU_UT, COMPLEX64_SAME_SHAPE_SUCC)
128+{
129+ vector<DataType> data_types = {DT_COMPLEX64, DT_COMPLEX64, DT_COMPLEX64};
130+ vector<vector<int64_t>> shapes = {{2}, {2}, {2}};
131+ vector<complex<float>> x1 = {{1.0f, 2.0f}, {3.0f, 4.0f}};
132+ vector<complex<float>> x2 = {{5.0f, 6.0f}, {7.0f, 8.0f}};
133+ vector<complex<float>> expect = {{1.0f * 5.0f - 2.0f * 6.0f, 1.0f * 6.0f + 2.0f * 5.0f},
134+ {3.0f * 7.0f - 4.0f * 8.0f, 3.0f * 8.0f + 4.0f * 7.0f}};
135+ RunMulKernel(shapes, data_types, x1, x2, expect);
136+}
137+ 
138+TEST_F(TEST_MUL_AICPU_UT, FLOAT_SCALAR_SUCC)
139+{
140+ vector<DataType> data_types = {DT_FLOAT, DT_FLOAT, DT_FLOAT};
141+ vector<vector<int64_t>> shapes = {{}, {}, {}};
142+ vector<float> x1 = {3.0f};
143+ vector<float> x2 = {4.0f};
144+ vector<float> expect = {12.0f};
145+ RunMulKernel(shapes, data_types, x1, x2, expect);
146+}
147+ 
148+TEST_F(TEST_MUL_AICPU_UT, FLOAT_BROADCAST_X_SCALAR_SUCC)
149+{
150+ vector<DataType> data_types = {DT_FLOAT, DT_FLOAT, DT_FLOAT};
151+ vector<vector<int64_t>> shapes = {{}, {2, 3}, {2, 3}};
152+ vector<float> x1 = {3.0f};
153+ vector<float> x2 = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f};
154+ vector<float> expect = {3.0f, 6.0f, 9.0f, 12.0f, 15.0f, 18.0f};
155+ RunMulKernel(shapes, data_types, x1, x2, expect);
156+}
157+ 
158+TEST_F(TEST_MUL_AICPU_UT, FLOAT_BROADCAST_Y_SCALAR_SUCC)
159+{
160+ vector<DataType> data_types = {DT_FLOAT, DT_FLOAT, DT_FLOAT};
161+ vector<vector<int64_t>> shapes = {{2, 3}, {}, {2, 3}};
162+ vector<float> x1 = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f};
163+ vector<float> x2 = {3.0f};
164+ vector<float> expect = {3.0f, 6.0f, 9.0f, 12.0f, 15.0f, 18.0f};
165+ RunMulKernel(shapes, data_types, x1, x2, expect);
166+}
167+ 
168+TEST_F(TEST_MUL_AICPU_UT, FLOAT_BROADCAST_BOTH_SUCC)
169+{
170+ vector<DataType> data_types = {DT_FLOAT, DT_FLOAT, DT_FLOAT};
171+ vector<vector<int64_t>> shapes = {{1, 2}, {2, 1}, {2, 2}};
172+ vector<float> x1 = {1.0f, 2.0f};
173+ vector<float> x2 = {3.0f, 4.0f};
174+ vector<float> expect = {3.0f, 6.0f, 4.0f, 8.0f};
175+ RunMulKernel(shapes, data_types, x1, x2, expect);
176+}
177+ 
178+TEST_F(TEST_MUL_AICPU_UT, INT64_LARGE_PARALLEL_SUCC)
179+{
180+ vector<DataType> data_types = {DT_INT64, DT_INT64, DT_INT64};
181+ vector<vector<int64_t>> shapes = {{4, 2048}, {4, 2048}, {4, 2048}};
182+ vector<int64_t> x1(4 * 2048);
183+ vector<int64_t> x2(4 * 2048);
184+ vector<int64_t> expect(4 * 2048);
185+ for (int i = 0; i < 4 * 2048; ++i) {
186+ x1[i] = static_cast<int64_t>(i % 100);
187+ x2[i] = static_cast<int64_t>(2);
188+ expect[i] = x1[i] * x2[i];
189+ }
190+ RunMulKernel(shapes, data_types, x1, x2, expect);
191+}
192+ 
193+TEST_F(TEST_MUL_AICPU_UT, DIFF_TYPE_INT8_UINT8_SUCC)
194+{
195+ vector<DataType> data_types = {DT_INT8, DT_UINT8, DT_INT16};
196+ vector<vector<int64_t>> shapes = {{2, 3}, {2, 3}, {2, 3}};
197+ int8_t x1[6] = {1, 2, 3, -1, -2, -3};
198+ uint8_t x2[6] = {1, 2, 3, 1, 2, 3};
199+ int16_t output[6] = {0};
200+ vector<void*> datas = {static_cast<void*>(x1), static_cast<void*>(x2), static_cast<void*>(output)};
201+ auto node_def = CreateMulNodeDef(shapes, data_types, datas);
202+ RUN_KERNEL(node_def, HOST, KERNEL_STATUS_OK);
203+ int16_t expect[6] = {1, 4, 9, static_cast<int16_t>(-1), static_cast<int16_t>(-4), static_cast<int16_t>(-9)};
204+ EXPECT_TRUE(CompareResult(output, expect, static_cast<uint64_t>(6)));
205+}
206+ 
207+TEST_F(TEST_MUL_AICPU_UT, DIFF_TYPE_FLOAT_DOUBLE_SUCC)
208+{
209+ vector<DataType> data_types = {DT_FLOAT, DT_DOUBLE, DT_DOUBLE};
210+ vector<vector<int64_t>> shapes = {{2}, {2}, {2}};
211+ float x1[2] = {1.5f, 2.5f};
212+ double x2[2] = {2.0, 4.0};
213+ double output[2] = {0.0};
214+ vector<void*> datas = {static_cast<void*>(x1), static_cast<void*>(x2), static_cast<void*>(output)};
215+ auto node_def = CreateMulNodeDef(shapes, data_types, datas);
216+ RUN_KERNEL(node_def, HOST, KERNEL_STATUS_OK);
217+ double expect[2] = {3.0, 10.0};
218+ EXPECT_TRUE(CompareResult(output, expect, static_cast<uint64_t>(2)));
219+}
220+ 
221+TEST_F(TEST_MUL_AICPU_UT, DIFF_TYPE_UINT8_INT32_SUCC)
222+{
223+ vector<DataType> data_types = {DT_UINT8, DT_INT32, DT_INT32};
224+ vector<vector<int64_t>> shapes = {{4}, {4}, {4}};
225+ uint8_t x1[4] = {0, 100, 200, 255};
226+ int32_t x2[4] = {3, 3, 3, 3};
227+ int32_t output[4] = {0};
228+ vector<void*> datas = {static_cast<void*>(x1), static_cast<void*>(x2), static_cast<void*>(output)};
229+ auto node_def = CreateMulNodeDef(shapes, data_types, datas);
230+ RUN_KERNEL(node_def, HOST, KERNEL_STATUS_OK);
231+ int32_t expect[4] = {0, 300, 600, 765};
232+ EXPECT_TRUE(CompareResult(output, expect, static_cast<uint64_t>(4)));
233+}
234+ 
235+TEST_F(TEST_MUL_AICPU_UT, INPUT_DTYPE_UNSUPPORT)
236+{
237+ vector<DataType> data_types = {DT_BOOL, DT_BOOL, DT_BOOL};
238+ vector<vector<int64_t>> shapes = {{2, 3}, {2, 3}, {2, 3}};
239+ bool x1[6] = {true};
240+ bool x2[6] = {true};
241+ bool output[6] = {false};
242+ vector<void*> datas = {static_cast<void*>(x1), static_cast<void*>(x2), static_cast<void*>(output)};
243+ auto node_def = CreateMulNodeDef(shapes, data_types, datas);
244+ RUN_KERNEL(node_def, HOST, KERNEL_STATUS_PARAM_INVALID);
245+}
246+ 
247+TEST_F(TEST_MUL_AICPU_UT, BCAST_SHAPE_MISMATCH)
248+{
249+ vector<DataType> data_types = {DT_FLOAT, DT_FLOAT, DT_FLOAT};
250+ vector<vector<int64_t>> shapes = {{1, 3}, {1, 2}, {1, 3}};
251+ float x1[3] = {1.0f, 2.0f, 3.0f};
252+ float x2[2] = {4.0f, 5.0f};
253+ float output[3] = {0.0f};
254+ vector<void*> datas = {static_cast<void*>(x1), static_cast<void*>(x2), static_cast<void*>(output)};
255+ auto node_def = CreateMulNodeDef(shapes, data_types, datas);
256+ RUN_KERNEL(node_def, HOST, KERNEL_STATUS_PARAM_INVALID);
257+}
258+ 
259+TEST_F(TEST_MUL_AICPU_UT, INPUT_NULL_EXCEPTION)
260+{
261+ vector<DataType> data_types = {DT_FLOAT, DT_FLOAT, DT_FLOAT};
262+ vector<vector<int64_t>> shapes = {{2, 3}, {2, 3}, {2, 3}};
263+ float output[6] = {0.0f};
264+ vector<void*> datas = {static_cast<void*>(nullptr), static_cast<void*>(nullptr), static_cast<void*>(output)};
265+ auto node_def = CreateMulNodeDef(shapes, data_types, datas);
266+ RUN_KERNEL(node_def, HOST, KERNEL_STATUS_PARAM_INVALID);
267+}