已关闭
[Bug-Report|缺陷反馈]: [BiSheng][Ascend950][CANN 9.2.0] HiIPUVectorLoopUnrollPass 在 O2/O3 编译 __simd_vf__ 时段错误 #1484
ddssz创建于  8月11日关闭于  9月10日
ddssz
ddssz
8月11日 创建

Thanks for sending an issue! Please fill in the following template to help quickly solve your problem.

Describe the current behavior / 问题描述 (Mandatory / 必填)

在 Ascend950PR_9599 目标上,以 -O2 或 -O3 编译 block_attn_res_update 的 AscendC Kernel 时,BiSheng clang frontend 直接发生 SIGSEGV(exit code 139)。目标文件未生成,随后出现的链接阶段“找不到 .o”只是该前端崩溃的连锁结果。

通过 LLVM pass trace,首个稳定复现的崩溃位置为:

Running pass: HiIPUVectorLoopUnrollPass on BlockAttnResUpdatePhase1OneVLVF

gdb 同样确认 SIGSEGV 发生在 LLVM Function Pass Manager 执行过程中。

该问题与优化级别强相关:

  • -O0:通过
  • -O1:通过
  • -O2:exit 139
  • -O3:exit 139
  • -O3 -mllvm -vloop-unroll-disable:通过
  • -O3 --cce-simd-vf-fusion=false:仍然 exit 139

因此问题并非普通语法错误或内存不足,而是 HiIPUVectorLoopUnrollPass 处理 simd_vf 内运行时 0/1 次循环时的编译器崩溃。

Environment / 环境信息 (Mandatory / 必填)

  • CANN 版本:9.2.0
  • BiSheng 版本:clang version 15.0.5 (clang-5c68a1cb1231 flang-5c68a1cb1231)
  • BiSheng 构建时间:2026-08-04T16:57:23+08:00
  • 编译目标:Ascend950PR_9599
  • 编译架构:dav-c310-vec
  • 硬件形态:Ascend 950 系列
  • 构建环境:Docker 容器
  • 内存:编译时约 108 GiB 可用,已排除 OOM
  • HDK/操作系统精确小版本:当前日志中未记录

Steps to reproduce the issue / 重现步骤 (Mandatory / 必填)

  1. 在 ops-transformer 仓库中编译 block_attn_res_update:
bash build.sh --pkg --soc=ascend950 --ops=block_attn_res_update -j16 -O3 |& tee compile_info.txt
  1. 可触发问题的循环形态如下(位于 simd_vf 路径,hasTail 为运行时 0/1 次 trip count):
for (uint16_t loop = 0; loop < hasTail; ++loop) {
    Reg::ShiftLefts(
        (Reg::RegTensor<uint32_t> &)queryReg,
        (Reg::RegTensor<uint32_t> &)queryReg,
        static_cast<int16_t>(0),
        dMask);
}
  1. 使用 -O2 或 -O3 编译,BiSheng frontend 以 exit code 139 退出。

  2. 在该循环前加入以下指令后,当前函数可通过,崩溃会移动到下一个相同形态的 hasXXX 循环:

#pragma clang loop unroll(disable)
  1. 对文件中全部 21 个 for (uint16_t ...; ... < hasXXX; ...) 循环加入该 pragma 后,完整 O3 asc_opc 流程通过并正常生成 .o 与 .json。

Describe the expected behavior / 预期结果 (Mandatory / 必填)

BiSheng 应在 -O2/-O3 下正确完成该 Kernel 的编译并生成目标文件。即使某种循环形态暂不受优化器支持,也应跳过该优化或给出可诊断的编译错误,不能在编译器内部触发 SIGSEGV。

核心报错:

bisheng: error: clang frontend command failed with exit code 139

Pass trace 在崩溃前最后输出:

Running pass: HiIPUVectorLoopUnrollPass on BlockAttnResUpdatePhase1OneVLVF

gdb 结果:收到 SIGSEGV,调用栈位于 LLVM Function Pass Manager。

后续“目标文件不存在/链接失败”是前端未生成 .o 的次生错误,不是根因。

Special notes for this issue/备注 (Optional / 选填)

已验证的临时规避方式:

  1. 在受影响的 hasXXX 循环前添加 #pragma clang loop unroll(disable)(局部规避,推荐用于缩小影响范围)。
  2. 增加编译参数 -mllvm -vloop-unroll-disable(全局关闭该循环展开优化)。
  3. 暂时降为 -O1。

补充定位结果:

  • 单独关闭 SIMD VF fusion(--cce-simd-vf-fusion=false)无效。
  • 注释首个触发循环后,崩溃会稳定移动到后续相同形态的循环。
  • 仅给普通主循环添加 unroll(disable) 无效;给运行时 hasXXX 循环添加才有效。
  • 原仓库代码未因定位测试而改动。
likedislike
ddsszddssz
8月11日 修改了issue 的描述
ddssz
ddssz
8月11日 评论:

补充验证:官方文档《VF循环优化》明确将以下写法作为 VF 尾块处理的正例:

