Skip to content

fix(vpto): lower A5 V→V barrier to pto.mem_bar#869

Open
TelGome wants to merge 1 commit into
hw-native-sys:mainfrom
TelGome:issue_866
Open

fix(vpto): lower A5 V→V barrier to pto.mem_bar#869
TelGome wants to merge 1 commit into
hw-native-sys:mainfrom
TelGome:issue_866

Conversation

@TelGome

@TelGome TelGome commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

问题

A5 上 PTOInsertSync 能正确识别 V→V 内存依赖(VSTI→VLDI / VLDI→VSTI /
VSTI→VSTI,作用于别名 UB 地址),并为它们创建了 PIPE_BARRIER
SyncOperation。但 SyncCodegen::CreateBarrierOp 在 A5 上无条件丢弃了这些
barrier(直接 return,注释写着 "PIPE_V intra-pipe ordering is
guaranteed by hardware; do not emit explicit vector barrier"
)。

这个假设不成立:VSTI 写一个 UB 地址后,VLDI 紧接着读同一地址是真实的
RAW hazard。VPTO 路径会把 Tile op 展开成低层 vlds/vsts,并把中间结果
spill 回 UB,于是产生了未同步的 V→V RAW hazard —— camodel 报了 688 条
RV_VLDI overlaps RV_VSTI
警告,真机 A5 结果也是错的(所以不是
camodel 误报)。

EmitC 路径不受影响:它把 Tile op 直接 lower 成 C++ intrinsic,在 vreg 之间
计算,中间结果不 spill 到 UB,所以没有 V→V UB 别名 hazard。

根因

lib/PTO/Transforms/InsertSync/SyncCodegen.cpp:286-291 —— A5 +
PIPE_V 分支直接 return,什么都不生成。而 PTOAS 其实已经有正确的原语
pto.mem_bar,定义在 docs/isa/micro-isa/01-pipeline-sync.md:116-147
文档里给的 vsts → mem_bar "VST_VLD" → vlds 例子正好就是这个场景),还有
完整的 MemBarKind 枚举和 LLVM lowering
LowerMemBarOpPatternllvm.hivm.mem.bar.*)。唯一缺的就是把
CreateBarrierOp 接上去。

修复

在 A5 上把 V→V barrier 降级为按 hazard 类型生成 pto.mem_bar,而不是丢弃。

  1. SyncCommon.h / SyncCommon.cpp —— 给 SyncOperation 增加
    pto::MemBarKind vvMemBarKind{VV_ALL} 字段(+ getter/setter),并在
    GetMatchSync 里传播。

  2. InsertSyncAnalysis.cpp —— 新增 classifyVvMemBarKind():对每个
    (nowAccess, frontAccess) 依赖对,判定是 RAW
    now.use vs front.defVST_VLD)、WAR
    now.def vs front.useVLD_VST)还是 WAW
    now.def vs front.defVST_VST);当多种 hazard 共存、或该 pair
    不属于内存访问(纯控制依赖 / ACC read-read 特殊 hazard)时,回退到
    VV_ALLInsertSyncOperation 在 V→V barrier 上设置该 kind。

  3. SyncCodegen.cpp:286 —— A5 + PIPE_V 不再静默 return仅在
    VPTO backend
    (通过 module 的 pto.backend = "vpto" 属性判断)下生成
    pto.mem_bar <kind>。EmitC 仍保留丢弃路径:EmitC 把 Tile op lower
    成自管 buffer 的 C++ intrinsic,且不合法化 pto.mem_bar,在那里生成它
    会让 tinsert_a5_vec_pipe_selection 等 EmitC 测试报
    "failed to legalize pto.mem_bar"

  4. test/lit/vpto/issue866_v_pipe_mem_bar.pto —— 回归测试:两个
    tcolexpandmul 共享目的 tile,产生 V→V RAW/WAW 依赖;断言生成了
    pto.mem_bar、且没有生成 pto.barrier <PIPE_V>

CanPrunePipeVBarrier 原有的 same-access + repeat ≥ 16 剪枝保持不变
—— 那种情况确实由硬件保证安全,仍然不需要 barrier。

验证

rope_kv_cache 在 camodel(aarch64,CANN 9.0.0-beta.2)上:

指标 修复前 修复后
RV_VLDI overlaps RV_VSTI 688 0
mte_gdma_write_overflow 12(修 v1 前)→ 0 0
v2(k cache)max ULP 30907 ~1(通过)
v3(v cache)max ULP 通过 通过
生成的 V→V pto.mem_bar 0 16(6× VST_VLD,1× VLD_VST,1× VV_ALL)

运行compare.py仍然有数值精度问题,但是camodel(0 overflow / 0 overlap)正常:

[ERROR] bf16 v1: max_ulp=30399 idx=6636 golden=-0.00262451171875 output=0.00445556640625
[ERROR] compare failed

涉及文件

  • include/PTO/Transforms/InsertSync/SyncCommon.h
  • lib/PTO/Transforms/InsertSync/SyncCommon.cpp
  • lib/PTO/Transforms/InsertSync/InsertSyncAnalysis.cpp
  • lib/PTO/Transforms/InsertSync/SyncCodegen.cpp
  • test/lit/vpto/issue866_v_pipe_mem_bar.pto(新增)

