Skip to content

[Bug] 提单:vpto_split_cv_module — named module CV split 触发 symbol 重定义 #1536

Description

@yaomicat

Component

Verifier / IR semantics (lib/PTO/IR)

Description

提单:vpto_split_cv_module — named module CV split 触发 symbol 重定义

1. 问题来源

  • pass: vpto_split_cv_module (-vpto-split-cv-module)
  • 涉及文件: /home/cplop/code/pto/PTOAS/lib/PTO/Transforms/VPTOSplitCVModule.cpp
  • 关键逻辑: splitCVModule → cloneModuleForKind(source->clone() 两次插入同一 outer)
  • 发现途径: 手工复核实锤;框架原先漏检(多顶层 module 稀释 + 设计拒绝误报抢占)
  • 确认时间: 2026-09-16

Pass 文件 git commit(提单引用)

在 PTOAS 仓库对 pass 文件取 log:

cd /home/cplop/code/pto/PTOAS
git log -1 --format='%H %s %ci' -- lib/PTO/Transforms/VPTOSplitCVModule.cpp
用途 commit id 说明
当前文件 tip(提单默认) f45729d4de88836ab28c5939cb12b60e180fb4de style: enforce C++ codecheck rules (2026-08-13)
引入 clone 拆分逻辑 0b9088de630cf9db961248e309a2f0917e49c795 Support VPTO section split input

2. 为什么是 bug

合法 sugar 输入:顶层 带 sym_name 的 module,体内同时有 pto.section.vector 与 pto.section.cube,且无 pto.kernel_kind。

Pass 走 CV split:

  1. cloneModuleForKind(module, Vector) / cloneModuleForKind(module, Cube) 各一次
  2. Operation::clone() 复制 inherent sym_name = "top_sym"
  3. 两个 clone 作为兄弟插入同一 SymbolTable → redefinition of symbol named 'top_sym'
  4. pass / verifier 失败(exit ≠ 0)

对照:去掉 @top_sym 的匿名 module,同样 sections,拆分成功。

注意:同一 .pto 文件写两个顶层 module 时,MLIR 会隐式包匿名 outer;此时 clone 的是 outer(无 name),每个 kind 子树各保留一份 @top_sym,不会报重定义——这是框架曾漏检的原因。

3. 实证

exit 现象
bug.pto(module @top_sym + CV) 1 redefinition of symbol named 'top_sym'
control.pto(匿名 + CV) 0 正常拆成 vector/cube 子 module
bug 无 pass 基线 0 输入本身合法
bash test/vpto_split_cv_module_bug_cursor/reproduce.sh
# 预期: 差分复现成功(bug: symbol redefinition + control 通过)

4. 修复建议(伪代码)

根因:clone 继承了 outer 的 inherent sym_name,而 CV 子 module 不应再占用 outer 的符号名。

在 cloneModuleForKind 中,clone 之后、builder.insert 之前剥离符号名:

static ModuleOp cloneModuleForKind(ModuleOp source, FunctionKernelKind kind,
                                   OpBuilder &builder) {
  auto cloned = cast<ModuleOp>(source->clone());

  // FIX: kind 子 module 不得继承 outer 的 sym_name,否则双 clone 兄弟冲突
  if (cloned.getSymName())
    cloned.setSymName(std::nullopt);  // 或项目内等价 API: removeSymName()

  cloned->setAttr(FunctionKernelKindAttr::name,
                  FunctionKernelKindAttr::get(cloned.getContext(), kind));
  eraseSectionSplitCandidatesWithoutSectionKind(cloned, kind);
  rewriteSectionsForKind(cloned, kind);
  builder.insert(cloned);
  return cloned;
}

可选加固(splitCVModule 组装 outer 时):

