已合并
DepthwiseDX算子融合pass相关代码开源到ops-nn仓 #8172
cheng_gao创建于 8月3日
DepthwiseDX算子融合pass相关代码开源到ops-nn仓 #8172
已合并
cheng_gao创建于 8月3日
7 个文件变更+1065-36
@@ -20,7 +20,10 @@ void ConvBackpropFusionBasePass::InitMember()
20 npuArch = NpuArch::DAV_RESV;20 npuArch = NpuArch::DAV_RESV;
21 input0Desc = TensorDesc();21 input0Desc = TensorDesc();
22 input1Desc = TensorDesc();22 input1Desc = TensorDesc();
23- input2Desc = TensorDesc();23+ if (isDynamic) {
24+ input2Desc = TensorDesc();
25+ }
26+ 
24 outputDesc = TensorDesc();27 outputDesc = TensorDesc();
25 convBpAttr.Reset();28 convBpAttr.Reset();
26}29}
@@ -43,15 +46,19 @@ bool ConvBackpropFusionBasePass::MeetRequirements(const GNode& convBpInputNode)
43bool ConvBackpropFusionBasePass::GetNodeDesc(const GNode& node)46bool ConvBackpropFusionBasePass::GetNodeDesc(const GNode& node)
44{47{
45 InitMember();48 InitMember();
46- // 获取输入索引
47 int32_t inputIdx0 = 0;49 int32_t inputIdx0 = 0;
48 int32_t inputIdx1 = 1;50 int32_t inputIdx1 = 1;
49- int32_t outBackpropIdx = 2;
50 OP_CHECK_IF(node.GetInputDesc(inputIdx0, input0Desc) != GRAPH_SUCCESS ||51 OP_CHECK_IF(node.GetInputDesc(inputIdx0, input0Desc) != GRAPH_SUCCESS ||
51 node.GetInputDesc(inputIdx1, input1Desc) != GRAPH_SUCCESS ||52 node.GetInputDesc(inputIdx1, input1Desc) != GRAPH_SUCCESS ||
52- node.GetInputDesc(outBackpropIdx, input2Desc) != GRAPH_SUCCESS ||
53 node.GetOutputDesc(OUTPUT_INDEX, outputDesc) != GRAPH_SUCCESS,53 node.GetOutputDesc(OUTPUT_INDEX, outputDesc) != GRAPH_SUCCESS,
54 OP_LOGE(GetNodeType().GetString(), "Get input/output desc failed"), return false);54 OP_LOGE(GetNodeType().GetString(), "Get input/output desc failed"), return false);
55+ 
56+ // 动态算子多了input_size或filter_size字段,需要获取第3个输入
H
Hhexinhui26 天前

950的图有静态算子吗?

likedislike
cheng_gao
cheng_gao
25 天前 评论:
57+ if (isDynamic) {
58+ int32_t inputIdx2 = 2;
59+ OP_CHECK_IF(node.GetInputDesc(inputIdx2, input2Desc) != GRAPH_SUCCESS,
60+ OP_LOGE(GetNodeType().GetString(), "Get input2 desc failed"), return false);
61+ }
55 return true;62 return true;
56}63}
57 64 
@@ -80,8 +87,14 @@ bool ConvBackpropFusionBasePass::GetNodeAttrs(const GNode& node)
80 87 
81 convBpAttr.dataFormat = std::string(format.GetString());88 convBpAttr.dataFormat = std::string(format.GetString());
82 89 
83- convBpAttr.hf32 = input2Desc.GetDataType() == DataType::DT_FLOAT &&90+ // 判断out_backprop的dtype,动态算子时为第3个输入,静态算子时为第2个输入
84- convBpAttr.opImplModeEnum == HF32_PRECISION_MODE_INT;91+ if (isDynamic) {
92+ convBpAttr.hf32 = input2Desc.GetDataType() == DataType::DT_FLOAT &&
93+ convBpAttr.opImplModeEnum == HF32_PRECISION_MODE_INT;
94+ } else {
95+ convBpAttr.hf32 = input1Desc.GetDataType() == DataType::DT_FLOAT &&
96+ convBpAttr.opImplModeEnum == HF32_PRECISION_MODE_INT;
97+ }
85 98 
86 convBpAttr.opImplModeEnum = convBpAttr.hf32 ? convBpAttr.opImplModeEnum : 0x1;99 convBpAttr.opImplModeEnum = convBpAttr.hf32 ? convBpAttr.opImplModeEnum : 0x1;
87 100 
@@ -30,6 +30,7 @@ struct ConvBackpropAttrs {
30 std::vector<int64_t> strides;30 std::vector<int64_t> strides;
31 std::vector<int64_t> pads;31 std::vector<int64_t> pads;
32 std::vector<int64_t> dilations;32 std::vector<int64_t> dilations;
33+ std::vector<int64_t> input_size;
Y
Yyuxin525 天前

新增的 input_size 和 filter_size 字段在 Reset() 中未被清理。由于pass实例会被复用处理多个匹配节点,前一个节点的 input_size 会残留到下一个节点,导致输出算子上写入脏数据

likedislike
33 int64_t groups = 0;34 int64_t groups = 0;
34 std::string dataFormat;35 std::string dataFormat;
35 int64_t opImplModeEnum = 0;36 int64_t opImplModeEnum = 0;
@@ -41,6 +42,7 @@ struct ConvBackpropAttrs {
41 strides.clear();42 strides.clear();
42 pads.clear();43 pads.clear();
43 dilations.clear();44 dilations.clear();
45+ input_size.clear();
44 groups = 0;46 groups = 0;
45 dataFormat = "";47 dataFormat = "";
46 opImplModeEnum = 0;48 opImplModeEnum = 0;
@@ -70,7 +72,7 @@ protected:
70 72 
71 virtual bool UpdateNodeInputDescInfo(ge::GNode* node);73 virtual bool UpdateNodeInputDescInfo(ge::GNode* node);
72 74 
73- void SetNodeAttrs(ge::GNode& outNode);75+ virtual void SetNodeAttrs(ge::GNode& outNode);
74 76 
75 virtual ge::AscendString GetNodeType() const = 0;77 virtual ge::AscendString GetNodeType() const = 0;
76 78 
@@ -87,6 +89,11 @@ protected:
87 ge::TensorDesc outputDesc;89 ge::TensorDesc outputDesc;
88 90 
89 ConvBackpropAttrs convBpAttr;91 ConvBackpropAttrs convBpAttr;
92+ 
93+ // 是否是动态算子调用,带D算子为false,950中动静归一全部为true
94+ bool isDynamic = true;
95+ 
96+ bool isArch35 = true;
90};97};
91 98 
92} // namespace ops99} // namespace ops
@@ -60,6 +60,17 @@ bool ConvBackpropFusionUtilsPass::CheckSocAndIntrinsic(const std::map<std::strin
60 return true;60 return true;
61}61}
62 62 
63+bool ConvBackpropFusionUtilsPass::IsArch35()
64+{
65+ fe::PlatformInfo platformInfo;
66+ fe::OptionalInfo optionalInfo;
67+ if (fe::PlatformInfoManager::Instance().GetPlatformInfoWithOutSocVersion(platformInfo, optionalInfo) != SUCCESS) {
68+ return false;
69+ }
70+ const std::string soc = platformInfo.str_info.short_soc_version;
71+ return SUPPORT_SOC_LIST.find(soc) != SUPPORT_SOC_LIST.end() && SUPPORT_SOC_LIST.at(soc) == NpuArch::DAV_3510;
72+}
73+ 
63bool ConvBackpropFusionUtilsPass::GetNodeName(const GNode& node, std::string& nodeName)74bool ConvBackpropFusionUtilsPass::GetNodeName(const GNode& node, std::string& nodeName)
64{75{
65 AscendString rawNodeName;76 AscendString rawNodeName;
@@ -82,53 +93,96 @@ int64_t ConvBackpropFusionUtilsPass::GetAiCoreCount()
82 return platformInfo.soc_info.ai_core_cnt;93 return platformInfo.soc_info.ai_core_cnt;
83}94}
84 95 
85-bool ConvBackpropFusionUtilsPass::CreateTransposeNode(EsGraphBuilder& builder, const TransposeNodeConfig& config,96+bool ConvBackpropFusionUtilsPass::CreateTransposeNodeImpl(EsGraphBuilder& builder, const TransposeNodeConfig& config,
86- EsTensorHolder& output, TensorDesc& outDesc,97+ EsTensorHolder& output, TensorDesc& outDesc,
87- const AscendString& opType)98+ const TensorDesc* inDescOverride, bool isTransposeD,
99+ const AscendString& opType)
88{100{
89 auto* graph = builder.GetCGraphBuilder()->GetGraph();101 auto* graph = builder.GetCGraphBuilder()->GetGraph();
90- OP_CHECK_IF(graph == nullptr, OP_LOGE(opType.GetString(), "create transpose node failed"), return false);102+ OP_CHECK_IF(graph == nullptr, OP_LOGE(opType.GetString(), "get graph failed"), return false);
91 103 
92 auto* producer = config.input.GetProducer();104 auto* producer = config.input.GetProducer();
93- OP_CHECK_IF(producer == nullptr, OP_LOGE(opType.GetString(), "input producer is nullptr in CreateTransposeNode"),105+ OP_CHECK_IF(producer == nullptr, OP_LOGE(opType.GetString(), "input producer is nullptr"), return false);
94- return false);
95 106 
96 TensorDesc inDesc;107 TensorDesc inDesc;
97- producer->GetOutputDesc(config.input.GetProducerOutIndex(), inDesc);108+ if (inDescOverride != nullptr) {
98- auto transposeNode = ge::es::CompliantNodeBuilder(graph)109+ inDesc = *inDescOverride;
99- .OpType("Transpose")110+ } else {
100- .Name(config.name.c_str())111+ OP_CHECK_IF(producer->GetOutputDesc(config.input.GetProducerOutIndex(), inDesc) != GRAPH_SUCCESS,
101- .IrDefInputs({{"x", ge::es::CompliantNodeBuilder::kEsIrInputRequired, ""},112+ OP_LOGE(opType.GetString(), "Get output desc failed"), return false);
102- {"perm", ge::es::CompliantNodeBuilder::kEsIrInputRequired, ""}})113+ }
103- .IrDefOutputs({{"y", ge::es::CompliantNodeBuilder::kEsIrOutputRequired, ""}})114+ 
104- .Build();115+ GNode transposeNode;
105- OP_CHECK_IF(ge::es::AddEdgeAndUpdatePeerDesc(*graph, *producer, TENSOR_DEFAULT_OUTPUT_INDEX, transposeNode,116+ if (isTransposeD) {
106- TRANSPOSE_INPUT_X_INDEX) != GRAPH_SUCCESS,117+ transposeNode = ge::es::CompliantNodeBuilder(graph)
107- OP_LOGE(opType.GetString(), "Add edge for transpose input failed"), return false);118+ .OpType("TransposeD")
108- transposeNode.UpdateInputDesc(TRANSPOSE_INPUT_X_INDEX, inDesc);119+ .Name(config.name.c_str())
109- auto permTensorHolder = builder.CreateVector(config.perm);120+ .IrDefInputs({{"x", ge::es::CompliantNodeBuilder::kEsIrInputRequired, ""}})
110- auto* permTensorProducer = permTensorHolder.GetProducer();121+ .IrDefOutputs({{"y", ge::es::CompliantNodeBuilder::kEsIrOutputRequired, ""}})
111- OP_CHECK_IF(permTensorProducer == nullptr, OP_LOGE(opType.GetString(), "perm producer is nullptr"), return false);122+ .Build();
112- OP_CHECK_IF(ge::es::AddEdgeAndUpdatePeerDesc(*graph, *permTensorProducer, TENSOR_DEFAULT_OUTPUT_INDEX,123+ } else {
113- transposeNode, TRANSPOSE_INPUT_PERM_INDEX) != GRAPH_SUCCESS,124+ transposeNode = ge::es::CompliantNodeBuilder(graph)
114- OP_LOGE(opType.GetString(), "Add edge for transpose perm failed"), return false);125+ .OpType("Transpose")
126+ .Name(config.name.c_str())
127+ .IrDefInputs({{"x", ge::es::CompliantNodeBuilder::kEsIrInputRequired, ""},
128+ {"perm", ge::es::CompliantNodeBuilder::kEsIrInputRequired, ""}})
129+ .IrDefOutputs({{"y", ge::es::CompliantNodeBuilder::kEsIrOutputRequired, ""}})
130+ .Build();
131+ }
132+ 
133+ OP_CHECK_IF(ge::es::AddEdgeAndUpdatePeerDesc(*graph, *producer, config.input.GetProducerOutIndex(), transposeNode,
134+ TRANSPOSE_INPUT_X_INDEX) != GRAPH_SUCCESS,
135+ OP_LOGE(opType.GetString(), "Add edge for transpose x input failed"), return false);
136+ OP_CHECK_IF(transposeNode.UpdateInputDesc(TRANSPOSE_INPUT_X_INDEX, inDesc) != GRAPH_SUCCESS,
137+ OP_LOGE(opType.GetString(), "Update transpose input desc failed"), return false);
138+ 
139+ if (isTransposeD) {
140+ std::vector<int32_t> perm(config.perm.begin(), config.perm.end());
141+ OP_CHECK_IF(transposeNode.SetAttr("perm", perm) != GRAPH_SUCCESS,
142+ OP_LOGE(opType.GetString(), "Set perm attr failed"), return false);
143+ } else {
144+ auto permTensorHolder = builder.CreateVector(config.perm);
145+ auto* permTensorProducer = permTensorHolder.GetProducer();
146+ OP_CHECK_IF(permTensorProducer == nullptr, OP_LOGE(opType.GetString(), "perm producer is nullptr"),
147+ return false);
148+ OP_CHECK_IF(ge::es::AddEdgeAndUpdatePeerDesc(*graph, *permTensorProducer, TENSOR_DEFAULT_OUTPUT_INDEX,
149+ transposeNode, TRANSPOSE_INPUT_PERM_INDEX) != GRAPH_SUCCESS,
150+ OP_LOGE(opType.GetString(), "Add edge for transpose perm failed"), return false);
151+ TensorDesc permTensorDesc;
152+ OP_CHECK_IF(permTensorProducer->GetOutputDesc(TENSOR_DEFAULT_OUTPUT_INDEX, permTensorDesc) != GRAPH_SUCCESS,
153+ OP_LOGE(opType.GetString(), "Get perm tensor desc failed"), return false);
154+ OP_CHECK_IF(transposeNode.UpdateInputDesc(TRANSPOSE_INPUT_PERM_INDEX, permTensorDesc) != GRAPH_SUCCESS,
155+ OP_LOGE(opType.GetString(), "Update perm input desc failed"), return false);
156+ }
115 157 
116- TensorDesc permTensorDesc;
117- permTensorProducer->GetOutputDesc(TENSOR_DEFAULT_OUTPUT_INDEX, permTensorDesc);
118- transposeNode.UpdateInputDesc(TRANSPOSE_INPUT_PERM_INDEX, permTensorDesc);
119 outDesc.SetDataType(inDesc.GetDataType());158 outDesc.SetDataType(inDesc.GetDataType());
120 auto outShape = CalcTransposeShape(inDesc.GetShape().GetDims(), config.perm);159 auto outShape = CalcTransposeShape(inDesc.GetShape().GetDims(), config.perm);
121 outDesc.SetShape(Shape(outShape));160 outDesc.SetShape(Shape(outShape));
122 outDesc.SetOriginShape(Shape(outShape));161 outDesc.SetOriginShape(Shape(outShape));
123 outDesc.SetFormat(config.format);162 outDesc.SetFormat(config.format);
124 outDesc.SetOriginFormat(config.format);163 outDesc.SetOriginFormat(config.format);
125- transposeNode.UpdateOutputDesc(TRANSPOSE_OUTPUT_Y_INDEX, outDesc);164+ OP_CHECK_IF(transposeNode.UpdateOutputDesc(TRANSPOSE_OUTPUT_Y_INDEX, outDesc) != GRAPH_SUCCESS,
165+ OP_LOGE(opType.GetString(), "Update transpose output desc failed"), return false);
166+ 
126 output = EsTensorHolder(167 output = EsTensorHolder(
127 builder.GetCGraphBuilder()->GetTensorHolderFromNode(transposeNode, TRANSPOSE_OUTPUT_Y_INDEX));168 builder.GetCGraphBuilder()->GetTensorHolderFromNode(transposeNode, TRANSPOSE_OUTPUT_Y_INDEX));
128- 
129 return true;169 return true;
130}170}
131 171 
172+bool ConvBackpropFusionUtilsPass::CreateTransposeNode(EsGraphBuilder& builder, const TransposeNodeConfig& config,
173+ EsTensorHolder& output, TensorDesc& outDesc,
174+ const AscendString& opType)
175+{
176+ return CreateTransposeNodeImpl(builder, config, output, outDesc, nullptr, false, opType);
177+}
178+ 
179+bool ConvBackpropFusionUtilsPass::CreateTransposeDNode(EsGraphBuilder& builder, const TransposeNodeConfig& config,
180+ EsTensorHolder& output, TensorDesc& outDesc,
181+ const TensorDesc& inputDesc, const AscendString& opType)
182+{
183+ return CreateTransposeNodeImpl(builder, config, output, outDesc, &inputDesc, true, opType);
184+}
185+ 
132int32_t ConvBackpropFusionUtilsPass::GetExpandAxis(ge::Format format2D)186int32_t ConvBackpropFusionUtilsPass::GetExpandAxis(ge::Format format2D)
133{187{
134 if (format2D == ge::FORMAT_NCHW) {188 if (format2D == ge::FORMAT_NCHW) {
@@ -121,6 +121,8 @@ public:
121 121 
122 static bool CheckSocAndIntrinsic(const std::map<std::string, NpuArch>& supportSocList, NpuArch& npuArch);122 static bool CheckSocAndIntrinsic(const std::map<std::string, NpuArch>& supportSocList, NpuArch& npuArch);
123 123 
124+ static bool IsArch35();
125+ 
124 static bool GetNodeName(const ge::GNode& node, std::string& nodeName);126 static bool GetNodeName(const ge::GNode& node, std::string& nodeName);
125 127 
126 static int64_t GetAiCoreCount();128 static int64_t GetAiCoreCount();
@@ -131,6 +133,10 @@ public:
131 ge::es::EsTensorHolder& output, ge::TensorDesc& outDesc,133 ge::es::EsTensorHolder& output, ge::TensorDesc& outDesc,
132 const ge::AscendString& opType);134 const ge::AscendString& opType);
133 135 
136+ static bool CreateTransposeDNode(ge::es::EsGraphBuilder& builder, const TransposeNodeConfig& config,
137+ ge::es::EsTensorHolder& output, ge::TensorDesc& outDesc,
138+ const ge::TensorDesc& inputDesc, const ge::AscendString& opType);
139+ 
134 static int32_t GetExpandAxis(ge::Format format2D);140 static int32_t GetExpandAxis(ge::Format format2D);
135 static ge::Format Get3DFormat(ge::Format format2D);141 static ge::Format Get3DFormat(ge::Format format2D);
136 static std::string Get3DDataFormatStr(const std::string& format2D);142 static std::string Get3DDataFormatStr(const std::string& format2D);
@@ -146,6 +152,12 @@ public:
146 std::string& dataFormat, std::vector<int64_t>* outputPadding = nullptr);152 std::string& dataFormat, std::vector<int64_t>* outputPadding = nullptr);
147 153 
148 static void ExpandOutputDesc(const ge::TensorDesc& output2DDesc, ge::TensorDesc& output3DDesc);154 static void ExpandOutputDesc(const ge::TensorDesc& output2DDesc, ge::TensorDesc& output3DDesc);
155+ 
156+private:
157+ static bool CreateTransposeNodeImpl(ge::es::EsGraphBuilder& builder, const TransposeNodeConfig& config,
158+ ge::es::EsTensorHolder& output, ge::TensorDesc& outDesc,
159+ const ge::TensorDesc* inDescOverride, bool isTransposeD,
160+ const ge::AscendString& opType);
149};161};
150 162 
151} // namespace ConvBackpropFusionUtils163} // namespace ConvBackpropFusionUtils
@@ -0,0 +1,365 @@
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 "depthwise_df_fusion_pass.h"
12+ 
13+#include <cstring>
14+#include <map>
15+#include <string>
16+#include <vector>
17+ 
18+namespace ops {
19+using namespace ge;
20+using namespace ge::es;
21+using namespace ge::fusion;
22+using namespace ConvBackpropFusionUtils;
23+namespace {
24+ 
25+const AscendString PASS_NAME = "DepthwiseDfFusionPass";
26+const AscendString DEPTHWISE_D = "DepthwiseConv2DBackpropInputD";
27+const AscendString DEPTHWISE_DYN = "DepthwiseConv2DBackpropInput";
28+ 
29+constexpr uint32_t FILTER_INDEX_D = 0U;
30+constexpr uint32_t FILTER_INDEX_DYN = 1U;
31+constexpr uint32_t GRAD_OUTPUT_INDEX_D = 1U;
32+constexpr uint32_t GRAD_OUTPUT_INDEX_DYN = 2U;
33+constexpr int32_t GRAD_OUTPUT_DIM = 4;
34+constexpr int64_t GROUP_MAX_RANGE = 65535;
35+constexpr size_t FILTER_DIM_EXPECT = 4;
36+constexpr int32_t INPUT_SIZE_INDEX = 0;
37+ 
38+const std::vector<int32_t> TRANSPOSE_PERM = {1, 0, 2, 3};
39+ 
40+const std::map<ge::Format, std::vector<int32_t>> FORMAT_TO_NCHW_DIM_MAP = {{ge::FORMAT_NCHW, {0, 1, 2, 3}},
41+ {ge::FORMAT_HWCN, {3, 2, 0, 1}}};
42+ 
43+constexpr const char* DEPTHWISE_PREFIX = "Depthwise";
44+ 
45+} // anonymous namespace
46+ 
47+AscendString DepthwiseDfFusionPass::GetNodeType() const { return PASS_NAME; }
48+ 
49+bool DepthwiseDfFusionPass::GetResizeDepthwiseFilterShape(std::vector<int64_t>& resizeShape)
50+{
51+ const TensorDesc& filterDesc = isDynamic ? input1Desc : input0Desc;
52+ const auto& oriShape = filterDesc.GetShape().GetDims();
53+ ge::Format format = filterDesc.GetOriginFormat();
54+ if (oriShape.size() != FILTER_DIM_EXPECT) {
55+ OP_LOGE(GetNodeType().GetString(), "filter dim only supports 4 dims, got %zu.", oriShape.size());
56+ return false;
57+ }
58+ const auto& dimVec = FORMAT_TO_NCHW_DIM_MAP.at(format);
59+ if (format == ge::FORMAT_NCHW) {
60+ resizeShape = {oriShape[dimVec[0]] * oriShape[dimVec[1]], 1, oriShape[dimVec[2]], oriShape[dimVec[3]]};
61+ } else {
62+ resizeShape = {oriShape[dimVec[2]], oriShape[dimVec[3]], 1, oriShape[dimVec[0]] * oriShape[dimVec[1]]};
63+ }
64+ return true;
65+}
66+ 
67+bool DepthwiseDfFusionPass::ValidateArch35Descs()
68+{
69+ const TensorDesc& filterDesc = isDynamic ? input1Desc : input0Desc;
70+ const TensorDesc& dedyDesc = isDynamic ? input2Desc : input1Desc;
71+ auto filterOriShapeDims = filterDesc.GetOriginShape().GetDims();
72+ for (auto filterDim : filterOriShapeDims) {
73+ OP_CHECK_IF(filterDim <= 0,
74+ OP_LOGE(GetNodeType().GetString(), "%ld in filter shape should be positive.", filterDim),
75+ return false);
76+ }
77+ auto dedyOriShape = dedyDesc.GetOriginShape();
78+ size_t dedyDims = dedyOriShape.GetDims().size();
79+ OP_CHECK_IF(dedyDims != static_cast<size_t>(GRAD_OUTPUT_DIM) &&
80+ (dedyDims != UNKNOWN_RANK_DIM || dedyOriShape.GetDim(0) != UNKNOWN_RANK_DIM_VALUE),
81+ OP_LOGE(GetNodeType().GetString(), "out_backprop dims should be 4 or shape is [-2], but got dims=%zu",
82+ dedyDims),
83+ return false);
84+ ge::Format gradOutputFormat = dedyDesc.GetOriginFormat();
85+ ge::Format outputFormat = outputDesc.GetOriginFormat();
86+ if (outputFormat != gradOutputFormat) {
87+ OP_LOGE(GetNodeType().GetString(), "output origin format and out_backprop origin format are not consistent.");
88+ return false;
89+ }
90+ return true;
91+}
92+ 
93+bool DepthwiseDfFusionPass::ValidateFilterDesc(const GNode& matchedNode)
94+{
95+ if (isArch35) {
96+ OP_CHECK_IF(!ValidateArch35Descs(), OP_LOGE(GetNodeType().GetString(), "ValidateArch35Descs failed"),
97+ return false);
98+ }
99+ int64_t groups = 0;
100+ OP_CHECK_IF(matchedNode.GetAttr("groups", groups) != GRAPH_SUCCESS,
101+ OP_LOGE(GetNodeType().GetString(), "Failed to get groups attr"), return false);
102+ OP_CHECK_IF(groups <= 0 || groups > GROUP_MAX_RANGE,
103+ OP_LOGE(GetNodeType().GetString(), "groups=%ld should be in range [1, 65535].", groups), return false);
104+ return true;
105+}
106+ 
107+bool DepthwiseDfFusionPass::MeetRequirements(const GNode& matchedNode)
108+{
109+ AscendString matchedTypeAsc;
110+ OP_CHECK_IF(matchedNode.GetType(matchedTypeAsc) != GRAPH_SUCCESS,
111+ OP_LOGD(GetNodeType().GetString(), "GetType failed"), return false);
112+ isDynamic = (matchedTypeAsc.GetString() == std::string(DEPTHWISE_DYN.GetString()));
113+ isArch35 = ConvBackpropFusionUtilsPass::IsArch35();
114+ OP_CHECK_IF(!GetNodeDesc(matchedNode), OP_LOGE(GetNodeType().GetString(), "GetNodeDesc failed"), return false);
115+ OP_CHECK_IF(!ValidateFilterDesc(matchedNode), OP_LOGD(GetNodeType().GetString(), "ValidateFilterDesc failed"),
116+ return false);
117+ const TensorDesc& filterDesc = isDynamic ? input1Desc : input0Desc;
118+ ge::Format originFormat = filterDesc.GetOriginFormat();
119+ OP_CHECK_IF(FORMAT_TO_NCHW_DIM_MAP.find(originFormat) == FORMAT_TO_NCHW_DIM_MAP.end(),
120+ OP_LOGE(GetNodeType().GetString(), "filter origin format only supports NCHW or HWCN, got %d",
121+ static_cast<int>(originFormat)),
122+ return false);
123+ OP_LOGD(GetNodeType().GetString(), "MeetRequirements passed");
124+ return true;
125+}
126+ 
127+bool DepthwiseDfFusionPass::GetNodeAttrs(const GNode& node)
128+{
129+ OP_CHECK_IF(!ConvBackpropFusionBasePass::GetNodeAttrs(node),
130+ OP_LOGE(GetNodeType().GetString(), "Base GetNodeAttrs failed"), return false);
131+ if (!isDynamic) {
132+ OP_CHECK_IF(node.GetAttr("input_size", convBpAttr.input_size) != GRAPH_SUCCESS,
133+ OP_LOGE(GetNodeType().GetString(), "Failed to get input_size attr"), return false);
134+ }
135+ return true;
136+}
137+ 
138+void DepthwiseDfFusionPass::SetNodeAttrs(GNode& outNode)
139+{
140+ ConvBackpropFusionBasePass::SetNodeAttrs(outNode);
141+ if (!isDynamic) {
142+ if (outNode.SetAttr("input_size", convBpAttr.input_size) != GRAPH_SUCCESS) {
143+ OP_LOGD(GetNodeType().GetString(), "Set input_size attr failed");
144+ }
145+ }
146+}
147+ 
148+void DepthwiseDfFusionPass::CreateBoundaryInputs(EsGraphBuilder& builder, EsTensorHolder& iFilterHolder,
149+ EsTensorHolder& iGradOutputHolder, EsTensorHolder& iInputSizeHolder)
150+{
151+ uint32_t filterIndex = isDynamic ? FILTER_INDEX_DYN : FILTER_INDEX_D;
152+ uint32_t gradOutputIndex = isDynamic ? GRAD_OUTPUT_INDEX_DYN : GRAD_OUTPUT_INDEX_D;
153+ const TensorDesc& filterDesc = isDynamic ? input1Desc : input0Desc;
154+ const TensorDesc& gradOutputDesc = isDynamic ? input2Desc : input1Desc;
155+ if (isDynamic) {
156+ iInputSizeHolder = builder.CreateInput(INPUT_SIZE_INDEX);
157+ ConvBackpropFusionUtilsPass::SetPlaceholderDesc(iInputSizeHolder, TENSOR_DEFAULT_OUTPUT_INDEX, input0Desc);
158+ }
159+ iFilterHolder = builder.CreateInput(static_cast<int64_t>(filterIndex));
160+ ConvBackpropFusionUtilsPass::SetPlaceholderDesc(iFilterHolder, TENSOR_DEFAULT_OUTPUT_INDEX, filterDesc);
161+ iGradOutputHolder = builder.CreateInput(static_cast<int64_t>(gradOutputIndex));
162+ ConvBackpropFusionUtilsPass::SetPlaceholderDesc(iGradOutputHolder, TENSOR_DEFAULT_OUTPUT_INDEX, gradOutputDesc);
163+}
164+ 
165+bool DepthwiseDfFusionPass::BuildOptionalTranspose(EsGraphBuilder& builder, const std::string& nodeNamePrefix,
166+ const EsTensorHolder& iFilterHolder,
167+ EsTensorHolder& reshapeInputHolder, TensorDesc& reshapeInputDesc)
168+{
169+ const TensorDesc& filterDesc = isDynamic ? input1Desc : input0Desc;
170+ ge::Format originFormat = filterDesc.GetOriginFormat();
171+ reshapeInputHolder = iFilterHolder;
172+ reshapeInputDesc = filterDesc;
173+ if (originFormat != ge::FORMAT_NCHW) {
174+ return true;
175+ }
176+ TensorDesc transposeOutDesc;
177+ EsTensorHolder transposeOut;
178+ if (isArch35) {
179+ TransposeNodeConfig config = TransposeNodeConfig::Create(iFilterHolder, TRANSPOSE_PERM,
180+ nodeNamePrefix + "/Transpose", originFormat);
181+ OP_CHECK_IF(!ConvBackpropFusionUtilsPass::CreateTransposeNode(builder, config, transposeOut, transposeOutDesc,
182+ GetNodeType()),
183+ OP_LOGE(GetNodeType().GetString(), "Create Transpose failed"), return false);
184+ } else {
185+ TransposeNodeConfig config = TransposeNodeConfig::Create(iFilterHolder, TRANSPOSE_PERM,
186+ nodeNamePrefix + "/TransposeD", originFormat);
187+ OP_CHECK_IF(!ConvBackpropFusionUtilsPass::CreateTransposeDNode(builder, config, transposeOut, transposeOutDesc,
188+ filterDesc, GetNodeType()),
189+ OP_LOGE(GetNodeType().GetString(), "Create TransposeD failed"), return false);
190+ }
191+ reshapeInputHolder = transposeOut;
192+ reshapeInputDesc = transposeOutDesc;
193+ OP_LOGD(GetNodeType().GetString(), "Insert Transpose before filter.");
194+ return true;
195+}
196+ 
197+bool DepthwiseDfFusionPass::BuildReshapeNode(EsGraphBuilder& builder, const std::vector<int64_t>& filterResetShape,
198+ const std::string& nodeNamePrefix,
199+ const EsTensorHolder& reshapeInputHolder,
200+ const TensorDesc& reshapeInputDesc, EsTensorHolder& reshapeOutput,
201+ TensorDesc& targetFilterDesc)
202+{
203+ const TensorDesc& filterDesc = isDynamic ? input1Desc : input0Desc;
204+ auto* graph = builder.GetCGraphBuilder()->GetGraph();
205+ auto* reshapeInProducer = reshapeInputHolder.GetProducer();
206+ OP_CHECK_IF(reshapeInProducer == nullptr, OP_LOGE(GetNodeType().GetString(), "Reshape input producer nullptr"),
207+ return false);
208+ TensorDesc reshapeOutDesc(reshapeInputDesc);
209+ reshapeOutDesc.SetShape(ge::Shape(filterResetShape));
210+ reshapeOutDesc.SetOriginShape(ge::Shape(filterResetShape));
211+ std::string reshapeName = nodeNamePrefix + "/Reshape";
212+ GNode reshapeNode = CompliantNodeBuilder(graph)
213+ .OpType("Reshape")
214+ .Name(reshapeName.c_str())
215+ .IrDefInputs({{"x", CompliantNodeBuilder::kEsIrInputRequired, ""}})
216+ .IrDefOutputs({{"y", CompliantNodeBuilder::kEsIrOutputRequired, ""}})
217+ .Build();
218+ OP_CHECK_IF(AddEdgeAndUpdatePeerDesc(*graph, *reshapeInProducer, reshapeInputHolder.GetProducerOutIndex(),
219+ reshapeNode, 0) != GRAPH_SUCCESS,
220+ OP_LOGE(GetNodeType().GetString(), "Add edge to reshape x failed"), return false);
221+ OP_CHECK_IF(reshapeNode.UpdateInputDesc(0, reshapeInputDesc) != GRAPH_SUCCESS ||
222+ reshapeNode.UpdateOutputDesc(0, reshapeOutDesc) != GRAPH_SUCCESS,
223+ OP_LOGE(GetNodeType().GetString(), "Update reshape node desc failed"), return false);
224+ std::vector<int64_t> shapeAttr = filterResetShape;
225+ OP_CHECK_IF(reshapeNode.SetAttr("shape", shapeAttr) != GRAPH_SUCCESS,
226+ OP_LOGE(GetNodeType().GetString(), "Set shape attr failed"), return false);
227+ reshapeOutput = EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(reshapeNode, OUTPUT_INDEX));
228+ targetFilterDesc = filterDesc;
229+ targetFilterDesc.SetShape(ge::Shape(filterResetShape));
230+ targetFilterDesc.SetOriginShape(ge::Shape(filterResetShape));
231+ return true;
232+}
233+ 
234+GraphUniqPtr DepthwiseDfFusionPass::BuildDynamicTargetNode(EsGraphBuilder& builder, const std::string& targetOpType,
235+ const std::string& targetNodeName,
236+ const EsTensorHolder& iInputSizeHolder,
237+ const EsTensorHolder& iGradOutputHolder,
238+ const EsTensorHolder& reshapeOutput,
239+ const TensorDesc& targetFilterDesc)
240+{
241+ auto* graph = builder.GetCGraphBuilder()->GetGraph();
242+ const std::string outputName = isArch35 ? "y" : "input_grad";
243+ GNode targetNode = CompliantNodeBuilder(graph)
244+ .OpType(targetOpType.c_str())
245+ .Name(targetNodeName.c_str())
246+ .IrDefInputs({{"input_size", CompliantNodeBuilder::kEsIrInputRequired, ""},
247+ {"filter", CompliantNodeBuilder::kEsIrInputRequired, ""},
248+ {"out_backprop", CompliantNodeBuilder::kEsIrInputRequired, ""}})
249+ .IrDefOutputs({{outputName, CompliantNodeBuilder::kEsIrOutputRequired, ""}})
250+ .Build();
251+ auto* inputSizeProducer = iInputSizeHolder.GetProducer();
252+ OP_CHECK_IF(inputSizeProducer == nullptr, OP_LOGE(GetNodeType().GetString(), "input_size producer nullptr"),
253+ return nullptr);
254+ OP_CHECK_IF(AddEdgeAndUpdatePeerDesc(*graph, *inputSizeProducer, iInputSizeHolder.GetProducerOutIndex(), targetNode,
255+ INPUT_SIZE_INDEX) != GRAPH_SUCCESS,
256+ OP_LOGE(GetNodeType().GetString(), "Add edge input_size failed"), return nullptr);
257+ auto* reshapeProducer = reshapeOutput.GetProducer();
258+ OP_CHECK_IF(reshapeProducer == nullptr, OP_LOGE(GetNodeType().GetString(), "reshape output producer nullptr"),
259+ return nullptr);
260+ OP_CHECK_IF(AddEdgeAndUpdatePeerDesc(*graph, *reshapeProducer, reshapeOutput.GetProducerOutIndex(), targetNode,
261+ static_cast<int32_t>(FILTER_INDEX_DYN)) != GRAPH_SUCCESS,
262+ OP_LOGE(GetNodeType().GetString(), "Add edge filter to target failed"), return nullptr);
263+ auto* gradOutProducer = iGradOutputHolder.GetProducer();
264+ OP_CHECK_IF(gradOutProducer == nullptr, OP_LOGE(GetNodeType().GetString(), "grad out producer nullptr"),
265+ return nullptr);
266+ OP_CHECK_IF(AddEdgeAndUpdatePeerDesc(*graph, *gradOutProducer, iGradOutputHolder.GetProducerOutIndex(), targetNode,
267+ static_cast<int32_t>(GRAD_OUTPUT_INDEX_DYN)) != GRAPH_SUCCESS,
268+ OP_LOGE(GetNodeType().GetString(), "Add edge grad output to target failed"), return nullptr);
269+ SetNodeAttrs(targetNode);
270+ OP_CHECK_IF(targetNode.SetAttr("from_depthwise", convBpAttr.from_depthwise) != GRAPH_SUCCESS,
271+ OP_LOGE(GetNodeType().GetString(), "Set from_depthwise attr failed"), return nullptr);
272+ OP_CHECK_IF(targetNode.UpdateInputDesc(INPUT_SIZE_INDEX, input0Desc) != GRAPH_SUCCESS ||
273+ targetNode.UpdateInputDesc(FILTER_INDEX_DYN, targetFilterDesc) != GRAPH_SUCCESS ||
274+ targetNode.UpdateInputDesc(GRAD_OUTPUT_INDEX_DYN, input2Desc) != GRAPH_SUCCESS ||
275+ targetNode.UpdateOutputDesc(OUTPUT_INDEX, outputDesc) != GRAPH_SUCCESS,
276+ OP_LOGE(GetNodeType().GetString(), "Update target node desc failed"), return nullptr);
277+ auto targetOutput = EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(targetNode, OUTPUT_INDEX));
278+ OP_LOGD(GetNodeType().GetString(), "DepthwiseDF Replacement success (type=%s)", targetOpType.c_str());
279+ return builder.BuildAndReset(std::vector<EsTensorHolder>{targetOutput});
280+}
281+ 
282+GraphUniqPtr DepthwiseDfFusionPass::BuildStaticTargetNode(EsGraphBuilder& builder, const std::string& targetOpType,
283+ const std::string& targetNodeName,
284+ const EsTensorHolder& iGradOutputHolder,
285+ const EsTensorHolder& reshapeOutput,
286+ const TensorDesc& targetFilterDesc)
287+{
288+ auto* graph = builder.GetCGraphBuilder()->GetGraph();
289+ GNode targetNode = CompliantNodeBuilder(graph)
290+ .OpType(targetOpType.c_str())
291+ .Name(targetNodeName.c_str())
292+ .IrDefInputs({{"filter", CompliantNodeBuilder::kEsIrInputRequired, ""},
293+ {"out_backprop", CompliantNodeBuilder::kEsIrInputRequired, ""}})
294+ .IrDefOutputs({{"input_grad", CompliantNodeBuilder::kEsIrOutputRequired, ""}})
295+ .Build();
296+ auto* reshapeProducer = reshapeOutput.GetProducer();
297+ OP_CHECK_IF(reshapeProducer == nullptr, OP_LOGE(GetNodeType().GetString(), "reshape output producer nullptr"),
298+ return nullptr);
299+ OP_CHECK_IF(AddEdgeAndUpdatePeerDesc(*graph, *reshapeProducer, reshapeOutput.GetProducerOutIndex(), targetNode,
300+ static_cast<int32_t>(FILTER_INDEX_D)) != GRAPH_SUCCESS,
301+ OP_LOGE(GetNodeType().GetString(), "Add edge filter to target failed"), return nullptr);
302+ auto* gradOutProducer = iGradOutputHolder.GetProducer();
303+ OP_CHECK_IF(gradOutProducer == nullptr, OP_LOGE(GetNodeType().GetString(), "grad out producer nullptr"),
304+ return nullptr);
305+ OP_CHECK_IF(AddEdgeAndUpdatePeerDesc(*graph, *gradOutProducer, iGradOutputHolder.GetProducerOutIndex(), targetNode,
306+ static_cast<int32_t>(GRAD_OUTPUT_INDEX_D)) != GRAPH_SUCCESS,
307+ OP_LOGE(GetNodeType().GetString(), "Add edge grad output to target failed"), return nullptr);
308+ SetNodeAttrs(targetNode);
309+ OP_CHECK_IF(targetNode.SetAttr("from_depthwise", convBpAttr.from_depthwise) != GRAPH_SUCCESS,
310+ OP_LOGE(GetNodeType().GetString(), "Set from_depthwise attr failed"), return nullptr);
311+ OP_CHECK_IF(targetNode.UpdateInputDesc(FILTER_INDEX_D, targetFilterDesc) != GRAPH_SUCCESS ||
312+ targetNode.UpdateInputDesc(GRAD_OUTPUT_INDEX_D, input1Desc) != GRAPH_SUCCESS ||
313+ targetNode.UpdateOutputDesc(OUTPUT_INDEX, outputDesc) != GRAPH_SUCCESS,
314+ OP_LOGE(GetNodeType().GetString(), "Update target node desc failed"), return nullptr);
315+ auto targetOutput = EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(targetNode, OUTPUT_INDEX));
316+ OP_LOGD(GetNodeType().GetString(), "DepthwiseDF Replacement success (D, type=%s)", targetOpType.c_str());
317+ return builder.BuildAndReset(std::vector<EsTensorHolder>{targetOutput});
318+}
319+ 
320+GraphUniqPtr DepthwiseDfFusionPass::Replacement(const GNode& matchedNode)
321+{
322+ OP_LOGD(GetNodeType().GetString(), "Replacement start");
323+ AscendString matchedTypeAsc;
324+ OP_CHECK_IF(matchedNode.GetType(matchedTypeAsc) != GRAPH_SUCCESS,
325+ OP_LOGE(GetNodeType().GetString(), "GetType failed"), return nullptr);
326+ std::string matchedType(matchedTypeAsc.GetString());
327+ OP_CHECK_IF(!GetNodeAttrs(matchedNode), OP_LOGE(GetNodeType().GetString(), "GetNodeAttrs failed"), return nullptr);
328+ std::string nodeNamePrefix;
329+ OP_CHECK_IF(!ConvBackpropFusionUtilsPass::GetNodeName(matchedNode, nodeNamePrefix),
330+ OP_LOGE(GetNodeType().GetString(), "GetNodeName failed"), return nullptr);
331+ std::vector<int64_t> filterResetShape;
332+ OP_CHECK_IF(!GetResizeDepthwiseFilterShape(filterResetShape),
333+ OP_LOGE(GetNodeType().GetString(), "Compute filter resize shape failed"), return nullptr);
334+ auto builder = EsGraphBuilder("replacement");
335+ OP_CHECK_IF(builder.GetCGraphBuilder()->GetGraph() == nullptr,
336+ OP_LOGE(GetNodeType().GetString(), "Get graph failed"), return nullptr);
337+ EsTensorHolder iFilterHolder, iGradOutputHolder, iInputSizeHolder;
338+ CreateBoundaryInputs(builder, iFilterHolder, iGradOutputHolder, iInputSizeHolder);
339+ EsTensorHolder reshapeInputHolder;
340+ TensorDesc reshapeInputDesc;
341+ OP_CHECK_IF(!BuildOptionalTranspose(builder, nodeNamePrefix, iFilterHolder, reshapeInputHolder, reshapeInputDesc),
342+ OP_LOGE(GetNodeType().GetString(), "BuildOptionalTranspose failed"), return nullptr);
343+ EsTensorHolder reshapeOutput;
344+ TensorDesc targetFilterDesc;
345+ OP_CHECK_IF(!BuildReshapeNode(builder, filterResetShape, nodeNamePrefix, reshapeInputHolder, reshapeInputDesc,
346+ reshapeOutput, targetFilterDesc),
347+ OP_LOGE(GetNodeType().GetString(), "BuildReshapeNode failed"), return nullptr);
348+ std::string targetOpType = matchedType;
349+ if (isArch35) {
350+ targetOpType.erase(0, strlen(DEPTHWISE_PREFIX));
351+ }
352+ std::string targetNodeName = nodeNamePrefix + "/dx";
353+ if (isDynamic) {
354+ return BuildDynamicTargetNode(builder, targetOpType, targetNodeName, iInputSizeHolder, iGradOutputHolder,
355+ reshapeOutput, targetFilterDesc);
356+ }
357+ return BuildStaticTargetNode(builder, targetOpType, targetNodeName, iGradOutputHolder, reshapeOutput,
358+ targetFilterDesc);
359+}
360+ 
361+const std::vector<AscendString> kMatchOpTypes = {DEPTHWISE_D, DEPTHWISE_DYN};
362+ 
363+REG_DECOMPOSE_PASS(DepthwiseDfFusionPass, kMatchOpTypes).Stage(CustomPassStage::kCompatibleInherited);
364+ 
365+} // namespace ops
@@ -0,0 +1,59 @@
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 DEPTHWISE_DF_FUSION_PASS_H
12+#define DEPTHWISE_DF_FUSION_PASS_H
13+ 
14+#include "../../conv/common/op_graph/fusion_pass/conv_backprop_fusion_base_pass.h"
15+ 
16+namespace ops {
17+ 
18+class __attribute__((visibility("default"))) DepthwiseDfFusionPass : public ConvBackpropFusionBasePass {
19+public:
20+ explicit DepthwiseDfFusionPass(const std::vector<ge::AscendString>& opTypes) : ConvBackpropFusionBasePass(opTypes)
21+ {}
22+ 
23+protected:
24+ ge::AscendString GetNodeType() const override;
25+ bool MeetRequirements(const ge::GNode& matchedNode) override;
26+ ge::fusion::GraphUniqPtr Replacement(const ge::GNode& matchedNode) override;
27+ bool GetNodeAttrs(const ge::GNode& node) override;
28+ void SetNodeAttrs(ge::GNode& outNode) override;
29+ 
30+ bool ValidateFilterDesc(const ge::GNode& matchedNode);
31+ bool GetResizeDepthwiseFilterShape(std::vector<int64_t>& resizeShape);
32+ 
33+private:
34+ bool ValidateArch35Descs();
35+ void CreateBoundaryInputs(ge::es::EsGraphBuilder& builder, ge::es::EsTensorHolder& iFilterHolder,
36+ ge::es::EsTensorHolder& iGradOutputHolder, ge::es::EsTensorHolder& iInputSizeHolder);
37+ bool BuildOptionalTranspose(ge::es::EsGraphBuilder& builder, const std::string& nodeNamePrefix,
38+ const ge::es::EsTensorHolder& iFilterHolder, ge::es::EsTensorHolder& reshapeInputHolder,
39+ ge::TensorDesc& reshapeInputDesc);
40+ bool BuildReshapeNode(ge::es::EsGraphBuilder& builder, const std::vector<int64_t>& filterResetShape,
41+ const std::string& nodeNamePrefix, const ge::es::EsTensorHolder& reshapeInputHolder,
42+ const ge::TensorDesc& reshapeInputDesc, ge::es::EsTensorHolder& reshapeOutput,
43+ ge::TensorDesc& targetFilterDesc);
44+ ge::fusion::GraphUniqPtr BuildDynamicTargetNode(ge::es::EsGraphBuilder& builder, const std::string& targetOpType,
45+ const std::string& targetNodeName,
46+ const ge::es::EsTensorHolder& iInputSizeHolder,
47+ const ge::es::EsTensorHolder& iGradOutputHolder,
48+ const ge::es::EsTensorHolder& reshapeOutput,
49+ const ge::TensorDesc& targetFilterDesc);
50+ ge::fusion::GraphUniqPtr BuildStaticTargetNode(ge::es::EsGraphBuilder& builder, const std::string& targetOpType,
51+ const std::string& targetNodeName,
52+ const ge::es::EsTensorHolder& iGradOutputHolder,
53+ const ge::es::EsTensorHolder& reshapeOutput,
54+ const ge::TensorDesc& targetFilterDesc);
55+};
56+ 
57+} // namespace ops
58+ 
59+#endif // DEPTHWISE_DF_FUSION_PASS_H
@@ -0,0 +1,519 @@
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 <gtest/gtest.h>
12+ 
13+#include <string>
14+#include <vector>
15+ 
16+#include "platform/platform_info.h"
17+#include "register/register_custom_pass.h"
18+#include "ge/compliant_node_builder.h"
19+#include "ge/es_graph_builder.h"
20+#include "../../../op_graph/fusion_pass/depthwise_df_fusion_pass.h"
21+ 
22+using namespace ge;
23+using namespace ge::es;
24+using namespace fe;
25+using namespace fusion;
26+using namespace ops::ConvBackpropFusionUtils;
27+ 
28+namespace {
29+ 
30+constexpr int64_t AI_CORE_CNT = 64;
31+constexpr int64_t INPUT_SIZE_DIM = 4;
32+int64_t DEFAULT_IMPL_MODE = 0x1;
33+ 
34+void SetPlatform(const std::string& soc)
35+{
36+ PlatformInfo platformInfo;
37+ OptionalInfo optionalInfo;
38+ platformInfo.soc_info.ai_core_cnt = AI_CORE_CNT;
39+ platformInfo.str_info.short_soc_version = soc;
40+ optionalInfo.soc_version = soc;
41+ if (soc == "Ascend950") {
42+ platformInfo.ai_core_intrinsic_dtype_map["Intrinsic_data_move_out2l1_dn2nz"] = {"float16", "float", "bfloat16"};
43+ }
44+ PlatformInfoManager::Instance().platform_info_map_[soc] = platformInfo;
45+ PlatformInfoManager::Instance().SetOptionalCompilationInfo(optionalInfo);
46+}
47+ 
48+EsTensorHolder CreateDepthwiseConv2DBpInputDNode(EsGraphBuilder& builder, const char* opType,
49+ const EsTensorHolder& filter, const EsTensorHolder& outBackprop,
50+ std::vector<int64_t> strides, std::vector<int64_t> pads,
51+ std::vector<int64_t> dilations, int64_t groups,
52+ const std::string& dataFormat, DataType outDtype,
53+ const std::vector<int64_t>& outShape, Format outFormat,
54+ bool fromDepthwise = false)
55+{
56+ auto* graph = builder.GetCGraphBuilder()->GetGraph();
57+ auto node = CompliantNodeBuilder(graph)
58+ .OpType(opType)
59+ .Name(opType)
60+ .IrDefInputs({{"filter", CompliantNodeBuilder::kEsIrInputRequired, ""},
61+ {"out_backprop", CompliantNodeBuilder::kEsIrInputRequired, ""}})
62+ .IrDefOutputs({{"y", CompliantNodeBuilder::kEsIrOutputRequired, ""}})
63+ .InstanceOutputDataType("y", outDtype)
64+ .InstanceOutputShape("y", outShape)
65+ .InstanceOutputFormat("y", outFormat)
66+ .Build();
67+ 
68+ AddEdgeAndUpdatePeerDesc(*graph, *filter.GetProducer(), filter.GetProducerOutIndex(), node, 0);
69+ AddEdgeAndUpdatePeerDesc(*graph, *outBackprop.GetProducer(), outBackprop.GetProducerOutIndex(), node, 1);
70+ 
71+ TensorDesc filterDesc, outBackpropDesc;
72+ filter.GetProducer()->GetOutputDesc(filter.GetProducerOutIndex(), filterDesc);
73+ outBackprop.GetProducer()->GetOutputDesc(outBackprop.GetProducerOutIndex(), outBackpropDesc);
74+ node.UpdateInputDesc(0, filterDesc);
75+ node.UpdateInputDesc(1, outBackpropDesc);
76+ 
77+ node.SetAttr("strides", strides);
78+ node.SetAttr("pads", pads);
79+ node.SetAttr("dilations", dilations);
80+ node.SetAttr("groups", groups);
81+ AscendString fmt = dataFormat.c_str();
82+ node.SetAttr("data_format", fmt);
83+ node.SetAttr("_op_impl_mode_enum", DEFAULT_IMPL_MODE);
84+ node.SetAttr("from_depthwise", fromDepthwise);
85+ std::vector<int64_t> inputSizeAttr = outShape;
86+ node.SetAttr("input_size", inputSizeAttr);
87+ 
88+ return EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 0));
89+}
90+ 
91+EsTensorHolder CreateDepthwiseConv2DBpInputNode(EsGraphBuilder& builder, const char* opType,
92+ const EsTensorHolder& inputSize, const EsTensorHolder& filter,
93+ const EsTensorHolder& outBackprop, std::vector<int64_t> strides,
94+ std::vector<int64_t> pads, std::vector<int64_t> dilations,
95+ int64_t groups, const std::string& dataFormat, DataType outDtype,
96+ const std::vector<int64_t>& outShape, Format outFormat,
97+ bool fromDepthwise = false)
98+{
99+ auto* graph = builder.GetCGraphBuilder()->GetGraph();
100+ auto node = CompliantNodeBuilder(graph)
101+ .OpType(opType)
102+ .Name(opType)
103+ .IrDefInputs({{"input_size", CompliantNodeBuilder::kEsIrInputRequired, ""},
104+ {"filter", CompliantNodeBuilder::kEsIrInputRequired, ""},
105+ {"out_backprop", CompliantNodeBuilder::kEsIrInputRequired, ""}})
106+ .IrDefOutputs({{"y", CompliantNodeBuilder::kEsIrOutputRequired, ""}})
107+ .InstanceOutputDataType("y", outDtype)
108+ .InstanceOutputShape("y", outShape)
109+ .InstanceOutputFormat("y", outFormat)
110+ .Build();
111+ 
112+ AddEdgeAndUpdatePeerDesc(*graph, *inputSize.GetProducer(), inputSize.GetProducerOutIndex(), node, 0);
113+ AddEdgeAndUpdatePeerDesc(*graph, *filter.GetProducer(), filter.GetProducerOutIndex(), node, 1);
114+ AddEdgeAndUpdatePeerDesc(*graph, *outBackprop.GetProducer(), outBackprop.GetProducerOutIndex(), node, 2);
115+ 
116+ TensorDesc inputSizeDesc, filterDesc, outBackpropDesc;
117+ inputSize.GetProducer()->GetOutputDesc(inputSize.GetProducerOutIndex(), inputSizeDesc);
118+ filter.GetProducer()->GetOutputDesc(filter.GetProducerOutIndex(), filterDesc);
119+ outBackprop.GetProducer()->GetOutputDesc(outBackprop.GetProducerOutIndex(), outBackpropDesc);
120+ node.UpdateInputDesc(0, inputSizeDesc);
121+ node.UpdateInputDesc(1, filterDesc);
122+ node.UpdateInputDesc(2, outBackpropDesc);
123+ 
124+ node.SetAttr("strides", strides);
125+ node.SetAttr("pads", pads);
126+ node.SetAttr("dilations", dilations);
127+ node.SetAttr("groups", groups);
128+ AscendString fmt = dataFormat.c_str();
129+ node.SetAttr("data_format", fmt);
130+ node.SetAttr("_op_impl_mode_enum", DEFAULT_IMPL_MODE);
131+ node.SetAttr("from_depthwise", fromDepthwise);
132+ 
133+ return EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 0));
134+}
135+ 
136+bool CheckNodeExists(GraphPtr& graph, const std::string& type)
137+{
138+ for (auto node : graph->GetAllNodes()) {
139+ AscendString nodeType;
140+ node.GetType(nodeType);
141+ if (nodeType.GetString() == type) {
142+ return true;
143+ }
144+ }
145+ return false;
146+}
147+ 
148+bool GetNodeBoolAttr(GraphPtr& graph, const std::string& type, const std::string& attrName, bool& attrValue)
149+{
150+ for (auto node : graph->GetAllNodes()) {
151+ AscendString nodeType;
152+ node.GetType(nodeType);
153+ if (nodeType.GetString() == type) {
154+ return node.GetAttr(attrName.c_str(), attrValue) == GRAPH_SUCCESS;
155+ }
156+ }
157+ return false;
158+}
159+ 
160+} // namespace
161+ 
162+class DepthwiseDfFusionPassTest : public testing::Test {
163+protected:
164+ void SetUp() override { SetPlatform("Ascend950"); }
165+};
166+ 
167+TEST_F(DepthwiseDfFusionPassTest, staticDNchwFp16Success)
168+{
169+ auto builder = EsGraphBuilder("staticDNchwFp16Success");
170+ auto filter = builder.CreateInput(0, "filter", DT_FLOAT16, FORMAT_NCHW, {32, 1, 3, 3});
171+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
172+ 
173+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
174+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_FLOAT16,
175+ {2, 32, 32, 32}, FORMAT_NCHW);
176+ 
177+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
178+ CustomPassContext ctx;
179+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
180+ EXPECT_EQ(pass.Run(graph, ctx), SUCCESS);
181+ EXPECT_TRUE(CheckNodeExists(graph, "Conv2DBackpropInputD"));
182+ EXPECT_TRUE(CheckNodeExists(graph, "Reshape"));
183+ EXPECT_TRUE(CheckNodeExists(graph, "Transpose"));
184+}
185+ 
186+TEST_F(DepthwiseDfFusionPassTest, dynamicNchwFp16Success)
187+{
188+ auto builder = EsGraphBuilder("dynamicNchwFp16Success");
189+ auto inputSize = builder.CreateInput(0, "input_size", DT_INT32, FORMAT_ND, {INPUT_SIZE_DIM});
190+ auto filter = builder.CreateInput(1, "filter", DT_FLOAT16, FORMAT_NCHW, {32, 1, 3, 3});
191+ auto outBackprop = builder.CreateInput(2, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
192+ 
193+ auto y = CreateDepthwiseConv2DBpInputNode(builder, "DepthwiseConv2DBackpropInput", inputSize, filter, outBackprop,
194+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_FLOAT16,
195+ {2, 32, 32, 32}, FORMAT_NCHW);
196+ 
197+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
198+ CustomPassContext ctx;
199+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInput")});
200+ EXPECT_EQ(pass.Run(graph, ctx), SUCCESS);
201+ EXPECT_TRUE(CheckNodeExists(graph, "Conv2DBackpropInput"));
202+ EXPECT_TRUE(CheckNodeExists(graph, "Reshape"));
203+ EXPECT_TRUE(CheckNodeExists(graph, "Transpose"));
204+}
205+ 
206+TEST_F(DepthwiseDfFusionPassTest, staticDHwcnFp16Success)
207+{
208+ auto builder = EsGraphBuilder("staticDHwcnFp16Success");
209+ auto filter = builder.CreateInput(0, "filter", DT_FLOAT16, FORMAT_HWCN, {3, 3, 1, 32});
210+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
211+ 
212+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
213+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_FLOAT16,
214+ {2, 32, 32, 32}, FORMAT_NCHW);
215+ 
216+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
217+ CustomPassContext ctx;
218+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
219+ EXPECT_EQ(pass.Run(graph, ctx), SUCCESS);
220+ EXPECT_TRUE(CheckNodeExists(graph, "Conv2DBackpropInputD"));
221+ EXPECT_TRUE(CheckNodeExists(graph, "Reshape"));
222+ EXPECT_FALSE(CheckNodeExists(graph, "Transpose"));
223+}
224+ 
225+TEST_F(DepthwiseDfFusionPassTest, dynamicHwcnFp16Success)
226+{
227+ auto builder = EsGraphBuilder("dynamicHwcnFp16Success");
228+ auto inputSize = builder.CreateInput(0, "input_size", DT_INT32, FORMAT_ND, {INPUT_SIZE_DIM});
229+ auto filter = builder.CreateInput(1, "filter", DT_FLOAT16, FORMAT_HWCN, {3, 3, 1, 32});
230+ auto outBackprop = builder.CreateInput(2, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
231+ 
232+ auto y = CreateDepthwiseConv2DBpInputNode(builder, "DepthwiseConv2DBackpropInput", inputSize, filter, outBackprop,
233+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_FLOAT16,
234+ {2, 32, 32, 32}, FORMAT_NCHW);
235+ 
236+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
237+ CustomPassContext ctx;
238+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInput")});
239+ EXPECT_EQ(pass.Run(graph, ctx), SUCCESS);
240+ EXPECT_TRUE(CheckNodeExists(graph, "Conv2DBackpropInput"));
241+ EXPECT_TRUE(CheckNodeExists(graph, "Reshape"));
242+ EXPECT_FALSE(CheckNodeExists(graph, "Transpose"));
243+}
244+ 
245+TEST_F(DepthwiseDfFusionPassTest, staticDNchwBf16Success)
246+{
247+ auto builder = EsGraphBuilder("staticDNchwBf16Success");
248+ auto filter = builder.CreateInput(0, "filter", DT_BF16, FORMAT_NCHW, {32, 1, 3, 3});
249+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_BF16, FORMAT_NCHW, {2, 32, 16, 16});
250+ 
251+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
252+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_BF16,
253+ {2, 32, 32, 32}, FORMAT_NCHW);
254+ 
255+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
256+ CustomPassContext ctx;
257+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
258+ EXPECT_EQ(pass.Run(graph, ctx), SUCCESS);
259+ EXPECT_TRUE(CheckNodeExists(graph, "Conv2DBackpropInputD"));
260+}
261+ 
262+TEST_F(DepthwiseDfFusionPassTest, staticDNchwFp32Success)
263+{
264+ auto builder = EsGraphBuilder("staticDNchwFp32Success");
265+ auto filter = builder.CreateInput(0, "filter", DT_FLOAT, FORMAT_NCHW, {32, 1, 3, 3});
266+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_FLOAT, FORMAT_NCHW, {2, 32, 16, 16});
267+ 
268+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
269+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_FLOAT,
270+ {2, 32, 32, 32}, FORMAT_NCHW);
271+ 
272+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
273+ CustomPassContext ctx;
274+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
275+ EXPECT_EQ(pass.Run(graph, ctx), SUCCESS);
276+ EXPECT_TRUE(CheckNodeExists(graph, "Conv2DBackpropInputD"));
277+}
278+ 
279+TEST_F(DepthwiseDfFusionPassTest, dynamicHwcnBf16Success)
280+{
281+ auto builder = EsGraphBuilder("dynamicHwcnBf16Success");
282+ auto inputSize = builder.CreateInput(0, "input_size", DT_INT32, FORMAT_ND, {INPUT_SIZE_DIM});
283+ auto filter = builder.CreateInput(1, "filter", DT_BF16, FORMAT_HWCN, {3, 3, 1, 32});
284+ auto outBackprop = builder.CreateInput(2, "out_backprop", DT_BF16, FORMAT_NCHW, {2, 32, 16, 16});
285+ 
286+ auto y = CreateDepthwiseConv2DBpInputNode(builder, "DepthwiseConv2DBackpropInput", inputSize, filter, outBackprop,
287+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_BF16,
288+ {2, 32, 32, 32}, FORMAT_NCHW);
289+ 
290+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
291+ CustomPassContext ctx;
292+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInput")});
293+ EXPECT_EQ(pass.Run(graph, ctx), SUCCESS);
294+ EXPECT_TRUE(CheckNodeExists(graph, "Conv2DBackpropInput"));
295+}
296+ 
297+TEST_F(DepthwiseDfFusionPassTest, nonArch35NchwTransposeDSuccess)
298+{
299+ SetPlatform("Ascend910_93");
300+ auto builder = EsGraphBuilder("nonArch35NchwTransposeDSuccess");
301+ auto filter = builder.CreateInput(0, "filter", DT_FLOAT16, FORMAT_NCHW, {32, 1, 3, 3});
302+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
303+ 
304+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
305+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_FLOAT16,
306+ {2, 32, 32, 32}, FORMAT_NCHW);
307+ 
308+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
309+ CustomPassContext ctx;
310+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
311+ EXPECT_EQ(pass.Run(graph, ctx), SUCCESS);
312+ EXPECT_TRUE(CheckNodeExists(graph, "DepthwiseConv2DBackpropInputD"));
313+ EXPECT_TRUE(CheckNodeExists(graph, "TransposeD"));
314+ EXPECT_FALSE(CheckNodeExists(graph, "Transpose"));
315+}
316+ 
317+TEST_F(DepthwiseDfFusionPassTest, groupsZeroFail)
318+{
319+ auto builder = EsGraphBuilder("groupsZeroFail");
320+ auto filter = builder.CreateInput(0, "filter", DT_FLOAT16, FORMAT_NCHW, {32, 1, 3, 3});
321+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
322+ 
323+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
324+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 0, "NCHW", DT_FLOAT16,
325+ {2, 32, 32, 32}, FORMAT_NCHW);
326+ 
327+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
328+ CustomPassContext ctx;
329+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
330+ EXPECT_EQ(pass.Run(graph, ctx), GRAPH_NOT_CHANGED);
331+ EXPECT_FALSE(CheckNodeExists(graph, "Conv2DBackpropInputD"));
332+}
333+ 
334+TEST_F(DepthwiseDfFusionPassTest, groupsTooLargeFail)
335+{
336+ auto builder = EsGraphBuilder("groupsTooLargeFail");
337+ auto filter = builder.CreateInput(0, "filter", DT_FLOAT16, FORMAT_NCHW, {32, 1, 3, 3});
338+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
339+ 
340+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
341+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 65536, "NCHW", DT_FLOAT16,
342+ {2, 32, 32, 32}, FORMAT_NCHW);
343+ 
344+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
345+ CustomPassContext ctx;
346+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
347+ EXPECT_EQ(pass.Run(graph, ctx), GRAPH_NOT_CHANGED);
348+ EXPECT_FALSE(CheckNodeExists(graph, "Conv2DBackpropInputD"));
349+}
350+ 
351+TEST_F(DepthwiseDfFusionPassTest, groupsMaxBoundarySuccess)
352+{
353+ auto builder = EsGraphBuilder("groupsMaxBoundarySuccess");
354+ auto filter = builder.CreateInput(0, "filter", DT_FLOAT16, FORMAT_HWCN, {3, 3, 1, 32});
355+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
356+ 
357+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
358+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 65535, "NCHW", DT_FLOAT16,
359+ {2, 32, 32, 32}, FORMAT_NCHW);
360+ 
361+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
362+ CustomPassContext ctx;
363+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
364+ EXPECT_EQ(pass.Run(graph, ctx), SUCCESS);
365+ EXPECT_TRUE(CheckNodeExists(graph, "Conv2DBackpropInputD"));
366+}
367+ 
368+TEST_F(DepthwiseDfFusionPassTest, groupsMinBoundarySuccess)
369+{
370+ auto builder = EsGraphBuilder("groupsMinBoundarySuccess");
371+ auto filter = builder.CreateInput(0, "filter", DT_FLOAT16, FORMAT_HWCN, {3, 3, 1, 32});
372+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
373+ 
374+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
375+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 1, "NCHW", DT_FLOAT16,
376+ {2, 32, 32, 32}, FORMAT_NCHW);
377+ 
378+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
379+ CustomPassContext ctx;
380+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
381+ EXPECT_EQ(pass.Run(graph, ctx), SUCCESS);
382+ EXPECT_TRUE(CheckNodeExists(graph, "Conv2DBackpropInputD"));
383+}
384+ 
385+TEST_F(DepthwiseDfFusionPassTest, invalidFilterDimFail)
386+{
387+ auto builder = EsGraphBuilder("invalidFilterDimFail");
388+ auto filter = builder.CreateInput(0, "filter", DT_FLOAT16, FORMAT_NCHW, {32, 1, 3});
389+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
390+ 
391+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
392+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_FLOAT16,
393+ {2, 32, 32, 32}, FORMAT_NCHW);
394+ 
395+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
396+ CustomPassContext ctx;
397+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
398+ EXPECT_NE(pass.Run(graph, ctx), SUCCESS);
399+ EXPECT_FALSE(CheckNodeExists(graph, "Conv2DBackpropInputD"));
400+}
401+ 
402+TEST_F(DepthwiseDfFusionPassTest, invalidFilterFormatFail)
403+{
404+ auto builder = EsGraphBuilder("invalidFilterFormatFail");
405+ auto filter = builder.CreateInput(0, "filter", DT_FLOAT16, FORMAT_ND, {32, 1, 3, 3});
406+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
407+ 
408+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
409+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_FLOAT16,
410+ {2, 32, 32, 32}, FORMAT_NCHW);
411+ 
412+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
413+ CustomPassContext ctx;
414+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
415+ EXPECT_EQ(pass.Run(graph, ctx), GRAPH_NOT_CHANGED);
416+ EXPECT_FALSE(CheckNodeExists(graph, "Conv2DBackpropInputD"));
417+}
418+ 
419+TEST_F(DepthwiseDfFusionPassTest, formatMismatchFail)
420+{
421+ auto builder = EsGraphBuilder("formatMismatchFail");
422+ auto filter = builder.CreateInput(0, "filter", DT_FLOAT16, FORMAT_NCHW, {32, 1, 3, 3});
423+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
424+ 
425+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
426+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_FLOAT16,
427+ {2, 32, 32, 32}, FORMAT_NHWC);
428+ 
429+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
430+ CustomPassContext ctx;
431+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
432+ EXPECT_EQ(pass.Run(graph, ctx), GRAPH_NOT_CHANGED);
433+ EXPECT_FALSE(CheckNodeExists(graph, "Conv2DBackpropInputD"));
434+}
435+ 
436+TEST_F(DepthwiseDfFusionPassTest, unknownRankDedySuccess)
437+{
438+ auto builder = EsGraphBuilder("unknownRankDedySuccess");
439+ auto inputSize = builder.CreateInput(0, "input_size", DT_INT32, FORMAT_ND, {INPUT_SIZE_DIM});
440+ auto filter = builder.CreateInput(1, "filter", DT_FLOAT16, FORMAT_HWCN, {3, 3, 1, 32});
441+ auto outBackprop = builder.CreateInput(2, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {-2});
442+ 
443+ auto y = CreateDepthwiseConv2DBpInputNode(builder, "DepthwiseConv2DBackpropInput", inputSize, filter, outBackprop,
444+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_FLOAT16, {-2},
445+ FORMAT_NCHW);
446+ 
447+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
448+ CustomPassContext ctx;
449+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInput")});
450+ EXPECT_EQ(pass.Run(graph, ctx), SUCCESS);
451+ EXPECT_TRUE(CheckNodeExists(graph, "Conv2DBackpropInput"));
452+}
453+ 
454+TEST_F(DepthwiseDfFusionPassTest, fromDepthwisePropagatedSuccess)
455+{
456+ auto builder = EsGraphBuilder("fromDepthwisePropagatedSuccess");
457+ auto filter = builder.CreateInput(0, "filter", DT_FLOAT16, FORMAT_HWCN, {3, 3, 1, 32});
458+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
459+ 
460+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
461+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_FLOAT16,
462+ {2, 32, 32, 32}, FORMAT_NCHW, true);
463+ 
464+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
465+ CustomPassContext ctx;
466+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
467+ EXPECT_EQ(pass.Run(graph, ctx), SUCCESS);
468+ EXPECT_TRUE(CheckNodeExists(graph, "Conv2DBackpropInputD"));
469+ bool fromDepthwise = false;
470+ EXPECT_TRUE(GetNodeBoolAttr(graph, "Conv2DBackpropInputD", "from_depthwise", fromDepthwise));
471+ EXPECT_TRUE(fromDepthwise);
472+}
473+ 
474+TEST_F(DepthwiseDfFusionPassTest, fromDepthwiseDefaultFalse)
475+{
476+ auto builder = EsGraphBuilder("fromDepthwiseDefaultFalse");
477+ auto filter = builder.CreateInput(0, "filter", DT_FLOAT16, FORMAT_HWCN, {3, 3, 1, 32});
478+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
479+ 
480+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
481+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_FLOAT16,
482+ {2, 32, 32, 32}, FORMAT_NCHW);
483+ 
484+ std::shared_ptr<Graph> graph = builder.BuildAndReset({y});
485+ CustomPassContext ctx;
486+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
487+ EXPECT_EQ(pass.Run(graph, ctx), SUCCESS);
488+ EXPECT_TRUE(CheckNodeExists(graph, "Conv2DBackpropInputD"));
489+ bool fromDepthwise = true;
490+ EXPECT_TRUE(GetNodeBoolAttr(graph, "Conv2DBackpropInputD", "from_depthwise", fromDepthwise));
491+ EXPECT_FALSE(fromDepthwise);
492+}
493+ 
494+TEST_F(DepthwiseDfFusionPassTest, staticDInputSizeAttrPropagated)
495+{
496+ auto builder = EsGraphBuilder("staticDInputSizeAttrPropagated");
497+ auto filter = builder.CreateInput(0, "filter", DT_FLOAT16, FORMAT_HWCN, {3, 3, 1, 32});
498+ auto outBackprop = builder.CreateInput(1, "out_backprop", DT_FLOAT16, FORMAT_NCHW, {2, 32, 16, 16});
499+ 
500+ auto y = CreateDepthwiseConv2DBpInputDNode(builder, "DepthwiseConv2DBackpropInputD", filter, outBackprop,
501+ {1, 1, 2, 2}, {0, 0, 1, 1}, {1, 1, 1, 1}, 32, "NCHW", DT_FLOAT16,
502+ {2, 32, 32, 32}, FORMAT_NCHW);
503+ auto* graph = builder.GetCGraphBuilder()->GetGraph();
504+ for (auto node : graph->GetAllNodes()) {
505+ AscendString nodeType;
506+ node.GetType(nodeType);
507+ if (std::string(nodeType.GetString()) == "DepthwiseConv2DBackpropInputD") {
508+ std::vector<int64_t> inputSizeAttr = {2, 32, 32, 32};
509+ node.SetAttr("input_size", inputSizeAttr);
510+ break;
511+ }
512+ }
513+ 
514+ std::shared_ptr<Graph> graphBuilt = builder.BuildAndReset({y});
515+ CustomPassContext ctx;
516+ ops::DepthwiseDfFusionPass pass({AscendString("DepthwiseConv2DBackpropInputD")});
517+ EXPECT_EQ(pass.Run(graphBuilt, ctx), SUCCESS);
518+ EXPECT_TRUE(CheckNodeExists(graphBuilt, "Conv2DBackpropInputD"));
519+}