已合并
add fusion pass: multi_add_rms_norm_dynamic_quant | dynamic_quant_update_scatter_v2 #5243
add fusion pass: multi_add_rms_norm_dynamic_quant | dynamic_quant_update_scatter_v2 #5243
已合并
liu-zihao35创建于 5月25日
共 11 个文件变更+2209-6
@@ -0,0 +1,601 @@
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 multi_add_rms_norm_dynamic_quant_fusion_pass.cpp
13+ * \brief quant fusion for multiaddrmsnorm
14+ *
15+ * Function: Define fusion multiple Add and AddRmsNorm and dynamicquant pattern;
16+ * Fusion pattern:
17+ * ...
18+ * x1_0 x1_1 or Add_1
19+ * \ / (x1: 1-5 list of tensor)
20+ * gamma x2 Add_0 (dynamic, 0-4 Add op) x1 x2 gamma s1 s2
21+ * \ | / \ | / / /
22+ * AddRmsNorm ==> MultiAddRmsNormDynamicQuant
23+ * / | / / | | \ \
24+ * x (Reshape) (Reshape) (Reshape) | (Reshape) \ \
25+ * | / / | | \ \
26+ * y--+ (s1) (s2) y1 y2 x y scale1 scale2
27+ * | * *
28+ * / \* * Note: Either all or none Reshape(s) are present.
29+ * / * \ *
30+ * DynamicQuant (DynamicQuant) (1-2 dynamic DQ)
31+ * / \ / \
32+ * y1 scale1 y2 scale2
33+ *
34+ */
35+ 
36+#include "multi_add_rms_norm_dynamic_quant_fusion_pass.h"
37+ 
38+#include <string>
39+#include <vector>
40+ 
41+#include "ge/ge_utils.h"
42+#include "common/inc/error_util.h"
43+#include "es_nn_ops.h"
44+#include "es_math_ops.h"
45+#include "platform/platform_info.h"
46+#include "compliant_node_builder.h"
47+ 
48+using namespace ge;
49+using namespace fe;
50+using namespace fusion;
51+ 
52+namespace ops {
53+ 
54+static const std::string FUSED_OP_TYPE = "MultiAddRmsNormDynamicQuant";
55+static const std::string SOC_ASCEND910B = "Ascend910b";
56+static const std::string SOC_ASCEND910_93 = "Ascend910_93";
57+ 
58+using UniqueGraphPtr = std::unique_ptr<Graph>;
59+static const std::string PASS_NAME = "MultiAddRmsNormDynamicQuantFusionPass";
60+ 
61+static es::EsTensorHolder BuildPatternConstNode(es::EsGraphBuilder& builder)
62+{
63+ static int counter = 0;
64+ std::string name = "Const_" + std::to_string(counter++);
65+ auto graph = builder.GetCGraphBuilder()->GetGraph();
66+ GNode node = es::CompliantNodeBuilder(graph)
67+ .OpType("Const")
68+ .Name(name.c_str())
69+ .IrDefOutputs({{"y", es::CompliantNodeBuilder::kEsIrOutputRequired, ""}})
70+ .Build();
71+ return es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 0));
72+}
73+ 
74+static es::EsTensorHolder BuildPatternReshapeNode(
75+ es::EsGraphBuilder& builder, const es::EsTensorHolder& x, const es::EsTensorHolder& shape)
76+{
77+ static int counter = 0;
78+ std::string name = "Reshape_" + std::to_string(counter++);
79+ auto graph = builder.GetCGraphBuilder()->GetGraph();
80+ GNode node = es::CompliantNodeBuilder(graph)
81+ .OpType("Reshape")
82+ .Name(name.c_str())
83+ .IrDefInputs({
84+ {"x", es::CompliantNodeBuilder::kEsIrInputRequired, ""},
85+ {"shape", es::CompliantNodeBuilder::kEsIrInputRequired, ""}
86+ })
87+ .IrDefOutputs({{"y", es::CompliantNodeBuilder::kEsIrOutputRequired, ""}})
88+ .Build();
89+ es::AddEdgeAndUpdatePeerDesc(*graph, *x.GetProducer(), x.GetProducerOutIndex(), node, 0);
90+ es::AddEdgeAndUpdatePeerDesc(*graph, *shape.GetProducer(), shape.GetProducerOutIndex(), node, 1);
91+ return es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 0));
92+}
93+ 
94+static constexpr int64_t OUTPUT_IDX_Y1 = 0;
95+static constexpr int64_t OUTPUT_IDX_Y2 = 1;
96+static constexpr int64_t OUTPUT_IDX_X = 2;
97+static constexpr int64_t OUTPUT_IDX_Y = 3;
98+static constexpr int64_t OUTPUT_IDX_SCALE1 = 4;
99+static constexpr int64_t OUTPUT_IDX_SCALE2 = 5;
100+ 
@@ -0,0 +1,41 @@
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 multi_add_rms_norm_dynamic_quant_fusion_pass.h
13+ * \brief quant fusion for multi-add, addrmsnorm
14+ */
15+ 
16+#ifndef OPS_BUILT_IN_FUSION_PASS_GRAPH_FUSION_AI_CORE_MULTI_ADD_RMS_NORM_DYNAMIC_QUANT_FUSION_PASS_H_
17+#define OPS_BUILT_IN_FUSION_PASS_GRAPH_FUSION_AI_CORE_MULTI_ADD_RMS_NORM_DYNAMIC_QUANT_FUSION_PASS_H_
18+ 
19+#include "ge/fusion/pass/pattern_fusion_pass.h"
20+ 
21+namespace ops {
22+using namespace ge;
23+using namespace fusion;
24+ 
25+class __attribute__((visibility("default"))) MultiAddRmsNormDynamicQuantFusionPass : public PatternFusionPass {
26+protected:
27+ std::vector<PatternUniqPtr> Patterns() override;
28+ bool MeetRequirements(const std::unique_ptr<MatchResult>& match_result) override;
29+ std::unique_ptr<Graph> Replacement(const std::unique_ptr<MatchResult>& match_result) override;
30+ 
31+private:
32+ static constexpr int64_t MAX_ADD_NUM = 4;
33+ static constexpr int64_t MAX_SMOOTH_NUM = 2;
34+ int64_t add_num_ = 0;
35+ int64_t smooth_num_ = 0;
36+ bool has_reshape_ = false;
37+};
38+ 
39+} // namespace ops
40+ 
41+#endif // OPS_BUILT_IN_FUSION_PASS_GRAPH_FUSION_AI_CORE_MULTI_ADD_RMS_NORM_DYNAMIC_QUANT_FUSION_PASS_H_
Rnorm/multi_add_rms_norm_dynamic_quant/CMakeLists.txt→norm/multi_add_rms_norm_dynamic_quant/tests/ut/op_graph/CMakeLists.txt+2-3
@@ -8,10 +8,9 @@
8# See LICENSE in the root of the software repository for the full text of the License.8# See LICENSE in the root of the software repository for the full text of the License.
9# ----------------------------------------------------------------------------9# ----------------------------------------------------------------------------
10 10 
11+# 每个目录下需要生成的可执行文件,具体参考:ops/built-in/test/CMakeLists.txt: 50~124
11file(GLOB CURRENT_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)12file(GLOB CURRENT_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)
12-if(NOT ENABLE_TEST AND NOT BENCHMARK)13+message(STATUS "=== Debug: CURRENT_DIRS =${CURRENT_DIRS} ")
13- list(REMOVE_ITEM CURRENT_DIRS tests)
14-endif()
15foreach(SUB_DIR ${CURRENT_DIRS})14foreach(SUB_DIR ${CURRENT_DIRS})
16 if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUB_DIR}/CMakeLists.txt")15 if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUB_DIR}/CMakeLists.txt")
17 add_subdirectory(${SUB_DIR})16 add_subdirectory(${SUB_DIR})
@@ -0,0 +1,331 @@
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+#include "multi_add_rms_norm_dynamic_quant_fusion_pass.h"
13+#include "es_nn_ops.h"
14+#include "es_math_ops.h"
15+#include "platform/platform_info.h"
16+#include "platform_info_stub.h"
17+#include "compliant_node_builder.h"
18+ 
19+using namespace ge;
20+using namespace fe;
21+using namespace fusion;
22+ 
23+static es::EsTensorHolder BuildConstNode(es::EsGraphBuilder& builder)
24+{
25+ static int counter = 0;
26+ std::string name = "Const_" + std::to_string(counter++);
27+ auto graph = builder.GetCGraphBuilder()->GetGraph();
28+ GNode node = es::CompliantNodeBuilder(graph)
29+ .OpType("Const")
30+ .Name(name.c_str())
31+ .IrDefOutputs({{"y", es::CompliantNodeBuilder::kEsIrOutputRequired, ""}})
32+ .Build();
33+ return es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 0));
34+}
35+ 
36+static es::EsTensorHolder BuildReshapeNode(
37+ es::EsGraphBuilder& builder, const es::EsTensorHolder& x, const es::EsTensorHolder& shape)
38+{
39+ static int counter = 0;
40+ std::string name = "Reshape_" + std::to_string(counter++);
41+ auto graph = builder.GetCGraphBuilder()->GetGraph();
42+ GNode node = es::CompliantNodeBuilder(graph)
43+ .OpType("Reshape")
44+ .Name(name.c_str())
45+ .IrDefInputs({
46+ {"x", es::CompliantNodeBuilder::kEsIrInputRequired, ""},
47+ {"shape", es::CompliantNodeBuilder::kEsIrInputRequired, ""}
48+ })
49+ .IrDefOutputs({{"y", es::CompliantNodeBuilder::kEsIrOutputRequired, ""}})
50+ .Build();
51+ es::AddEdgeAndUpdatePeerDesc(*graph, *x.GetProducer(), x.GetProducerOutIndex(), node, 0);
52+ es::AddEdgeAndUpdatePeerDesc(*graph, *shape.GetProducer(), shape.GetProducerOutIndex(), node, 1);
53+ return es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 0));
54+}
55+ 
56+static GNode BuildAddNode(
57+ es::EsGraphBuilder& builder, const es::EsTensorHolder& x1, const es::EsTensorHolder& x2)
58+{
59+ static int counter = 0;
60+ std::string name = "Add_" + std::to_string(counter++);
61+ auto graph = builder.GetCGraphBuilder()->GetGraph();
62+ GNode node = es::CompliantNodeBuilder(graph)
63+ .OpType("Add")
64+ .Name(name.c_str())
65+ .IrDefInputs({
66+ {"x1", es::CompliantNodeBuilder::kEsIrInputRequired, ""},
67+ {"x2", es::CompliantNodeBuilder::kEsIrInputRequired, ""}
68+ })
69+ .IrDefOutputs({{"y", es::CompliantNodeBuilder::kEsIrOutputRequired, ""}})
70+ .Build();
71+ es::AddEdgeAndUpdatePeerDesc(*graph, *x1.GetProducer(), x1.GetProducerOutIndex(), node, 0);
72+ es::AddEdgeAndUpdatePeerDesc(*graph, *x2.GetProducer(), x2.GetProducerOutIndex(), node, 1);
73+ return node;
74+}
75+ 
76+static es::EsTensorHolder BuildAddTensor(
77+ es::EsGraphBuilder& builder, const es::EsTensorHolder& x1, const es::EsTensorHolder& x2)
78+{
79+ GNode node = BuildAddNode(builder, x1, x2);
80+ return es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 0));
81+}
82+ 
83+struct AddRmsNormResult {
84+ es::EsTensorHolder x;
85+ es::EsTensorHolder y;
86+ es::EsTensorHolder rstd;
87+};
88+ 
89+static AddRmsNormResult BuildAddRmsNormNode(
90+ es::EsGraphBuilder& builder, const es::EsTensorHolder& x1,
91+ const es::EsTensorHolder& x2, const es::EsTensorHolder& gamma)
92+{
93+ static int counter = 0;
94+ std::string name = "AddRmsNorm_" + std::to_string(counter++);
95+ auto graph = builder.GetCGraphBuilder()->GetGraph();
96+ GNode node = es::CompliantNodeBuilder(graph)
97+ .OpType("AddRmsNorm")
98+ .Name(name.c_str())
99+ .IrDefInputs({
100+ {"x1", es::CompliantNodeBuilder::kEsIrInputRequired, ""},
101+ {"x2", es::CompliantNodeBuilder::kEsIrInputRequired, ""},
102+ {"gamma", es::CompliantNodeBuilder::kEsIrInputRequired, ""}
103+ })
104+ .IrDefOutputs({
105+ {"y", es::CompliantNodeBuilder::kEsIrOutputRequired, ""},
106+ {"rstd", es::CompliantNodeBuilder::kEsIrOutputRequired, ""},
107+ {"x", es::CompliantNodeBuilder::kEsIrOutputRequired, ""}
108+ })
109+ .IrDefAttrs({
110+ {"epsilon", es::CompliantNodeBuilder::kEsAttrOptional, "Float",
111+ es::CreateFrom(1e-6f)}
112+ })
113+ .Build();
114+ es::AddEdgeAndUpdatePeerDesc(*graph, *x1.GetProducer(), x1.GetProducerOutIndex(), node, 0);
115+ es::AddEdgeAndUpdatePeerDesc(*graph, *x2.GetProducer(), x2.GetProducerOutIndex(), node, 1);
116+ es::AddEdgeAndUpdatePeerDesc(*graph, *gamma.GetProducer(), gamma.GetProducerOutIndex(), node, 2);
117+ return {
118+ es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 2)),
119+ es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 0)),
120+ es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 1))
121+ };
122+}
123+ 
124+struct DynamicQuantResult {
125+ es::EsTensorHolder y;
126+ es::EsTensorHolder scale;
127+};
128+ 
129+static DynamicQuantResult BuildDynamicQuantNode(
130+ es::EsGraphBuilder& builder, const es::EsTensorHolder& x, const es::EsTensorHolder& smooth)
131+{
132+ static int counter = 0;
133+ std::string name = "DynamicQuant_" + std::to_string(counter++);
134+ auto graph = builder.GetCGraphBuilder()->GetGraph();
135+ GNode node = es::CompliantNodeBuilder(graph)
136+ .OpType("DynamicQuant")
137+ .Name(name.c_str())
138+ .IrDefInputs({
139+ {"x", es::CompliantNodeBuilder::kEsIrInputRequired, ""},
140+ {"smooth_scales", es::CompliantNodeBuilder::kEsIrInputRequired, ""},
141+ {"group_index", es::CompliantNodeBuilder::kEsIrInputOptional, ""}
142+ })
143+ .IrDefOutputs({
144+ {"y", es::CompliantNodeBuilder::kEsIrOutputRequired, ""},
145+ {"scale", es::CompliantNodeBuilder::kEsIrOutputRequired, ""}
146+ })
147+ .Build();
148+ es::AddEdgeAndUpdatePeerDesc(*graph, *x.GetProducer(), x.GetProducerOutIndex(), node, 0);
149+ es::AddEdgeAndUpdatePeerDesc(*graph, *smooth.GetProducer(), smooth.GetProducerOutIndex(), node, 1);
150+ return {
151+ es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 0)),
152+ es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 1))
153+ };
154+}
155+ 
156+class multi_add_rms_norm_dynamic_quant_fusion_test : public testing::Test {
157+protected:
158+ static void SetUpTestCase()
159+ {
160+ std::cout << "multi_add_rms_norm_dynamic_quant_fusion_test SetUp" << std::endl;
161+ }
162+ 
163+ static void TearDownTestCase()
164+ {
165+ std::cout << "multi_add_rms_norm_dynamic_quant_fusion_test TearDown" << std::endl;
166+ }
167+ 
168+ void SetUpPlatform()
169+ {
170+ PlatformInfo platform_info;
171+ OptionalInfo opti_compilation_info;
172+ platform_info.soc_info.ai_core_cnt = 40;
173+ opti_compilation_info.soc_version = "Ascend910B1";
174+ platform_info.str_info.short_soc_version = "Ascend910B";
175+ PlatformInfoManager::Instance().platform_info_map_["Ascend910B1"] = platform_info;
176+ PlatformInfoManager::Instance().SetOptionalCompilationInfo(opti_compilation_info);
177+ }
178+ 
179+ void ClearPlatform()
180+ {
181+ PlatformInfoManager::Instance().platform_info_map_.clear();
182+ }
183+ 
184+ bool HasFusedOp(std::shared_ptr<Graph>& graph, const std::string& op_type)
185+ {
186+ for (auto node : graph->GetAllNodes()) {
187+ AscendString type;
188+ node.GetType(type);
189+ if (type == op_type.c_str()) {
190+ return true;
191+ }
192+ }
193+ return false;
194+ }
195+};
196+ 
197+/**
198+ * Test dual: 2 Add + AddRmsNorm + 2 DynamicQuant (with separate smooth_scales inputs)
199+ * Graph topology:
200+ * x1_0, x1_1 -> Add1 -> Add2(with x1_2) -> AddRmsNorm(with x2, gamma) -> [y, x_out]
201+ * y, smooth1 -> DynamicQuant1 -> [y1, scale1]
202+ * y, smooth2 -> DynamicQuant2 -> [y2, scale2]
203+ */
204+TEST_F(multi_add_rms_norm_dynamic_quant_fusion_test, multi_add_rms_norm_dynamic_quant_fusion_test_dual)
205+{
206+ SetUpPlatform();
207+ 
208+ std::vector<int64_t> dims_x{1, 1024, 8192};
209+ std::vector<int64_t> dims_gamma{8192};
210+ 
211+ auto graph_builder = es::EsGraphBuilder("multi_add_rms_norm_dq_dual");
212+ auto x1_0 = graph_builder.CreateInput(0, "x1_0", DT_FLOAT16, FORMAT_ND, dims_x);
213+ auto x1_1 = graph_builder.CreateInput(1, "x1_1", DT_FLOAT16, FORMAT_ND, dims_x);
214+ auto x1_2 = graph_builder.CreateInput(2, "x1_2", DT_FLOAT16, FORMAT_ND, dims_x);
215+ auto x2 = graph_builder.CreateInput(3, "x2", DT_FLOAT16, FORMAT_ND, dims_x);
216+ auto gamma = graph_builder.CreateInput(4, "gamma", DT_FLOAT16, FORMAT_ND, dims_gamma);
217+ auto smooth1 = graph_builder.CreateInput(5, "smooth1", DT_FLOAT16, FORMAT_ND, dims_gamma);
218+ auto smooth2 = graph_builder.CreateInput(6, "smooth2", DT_FLOAT16, FORMAT_ND, dims_gamma);
219+ 
220+ // Build pattern: 2 Add -> AddRmsNorm -> 2 DynamicQuant(with smooth)
221+ auto add1 = BuildAddTensor(graph_builder, x1_0, x1_1);
222+ auto add2 = BuildAddTensor(graph_builder, add1, x1_2);
223+ auto arnResult = BuildAddRmsNormNode(graph_builder, add2, x2, gamma);
224+ auto dq1 = BuildDynamicQuantNode(graph_builder, arnResult.y, smooth1);
225+ auto dq2 = BuildDynamicQuantNode(graph_builder, arnResult.y, smooth2);
226+ 
227+ std::vector<es::EsTensorHolder> outputs;
228+ outputs.emplace_back(std::move(dq1.y));
229+ outputs.emplace_back(std::move(dq2.y));
230+ outputs.emplace_back(std::move(arnResult.x));
231+ outputs.emplace_back(std::move(arnResult.y));
232+ outputs.emplace_back(std::move(dq1.scale));
233+ outputs.emplace_back(std::move(dq2.scale));
234+ std::shared_ptr<Graph> graph = graph_builder.Build(outputs);
235+ CustomPassContext pass_context;
236+ 
237+ ops::MultiAddRmsNormDynamicQuantFusionPass pass;
238+ Status status = pass.Run(graph, pass_context);
239+ ClearPlatform();
240+ EXPECT_EQ(status, SUCCESS);
241+ EXPECT_TRUE(HasFusedOp(graph, "MultiAddRmsNormDynamicQuant"));
242+}
243+ 
244+/**
245+ * Test dual_reshape: 2 Add + AddRmsNorm + Reshape + 2 DynamicQuant
246+ * Graph topology:
247+ * x1_0, x1_1 -> Add1 -> Add2(with x1_2) -> AddRmsNorm(with x2, gamma) -> [y, x_out]
248+ * y -> Reshape -> smooth_reshaped
249+ * y, smooth_reshaped -> DynamicQuant1 -> [y1, scale1]
250+ * y, smooth_reshaped -> DynamicQuant2 -> [y2, scale2]
251+ */
252+TEST_F(multi_add_rms_norm_dynamic_quant_fusion_test, multi_add_rms_norm_dynamic_quant_fusion_test_dual_reshape)
253+{
254+ SetUpPlatform();
255+ 
256+ std::vector<int64_t> dims_x{1, 1024, 8192};
257+ std::vector<int64_t> dims_gamma{8192};
258+ 
259+ auto graph_builder = es::EsGraphBuilder("multi_add_rms_norm_dq_dual_reshape");
260+ auto x1_0 = graph_builder.CreateInput(0, "x1_0", DT_FLOAT16, FORMAT_ND, dims_x);
261+ auto x1_1 = graph_builder.CreateInput(1, "x1_1", DT_FLOAT16, FORMAT_ND, dims_x);
262+ auto x1_2 = graph_builder.CreateInput(2, "x1_2", DT_FLOAT16, FORMAT_ND, dims_x);
263+ auto x2 = graph_builder.CreateInput(3, "x2", DT_FLOAT16, FORMAT_ND, dims_x);
264+ auto gamma = graph_builder.CreateInput(4, "gamma", DT_FLOAT16, FORMAT_ND, dims_gamma);
265+ 
266+ // Build pattern: 2 Add -> AddRmsNorm -> Reshape -> 2 DynamicQuant
267+ auto add1 = BuildAddTensor(graph_builder, x1_0, x1_1);
268+ auto add2 = BuildAddTensor(graph_builder, add1, x1_2);
269+ auto arnResult = BuildAddRmsNormNode(graph_builder, add2, x2, gamma);
270+ auto smooth_reshaped = BuildReshapeNode(graph_builder, arnResult.y, BuildConstNode(graph_builder));
271+ auto dq1 = BuildDynamicQuantNode(graph_builder, arnResult.y, smooth_reshaped);
272+ auto dq2 = BuildDynamicQuantNode(graph_builder, arnResult.y, smooth_reshaped);
273+ 
274+ std::vector<es::EsTensorHolder> outputs;
275+ outputs.emplace_back(std::move(dq1.y));
276+ outputs.emplace_back(std::move(dq2.y));
277+ outputs.emplace_back(std::move(arnResult.x));
278+ outputs.emplace_back(std::move(smooth_reshaped));
279+ outputs.emplace_back(std::move(dq1.scale));
280+ outputs.emplace_back(std::move(dq2.scale));
281+ std::shared_ptr<Graph> graph = graph_builder.Build(outputs);
282+ CustomPassContext pass_context;
283+ 
284+ ops::MultiAddRmsNormDynamicQuantFusionPass pass;
285+ Status status = pass.Run(graph, pass_context);
286+ ClearPlatform();
287+ EXPECT_EQ(status, SUCCESS);
288+ EXPECT_TRUE(HasFusedOp(graph, "MultiAddRmsNormDynamicQuant"));
289+}
290+ 
291+/**
292+ * Test sole: 2 Add + AddRmsNorm + 1 DynamicQuant (with smooth_scales)
293+ * Graph topology:
294+ * x1_0, x1_1 -> Add1 -> Add2(with x1_2) -> AddRmsNorm(with x2, gamma) -> [y, x_out]
295+ * y, smooth -> DynamicQuant -> [y1, scale1]
296+ */
297+TEST_F(multi_add_rms_norm_dynamic_quant_fusion_test, multi_add_rms_norm_dynamic_quant_fusion_test_sole)
298+{
299+ SetUpPlatform();
300+ 
301+ std::vector<int64_t> dims_x{1, 1024, 8192};
302+ std::vector<int64_t> dims_gamma{8192};
303+ 
304+ auto graph_builder = es::EsGraphBuilder("multi_add_rms_norm_dq_sole");
305+ auto x1_0 = graph_builder.CreateInput(0, "x1_0", DT_FLOAT16, FORMAT_ND, dims_x);
306+ auto x1_1 = graph_builder.CreateInput(1, "x1_1", DT_FLOAT16, FORMAT_ND, dims_x);
307+ auto x1_2 = graph_builder.CreateInput(2, "x1_2", DT_FLOAT16, FORMAT_ND, dims_x);
308+ auto x2 = graph_builder.CreateInput(3, "x2", DT_FLOAT16, FORMAT_ND, dims_x);
309+ auto gamma = graph_builder.CreateInput(4, "gamma", DT_FLOAT16, FORMAT_ND, dims_gamma);
310+ auto smooth = graph_builder.CreateInput(5, "smooth", DT_FLOAT16, FORMAT_ND, dims_gamma);
311+ 
312+ // Build pattern: 2 Add -> AddRmsNorm -> 1 DynamicQuant(with smooth)
313+ auto add1 = BuildAddTensor(graph_builder, x1_0, x1_1);
314+ auto add2 = BuildAddTensor(graph_builder, add1, x1_2);
315+ auto arnResult = BuildAddRmsNormNode(graph_builder, add2, x2, gamma);
316+ auto dq1 = BuildDynamicQuantNode(graph_builder, arnResult.y, smooth);
317+ 
318+ std::vector<es::EsTensorHolder> outputs;
319+ outputs.emplace_back(std::move(dq1.y));
320+ outputs.emplace_back(std::move(arnResult.x));
321+ outputs.emplace_back(std::move(arnResult.y));
322+ outputs.emplace_back(std::move(dq1.scale));
323+ std::shared_ptr<Graph> graph = graph_builder.Build(outputs);
324+ CustomPassContext pass_context;
325+ 
326+ ops::MultiAddRmsNormDynamicQuantFusionPass pass;
327+ Status status = pass.Run(graph, pass_context);
328+ ClearPlatform();
329+ EXPECT_EQ(status, SUCCESS);
330+ EXPECT_TRUE(HasFusedOp(graph, "MultiAddRmsNormDynamicQuant"));
331+}
@@ -1,12 +1,14 @@
1# ----------------------------------------------------------------------------1# ----------------------------------------------------------------------------
2# Copyright (c) 2025 Huawei Technologies Co., Ltd.2# Copyright (c) 2025 Huawei Technologies Co., Ltd.
3-# This program is free software, you can redistribute it and/or modify it under the terms and conditions of 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").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.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, 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.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.8# See LICENSE in the root of the software repository for the full text of the License.
9# ----------------------------------------------------------------------------9# ----------------------------------------------------------------------------
10 10 
11message(STATUS "=== Debug:start ops.quant.dynamic_quant_update_scatter_v2.op_graph.CMakeLists.txt")11message(STATUS "=== Debug:start ops.quant.dynamic_quant_update_scatter_v2.op_graph.CMakeLists.txt")
12-add_graph_plugin_sources()12+if(BUILD_OPEN_PROJECT)
13+ add_graph_plugin_sources()
14+endif()
@@ -0,0 +1,562 @@
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+ * \brief dynamic_quant_update_scatter_v2_fusion_pass.cpp
13+ * \details fusion pass
14+ * x x indices In0 In1 In2
15+ * | | | | | |
16+ * Const0 DynamicQuantV2 | | | | |
17+ * \ /|\ | | | | |
18+ * Reshape0| \ ---> DynamicQuantUpdateScatterV2
19+ * / | Neg indices | | |
20+ * Const1 Bitcast0| \ * /| | | |
21+ * \ / | * \ / | var var_scale var_offset
22+ * In0 Reshape1 *| In1 X | In2
23+ * \ / * | | / \ | |
24+ * Const2 Scatter0 Scatter1 Scatter2
25+ * \ | | |
26+ * Reshape2 | |
27+ * | | |
28+ * Const3 Bitcast1 Unsqueeze0 Unsqueeze1
29+ * \ | | |
30+ * Reshape3 | |
31+ * | | |
32+ * var var_scale var_offset
33+ */
34+ 
35+#include "dynamic_quant_update_scatter_v2_fusion_pass.h"
36+ 
37+#include <string>
38+ 
39+#include "es_nn_ops.h"
40+#include "es_math_ops.h"
41+#include "ge/ge_utils.h"
42+#include "common/inc/error_util.h"
43+#include "platform/platform_info.h"
44+#include "compliant_node_builder.h"
45+ 
46+using namespace ge;
47+using namespace fe;
48+using namespace fusion;
49+ 
50+namespace ops {
51+ 
52+static constexpr uint32_t IDX_0 = 0;
53+static constexpr uint32_t IDX_1 = 1;
54+static constexpr uint32_t IDX_2 = 2;
55+static constexpr uint32_t IDX_3 = 3;
56+static constexpr uint32_t IDX_4 = 4;
57+static constexpr uint32_t IDX_5 = 5;
58+static constexpr uint32_t IDX_8 = 8;
59+static constexpr int64_t FULL_LOAD_H_SIZE = 14960;
60+ 
61+static const std::string FUSED_OP_TYPE = "DynamicQuantUpdateScatterV2";
62+static const std::string PASS_NAME = "DynamicQuantUpdateScatterV2FusionPass";
63+ 
64+using UniqueGraphPtr = std::unique_ptr<Graph>;
65+ 
66+static es::EsTensorHolder BuildPatternReshapeNode(
67+ es::EsGraphBuilder& builder, const es::EsTensorHolder& x, const es::EsTensorHolder& shape)
68+{
69+ static int counter = 0;
70+ auto graph = builder.GetCGraphBuilder()->GetGraph();
71+ std::string name = "Reshape_" + std::to_string(counter++);
72+ GNode node = es::CompliantNodeBuilder(graph)
73+ .OpType("Reshape")
74+ .IrDefInputs({
75+ {"x", es::CompliantNodeBuilder::kEsIrInputRequired, ""},
76+ {"shape", es::CompliantNodeBuilder::kEsIrInputRequired, ""}
77+ })
78+ .Name(name.c_str())
79+ .IrDefOutputs({{"y", es::CompliantNodeBuilder::kEsIrOutputRequired, ""}})
80+ .Build();
81+ es::AddEdgeAndUpdatePeerDesc(*graph, *x.GetProducer(), x.GetProducerOutIndex(), node, 0);
82+ es::AddEdgeAndUpdatePeerDesc(*graph, *shape.GetProducer(), shape.GetProducerOutIndex(), node, 1);
83+ return es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 0));
84+}
85+ 
86+static es::EsTensorHolder BuildPatternConstNode(es::EsGraphBuilder& builder)
87+{
88+ static int counter = 0;
89+ auto graph = builder.GetCGraphBuilder()->GetGraph();
90+ std::string name = "Const_" + std::to_string(counter++);
91+ GNode node = es::CompliantNodeBuilder(graph)
92+ .OpType("Const")
93+ .Name(name.c_str())
94+ .IrDefOutputs({{"y", es::CompliantNodeBuilder::kEsIrOutputRequired, ""}})
95+ .Build();
96+ return es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 0));
97+}
98+ 
99+static es::EsTensorHolder BuildNegNode(es::EsGraphBuilder& builder, const es::EsTensorHolder& x)
100+{
@@ -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+/*!
12+ * \file dynamic_quant_update_scatter_v2_fusion_pass.h
13+ * \brief
14+ */
15+#ifndef OPS_BUILT_IN_FUSION_PASS_GRAPH_DYNAMIC_QUANT_SCATTER_V2_H
16+#define OPS_BUILT_IN_FUSION_PASS_GRAPH_DYNAMIC_QUANT_SCATTER_V2_H
17+ 
18+#include "ge/fusion/pass/pattern_fusion_pass.h"
19+ 
20+namespace ops {
21+using namespace ge;
22+using namespace fusion;
23+ 
24+class __attribute__((visibility("default"))) DynamicQuantUpdateScatterV2FusionPass : public PatternFusionPass {
25+protected:
26+ std::vector<PatternUniqPtr> Patterns() override;
27+ bool MeetRequirements(const std::unique_ptr<MatchResult>& match_result) override;
28+ std::unique_ptr<Graph> Replacement(const std::unique_ptr<MatchResult>& match_result) override;
29+};
30+ 
31+} // namespace ops
32+ 
33+#endif // OPS_BUILT_IN_FUSION_PASS_GRAPH_DYNAMIC_QUANT_SCATTER_V2_H
@@ -0,0 +1,18 @@
1+# ----------------------------------------------------------------------------
2+# This program is free software, you can redistribute it and/or modify.
3+# Copyright (c) 2025 Huawei Technologies Co., Ltd.
4+# This file is a part of the CANN Open Software.
5+# Licensed under CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING 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+# 每个目录下需要生成的可执行文件,具体参考:ops/built-in/test/CMakeLists.txt: 50~124
12+file(GLOB CURRENT_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)
13+message(STATUS "=== Debug: CURRENT_DIRS =${CURRENT_DIRS} ")
14+foreach(SUB_DIR ${CURRENT_DIRS})
15+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUB_DIR}/CMakeLists.txt")
16+ add_subdirectory(${SUB_DIR})
17+ endif()
18+endforeach()
@@ -0,0 +1,616 @@
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 "array_ops.h"
12+#include "nn_quantize.h"
13+#include "experiment_ops.h"
14+#include "matrix_calculation_ops.h"
15+#include "fusion_ops.h"
16+#include "elewise_calculation_ops.h"
17+#include "selection_ops.h"
18+#include "fusion_pass_test_utils.h"
19+#include "graph_dsl/graph_checker.h"
20+#include "graph_dsl/graph_dsl.h"
21+#include "graph/utils/graph_utils.h"
22+#include "graph/utils/graph_utils_ex.h"
23+#include "gtest/gtest.h"
24+#include "graph_builder_utils.h"
25+#include "platform_info_stub.h"
26+#include "graph/compute_graph.h"
27+#include "graph/graph.h"
28+#include "graph/utils/op_desc_utils.h"
29+#include "graph/operator.h"
30+#include "util/util.h"
31+#include "platform/platform_info.h"
32+#include "common/configuration.h"
33+#include "common/utils/ut_op_util.h"
34+#include "graph/utils/attr_utils.h"
35+#include "register/graph_optimizer/fusion_common/fusion_statistic_recorder.h"
36+#include "es_nn_ops.h"
37+#include "es_math_ops.h"
38+#include "dynamic_quant_update_scatter_v2_fusion_pass.h"
39+#include "compliant_node_builder.h"
40+ 
41+using namespace ge;
42+using namespace op;
43+ 
44+static es::EsTensorHolder BuildConstNode(es::EsGraphBuilder& builder)
45+{
46+ static int counter = 0;
47+ std::string name = "Const_" + std::to_string(counter++);
48+ auto graph = builder.GetCGraphBuilder()->GetGraph();
49+ GNode node = es::CompliantNodeBuilder(graph)
50+ .OpType("Const")
51+ .Name(name.c_str())
52+ .IrDefOutputs({{"y", es::CompliantNodeBuilder::kEsIrOutputRequired, ""}})
53+ .Build();
54+ return es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 0));
55+}
56+ 
57+static es::EsTensorHolder BuildReshapeNode(
58+ es::EsGraphBuilder& builder, const es::EsTensorHolder& x, const es::EsTensorHolder& shape)
59+{
60+ static int counter = 0;
61+ std::string name = "Reshape_" + std::to_string(counter++);
62+ auto graph = builder.GetCGraphBuilder()->GetGraph();
63+ GNode node = es::CompliantNodeBuilder(graph)
64+ .OpType("Reshape")
65+ .Name(name.c_str())
66+ .IrDefInputs({
67+ {"x", es::CompliantNodeBuilder::kEsIrInputRequired, ""},
68+ {"shape", es::CompliantNodeBuilder::kEsIrInputRequired, ""}
69+ })
70+ .IrDefOutputs({{"y", es::CompliantNodeBuilder::kEsIrOutputRequired, ""}})
71+ .Build();
72+ es::AddEdgeAndUpdatePeerDesc(*graph, *x.GetProducer(), x.GetProducerOutIndex(), node, 0);
73+ es::AddEdgeAndUpdatePeerDesc(*graph, *shape.GetProducer(), shape.GetProducerOutIndex(), node, 1);
74+ return es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 0));
75+}
76+ 
77+static es::EsTensorHolder BuildBitcastNode(es::EsGraphBuilder& builder, const es::EsTensorHolder& x, DataType dt)
78+{
79+ static int counter = 0;
80+ std::string name = "Bitcast_" + std::to_string(counter++);
81+ auto graph = builder.GetCGraphBuilder()->GetGraph();
82+ GNode node = es::CompliantNodeBuilder(graph)
83+ .OpType("Bitcast")
84+ .Name(name.c_str())
85+ .IrDefInputs({{"x", es::CompliantNodeBuilder::kEsIrInputRequired, ""}})
86+ .IrDefOutputs({{"y", es::CompliantNodeBuilder::kEsIrOutputRequired, ""}})
87+ .IrDefAttrs({
88+ {"type", es::CompliantNodeBuilder::kEsAttrRequired, "Int",
89+ es::CreateFrom(static_cast<int64_t>(dt))}
90+ })
91+ .Build();
92+ es::AddEdgeAndUpdatePeerDesc(*graph, *x.GetProducer(), x.GetProducerOutIndex(), node, 0);
93+ return es::EsTensorHolder(builder.GetCGraphBuilder()->GetTensorHolderFromNode(node, 0));
94+}
95+ 
96+static es::EsTensorHolder BuildNegNode(es::EsGraphBuilder& builder, const es::EsTensorHolder& x)
97+{
98+ static int counter = 0;
99+ std::string name = "Neg_" + std::to_string(counter++);
100+ auto graph = builder.GetCGraphBuilder()->GetGraph();