// outer 保留原 module 的 discardable attrs;
// 原 module 的 inherent sym_name 留在「外层 wrapper」上即可,不要拷进 clone。
auto outer = ModuleOp::create(module.getLoc());
// ... setAttrs(outerAttrs without sym) ...
if (needVector) cloneModuleForKind(module, FunctionKernelKind::Vector, builder);
if (needCube)   cloneModuleForKind(module, FunctionKernelKind::Cube, builder);
module.getBodyRegion().takeBody(outer.getBodyRegion());
module->setAttrs(outer->getAttrs());
// module 自身的 inherent sym_name 若需保留,勿被 setAttrs 清掉;
// 子 module 已无 sym_name,不会与 wrapper 冲突。

lit:采用本目录 bug.pto;修复后应 exit=0,且子 module 匿名(或显式改名),不再出现 redefinition of symbol。

5. 影响面

  • 后果:带符号名的 sugar module 无法完成 CV 拆分,编译失败。
  • 触发:frontend / 手工 IR 给顶层 module 起名;匿名路径(lit 常见)不受影响。
  • 优先级:Medium-High(正确性;合法输入失败)。

Reproduction (minimal)

bug.pto
module @top_sym attributes {pto.target_arch = "a5"} {
  func.func @kernel_fn(%arg0: i32) attributes {pto.kernel} {
    pto.section.vector {
      pto.sync.set <PIPE_V>, 0
    }
    pto.section.cube {
      pto.sync.set <PIPE_MTE1>, 0
    }
    return
  }
}
control.pto
module attributes {pto.target_arch = "a5"} {
  func.func @kernel_fn(%arg0: i32) attributes {pto.kernel} {
    pto.section.vector {
      pto.sync.set <PIPE_V>, 0
    }
    pto.section.cube {
      pto.sync.set <PIPE_MTE1>, 0
    }
    return
  }
}
 reproduce.sh
#!/usr/bin/env bash
# 一键复现: vpto_split_cv_module / vpto_split_cv_module_bug_cursor
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
REPO=/home/cplop/code/pto/PTOAS
PASS='-vpto-split-cv-module'
BIN="${1:-}"
if [ -z "$BIN" ]; then
  for cand in "$REPO/build/tools/pto-test-opt/pto-test-opt" \
              "$REPO/build-coverage/tools/pto-test-opt/pto-test-opt"; do
    [ -x "$cand" ] && BIN="$cand" && break
  done
fi
if [ -z "$BIN" ] || [ ! -x "$BIN" ]; then
  echo "[!] 未找到 pto-test-opt" >&2
  exit 3
fi
echo "[i] 使用二进制: $BIN"
run_one() {
  local name="$1"
  "$BIN" $PASS "$HERE/${name}.pto" >"$HERE/${name}.out.pto" 2>"$HERE/${name}.stderr.txt"
  echo "$?"
}

ctl=$(run_one control)
bug=$(run_one bug)
echo "[control] exit=$ctl (预期 0)"
echo "[bug] exit=$bug (预期非 0,redefinition of symbol)"
echo "--- bug.stderr ---"
cat "$HERE/bug.stderr.txt"
if [ "$ctl" -eq 0 ] && [ "$bug" -ne 0 ]; then
  if grep -q "redefinition of symbol" "$HERE/bug.stderr.txt"; then
    echo "[OK] 差分复现成功(bug: symbol redefinition + control 通过)"
    exit 1
  fi
fi
if [ "$ctl" -eq 0 ] && [ "$bug" -eq 0 ]; then
  echo "[OK] 未复现(可能已修复)"
  exit 0
fi
echo "[!] 结果异常"
exit 2

Expected behavior

输入合法(带名字的 module @top_sym,里面同时有 cube/vector section),跑 -vpto-split-cv-module 应该成功,拆成两个子 module(一个 vector、一个 cube)

Actual behavior / error logs

pass 把 module clone 了两次,两个子 module 都还叫 @top_sym,SymbolTable 报 redefinition of symbol,opt 失败(exit≠0)。

Git commit

f45729d

Host platform

None

Target Ascend arch (if relevant)

None

PTOAS build level (if relevant)

None

Metadata

Metadata

Assignees

Labels

QAIssues from the QA teambugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions