有。我的建议是:不要把前端做成“AscendC 源码直接推导完整后端 IR”的编译器,而是做成一个“证据驱动的行为提取器”。 核心链路是: AscendC 源码/编译产物 + CPU-debug 或运行时观测 + host launch 参数 + 可替换 topology ↓ Kernel Behavior IR ↓ ns-3-ub / 其他仿真器输入 一、前端分成四层 1. Source Observer 负责识别和记录源码中的语义动作: GM -> UB UB -> GM GM flag record/wait PipeBarrier / SyncAll queue alloc/enqueue/dequeue/free remote read/write notify record/wait fence/drain 最推荐的方式是给这些“语义边界”加轻量 wrapper 或 site ID。例如: OBS_DATA_COPY("copy_input", ub, gm, copy_params); OBS_FLAG_WAIT("wait_peer", flag_id); OBS_QUEUE_ENQUEUE("enqueue_send", queue_id, buffer_id); 不用给加法、乘法等计算指令加标记。只在计算会影响 buffer 生命周期、通信开始条件或同步关系时,保留一个 opaque 的 local_work 阶段。 对于没有加 wrapper 的代码,再使用 LLVM intrinsic、DebugLoc、已注册 descriptor 做自动识别。也就是说: 显式 site ID 是主证据 LLVM 分析是静态校验和补充恢复 完全依赖 C++ AST 或 LLVM 自动猜出所有 AscendC 语义,面对模板、宏、inline API 和优化后代码会比较脆弱。 2. Launch Binder 源码和 LLVM 不能单独证明一次 kernel 实际怎么启动,所以需要一个独立的 launch 输入: { "kernel": "AivKernel", "engine": "aiv", "entry_symbol": "foo_kernel", "num_blocks": 8, "stream_id": 3, "rank_id": 1, "rank_size": 8, "arguments": { "input": {"role": "input", "buffer": "gm_input"}, "output": {"role": "output", "buffer": "gm_output"}, "workspace": {"role": "workspace", "buffer": "workspace0"} }, "tiling": { "tile_count": 32, "tile_bytes": 4096 } } 这里显式提供: - kernel symbol; - AIV/AICPU/custom engine; - numBlocks; - rank、rank size; - stream; - 参数角色; - GM buffer; - workspace; - tiling 和运行时 shape。 这样可以避免从后端参数类型或 LLVM entry 参数反推“哪个指针是什么”。 3. Topology Binder 拓扑不要直接写进 kernel 行为里,而是单独输入: ranks: - rank: 0 device: device0 - rank: 1 device: device1 links: - src: device0 dst: device1 bandwidth: ... latency: ... channels: - logical_channel: ch0 src_rank: 0 dst_rank: 1 physical_path: link0 kernel 行为里只保存逻辑信息: peer_rank = 1 logical_channel = ch0 拓扑绑定阶段再决定它经过哪个 Jetty、TP、端口或链路。这样同一个自定义 kernel 行为可以换不同 topology 重放。 但 HCCL 有一个例外:如果拓扑变化导致 HCCL 重新选择算法、channel 数或 kernel,那么这不是同一个行为实例,需要重新采集对应的 launch/artifact 证据。 4. Normalizer 把不同来源统一成仿真器无关的行为表示: 源码 wrapper LLVM artifact CPU-debug HCOMM callback host launch topology ↓ 统一 action + dependency 二、我建议的输出不是一个文件,而是三个逻辑结果 第一层是拓扑无关的 KernelBehaviorIR: kernel ├── phases ├── actions ├── dependencies └── evidence 第二层是绑定了具体 launch 的 BoundExecutionIR: rank 0 ├── block 0 actions ├── block 1 actions └── ... rank 1 ├── block 0 actions └── ... 第三层才是面向 ns-3-ub 的投影: remote_write -> typed Stars request flag_wait -> synchronization dependency drain -> channel/order-domain prefix barrier 不要让前端一开始就直接生成 ns-3-ub 专用输入,否则以后换仿真器或调整仿真粒度会受到限制。 三、Action 最少需要保留这些字段 action_id site_id kernel_symbol rank_id logical_block_id phase_id local_sequence kind src/dst memory space src/dst buffer and offset bytes/count stride/padding/descriptor peer_rank logical_channel queue_id flag/notify_id fence/order/completion metadata depends_on evidence confidence 动作类型可以先控制在: local_copy remote_read remote_write flag_record flag_wait notify_record notify_wait queue_alloc queue_enqueue queue_dequeue queue_free pipeline_barrier cross_core_barrier drain 其中 HCOMM A5 的信息建议分成两层: semantic action: remote_write(src, dst, bytes) submission evidence: wqe_count jetty_id pi_before pi_after doorbell_sqe fence/order 这样既能让 ns-3 使用高层通信语义,也不会丢掉 WQE、Jetty、PI 和 doorbell 的证据。不会把一个 HCOMM primitive 错误地等同成一个 A5 SQE。 四、最好同时支持具体模式和参数化模式 如果只依赖一次 ICPU_RUN_KF,得到的是某一组参数下的具体 trace: block 0: copy 4096B wait flag 3 copy 4096B 这适合直接仿真,但不适合形状变化。 所以输出最好保留两种信息: concrete value: bytes = 4096 offset = 8192 provenance/expression: bytes = tile_bytes offset = block_id * tile_bytes 第一版不必做完整符号执行,可以只保留: - 运行时实际值; - 来源参数; - block/rank 计算关系; - 条件分支是否命中。 以后再逐步加入更复杂的参数化表达式。 五、实现技术上的选择 我倾向于: Python frontend driver + C++/AscendC observer wrapper + 现有 collective-llvm-trace LLVM analyzer + CPU-debug collector + HCOMM TaskParam callback adapter + JSON/YAML launch/topology binding 具体职责: - Python:编排采集、归一化、生成 IR; - C++ wrapper:记录源码级 site ID 和 API 参数; - LLVM analyzer:检查 artifact 中真正出现了哪些 intrinsic、地址空间和 descriptor; - CPU-debug:获得每个 block 的真实源码控制流; - HCOMM callback:补充 A5 remote primitive、EID、notify、Jetty 等通信信息; - topology adapter:将逻辑 peer/channel 映射到仿真器资源。 不建议第一版就实现一个完整的 AscendC C++ 静态解析器。对于任意自定义 kernel,未经标注、没有可识别 artifact 规则的部分,前端应输出: unknown action missing binding unsupported intrinsic unpaired synchronization 而不是默默猜一个通信动作。 我认为最重要的设计原则是: 行为证据和运行环境绑定分离 源码语义和硬件提交细节分离 语义动作和 evidence 分离 计算细节和搬移/同步细节分离 这样最终得到的前端不是“固定 HCCL 算子 trace 生成器”,而是一个可以接收自定义 AscendC kernel、改变 rank/channel/topology,并输出中间粒度行为模型的工具。