文档位置:docs/zh/guide/算子实践参考/SIMD算子性能优化/矢量计算/VF性能优化/VF循环优化.md

uint16_t tailK = srcK % floatRepSize;
uint16_t hasTail = 0;
hasTail = !!tailK;
for (uint16_t i = 0; i < hasTail; i++) {
    // 尾块处理内容
}

我在同一环境中将首个稳定触发点临时改为上述 tailK + !!tailK + for(1) 形式后重新执行 O3 编译,结果仍然是:

OPC_RC=1
Running pass: HiIPUVectorLoopUnrollPass on BlockAttnResUpdatePhase1OneVLVF
bisheng: error: clang frontend command failed with exit code 139

原写法使用比较表达式后转换为 uint16_t,本身也只能生成 0/1;改成文档推荐的 !!tailK 后并未改变崩溃位置。

这说明问题不是 hasTail 未归一化,而是当前 BiSheng 在 O2/O3 下处理官方文档推荐的 VF 运行时 0/1 trip-count 循环时,HiIPUVectorLoopUnrollPass 发生内部崩溃。该范式应当能够正常编译,至少不应导致编译器 SIGSEGV。

likedislike
Llihuaichao成员
8月12日 将 lihuaichao 设为负责人
lihuaichao成员
8月12日 评论:

您好,感谢您的反馈,SIMD VF 和调用SIMD VF 的代码片段麻烦发一下~

likedislike
ddssz
ddssz
8月12日 评论:

您好,补充说明:触发问题的版本中,hasTail 是在 SIMD VF 内根据运行时参数 dSize 计算的,调用侧不传入 hasTail。

精简后的故障代码结构如下:

__simd_vf__ inline void BlockAttnResUpdatePhase1OneVLVF(
    __ubuf__ float *effectiveQuery, uint32_t dSize)
{
    constexpr uint32_t VL = 64;
    const uint16_t hasTail =
        static_cast<uint16_t>(dSize < VL);

    uint32_t remainingD = dSize;
    Reg::MaskReg dMask =
        Reg::UpdateMask<float>(remainingD);
    Reg::RegTensor<float> queryReg;

    Reg::LoadAlign<float, Reg::LoadDist::DIST_NORM>(
        queryReg, effectiveQuery);

    for (uint16_t i = 0; i < hasTail; ++i) {
        Reg::ShiftLefts(
            (Reg::RegTensor<uint32_t> &)queryReg,
            (Reg::RegTensor<uint32_t> &)queryReg,
            static_cast<int16_t>(0),
            dMask);
    }

    // 后续还有常规 VF 计算,已省略
}

调用 SIMD VF 的代码结构如下,调用侧没有计算或传入 hasTail:

if (dSize <= 64U) {
    asc_vf_call<BlockAttnResUpdatePhase1OneVLVF>(
        effectiveQueryUbAddr, dSize);
}

在 Ascend950PR_9599、CANN 9.2.0、-O2/-O3 下,该写法稳定崩溃:

Running pass: HiIPUVectorLoopUnrollPass on BlockAttnResUpdatePhase1OneVLVF
bisheng: error: clang frontend command failed with exit code 139

目前验证可通过的规避方式,是在 Scalar 侧计算 hasTail,然后作为参数传给 SIMD VF:

const uint16_t hasTail =
    static_cast<uint16_t>(dSize < 64U);

asc_vf_call<BlockAttnResUpdatePhase1OneVLVF>(
    effectiveQueryUbAddr, dSize, hasTail);

因此需要特别更正:此前最新代码已经采用 Scalar 侧计算并传参的规避方式,可以正常编译;真正触发崩溃的是在 VF 内根据运行时参数计算 hasTail,再将其用作 0/1 次循环上界。

另外,将相同故障结构改成 __VEC_SCOPE__ 后仍会在提取出的 VF 函数上崩溃:

HiIPUVectorLoopUnrollPass on BlockAttnResUpdatePhase1OneVLVF...extracted

说明问题与 VF 内部生成的运行时 0/1 循环有关,并不局限于 asc_vf_call 调用形式。

likedislike
lihuaichao成员
8月13日 评论:

您好,该问题已本地复现,近期将会与编译器确认。

感谢您的反馈~

likedislike
lihuaichao成员
8月18日 评论:

您好,编译器已确认该问题,编译器近期将会修复。

likedislike
lihuaichao成员
9月1日 评论:

您好,该问题我们已经提交给编译器团队进行根因分析。目前正在跟踪处理中,一旦有明确结论或修复进展,我会第一时间在这里同步给您。

likedislike
lihuaichao成员
9月7日 评论:

您好,编译器团队已修复该问题,周三会更新CANN包,可参考:
https://gitcode.com/cann/asc-devkit/blob/master/docs/zh/quick_start.md#下载cann包

likedislike
ddsszddssz
9月10日 issue状态由 待办的 改变为 已解决
ddsszddssz
9月10日 关闭了 issue
Llihuaichao成员
29 天前 添加了label:Accepted