Closes #866

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements support for lowering V→V barriers to pto.mem_bar on the A5 architecture when using the vpto backend, addressing issue #866. It introduces hazard classification (classifyVvMemBarKind) to determine the appropriate memory barrier variant (RAW, WAR, WAW, or the conservative VV_ALL fallback). The reviewer identified a critical bug in the hazard classification logic where the use of if-else if statements can cause coexisting hazards to be misclassified, potentially leading to missing synchronization and data corruption. A correction was suggested to evaluate each hazard independently.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +194 to +202
if (nowUse && frontDef)
hasRaw = true;
else if (nowDef && frontUse)
hasWar = true;
else if (nowDef && frontDef)
hasWaw = true;
else
// ACC read/read special hazard or unmatched aliasing: stay conservative.
return MemBarKind::VV_ALL;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

classifyVvMemBarKind 中,对每个依赖对(pair)使用 if-else if 结构来判定 RAW、WAR 和 WAW 冒险。\n\n然而,一个操作可能会同时读取和写入同一个 Buffer(即 pair.firstpair.second 同时存在于 useVecdefVec 中)。在这种情况下,如果同时存在 RAW 和 WAW 冒险(例如 nowUsenowDeffrontDef 均为 true),由于 else if 的限制,代码只会将 hasRaw 设为 true,而漏掉 hasWaw。\n\n这会导致 hazardCount 计算为 1,从而错误地返回 MemBarKind::VST_VLD。但实际上该场景还需要 VST_VST(或 VV_ALL)来同步 WAW 冒险,这可能会导致硬件上的同步缺失和数据错误。\n\n建议:\n将 if-else if 改为独立的 if 语句,并使用一个 matched 标志来处理未匹配的情况。

    bool matched = false;
    if (nowUse && frontDef) {
      hasRaw = true;
      matched = true;
    }
    if (nowDef && frontUse) {
      hasWar = true;
      matched = true;
    }
    if (nowDef && frontDef) {
      hasWaw = true;
      matched = true;
    }
    if (!matched)
      // ACC read/read special hazard or unmatched aliasing: stay conservative.
      return MemBarKind::VV_ALL;

@Zhendong404 Zhendong404 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

问题原因没分析清楚

@TaoTao-real

Copy link
Copy Markdown
Contributor

能详细说明场景么?在除了VPTO之外的场景也会有精度问题么?

@TelGome

TelGome commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

问题原因没分析清楚

能详细说明场景么?在除了VPTO之外的场景也会有精度问题么?

目前只在vpto场景下发现有精度问题,打印ir发现是两个op展开后是单次循环,会循环展开,然后产生的RAW冲突,这里mem_bar是后面加的
img_v3_02134_51e5a9f6-a118-4c0f-8d71-4effd164fdeg
img_v3_02131_2416764c-a0a8-474c-a654-68d569a8f07g

在加上这个membar之后,发现v1报错,这个是textract模板处理in_place出错

[ERROR] bf16 v1: max_ulp=30399 idx=6636 golden=-0.00262451171875 output=0.00445556640625
[ERROR] compare failed
img_v3_02134_5e762a83-117b-4c87-b831-3a0ac710d87g

@reedhecre

reedhecre commented Jun 29, 2026

Copy link
Copy Markdown

Codex Review

该评论由 review 机器人自动更新。

Summary

V→V barrier 改成方向性 pto.mem_bar 后,当前实现仍按原来“全 PIPE_V barrier”的方式做去重和 hazard 归类,混合依赖场景会被欠同步。

Findings

  1. P1 V→V `mem_bar` 仍按整条 PIPE_V 去重,混合 hazard 会被错误合并 lib/PTO/Transforms/InsertSync/InsertSyncAnalysis.cpp:648

这个 PR 把 A5 的同 PIPE_V 同步从“全量 PIPE_V barrier”改成了方向性的 pto.mem_bar,但 InsertSyncAnalysis 仍然只按 frontPipe 记录 alreadySync。一旦最近的前驱先插入了一个 VLD_VST/VST_VLD/VST_VST,后面更早的同 PIPE_V 依赖就会被直接跳过;旧实现里这没问题,因为 pipe_barrier(PIPE_V) 覆盖所有向量流水线顺序,但现在这些 mem_bar 只覆盖单一方向。比如“更早的 op0 写 x,较近的 op1 读 y,当前 op2 读 x 并写 y”需要同时满足 op0 -> op2VST_VLDop1 -> op2VLD_VST,当前扫描顺序只会保留离得最近的那个,另一个会被 alreadySync[PIPE_V] 吃掉,最终仍然可能让一部分 VLD/VST 乱序。SyncCodegen 里的签名去重也同样没有把 vvMemBarKind 纳入比较,会进一步放大这个问题。

  1. P2 `MemBarKind` 分类丢失 RAW/WAR/WAW 来源,读写同址的向量 op 会被误判 lib/PTO/Transforms/InsertSync/InsertSyncAnalysis.cpp:175

depBaseMemInfosVec 是由三次独立探测拼出来的:RAW(now.use/front.def)、WAR(now.def/front.use) 和 WAW(now.def/front.def)。但新加的 classifyVvMemBarKind 没保留“这个 pair 来自哪一次探测”,而是重新靠 useVec/defVec 成员关系反推 hazard。只要某个访问同时是 use 和 def(就地更新 / read-modify-write 的向量 op 很常见),同一个 pair 会同时满足多个类别,而当前 if/else if 只会命中第一个分支,把真实的混合 hazard 压成单一 VST_VLDVLD_VST。这样生成的 mem_bar 会比原来的全量 barrier 更弱,仍可能放过前一条 op 的 store/load 与当前 op 的 load/store 重叠。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] rope_kv_cache精度问题

4 participants