LCOV - code coverage report
Current view: top level - ut/autofuse/ascir/meta - ascendc_graph_txt_dumper.cpp Coverage Total Hit
Test: CHG Lines: 100.0 % 6 6
Test Date: 2026-08-24 14:48:59
Legend: Lines:     hit not hit

            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            9 :     const auto axis_id = attr.axis[i];
     145              : 
     146              :     // 如果是 repeats,输出大小
     147              :     if (i < attr.repeats.size()) {
     148            9 :       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          106 :     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          396 :     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          396 :     const auto peer_out_anchor = in_anchor->GetPeerOutAnchor();
     246              :     if (peer_out_anchor == nullptr) {
     247              :       input_names.push_back("nil");
     248              :     } else {
     249          792 :       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 &params) {
     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 &param : params.workspace_params) {
     652              :     ss << "#   workspace: " << param.name << ": " << ExtractDtypeFromTensorType(param.type) << "[]" << std::endl;
     653              :   }
     654              : 
     655              :   // Output 参数
     656              :   for (const auto &param : 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 &params) {
     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 &param : params.data_params) {
     784              :     if (!first) ss << ", ";
     785              :     ss << "%" << param.name << ": " << param.type;
     786              :     first = false;
     787              :   }
     788              : 
     789              :   // Workspace 参数
     790              :   for (const auto &param : params.workspace_params) {
     791              :     if (!first) ss << ", ";
     792              :     ss << "%" << param.name << ": " << param.type;
     793              :     first = false;
     794              :   }
     795              : 
     796              :   // Output 参数
     797              :   for (const auto &param : 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 &current_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 &current_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
        

Generated by: LCOV version 2.3.2-1