Line data Source code
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 "ascendc_graph_txt_dumper.h"
12 :
13 : #include <sstream>
14 : #include <functional>
15 : #include <algorithm>
16 : #include "graph/utils/type_utils.h"
17 : #include "graph/symbolizer/symbolic_utils.h"
18 :
19 : namespace {
20 : constexpr int32_t kAxisPriorityBlockOuter = 1;
21 : constexpr int32_t kAxisPriorityBlockInner = 2;
22 : constexpr int32_t kAxisPriorityTileOuter = 3;
23 : constexpr int32_t kAxisPriorityTileInner = 4;
24 : constexpr int32_t kAxisPriorityOriginal = 5;
25 : constexpr int32_t kAxisPriorityMerged = 6;
26 : constexpr int32_t kAxisPriorityDefault = 999;
27 : } // namespace
28 : namespace ascir {
29 : namespace dumper {
30 : // =============================================================================
31 : // 工具函数实现
32 : // =============================================================================
33 :
34 : /**
35 : * @brief 统一的 Dtype 映射表
36 : */
37 : static const std::map<ge::DataType, DtypeInfo> kDtypeInfoMap = {
38 : {ge::DT_FLOAT, {"float32", "f32", "32f"}}, {ge::DT_FLOAT16, {"float16", "f16", "16f"}},
39 : {ge::DT_BF16, {"bfloat16", "bf16", "16f"}}, {ge::DT_INT8, {"int8_t", "i8", "8i"}},
40 : {ge::DT_INT16, {"int16_t", "i16", "16i"}}, {ge::DT_INT32, {"int32_t", "i32", "32i"}},
41 : {ge::DT_INT64, {"int64_t", "i64", "64i"}}, {ge::DT_UINT8, {"uint8_t", "u8", "8u"}},
42 : {ge::DT_UINT16, {"uint16_t", "u16", "16u"}}, {ge::DT_UINT32, {"uint32_t", "u32", "32u"}},
43 : {ge::DT_UINT64, {"uint64_t", "u64", "64u"}}, {ge::DT_BOOL, {"bool", "i1", "1i"}},
44 : {ge::DT_DOUBLE, {"float64", "f64", "64f"}},
45 : };
46 :
47 : const DtypeInfo *GetDtypeInfo(ge::DataType dtype) {
48 : const auto it = kDtypeInfoMap.find(dtype);
49 : if (it != kDtypeInfoMap.end()) {
50 : return &it->second;
51 : }
52 : return nullptr;
53 : }
54 :
55 : int32_t GetAxisTypePriority(af::Axis::Type type) {
56 : switch (type) {
57 : case af::Axis::Type::kAxisTypeBlockOuter:
58 : return kAxisPriorityBlockOuter;
59 : case af::Axis::Type::kAxisTypeBlockInner:
60 : return kAxisPriorityBlockInner;
61 : case af::Axis::Type::kAxisTypeTileOuter:
62 : return kAxisPriorityTileOuter;
63 : case af::Axis::Type::kAxisTypeTileInner:
64 : return kAxisPriorityTileInner;
65 : case af::Axis::Type::kAxisTypeOriginal:
66 : return kAxisPriorityOriginal;
67 : case af::Axis::Type::kAxisTypeMerged:
68 : return kAxisPriorityMerged;
69 : default:
70 : return kAxisPriorityDefault;
71 : }
72 : }
73 :
74 : std::string GetAxisTypeSuffix(af::Axis::Type type) {
75 : switch (type) {
76 : case af::Axis::Type::kAxisTypeOriginal:
77 : return "ORIGINAL";
78 : case af::Axis::Type::kAxisTypeTileOuter:
79 : return "TILE_OUT";
80 : case af::Axis::Type::kAxisTypeBlockOuter:
81 : return "BLOCK_OUT";
82 : case af::Axis::Type::kAxisTypeBlockInner:
83 : return "BLOCK_IN";
84 : case af::Axis::Type::kAxisTypeTileInner:
85 : return "TILE_IN";
86 : case af::Axis::Type::kAxisTypeMerged:
87 : return "MERGED";
88 : default:
89 : return "UNKNOWN";
90 : }
91 : }
92 :
93 : std::map<af::AxisId, std::string> BuildAxisIdToNameMap(const std::vector<af::AxisPtr> &axes) {
94 : std::map<af::AxisId, std::string> id_to_name;
95 : for (const auto &axis : axes) {
96 : id_to_name[axis->id] = axis->name;
97 : }
98 : return id_to_name;
99 : }
100 :
101 : std::map<int64_t, af::Axis::Type> BuildAxisIdToTypeMap(const std::vector<af::AxisPtr> &axes) {
102 : std::map<int64_t, af::Axis::Type> id_to_type;
103 : for (const auto &axis : axes) {
104 : id_to_type[axis->id] = axis->type;
105 : }
106 : return id_to_type;
107 : }
108 :
109 : /**
110 : * @brief 获取 DataType 的字符串表示(短名字)
111 : */
112 : static std::string GetDtypeString(ge::DataType dtype) {
113 : const DtypeInfo *info = GetDtypeInfo(dtype);
114 : if (info != nullptr) {
115 : return info->short_name;
116 : }
117 : return ge::TypeUtils::DataTypeToSerialString(dtype);
118 : }
119 :
120 : /**
121 : * @brief 获取 tensor 类型字符串(用于函数签名)
122 : */
123 : static std::string GetTensorTypeStr(const af::AscGraph &graph, const af::AscTensorAttr &attr,
124 : const std::map<af::AxisId, std::string> &axis_id_to_name) {
125 : (void)graph;
126 : std::stringstream ss;
127 :
128 : // 数据类型 - 使用简写类型名
129 : const auto dtype = attr.dtype;
130 : std::string dtype_str;
131 : const DtypeInfo *info = GetDtypeInfo(dtype);
132 : if (info != nullptr) {
133 : dtype_str = info->short_name;
134 : } else {
135 : // 使用完整类型名
136 : dtype_str = GetDtypeString(dtype);
137 : }
138 :
139 : ss << dtype_str << "[";
140 :
141 : // 形状
142 : for (size_t i = 0; i < attr.axis.size(); ++i) {
143 : if (i > 0) ss << ",";
144 63 : const auto axis_id = attr.axis[i];
145 :
146 : // 如果是 repeats,输出大小
147 : if (i < attr.repeats.size()) {
148 63 : const auto repeat = attr.repeats[i];
149 : if (repeat.GetExprType() == af::ExprType::kExprConstantRation) {
150 : int64_t val = 0;
151 : if (repeat.GetConstValue(val)) {
152 : ss << val;
153 : } else {
154 : const auto it = axis_id_to_name.find(axis_id);
155 : ss << (it != axis_id_to_name.end() ? it->second : "axis") << "_size";
156 : }
157 : } else {
158 : ss << af::SymbolicUtils::ToString(repeat);
159 : }
160 : } else {
161 : const auto it = axis_id_to_name.find(axis_id);
162 : ss << (it != axis_id_to_name.end() ? it->second : "axis") << "_size";
163 : }
164 : }
165 :
166 : ss << "]";
167 : return ss.str();
168 : }
169 :
170 : DumpContext BuildDumpContext(const ascir::Graph &graph) {
171 : DumpContext ctx;
172 : ctx.all_axis = graph.GetAllAxis();
173 : ctx.all_size_vars = graph.GetAllSizeVar();
174 : ctx.axis_id_to_name = BuildAxisIdToNameMap(ctx.all_axis);
175 : ctx.axis_id_to_type = BuildAxisIdToTypeMap(ctx.all_axis);
176 : ctx.ssa_mapping = BuildSSAMapping(graph.GetAllNodes());
177 :
178 : // 收集函数参数(data, workspace, output)
179 : for (auto node : graph.GetAllNodes()) {
180 72 : const auto node_type = node->GetType();
181 : if (node_type == NodeType::kData) {
182 : if (!node->outputs().empty()) {
183 : auto &output_attr = node->outputs()[0]->attr;
184 : ctx.func_params.data_params.push_back(
185 : {node->GetName(), GetTensorTypeStr(graph, output_attr, ctx.axis_id_to_name)});
186 : }
187 : } else if (node_type == NodeType::kWorkspace) {
188 : if (!node->outputs().empty()) {
189 : auto &output_attr = node->outputs()[0]->attr;
190 : ctx.func_params.workspace_params.push_back(
191 : {node->GetName(), GetTensorTypeStr(graph, output_attr, ctx.axis_id_to_name)});
192 : }
193 : } else if (node_type == NodeType::kOutput) {
194 : if (!node->outputs().empty()) {
195 : auto &output_attr = node->outputs()[0]->attr;
196 : ctx.func_params.output_params.push_back(
197 : {node->GetName(), GetTensorTypeStr(graph, output_attr, ctx.axis_id_to_name)});
198 : }
199 : }
200 : }
201 :
202 : return ctx;
203 : }
204 :
205 : std::string ExtractDtypeFromTensorType(const std::string &tensor_type) {
206 : // tensor_type 格式: f32[...] 或 float32[...]
207 : size_t pos = tensor_type.find('[');
208 : if (pos != std::string::npos) {
209 : std::string dtype = tensor_type.substr(0, pos);
210 : // 转换完整名称为简写
211 : if (dtype == "float32") return "f32";
212 : if (dtype == "float16") return "f16";
213 : if (dtype == "int32") return "i32";
214 : if (dtype == "int8") return "i8";
215 : if (dtype == "int16") return "i16";
216 : if (dtype == "int64") return "i64";
217 : if (dtype == "uint8") return "u8";
218 : if (dtype == "uint16") return "u16";
219 : if (dtype == "uint32") return "u32";
220 : if (dtype == "uint64") return "u64";
221 : if (dtype == "bfloat16") return "bf16";
222 : return dtype;
223 : }
224 : return tensor_type;
225 : }
226 :
227 : std::string ExtractAxisListFromTensorType(const std::string &tensor_type) {
228 : size_t pos = tensor_type.find('[');
229 : if (pos != std::string::npos) {
230 : return tensor_type.substr(pos);
231 : }
232 : return "[]";
233 : }
234 :
235 : std::vector<std::string> CollectInputNames(const ascir::Graph &graph, const af::AscNodePtr &node) {
236 : (void)graph;
237 : std::vector<std::string> input_names;
238 :
239 : for (uint32_t index = 0U; index < node->GetAllInDataAnchorsSize(); index++) {
240 179 : const auto in_anchor = node->GetInDataAnchor(static_cast<int32_t>(index));
241 : if (in_anchor == nullptr) {
242 : input_names.push_back("nil");
243 : continue;
244 : }
245 179 : const auto peer_out_anchor = in_anchor->GetPeerOutAnchor();
246 : if (peer_out_anchor == nullptr) {
247 : input_names.push_back("nil");
248 : } else {
249 358 : const auto peer_name = peer_out_anchor->GetOwnerNode()->GetName();
250 : int32_t out_idx = peer_out_anchor->GetIdx();
251 : // 检查源节点是否有多个输出,如果有则显示索引
252 : auto peer_node = peer_out_anchor->GetOwnerNodeBarePtr();
253 : if (peer_node && peer_node->GetAllOutDataAnchorsSize() > 1) {
254 : input_names.push_back(peer_name + ".y[" + std::to_string(out_idx) + "]");
255 : } else {
256 : input_names.push_back(peer_name + ".y");
257 : }
258 : }
259 : }
260 : return input_names;
261 : }
262 :
263 : SSAMappingInfo BuildSSAMapping(af::AscNodeVisitor all_nodes) {
264 : SSAMappingInfo info;
265 : size_t topo_id = 0;
266 :
267 : for (auto node : all_nodes) {
268 : auto node_type = node->GetType();
269 : if (node_type == NodeType::kData) {
270 : info.data_node_names.insert(node->GetName());
271 : } else if (node_type != NodeType::kOutput && node_type != NodeType::kWorkspace) {
272 : info.node_name_to_ssa_id[node->GetName()] = topo_id + 1;
273 : info.node_name_to_topo_id[node->GetName()] = topo_id;
274 : topo_id++;
275 : }
276 : }
277 :
278 : return info;
279 : }
280 :
281 : // =============================================================================
282 : // VIEW 1: Loop Execution 内部辅助函数
283 : // =============================================================================
284 :
285 : namespace {
286 : // =============================================================================
287 : // 向量化相关辅助函数实现
288 : // =============================================================================
289 :
290 : /**
291 : * @brief 检查向量化维度是否为广播维度
292 : */
293 : bool IsBroadcastDimension(const af::Expression &stride) {
294 : if (stride.GetExprType() == af::ExprType::kExprConstantRation) {
295 : int64_t val = 0;
296 : if (stride.GetConstValue(val) && val == 0) {
297 : return true;
298 : }
299 : }
300 : return false;
301 : }
302 :
303 : /**
304 : * @brief 获取向量化维度的大小字符串
305 : */
306 : std::string GetVectorizedDimSize(const af::AscTensorAttr &attr, af::AxisId axis_id,
307 : const std::map<af::AxisId, std::string> &axis_id_to_name) {
308 : // 找到 axis_id 在 attr.axis 中的位置
309 : size_t found_axis_idx = 0;
310 : for (; found_axis_idx < attr.axis.size(); ++found_axis_idx) {
311 : if (attr.axis[found_axis_idx] == axis_id) {
312 : break;
313 : }
314 : }
315 :
316 : // 获取对应的 repeat 值
317 : if (found_axis_idx < attr.repeats.size()) {
318 : auto repeat = attr.repeats[found_axis_idx];
319 : if (repeat.GetExprType() == af::ExprType::kExprConstantRation) {
320 : int64_t val = 0;
321 : if (repeat.GetConstValue(val)) {
322 : return std::to_string(val);
323 : }
324 : return "1";
325 : }
326 : return af::SymbolicUtils::ToString(repeat);
327 : }
328 :
329 : // 找不到对应的 repeat,使用轴名
330 : auto it = axis_id_to_name.find(axis_id);
331 : if (it != axis_id_to_name.end()) {
332 : return it->second + "_size";
333 : }
334 : return "1";
335 : }
336 :
337 : /**
338 : * @brief 获取 dtype 的简写后缀
339 : */
340 : std::string GetDtypeSuffix(ge::DataType dtype) {
341 : const DtypeInfo *info = GetDtypeInfo(dtype);
342 : if (info != nullptr) {
343 : return info->short_name;
344 : }
345 :
346 : // 未知类型,生成位宽表示
347 : int32_t size_bytes = ge::GetSizeByDataType(dtype);
348 : std::string suffix = (size_bytes > 0) ? std::to_string(size_bytes * 8) : "32";
349 :
350 : std::string type_name = ge::TypeUtils::DataTypeToSerialString(dtype);
351 : if (type_name.find("UINT") != std::string::npos || type_name.find("uint") != std::string::npos) {
352 : suffix += "u";
353 : } else if (type_name.find("INT") != std::string::npos || type_name.find("int") != std::string::npos ||
354 : type_name.find("BOOL") != std::string::npos) {
355 : suffix += "i";
356 : } else {
357 : suffix += "f";
358 : }
359 : return suffix;
360 : }
361 :
362 : /**
363 : * @brief 获取向量化轴的字符串表示
364 : */
365 : static std::string GetVectorizedAxesStr(const ascir::Graph &graph, const af::AscTensorAttr &attr,
366 : const std::map<af::AxisId, std::string> &axis_id_to_name) {
367 : (void)graph;
368 : if (attr.vectorized_axis.empty()) {
369 : return "";
370 : }
371 :
372 : std::stringstream ss;
373 : ss << "vector<";
374 :
375 : for (size_t i = 0; i < attr.vectorized_axis.size(); ++i) {
376 : if (i > 0) ss << "x";
377 :
378 : auto axis_id = attr.vectorized_axis[i];
379 :
380 : // 检查是否为广播维度
381 : bool is_broadcast = false;
382 : if (i < attr.vectorized_strides.size()) {
383 : is_broadcast = IsBroadcastDimension(attr.vectorized_strides[i]);
384 : }
385 :
386 : if (is_broadcast) {
387 : ss << "1";
388 : } else {
389 : ss << GetVectorizedDimSize(attr, axis_id, axis_id_to_name);
390 : }
391 : }
392 :
393 : ss << "x" << GetDtypeSuffix(attr.dtype) << ">";
394 :
395 : return ss.str();
396 : }
397 :
398 : /**
399 : * @brief 格式化输入参数列表
400 : */
401 : static std::string FormatInputParams(const std::vector<std::string> &input_names, const SSAMappingInfo &ssa_info) {
402 : std::stringstream ss;
403 : for (size_t i = 0; i < input_names.size(); ++i) {
404 : if (i > 0) ss << ", ";
405 : if (input_names[i] != "nil") {
406 : std::string input_name = input_names[i];
407 : // 去掉 .y 或 .y[index] 后缀
408 : size_t pos = input_name.find(".y");
409 : if (pos != std::string::npos) {
410 : input_name = input_name.substr(0, pos);
411 : }
412 :
413 : // 判断是 Data 节点还是中间节点
414 : if (ssa_info.IsDataNode(input_name)) {
415 : // Data 节点,使用节点名称
416 : ss << "%" << input_name;
417 : } else {
418 : // 中间节点,使用 SSA 编号
419 : size_t ssa_id = ssa_info.GetSsaId(input_name);
420 : if (ssa_id > 0) {
421 : ss << "%" << ssa_id;
422 : } else {
423 : ss << "%" << input_name;
424 : }
425 : }
426 : }
427 : }
428 : return ss.str();
429 : }
430 :
431 : /**
432 : * @brief 收集并排序子图的 loop_axis
433 : */
434 : static std::vector<int64_t> CollectSubgraphLoopAxes(const ascir::Graph &graph,
435 : const std::map<int64_t, af::Axis::Type> &axis_id_to_type) {
436 : auto all_nodes = graph.GetAllNodes();
437 :
438 : // 收集所有节点的 loop_axis(去重)
439 : std::vector<int64_t> loop_axes_in_order;
440 : std::set<int64_t> seen_loop_axes;
441 :
442 : for (auto node : all_nodes) {
443 : auto node_type = node->GetType();
444 : if (node_type == NodeType::kData || node_type == NodeType::kOutput || node_type == NodeType::kWorkspace) {
445 : continue;
446 : }
447 : auto loop_axis = node->attr.sched.loop_axis;
448 : if (loop_axis != kInvalidLoopAxis && seen_loop_axes.find(loop_axis) == seen_loop_axes.end()) {
449 : seen_loop_axes.insert(loop_axis);
450 : loop_axes_in_order.push_back(loop_axis);
451 : }
452 : }
453 :
454 : // 按轴类型排序
455 : std::sort(loop_axes_in_order.begin(), loop_axes_in_order.end(), [&axis_id_to_type](int64_t a, int64_t b) {
456 : int32_t priority_a = GetAxisTypePriority(axis_id_to_type.at(a));
457 : int32_t priority_b = GetAxisTypePriority(axis_id_to_type.at(b));
458 : if (priority_a != priority_b) {
459 : return priority_a < priority_b;
460 : }
461 : return a < b;
462 : });
463 :
464 : return loop_axes_in_order;
465 : }
466 :
467 : /**
468 : * @brief 按 loop_axis 分组节点
469 : */
470 : static std::map<int64_t, std::vector<af::AscNodePtr> > GroupNodesByLoopAxis(const ascir::Graph &graph) {
471 : std::map<int64_t, std::vector<af::AscNodePtr> > nodes_by_loop_axis;
472 : auto all_nodes = graph.GetAllNodes();
473 :
474 : for (auto node : all_nodes) {
475 : auto node_type = node->GetType();
476 : if (node_type == NodeType::kData || node_type == NodeType::kOutput || node_type == NodeType::kWorkspace) {
477 : continue;
478 : }
479 : auto loop_axis = node->attr.sched.loop_axis;
480 : nodes_by_loop_axis[loop_axis].push_back(node);
481 : }
482 :
483 : return nodes_by_loop_axis;
484 : }
485 :
486 : /**
487 : * @brief 输出子图中的单个节点
488 : */
489 : static void DumpSubgraphNode(std::stringstream &ss, const ascir::Graph &graph, const af::AscNodePtr &node,
490 : const SSAMappingInfo &ssa_info, size_t indent) {
491 : std::string node_name = node->GetName();
492 : size_t topo_id = ssa_info.GetTopoId(node_name);
493 : auto node_type = node->GetType();
494 :
495 : ss << std::string(indent, ' ') << "%" << (topo_id + 1) << " = ascir.ops." << node_type << "(";
496 :
497 : // 输入参数
498 : auto input_names = CollectInputNames(graph, node);
499 : ss << FormatInputParams(input_names, ssa_info);
500 :
501 : ss << ")";
502 :
503 : // 子图显示标量类型
504 : if (!node->outputs().empty()) {
505 : auto &output_attr = node->outputs()[0]->attr;
506 : ss << " → " << GetDtypeString(output_attr.dtype);
507 : }
508 :
509 : ss << " # @" << node_name << " (topo_id=" << topo_id << ")" << std::endl;
510 : }
511 :
512 : /**
513 : * @brief 输出嵌套循环中的节点
514 : */
515 : static void DumpNodesInLoops(std::stringstream &ss, const ascir::Graph &graph,
516 : const std::vector<int64_t> &loop_axes_in_order,
517 : const std::map<int64_t, std::vector<af::AscNodePtr> > &nodes_by_loop_axis,
518 : const std::map<af::AxisId, std::string> &axis_id_to_name, const SSAMappingInfo &ssa_info) {
519 : std::set<int64_t> opened_loops;
520 : size_t current_depth = 0;
521 :
522 : for (auto axis_id : loop_axes_in_order) {
523 : // 打开循环
524 : ss << std::string(current_depth * kIndentSpaces, ' ') << "for %" << axis_id_to_name.at(axis_id) << " in "
525 : << axis_id_to_name.at(axis_id) << "_size {" << std::endl;
526 : opened_loops.insert(axis_id);
527 : current_depth++;
528 :
529 : // 输出 loop_axis = 当前轴的节点
530 : if (nodes_by_loop_axis.count(axis_id) > 0) {
531 : for (auto node : nodes_by_loop_axis.at(axis_id)) {
532 : DumpSubgraphNode(ss, graph, node, ssa_info, current_depth * kIndentSpaces);
533 : }
534 : }
535 : }
536 :
537 : // 闭合所有循环
538 : for (size_t i = 0; i < loop_axes_in_order.size(); ++i) {
539 : current_depth--;
540 : ss << std::string(current_depth * kIndentSpaces, ' ') << "}" << std::endl;
541 : }
542 : }
543 :
544 : /**
545 : * @brief 输出外层节点(loop_axis = kInvalidLoopAxis)
546 : */
547 : static void DumpOuterNodes(std::stringstream &ss, const ascir::Graph &graph,
548 : const std::map<int64_t, std::vector<af::AscNodePtr> > &nodes_by_loop_axis,
549 : const SSAMappingInfo &ssa_info) {
550 : if (nodes_by_loop_axis.count(kInvalidLoopAxis) == 0) {
551 : return;
552 : }
553 :
554 : for (auto node : nodes_by_loop_axis.at(kInvalidLoopAxis)) {
555 : DumpSubgraphNode(ss, graph, node, ssa_info, 2); // 2空格缩进
556 : }
557 : }
558 :
559 : /**
560 : * @brief 生成子图模式的循环执行视图
561 : */
562 : static std::string DumpSubgraphLoopExecution(const ascir::Graph &graph,
563 : const std::map<af::AxisId, std::string> &axis_id_to_name,
564 : const std::map<int64_t, af::Axis::Type> &axis_id_to_type) {
565 : std::stringstream ss;
566 :
567 : auto all_nodes = graph.GetAllNodes();
568 : SSAMappingInfo ssa_info = BuildSSAMapping(all_nodes);
569 :
570 : // 收集并排序 loop_axis
571 : auto loop_axes_in_order = CollectSubgraphLoopAxes(graph, axis_id_to_type);
572 :
573 : // 按 loop_axis 分组节点
574 : auto nodes_by_loop_axis = GroupNodesByLoopAxis(graph);
575 :
576 : // 生成嵌套循环并输出节点
577 : DumpNodesInLoops(ss, graph, loop_axes_in_order, nodes_by_loop_axis, axis_id_to_name, ssa_info);
578 :
579 : // 输出外层节点
580 : DumpOuterNodes(ss, graph, nodes_by_loop_axis, ssa_info);
581 :
582 : return ss.str();
583 : }
584 : } // namespace
585 :
586 : // =============================================================================
587 : // VIEW 1: Loop Execution 辅助函数实现
588 : // =============================================================================
589 :
590 : namespace {
591 : /**
592 : * @brief 检测图是否为子图
593 : */
594 : bool IsSubgraph(const std::string &graph_name) {
595 : return (graph_name.find("_VfSubgraph_") != std::string::npos || graph_name.find("_Subgraph_") != std::string::npos);
596 : }
597 :
598 : /**
599 : * @brief 收集所有被向量化的轴
600 : */
601 : std::set<int64_t> CollectVectorizedAxes(const ascir::Graph &graph) {
602 : std::set<int64_t> vectorized_axes;
603 : auto all_nodes = graph.GetAllNodes();
604 :
605 : for (auto node : all_nodes) {
606 : auto node_type = node->GetType();
607 : if (node_type == NodeType::kData || node_type == NodeType::kOutput) {
608 : continue;
609 : }
610 : if (!node->outputs().empty()) {
611 : auto &output_attr = node->outputs()[0]->attr;
612 : for (auto axis_id : output_attr.vectorized_axis) {
613 : vectorized_axes.insert(axis_id);
614 : }
615 : }
616 : }
617 :
618 : return vectorized_axes;
619 : }
620 :
621 : /**
622 : * @brief 生成原始 tensor 形状的注释
623 : */
624 : std::string GenerateOriginalShapesComment(const FunctionParams ¶ms) {
625 : std::stringstream ss;
626 :
627 : ss << "# Original tensor shapes:" << std::endl;
628 :
629 : // Data 参数
630 : if (!params.data_params.empty()) {
631 : // 按类型分组输入
632 : std::map<std::string, std::vector<std::string> > inputs_by_type;
633 : for (const auto &input : params.data_params) {
634 : auto dtype = ExtractDtypeFromTensorType(input.type);
635 : auto axes = ExtractAxisListFromTensorType(input.type);
636 : std::string full_type = dtype + axes;
637 : inputs_by_type[full_type].push_back(input.name);
638 : }
639 :
640 : for (const auto &entry : inputs_by_type) {
641 : ss << "# ";
642 : for (size_t i = 0; i < entry.second.size(); ++i) {
643 : if (i > 0) ss << ", ";
644 : ss << entry.second[i];
645 : }
646 : ss << ": " << ExtractDtypeFromTensorType(entry.first) << ExtractAxisListFromTensorType(entry.first) << std::endl;
647 : }
648 : }
649 :
650 : // Workspace 参数
651 : for (const auto ¶m : params.workspace_params) {
652 : ss << "# workspace: " << param.name << ": " << ExtractDtypeFromTensorType(param.type) << "[]" << std::endl;
653 : }
654 :
655 : // Output 参数
656 : for (const auto ¶m : params.output_params) {
657 : ss << "# output: " << param.name << ": " << ExtractDtypeFromTensorType(param.type) << "[]" << std::endl;
658 : }
659 :
660 : ss << "#" << std::endl;
661 :
662 : return ss.str();
663 : }
664 :
665 : /**
666 : * @brief 构建 Tile/Block 分解树
667 : */
668 : AxisTreeNode BuildAxisDecompositionTree(const af::AxisPtr &axis, const std::vector<af::AxisPtr> &all_axis,
669 : const std::set<int64_t> &merged_axes) {
670 : AxisTreeNode node;
671 : node.axis = axis;
672 : node.is_merge = (merged_axes.count(axis->id) > 0);
673 :
674 : // 找到所有直接从 axis 分解或合并出来的轴
675 : std::vector<af::AxisPtr> direct_derived;
676 : for (auto &target_axis : all_axis) {
677 : if (target_axis->id == axis->id) {
678 : continue;
679 : }
680 : if (!target_axis->from.empty()) {
681 : bool is_child = false;
682 : for (auto from_id : target_axis->from) {
683 : if (from_id == axis->id) {
684 : is_child = true;
685 : break;
686 : }
687 : }
688 : if (is_child) {
689 : direct_derived.push_back(target_axis);
690 : }
691 : }
692 : }
693 :
694 : // 按类型排序
695 : std::sort(direct_derived.begin(), direct_derived.end(), [](const af::AxisPtr &a, const af::AxisPtr &b) {
696 : return GetAxisTypePriority(a->type) < GetAxisTypePriority(b->type);
697 : });
698 :
699 : // 递归构建子树
700 : for (auto &derived : direct_derived) {
701 : node.children.push_back(BuildAxisDecompositionTree(derived, all_axis, merged_axes));
702 : }
703 :
704 : return node;
705 : }
706 :
707 : /**
708 : * @brief 输出 Tile/Block 分解树(递归)
709 : */
710 : void PrintAxisDecompositionTree(std::stringstream &ss, const AxisTreeNode &node, const std::string &prefix,
711 : const std::string &child_prefix) {
712 : ss << "# " << prefix << node.axis->name;
713 :
714 : if (node.children.empty()) {
715 : if (node.is_merge) {
716 : ss << " ⋈";
717 : }
718 : ss << std::endl;
719 : return;
720 : }
721 :
722 : // 判断是否是合并操作
723 : if (node.is_merge) {
724 : ss << "-⋈" << std::endl;
725 : } else {
726 : ss << "-" << std::endl;
727 : }
728 :
729 : for (size_t i = 0; i < node.children.size(); ++i) {
730 : bool is_last = (i == node.children.size() - 1);
731 : std::string connector = is_last ? "└->" : "┬->";
732 : std::string next_prefix = child_prefix + " " + connector;
733 : std::string next_child_prefix = child_prefix + (is_last ? " " : "│ ");
734 : PrintAxisDecompositionTree(ss, node.children[i], next_prefix, next_child_prefix);
735 : }
736 : }
737 :
738 : /**
739 : * @brief 生成 Tile/Block 分解的注释
740 : */
741 : std::string GenerateTileBlockDecompositionComment(const std::vector<af::AxisPtr> &all_axis) {
742 : std::stringstream ss;
743 : ss << "# Tile/Block decomposition:" << std::endl;
744 :
745 : // 收集所有涉及合并的轴
746 : std::set<int64_t> merged_axes;
747 : for (auto &axis : all_axis) {
748 : if (!axis->from.empty() && axis->from.size() > 1) {
749 : merged_axes.insert(axis->id);
750 : }
751 : }
752 :
753 : // 构建分解树并按树状图输出
754 : for (auto &axis : all_axis) {
755 : if (axis->type == af::Axis::Type::kAxisTypeOriginal) {
756 : AxisTreeNode root = BuildAxisDecompositionTree(axis, all_axis, merged_axes);
757 : if (root.children.empty()) {
758 : ss << "# " << axis->name << ": original (no tiling)" << std::endl;
759 : continue;
760 : }
761 :
762 : PrintAxisDecompositionTree(ss, root, "", "");
763 : }
764 : }
765 :
766 : ss << "#" << std::endl;
767 :
768 : return ss.str();
769 : }
770 :
771 : /**
772 : * @brief 生成函数签名
773 : */
774 : std::string GenerateFunctionSignature(const std::string &graph_name, const FunctionParams ¶ms) {
775 : std::stringstream ss;
776 :
777 : ss << "func @" << graph_name << "(";
778 :
779 : // 按 data, workspace, output 顺序排布参数
780 : bool first = true;
781 :
782 : // Data 参数
783 : for (const auto ¶m : params.data_params) {
784 : if (!first) ss << ", ";
785 : ss << "%" << param.name << ": " << param.type;
786 : first = false;
787 : }
788 :
789 : // Workspace 参数
790 : for (const auto ¶m : params.workspace_params) {
791 : if (!first) ss << ", ";
792 : ss << "%" << param.name << ": " << param.type;
793 : first = false;
794 : }
795 :
796 : // Output 参数
797 : for (const auto ¶m : params.output_params) {
798 : if (!first) ss << ", ";
799 : ss << "%" << param.name << ": " << param.type;
800 : first = false;
801 : }
802 :
803 : // 核函数无返回值
804 : ss << ") {" << std::endl;
805 :
806 : return ss.str();
807 : }
808 :
809 : /**
810 : * @brief 检查是否有节点设置了 loop_axis
811 : */
812 : bool HasLoopAxis(const ascir::Graph &graph) {
813 : auto all_nodes = graph.GetAllNodes();
814 :
815 : for (auto node : all_nodes) {
816 : auto node_type = node->GetType();
817 : if (node_type == NodeType::kData || node_type == NodeType::kOutput) {
818 : continue;
819 : }
820 : auto loop_axis = node->attr.sched.loop_axis;
821 : if (loop_axis != kInvalidLoopAxis) {
822 : return true;
823 : }
824 : }
825 :
826 : return false;
827 : }
828 :
829 : /**
830 : * @brief 收集所有需要循环的轴
831 : */
832 : /**
833 : * @brief 收集有 loop_axis 时的循环轴
834 : */
835 : static void CollectLoopAxesWithLoopAxis(const ascir::Graph &graph, const std::set<int64_t> &vectorized_axes,
836 : std::set<int64_t> &all_loop_axes) {
837 : auto all_nodes = graph.GetAllNodes();
838 :
839 : for (auto node : all_nodes) {
840 : auto node_type = node->GetType();
841 : if (node_type == NodeType::kData || node_type == NodeType::kOutput) {
842 : continue;
843 : }
844 :
845 : auto loop_axis = node->attr.sched.loop_axis;
846 : if (loop_axis != kInvalidLoopAxis) {
847 : auto &axis_list = node->attr.sched.axis;
848 : for (auto axis_id : axis_list) {
849 : if (axis_id == loop_axis) {
850 : break; // 到 loop_axis 为止
851 : }
852 : if (vectorized_axes.count(axis_id) == 0) {
853 : all_loop_axes.insert(axis_id);
854 : }
855 : }
856 : if (vectorized_axes.count(loop_axis) == 0) {
857 : all_loop_axes.insert(loop_axis);
858 : }
859 : }
860 : }
861 : }
862 :
863 : /**
864 : * @brief 收集无 loop_axis 时的循环轴
865 : */
866 : static void CollectLoopAxesWithoutLoopAxis(const ascir::Graph &graph, const std::set<int64_t> &vectorized_axes,
867 : std::set<int64_t> &all_loop_axes) {
868 : auto all_nodes = graph.GetAllNodes();
869 :
870 : for (auto node : all_nodes) {
871 : auto node_type = node->GetType();
872 : if (node_type == NodeType::kData || node_type == NodeType::kOutput) {
873 : continue;
874 : }
875 :
876 : auto &axis_list = node->attr.sched.axis;
877 : for (auto axis_id : axis_list) {
878 : if (vectorized_axes.count(axis_id) == 0) {
879 : all_loop_axes.insert(axis_id);
880 : }
881 : }
882 : }
883 : }
884 :
885 : /**
886 : * @brief 按轴类型优先级排序循环轴
887 : */
888 : static std::vector<int64_t> SortLoopAxesByPriority(const std::set<int64_t> &all_loop_axes,
889 : const std::map<int64_t, af::Axis::Type> &axis_id_to_type) {
890 : std::vector<int64_t> sorted_loop_axes(all_loop_axes.begin(), all_loop_axes.end());
891 : std::sort(sorted_loop_axes.begin(), sorted_loop_axes.end(), [&axis_id_to_type](int64_t a, int64_t b) {
892 : int32_t priority_a = GetAxisTypePriority(axis_id_to_type.at(a));
893 : int32_t priority_b = GetAxisTypePriority(axis_id_to_type.at(b));
894 : if (priority_a != priority_b) {
895 : return priority_a < priority_b;
896 : }
897 : return a < b;
898 : });
899 : return sorted_loop_axes;
900 : }
901 :
902 : /**
903 : * @brief 收集所有需要循环的轴
904 : */
905 : std::vector<int64_t> CollectLoopAxes(const ascir::Graph &graph, const std::set<int64_t> &vectorized_axes,
906 : const std::map<int64_t, af::Axis::Type> &axis_id_to_type) {
907 : std::set<int64_t> all_loop_axes;
908 : bool has_loop_axis = HasLoopAxis(graph);
909 : if (has_loop_axis) {
910 : CollectLoopAxesWithLoopAxis(graph, vectorized_axes, all_loop_axes);
911 : } else {
912 : CollectLoopAxesWithoutLoopAxis(graph, vectorized_axes, all_loop_axes);
913 : }
914 :
915 : return SortLoopAxesByPriority(all_loop_axes, axis_id_to_type);
916 : }
917 :
918 : /**
919 : * @brief 输出 Scalar 节点
920 : */
921 : void DumpScalarNode(std::stringstream &ss, const af::AscNodePtr &node, size_t indent_spaces, size_t topo_id) {
922 : ss << std::string(indent_spaces, ' ') << "%" << (topo_id + 1) << " = ";
923 :
924 : if (node->attr.ir_attr != nullptr) {
925 : std::string scalar_value;
926 : if (node->attr.ir_attr->GetAttrValue("value", scalar_value) == ge::GRAPH_SUCCESS) {
927 : ss << scalar_value << "f";
928 : } else {
929 : ss << "0.0f";
930 : }
931 : } else {
932 : ss << "0.0f";
933 : }
934 :
935 : ss << " # @" << node->GetName() << " (topo_id=" << topo_id << ")" << std::endl;
936 : }
937 :
938 : /**
939 : * @brief 获取 ExecuteCondition 的字符串表示
940 : */
941 : static std::string ExecuteConditionToString(af::ExecuteCondition condition) {
942 : switch (condition) {
943 : case af::ExecuteCondition::kNoCache:
944 : return "no_cache";
945 : case af::ExecuteCondition::kCacheBlockSplitFusedBroadcastAxis:
946 : return "cache_block_split_fused_brc_axis";
947 : case af::ExecuteCondition::kCacheBlockSplitOriginBroadcastAxis:
948 : return "cache_block_split_origin_brc_axis";
949 : case af::ExecuteCondition::kConditionInvalid:
950 : return "invalid";
951 : default:
952 : return "unknown";
953 : }
954 : }
955 :
956 : /**
957 : * @brief 获取 Store 节点写入目标的注释字符串
958 : */
959 : std::string GetStoreDestinationComment(const af::AscNodePtr &node) {
960 : if (node->GetType() != "Store" || node->GetAllOutDataAnchorsSize() == 0) {
961 : return "";
962 : }
963 : auto out_anchor = node->GetOutDataAnchor(0);
964 : if (out_anchor == nullptr) {
965 : return "";
966 : }
967 : for (const auto &peer_in_anchor : out_anchor->GetPeerInDataAnchorsPtr()) {
968 : auto peer_node = peer_in_anchor->GetOwnerNodeBarePtr();
969 : if (peer_node == nullptr) continue;
970 : auto dest_type = peer_node->GetType();
971 : if (dest_type == NodeType::kOutput) {
972 : return " → output: %" + peer_node->GetName();
973 : }
974 : if (dest_type == NodeType::kWorkspace) {
975 : return " → workspace: %" + peer_node->GetName();
976 : }
977 : }
978 : return "";
979 : }
980 :
981 : /**
982 : * @brief 输出单个节点的执行语句
983 : */
984 : void DumpNodeExecution(std::stringstream &ss, const ascir::Graph &graph, const af::AscNodePtr &node,
985 : const SSAMappingInfo &ssa_info, const std::map<af::AxisId, std::string> &axis_id_to_name,
986 : size_t indent_spaces) {
987 : std::string node_name = node->GetName();
988 : auto node_type = node->GetType();
989 : size_t topo_id = ssa_info.GetTopoId(node_name);
990 :
991 : // Scalar 节点特殊处理
992 : if (node_type == NodeType::kScalar) {
993 : DumpScalarNode(ss, node, indent_spaces, topo_id);
994 : return;
995 : }
996 :
997 : // ExecuteCondition 条件判断
998 : auto exec_condition = node->attr.sched.exec_condition;
999 : if (exec_condition != af::ExecuteCondition::kNoCache) {
1000 : ss << std::string(indent_spaces, ' ') << "if (" << ExecuteConditionToString(exec_condition) << ") {" << std::endl;
1001 : indent_spaces += kIndentSpaces;
1002 : }
1003 :
1004 : // 非Scalar节点的通用处理
1005 : ss << std::string(indent_spaces, ' ') << "%" << (topo_id + 1) << " = ascir.ops." << node_type << "(";
1006 :
1007 : // 输入参数
1008 : auto input_names = CollectInputNames(graph, node);
1009 : ss << FormatInputParams(input_names, ssa_info);
1010 : ss << ")";
1011 :
1012 : // 类型转换(Store 节点不需要显示)
1013 : if (!node->outputs().empty() && node_type != NodeType::kStore) {
1014 : auto &output_attr = node->outputs()[0]->attr;
1015 : auto vectorized_str = GetVectorizedAxesStr(graph, output_attr, axis_id_to_name);
1016 : ss << " → " << (vectorized_str.empty() ? GetDtypeString(output_attr.dtype) : vectorized_str);
1017 : }
1018 :
1019 : // 注释:节点名称 + topo_id + Store目标
1020 : ss << " # @" << node_name << " (topo_id=" << topo_id << ")";
1021 : ss << GetStoreDestinationComment(node);
1022 : ss << std::endl;
1023 :
1024 : // 关闭 ExecuteCondition 条件判断
1025 : if (exec_condition != af::ExecuteCondition::kNoCache) {
1026 : indent_spaces -= kIndentSpaces;
1027 : ss << std::string(indent_spaces, ' ') << "}" << std::endl;
1028 : }
1029 : }
1030 :
1031 : /**
1032 : * @brief 确定节点应该放置的循环深度
1033 : * @param node 节点对象
1034 : * @param has_loop_axis 图是否有 loop_axis
1035 : * @param loop_axis_to_depth loop_axis 到深度的映射
1036 : * @param sorted_loop_axes_size 排序后的 loop_axes 数量
1037 : * @param current_depth 当前深度
1038 : * @return 目标深度
1039 : */
1040 : static size_t DetermineNodeTargetDepth(const af::AscNodePtr &node, bool has_loop_axis,
1041 : const std::map<int64_t, size_t> &loop_axis_to_depth,
1042 : size_t sorted_loop_axes_size, size_t current_depth) {
1043 : auto node_type = node->GetType();
1044 : auto loop_axis = node->attr.sched.loop_axis;
1045 : bool is_scalar = (node_type == NodeType::kScalar);
1046 :
1047 : if (is_scalar) {
1048 : return current_depth;
1049 : } else if (has_loop_axis && loop_axis != kInvalidLoopAxis && loop_axis_to_depth.count(loop_axis) > 0) {
1050 : return loop_axis_to_depth.at(loop_axis);
1051 : } else if (!has_loop_axis) {
1052 : return sorted_loop_axes_size;
1053 : }
1054 : return 0;
1055 : }
1056 :
1057 : /**
1058 : * @brief 关闭不需要的循环
1059 : * @param ss 输出流
1060 : * @param current_depth 当前深度(会被修改)
1061 : * @param target_depth 目标深度
1062 : * @param opened_loops 已打开的循环集合(会被修改)
1063 : */
1064 : static void CloseUnneededLoops(std::stringstream &ss, size_t ¤t_depth, size_t target_depth,
1065 : std::set<int64_t> &opened_loops) {
1066 : while (current_depth > target_depth) {
1067 : current_depth--;
1068 : ss << std::string(current_depth * kIndentSpaces, ' ') << "}" << std::endl;
1069 : if (!opened_loops.empty()) {
1070 : auto it = opened_loops.end();
1071 : it--;
1072 : opened_loops.erase(it);
1073 : }
1074 : }
1075 : }
1076 :
1077 : /**
1078 : * @brief 打开需要的循环
1079 : * @param ss 输出流
1080 : * @param sorted_loop_axes 排序后的 loop_axes
1081 : * @param target_depth 目标深度
1082 : * @param axis_id_to_name axis_id 到 name 的映射
1083 : * @param current_depth 当前深度(会被修改)
1084 : * @param opened_loops 已打开的循环集合(会被修改)
1085 : */
1086 : static void OpenNeededLoops(std::stringstream &ss, const std::vector<int64_t> &sorted_loop_axes, size_t target_depth,
1087 : const std::map<af::AxisId, std::string> &axis_id_to_name, size_t ¤t_depth,
1088 : std::set<int64_t> &opened_loops) {
1089 : for (auto axis_id : sorted_loop_axes) {
1090 : if (opened_loops.count(axis_id) == 0) {
1091 : auto depth_it = std::find(sorted_loop_axes.begin(), sorted_loop_axes.end(), axis_id);
1092 : if (depth_it != sorted_loop_axes.end()) {
1093 : size_t axis_depth = std::distance(sorted_loop_axes.begin(), depth_it) + 1;
1094 : if (axis_depth <= target_depth) {
1095 : ss << std::string(current_depth * kIndentSpaces, ' ') << "for %" << axis_id_to_name.at(axis_id) << " in "
1096 : << axis_id_to_name.at(axis_id) << "_size {" << std::endl;
1097 : opened_loops.insert(axis_id);
1098 : current_depth++;
1099 : }
1100 : }
1101 : }
1102 : if (current_depth >= target_depth) {
1103 : break;
1104 : }
1105 : }
1106 : }
1107 :
1108 : /**
1109 : * @brief 输出常规图(非子图)的循环执行内容
1110 : * @param graph 图对象
1111 : * @param axis_id_to_name axis_id 到 name 的映射
1112 : * @param axis_id_to_type axis_id 到 type 的映射
1113 : * @param vectorized_axes 向量化轴集合
1114 : * @param ssa_info SSA 映射信息
1115 : * @return 循环执行内容的字符串
1116 : */
1117 : static std::string DumpRegularGraphLoopExecution(const ascir::Graph &graph,
1118 : const std::map<af::AxisId, std::string> &axis_id_to_name,
1119 : const std::map<int64_t, af::Axis::Type> &axis_id_to_type,
1120 : const std::set<int64_t> &vectorized_axes,
1121 : const SSAMappingInfo &ssa_info) {
1122 : std::stringstream ss;
1123 :
1124 : auto all_nodes = graph.GetAllNodes();
1125 : auto sorted_loop_axes = CollectLoopAxes(graph, vectorized_axes, axis_id_to_type);
1126 :
1127 : // 建立 loop_axis 到深度的映射
1128 : std::map<int64_t, size_t> loop_axis_to_depth;
1129 : for (size_t i = 0; i < sorted_loop_axes.size(); ++i) {
1130 : loop_axis_to_depth[sorted_loop_axes[i]] = i + 1;
1131 : }
1132 :
1133 : bool has_loop_axis = HasLoopAxis(graph);
1134 :
1135 : // 按拓扑序遍历节点,动态打开/关闭循环
1136 : std::set<int64_t> opened_loops;
1137 : size_t current_depth = 0;
1138 :
1139 : for (auto node : all_nodes) {
1140 : auto node_type = node->GetType();
1141 : if (node_type == NodeType::kData || node_type == NodeType::kOutput || node_type == NodeType::kWorkspace) {
1142 : continue;
1143 : }
1144 :
1145 : // 确定节点应该在哪个深度
1146 : size_t target_depth =
1147 : DetermineNodeTargetDepth(node, has_loop_axis, loop_axis_to_depth, sorted_loop_axes.size(), current_depth);
1148 :
1149 : // 关闭不需要的循环
1150 : CloseUnneededLoops(ss, current_depth, target_depth, opened_loops);
1151 :
1152 : // 打开需要的循环
1153 : OpenNeededLoops(ss, sorted_loop_axes, target_depth, axis_id_to_name, current_depth, opened_loops);
1154 :
1155 : // 输出节点
1156 : DumpNodeExecution(ss, graph, node, ssa_info, axis_id_to_name, current_depth * kIndentSpaces);
1157 : }
1158 :
1159 : // 闭合所有剩余循环
1160 : while (current_depth > 0) {
1161 : current_depth--;
1162 : ss << std::string(current_depth * kIndentSpaces, ' ') << "}" << std::endl;
1163 : }
1164 :
1165 : return ss.str();
1166 : }
1167 : } // namespace
1168 :
1169 : // =============================================================================
1170 : // VIEW 1: Loop Execution
1171 : // =============================================================================
1172 :
1173 : std::string DumpLoopExecutionView(const ascir::Graph &graph, const DumpContext &ctx) {
1174 : std::stringstream ss;
1175 :
1176 : // 获取基本信息
1177 : std::string graph_name = graph.GetName();
1178 : bool is_subgraph = IsSubgraph(graph_name);
1179 :
1180 : // 收集向量化轴
1181 : std::set<int64_t> vectorized_axes = CollectVectorizedAxes(graph);
1182 :
1183 : // 生成说明性注释
1184 : ss << GenerateOriginalShapesComment(ctx.func_params);
1185 :
1186 : // 生成 Tile/Block 分解注释(仅非子图)
1187 : if (!is_subgraph) {
1188 : ss << GenerateTileBlockDecompositionComment(ctx.all_axis);
1189 : }
1190 :
1191 : // 生成函数签名(无返回值,按 data/workspace/output 顺序)
1192 : ss << GenerateFunctionSignature(graph_name, ctx.func_params);
1193 :
1194 : // 生成函数体
1195 : if (is_subgraph) {
1196 : // 子图模式
1197 : ss << DumpSubgraphLoopExecution(graph, ctx.axis_id_to_name, ctx.axis_id_to_type);
1198 : } else {
1199 : // 非子图模式:按照 loop_axis 分层输出节点
1200 : ss << DumpRegularGraphLoopExecution(graph, ctx.axis_id_to_name, ctx.axis_id_to_type, vectorized_axes,
1201 : ctx.ssa_mapping);
1202 : }
1203 :
1204 : ss << "}" << std::endl;
1205 :
1206 : return ss.str();
1207 : }
1208 :
1209 : // =============================================================================
1210 : // VIEW 2: Graph Structure 辅助函数
1211 : // =============================================================================
1212 :
1213 : /**
1214 : * @brief 获取 Position 的字符串表示
1215 : */
1216 : std::string PositionToString(af::Position position) {
1217 : switch (position) {
1218 : case af::Position::kPositionVecIn:
1219 : return "VECIN";
1220 : case af::Position::kPositionVecCalc:
1221 : return "VECCALC";
1222 : case af::Position::kPositionVecOut:
1223 : return "VECOUT";
1224 : case af::Position::kPositionGM:
1225 : return "GM";
1226 : default:
1227 : return "UNKNOWN";
1228 : }
1229 : }
1230 :
1231 : /**
1232 : * @brief 获取 MemHardware 的字符串表示
1233 : */
1234 : std::string MemHardwareToString(af::MemHardware hardware) {
1235 : switch (hardware) {
1236 : case af::MemHardware::kMemHardwareGM:
1237 : return "GM";
1238 : case af::MemHardware::kMemHardwareUB:
1239 : return "UB";
1240 : default:
1241 : return "UNKNOWN";
1242 : }
1243 : }
1244 :
1245 : // =============================================================================
1246 : // VIEW 2: Graph Structure
1247 : // =============================================================================
1248 :
1249 : namespace {
1250 : /**
1251 : * @brief 输出形状信息 (axis, repeats, strides)
1252 : */
1253 : static std::stringstream &OutputShapeStr(std::stringstream &ss, const ascir::Graph &graph,
1254 : const af::AscTensorAttr &output_attr,
1255 : const std::map<af::AxisId, std::string> &axis_id_to_name) {
1256 : (void)graph;
1257 : // 输出 axis 列表
1258 : if (!output_attr.axis.empty()) {
1259 : ss << std::string(kTensorPropertyIndent, ' ') << ".axis = {";
1260 : for (size_t i = 0; i < output_attr.axis.size(); ++i) {
1261 : if (i > 0) ss << ", ";
1262 : auto it = axis_id_to_name.find(output_attr.axis[i]);
1263 : ss << (it != axis_id_to_name.end() ? it->second : "unknown");
1264 : }
1265 : ss << "}" << std::endl;
1266 : }
1267 :
1268 : // 输出 repeats
1269 : if (!output_attr.repeats.empty()) {
1270 : ss << std::string(kTensorPropertyIndent, ' ') << ".repeats = (";
1271 : for (size_t i = 0; i < output_attr.repeats.size(); ++i) {
1272 : if (i > 0) ss << ", ";
1273 : ss << af::SymbolicUtils::ToString(output_attr.repeats[i]);
1274 : }
1275 : ss << ")" << std::endl;
1276 : }
1277 :
1278 : // 输出 strides
1279 : if (!output_attr.strides.empty()) {
1280 : ss << std::string(kTensorPropertyIndent, ' ') << ".strides = (";
1281 : for (size_t i = 0; i < output_attr.strides.size(); ++i) {
1282 : if (i > 0) ss << ", ";
1283 : ss << af::SymbolicUtils::ToString(output_attr.strides[i]);
1284 : }
1285 : ss << ")" << std::endl;
1286 : }
1287 :
1288 : return ss;
1289 : }
1290 :
1291 : /**
1292 : * @brief 输出 vectorized 信息
1293 : */
1294 : static std::stringstream &OutputVectorizedStr(std::stringstream &ss, const ascir::Graph &graph,
1295 : const af::AscTensorAttr &output_attr,
1296 : const std::map<af::AxisId, std::string> &axis_id_to_name) {
1297 : (void)graph;
1298 : if (!output_attr.vectorized_axis.empty()) {
1299 : ss << std::string(kTensorPropertyIndent, ' ') << ".vectorized = {";
1300 : for (size_t i = 0; i < output_attr.vectorized_axis.size(); ++i) {
1301 : if (i > 0) ss << ", ";
1302 : auto it = axis_id_to_name.find(output_attr.vectorized_axis[i]);
1303 : std::string axis_name = (it != axis_id_to_name.end()) ? it->second : "unknown";
1304 : ss << axis_name << ":";
1305 : if (i < output_attr.vectorized_strides.size()) {
1306 : ss << af::SymbolicUtils::ToString(output_attr.vectorized_strides[i]);
1307 : }
1308 : }
1309 : ss << "}" << std::endl;
1310 : }
1311 : return ss;
1312 : }
1313 :
1314 : /**
1315 : * @brief 输出内存信息
1316 : */
1317 : static std::stringstream &OutputMemStr(std::stringstream &ss, const af::AscTensorAttr &output_attr, bool verbose) {
1318 : if (!verbose && (output_attr.mem.alloc_type != af::AllocType::kAllocTypeQueue) &&
1319 : (output_attr.mem.alloc_type != af::AllocType::kAllocTypeBuffer)) {
1320 : return ss;
1321 : }
1322 :
1323 : std::string pos_str = PositionToString(output_attr.mem.position);
1324 : std::string hardware_str = MemHardwareToString(output_attr.mem.hardware);
1325 :
1326 : ss << std::string(kTensorPropertyIndent, ' ') << ".mem = " << hardware_str << "[";
1327 :
1328 : // 输出 tensor_id(如果存在)
1329 : if (output_attr.mem.tensor_id != af::kIdNone) {
1330 : ss << "tensor_id=" << output_attr.mem.tensor_id << ", ";
1331 : }
1332 :
1333 : if (output_attr.mem.alloc_type == af::AllocType::kAllocTypeQueue) {
1334 : const auto &que = output_attr.que;
1335 : ss << "que_id=" << que.id;
1336 : if (que.buf_num > 0) {
1337 : ss << ", buf_num=" << que.buf_num;
1338 : }
1339 : if (output_attr.mem.reuse_id >= 0) {
1340 : ss << ", reuse_id=" << output_attr.mem.reuse_id;
1341 : }
1342 : ss << ", depth=" << que.depth << ", pos=" << pos_str;
1343 : } else if (output_attr.mem.alloc_type == af::AllocType::kAllocTypeBuffer) {
1344 : ss << "buf_id=" << output_attr.buf.id;
1345 : if (output_attr.mem.reuse_id >= 0) {
1346 : ss << ", reuse_id=" << output_attr.mem.reuse_id;
1347 : }
1348 : ss << ", pos=" << pos_str;
1349 : } else {
1350 : ss << "pos=" << pos_str;
1351 : }
1352 :
1353 : ss << "]" << std::endl;
1354 :
1355 : return ss;
1356 : }
1357 : } // namespace
1358 :
1359 : // =============================================================================
1360 : // VIEW 2: Graph Structure 辅助函数实现
1361 : // =============================================================================
1362 :
1363 : /**
1364 : * @brief 输出 Size 变量列表
1365 : */
1366 : void DumpSizeVars(std::stringstream &ss, const ascir::Graph &graph) {
1367 : ss << "Sizes:" << std::endl;
1368 : auto all_size_var = graph.GetAllSizeVar();
1369 :
1370 : for (const auto &size_var : all_size_var) {
1371 : if (size_var->expr.GetExprType() == af::ExprType::kExprVariable) {
1372 : ss << " " << size_var->expr.Str().get() << ": VAR" << std::endl;
1373 : } else {
1374 : ss << " " << size_var->name << ": " << af::SymbolicUtils::ToString(size_var->expr) << std::endl;
1375 : }
1376 : }
1377 : }
1378 :
1379 : /**
1380 : * @brief 输出 Axis 列表
1381 : */
1382 : void DumpAxisList(std::stringstream &ss, const ascir::Graph &graph,
1383 : const std::map<af::AxisId, std::string> &axis_id_to_name) {
1384 : ss << std::endl << "Axis:" << std::endl;
1385 : auto all_axis = graph.GetAllAxis();
1386 :
1387 : for (auto &axis : all_axis) {
1388 : ss << " " << axis->name << "(" << axis->id << ") : ";
1389 : ss << GetAxisTypeSuffix(axis->type);
1390 : ss << ", size:" << af::SymbolicUtils::ToString(axis->size);
1391 :
1392 : if (!axis->from.empty()) {
1393 : ss << ", from: {";
1394 : for (size_t i = 0; i < axis->from.size(); ++i) {
1395 : if (i > 0) ss << ", ";
1396 : auto it = axis_id_to_name.find(axis->from[i]);
1397 : ss << (it != axis_id_to_name.end() ? it->second : "unknown");
1398 : }
1399 : ss << "}";
1400 : }
1401 :
1402 : ss << std::endl;
1403 : }
1404 : }
1405 :
1406 : /**
1407 : * @brief 输出节点调度属性(axis, loop_axis)
1408 : */
1409 : static void DumpNodeSchedProps(std::stringstream &ss, const af::AscNodePtr &node,
1410 : const std::map<af::AxisId, std::string> &axis_id_to_name) {
1411 : // 输出 axis 列表
1412 : if (!node->attr.sched.axis.empty()) {
1413 : ss << std::string(kPropertyIndent, ' ') << ".axis = {";
1414 : for (size_t i = 0; i < node->attr.sched.axis.size(); ++i) {
1415 : if (i > 0) ss << ", ";
1416 : auto it = axis_id_to_name.find(node->attr.sched.axis[i]);
1417 : ss << (it != axis_id_to_name.end() ? it->second : "unknown");
1418 : }
1419 : ss << "}" << std::endl;
1420 : }
1421 :
1422 : // 输出 loop_axis
1423 : if (node->attr.sched.loop_axis >= 0) {
1424 : auto it = axis_id_to_name.find(node->attr.sched.loop_axis);
1425 : ss << std::string(kPropertyIndent, ' ') << ".loop_axis = " << (it != axis_id_to_name.end() ? it->second : "unknown")
1426 : << std::endl;
1427 : }
1428 :
1429 : // 输出 exec_condition(只显示非默认值)
1430 : if (node->attr.sched.exec_condition != af::ExecuteCondition::kNoCache) {
1431 : ss << std::string(kPropertyIndent, ' ')
1432 : << ".exec_condition = " << ExecuteConditionToString(node->attr.sched.exec_condition) << std::endl;
1433 : }
1434 :
1435 : const auto &tmp_buffers = node->attr.tmp_buffers;
1436 : if (!tmp_buffers.empty()) {
1437 : ss << std::string(kPropertyIndent, ' ') << ".tmp_buf = {";
1438 : for (size_t i = 0; i < tmp_buffers.size(); ++i) {
1439 : if (i > 0) ss << ", ";
1440 : ss << "{id=" << tmp_buffers[i].id << ", size=" << af::SymbolicUtils::ToString(tmp_buffers[i].buf_desc.size)
1441 : << ", life_cycle=" << tmp_buffers[i].buf_desc.life_time_axis_id << "}";
1442 : }
1443 : ss << "}" << std::endl;
1444 : }
1445 : }
1446 :
1447 : /**
1448 : * @brief 输出节点的 ir_attr 属性
1449 : */
1450 : static void DumpNodeIrAttr(std::stringstream &ss, const af::AscNodePtr &node) {
1451 : auto &ir_attr = node->GetOpDesc()->GetAttrsGroup<af::AscNodeAttr>()->ir_attr;
1452 : if (ir_attr != nullptr) {
1453 : ascendc_ir::proto::AscIrAttrDef asc_ir_attr_def;
1454 : (void)ir_attr->Serialize(asc_ir_attr_def);
1455 : if (!asc_ir_attr_def.attr().empty()) {
1456 : for (const auto &pair : asc_ir_attr_def.attr()) {
1457 : ss << std::string(kPropertyIndent, ' ') << ".ir_attr." << pair.first << " = " << pair.second.ShortDebugString()
1458 : << std::endl;
1459 : }
1460 : }
1461 : }
1462 : }
1463 :
1464 : /**
1465 : * @brief 输出节点输入
1466 : */
1467 : static void DumpNodeInputs(std::stringstream &ss, const ascir::Graph &graph, const af::AscNodePtr &node) {
1468 : auto input_names = CollectInputNames(graph, node);
1469 : if (input_names.empty()) {
1470 : return;
1471 : }
1472 :
1473 : // 检查是否全部为 nil
1474 : bool all_nil = true;
1475 : for (const auto &name : input_names) {
1476 : if (name != "nil") {
1477 : all_nil = false;
1478 : break;
1479 : }
1480 : }
1481 : if (all_nil) {
1482 : return;
1483 : }
1484 :
1485 : ss << std::string(kPropertyIndent, ' ') << ".x = {";
1486 : for (size_t i = 0; i < input_names.size(); ++i) {
1487 : if (i > 0) ss << ", ";
1488 : if (input_names[i] != "nil") {
1489 : ss << input_names[i];
1490 : }
1491 : }
1492 : ss << "}" << std::endl;
1493 : }
1494 :
1495 : /**
1496 : * @brief 输出节点输出
1497 : */
1498 : static void DumpNodeOutputs(std::stringstream &ss, const ascir::Graph &graph, const af::AscNodePtr &node,
1499 : const std::map<af::AxisId, std::string> &axis_id_to_name, bool verbose, bool is_subgraph) {
1500 : size_t output_count = node->outputs().size();
1501 : for (size_t i = 0; i < output_count; ++i) {
1502 : auto &output_attr = node->outputs()[i]->attr;
1503 :
1504 : // 构建 tensor 名称:多输出用 y[0],单输出用 y
1505 : std::string tensor_name = (output_count > 1) ? ("y[" + std::to_string(i) + "]") : "y";
1506 : ss << std::string(kPropertyIndent, ' ') << "." << tensor_name << ": " << GetDtypeString(output_attr.dtype)
1507 : << std::endl;
1508 :
1509 : // 输出形状、向量化信息
1510 : OutputShapeStr(ss, graph, output_attr, axis_id_to_name);
1511 : OutputVectorizedStr(ss, graph, output_attr, axis_id_to_name);
1512 :
1513 : // mem 信息 - 仅非子图显示
1514 : if (!is_subgraph) {
1515 : OutputMemStr(ss, output_attr, verbose);
1516 : }
1517 : }
1518 : }
1519 :
1520 : /**
1521 : * @brief 输出单个节点的详细信息
1522 : */
1523 : void DumpNodeDetails(std::stringstream &ss, const ascir::Graph &graph, const af::AscNodePtr &node, size_t idx,
1524 : const std::map<af::AxisId, std::string> &axis_id_to_name, bool verbose, bool is_subgraph) {
1525 : // 节点名和类型
1526 : ss << " [" << idx << "] " << node->GetName() << " : ascir.ops." << node->GetType() << std::endl;
1527 :
1528 : // 输出调度属性
1529 : DumpNodeSchedProps(ss, node, axis_id_to_name);
1530 :
1531 : // 输出 ir_attr 属性
1532 : DumpNodeIrAttr(ss, node);
1533 :
1534 : // 输出输入
1535 : DumpNodeInputs(ss, graph, node);
1536 :
1537 : // 输出输出
1538 : DumpNodeOutputs(ss, graph, node, axis_id_to_name, verbose, is_subgraph);
1539 : }
1540 :
1541 : std::string DumpGraphStructureView(const ascir::Graph &graph, const DumpContext &ctx, bool verbose, bool is_subgraph) {
1542 : std::stringstream ss;
1543 :
1544 : // Header
1545 : ss << "Graph: " << graph.GetName() << std::endl;
1546 :
1547 : // Sizes
1548 : DumpSizeVars(ss, graph);
1549 :
1550 : // Axis
1551 : DumpAxisList(ss, graph, ctx.axis_id_to_name);
1552 :
1553 : // Nodes
1554 : ss << std::endl << "Nodes:" << std::endl;
1555 : size_t idx = 0UL;
1556 :
1557 : for (auto node : graph.GetAllNodes()) {
1558 : DumpNodeDetails(ss, graph, node, idx++, ctx.axis_id_to_name, verbose, is_subgraph);
1559 : }
1560 :
1561 : return ss.str();
1562 : }
1563 :
1564 : namespace {
1565 : void CollectQueueInfo(const af::AscNodePtr &node, size_t topo_id, std::map<int32_t, dumper::QueueInfo> &queues) {
1566 : if (node->outputs().empty()) {
1567 : return;
1568 : }
1569 : auto &output_attr = node->outputs()[0]->attr;
1570 : auto &mem = output_attr.mem;
1571 : if (mem.alloc_type != af::AllocType::kAllocTypeQueue) {
1572 : return;
1573 : }
1574 :
1575 : int32_t que_id = output_attr.que.id;
1576 : if (queues.find(que_id) == queues.end()) {
1577 : dumper::QueueInfo info;
1578 : info.que_id = que_id;
1579 : info.depth = output_attr.que.depth;
1580 : info.buf_num = static_cast<int32_t>(output_attr.que.buf_num);
1581 : info.position = "TPosition::" + PositionToString(mem.position);
1582 : queues[que_id] = info;
1583 : }
1584 : queues[que_id].nodes.push_back({topo_id, node->GetName(), static_cast<int32_t>(mem.reuse_id), ""});
1585 : }
1586 :
1587 : void CollectBufferInfo(const af::AscNodePtr &node, size_t topo_id, std::map<int32_t, dumper::BufferInfo> &buffers) {
1588 : if (node->outputs().empty()) {
1589 : return;
1590 : }
1591 : auto &output_attr = node->outputs()[0]->attr;
1592 : auto &mem = output_attr.mem;
1593 : if (mem.alloc_type != af::AllocType::kAllocTypeBuffer) {
1594 : return;
1595 : }
1596 :
1597 : int32_t buf_id = output_attr.buf.id;
1598 : if (buffers.find(buf_id) == buffers.end()) {
1599 : dumper::BufferInfo info;
1600 : info.buf_id = buf_id;
1601 : buffers[buf_id] = info;
1602 : }
1603 : buffers[buf_id].nodes.push_back({topo_id, node->GetName(), "", false, 0});
1604 : }
1605 :
1606 : std::string GetTmpBufSizeStr(const af::TmpBufDesc &buf_desc) {
1607 : if (buf_desc.size.GetExprType() == af::ExprType::kExprConstantRation) {
1608 : int64_t val = 0;
1609 : if (buf_desc.size.GetConstValue(val)) {
1610 : return std::to_string(val);
1611 : }
1612 : }
1613 : return af::SymbolicUtils::ToString(buf_desc.size);
1614 : }
1615 :
1616 : void CollectTmpBufferInfo(const af::AscNodePtr &node, size_t topo_id, std::map<int32_t, dumper::BufferInfo> &buffers) {
1617 : const auto &tmp_buffers = node->attr.tmp_buffers;
1618 : for (size_t i = 0; i < tmp_buffers.size(); ++i) {
1619 : const auto &tmp_buf = tmp_buffers[i];
1620 : int32_t buf_id = static_cast<int32_t>(tmp_buf.id);
1621 : if (buf_id < 0) {
1622 : continue;
1623 : }
1624 :
1625 : if (buffers.find(buf_id) == buffers.end()) {
1626 : dumper::BufferInfo info;
1627 : info.buf_id = buf_id;
1628 : buffers[buf_id] = info;
1629 : }
1630 : std::string size_str = GetTmpBufSizeStr(tmp_buf.buf_desc);
1631 : buffers[buf_id].nodes.push_back({topo_id, node->GetName(), size_str, true, static_cast<int32_t>(i)});
1632 : }
1633 : }
1634 :
1635 : /**
1636 : * @brief 收集 Queue 和 Buffer 信息
1637 : */
1638 : void CollectMemoryInfo(const ascir::Graph &graph, std::map<int32_t, dumper::QueueInfo> &queues,
1639 : std::map<int32_t, dumper::BufferInfo> &buffers) {
1640 : size_t topo_id = 0;
1641 : for (auto node : graph.GetAllNodes()) {
1642 : auto node_type = node->GetType();
1643 : if (node_type != NodeType::kData && node_type != NodeType::kOutput && node_type != NodeType::kWorkspace) {
1644 : CollectQueueInfo(node, topo_id, queues);
1645 : CollectBufferInfo(node, topo_id, buffers);
1646 : CollectTmpBufferInfo(node, topo_id, buffers);
1647 : }
1648 : topo_id++;
1649 : }
1650 : }
1651 :
1652 : /**
1653 : * @brief 输出 Queues 部分
1654 : */
1655 : void DumpQueues(std::stringstream &ss, const std::map<int32_t, dumper::QueueInfo> &queues) {
1656 : ss << "# Queues (" << queues.size() << " queues)" << std::endl;
1657 : ss << std::endl;
1658 :
1659 : for (auto &entry : queues) {
1660 : auto &info = entry.second;
1661 : ss << "Queue " << info.que_id << " [" << info.position;
1662 : if (info.buf_num > 0) {
1663 : ss << ", buf_num=" << info.buf_num;
1664 : }
1665 : ss << ", depth=" << info.depth << "]:" << std::endl;
1666 :
1667 : // 按 reuse_id 分组
1668 : std::map<int32_t, std::vector<dumper::QueueNodeInfo> > reuse_groups;
1669 : for (auto &node_info : info.nodes) {
1670 : reuse_groups[node_info.reuse_id].push_back(node_info);
1671 : }
1672 :
1673 : // 输出每个 reuse 组
1674 : for (auto &reuse_entry : reuse_groups) {
1675 : auto &nodes = reuse_entry.second;
1676 : for (size_t i = 0; i < nodes.size(); ++i) {
1677 : ss << " [" << nodes[i].topo_id << "] " << nodes[i].node_name << ".y" << std::endl;
1678 : }
1679 : }
1680 : ss << std::endl;
1681 : }
1682 : }
1683 :
1684 : /**
1685 : * @brief 输出 Buffers 部分
1686 : */
1687 : void DumpBuffers(std::stringstream &ss, const std::map<int32_t, dumper::BufferInfo> &buffers) {
1688 : ss << "# Buffers (" << buffers.size() << " buffers)" << std::endl;
1689 : ss << std::endl;
1690 :
1691 : for (auto &entry : buffers) {
1692 : auto &info = entry.second;
1693 : ss << "Buffer " << info.buf_id << ":" << std::endl;
1694 :
1695 : // 按 topo_id 排序,tmpbuf 排在普通节点之后
1696 : auto sorted_nodes = info.nodes;
1697 : std::sort(sorted_nodes.begin(), sorted_nodes.end(),
1698 : [](const dumper::BufferNodeInfo &a, const dumper::BufferNodeInfo &b) {
1699 : if (a.is_tmpbuf != b.is_tmpbuf) {
1700 : return !a.is_tmpbuf; // 普通 buffer 在前
1701 : }
1702 : if (a.topo_id != b.topo_id) {
1703 : return a.topo_id < b.topo_id;
1704 : }
1705 : return a.tmpbuf_idx < b.tmpbuf_idx;
1706 : });
1707 :
1708 : for (auto &node_info : sorted_nodes) {
1709 : ss << " [" << node_info.topo_id << "] " << node_info.node_name;
1710 : if (node_info.is_tmpbuf) {
1711 : ss << ".tmpbuf[" << node_info.tmpbuf_idx << "]";
1712 : // tmpbuf 显示 size
1713 : if (!node_info.size_str.empty()) {
1714 : ss << " # size:" << node_info.size_str;
1715 : }
1716 : } else {
1717 : ss << ".y";
1718 : }
1719 : ss << std::endl;
1720 : }
1721 : ss << std::endl;
1722 : }
1723 : }
1724 : } // namespace
1725 :
1726 : std::string DumpMemoryLayoutView(const ascir::Graph &graph, bool verbose) {
1727 : if (!verbose) {
1728 : return "";
1729 : }
1730 :
1731 : std::stringstream ss;
1732 :
1733 : // 收集内存信息
1734 : std::map<int32_t, QueueInfo> queues;
1735 : std::map<int32_t, BufferInfo> buffers;
1736 : CollectMemoryInfo(graph, queues, buffers);
1737 :
1738 : // 输出 Queues 和 Buffers
1739 : DumpQueues(ss, queues);
1740 : DumpBuffers(ss, buffers);
1741 :
1742 : return ss.str();
1743 : }
1744 :
1745 : std::string DumpGraphText(const ascir::Graph &graph, bool verbose, bool is_subgraph) {
1746 : std::stringstream ss;
1747 :
1748 : // 构建 Dump 上下文(只获取一次数据,避免重复计算)
1749 : DumpContext ctx = BuildDumpContext(graph);
1750 : // Header
1751 : ss << "================================================================================" << std::endl;
1752 : ss << "Graph: " << graph.GetName() << std::endl;
1753 : ss << "================================================================================" << std::endl;
1754 : ss << std::endl;
1755 :
1756 : // VIEW 1: Loop Execution
1757 : ss << "--------------------------------------------------------------------------------" << std::endl;
1758 : ss << "VIEW 1: Loop Execution" << std::endl;
1759 : ss << "--------------------------------------------------------------------------------" << std::endl;
1760 : ss << DumpLoopExecutionView(graph, ctx);
1761 : ss << std::endl;
1762 :
1763 : // VIEW 2: Graph Structure
1764 : ss << "--------------------------------------------------------------------------------" << std::endl;
1765 : ss << "VIEW 2: Graph Structure" << std::endl;
1766 : ss << "--------------------------------------------------------------------------------" << std::endl;
1767 : ss << DumpGraphStructureView(graph, ctx, verbose, is_subgraph);
1768 : ss << std::endl;
1769 :
1770 : // VIEW 3: Memory Layout (子图不显示,非子图仅在 verbose=true 时显示)
1771 : if (!is_subgraph) {
1772 : auto memory_layout = DumpMemoryLayoutView(graph, verbose);
1773 : if (!memory_layout.empty()) {
1774 : ss << "--------------------------------------------------------------------------------" << std::endl;
1775 : ss << "VIEW 3: Memory Layout" << std::endl;
1776 : ss << "--------------------------------------------------------------------------------" << std::endl;
1777 : ss << memory_layout;
1778 : ss << std::endl;
1779 : }
1780 : }
1781 :
1782 : ss << "================================================================================" << std::endl;
1783 : ss << "End of Dump" << std::endl;
1784 : ss << "================================================================================" << std::endl;
1785 :
1786 : return ss.str();
1787 : }
1788 : } // namespace dumper
1789 : } // namespace ascir
|