Skip to content

[Bug] VMI brc+stride 降成 RV_VSLDB(block-stride),该指令要求 32B 对齐,与 BRC_B8 的 1 字节对齐契约矛盾,导致真机 ACL 507035 #1585

Description

@Zhendong404

Description

VMI 的 vload(..., stride=1, dist_mode="brc", group=N) 会被降到 block-stride 形式(RV_VSLDB),而该指令要求有效地址 32 字节对齐;但编译器放行时用的对齐模型是 BRC_B8/B16/B32 的 contract(Size::Element → 1/2/4 字节)。两者矛盾时编译器不报错,而是生成一条非对齐的 RV_VSLDB,真机上表现为 ACL 507035 device fault。

根因链(逐条已核)

  1. lib/PTO/IR/VPTOMemoryDist.cpp 的 contract 表:
{Load, LoadNorm, "NORM",    0, 0, 1, ..., Size::Block,   Size::Vector,  {}},
{Load, BrcB8,   "BRC_B8",   8, 1, 1, ..., Size::Element, Size::Element, {}},
{Load, BrcB16,  "BRC_B16", 16, 2, 1, ..., Size::Element, Size::Element, {}},
{Load, BrcB32,  "BRC_B32", 32, 3, 1, ..., Size::Element, Size::Element, {}},
case Size::Element: return operandElementBits / 8;   // BRC_B8→1, BRC_B16→2, BRC_B32→4 字节
case Size::Block:   return 32;                        // NORM/NORM_B8/NORM_B16/NORM_B32 → 32 字节
  1. lib/PTO/Transforms/VMIToVPTOConversionInternals.cpp:1528 的 isDirectMemoryDistAddressLegal() 就用这个 contract 算 requiredAlignment,对 BRC_B8 得到 1 字节 → 判定合法;
  2. VMIToVPTOPatternInternals3.cpp 的 tryLowerDirectBRC / buildDirectBRCResult 于是发出 VldsOp{ dist = "BRC_B8" },偏移是原始字节偏移;
  3. 后端把它编成 RV_VSLDB(block-stride),该指令要求 32B 对齐 → 地址不满足 → trap。

Reproduction(最小)

canonical (1,32) 的 SF 惰性展开(模仿 ASC 的 lazy packed scale):sf_src_ub 为 uint8、行宽 32,读 sf_src_ub[row, half*4],即地址 ≡ 4 (mod 32):

raw = T.vmi.vload(sf_src_ub[group * bm, half * scales_per_chunk],
                  size=vector_lanes, stride=1, dist_mode='brc', group=vector_lanes // bk)

编译通过;真机运行报:

RuntimeError: npuSynchronizeDevice ... device error type 3, error code is 507035

Evidence:模拟器给出精确原因

npusim record -s Ascend950(用户态即可,无需设备):

[ERROR] rvec_dec.cc:1319 block_stride_proc ISA name RV_VSLDB, pc:0x9000d1247c
        Address.Sn 0x00008004 is not aligned to 32 bytes
[ERROR] rvec_dec.cc:1319 block_stride_proc ISA name RV_VSLDB, pc:0x9000d1247c
        Address.Sn 0x00008024 is not aligned to 32 bytes
... (同一条指令共 4096 次)

失败地址全部 ≡ 4 (mod 32)、步进 0x20(= 行距 32)。即 RV_VSLDB 的 32B 对齐要求与行内 4 字节偏移直接冲突。

对照:ASC 同样的语义没有这个问题

Nightly ASC(tile_kernels/quant/cast_back_asc.py,use_lazy_packed_scale 路径):

packed = S.vld(sf_ub[group, col // 64], dist='BRC_B16')     # 注意:没有 stride 操作数

它在同一硬件上以 2 字节粒度的偏移(0,2,4,6,…)正常工作。差别在于它发的是纯广播形式,不是 block-stride。

去掉 stride 也不是可行规避

VMI 前端里 group 强制要求 stride(_validate_vmi_load_modes:vload(...) with group=... requires stride),所以只能 group/stride 一起去掉,退成 vload(ptr+off, size=N, dist_mode='brc');那样 PTOAS 会以另一个错误失败:

kernel.pto:274:24: error: VMI-LAYOUT-CONTRACT:
  cannot materialize requested VMI operand layout #pto.vmi.layout<contiguous, lane_stride = 2>

即 grouped-brc 目前是该形状唯一能落下去的形态。

Expected behavior

二选一(任一即可让该形状可用):

  1. stride == 1 的 BRC 不走 block-stride 形式,改用纯广播指令(与 ASC 等价);或
  2. 若必须用 RV_VSLDB,则让 isDirectMemoryDistAddressLegal 对这条路径按 32 字节校验,编译器据此拒绝并走回退,而不是发一条必然 trap 的指令。顺带:verifySupportedVMIFloatOp 里的针对性诊断(... supports contiguous 16-bit float-like or fp8-like physical source chunks ...)在这条路径上没有触发,最终只留下通用报错。

附:同一 repro 里另有一个独立的 lowering 缺口

(fp8 → f32 的 extf 需要它才能编过,已本地验证修法)

VMIToVPTOPatternInternals5.cpp 的 buildFactorPlan 只有两个分支:

if (sourceBits == 16 && resultPartCount == 2 * sourcePartCount) → {EVEN, ODD}, factor 2
if (sourceBits == 8  && resultPartCount == 4 * sourcePartCount) → {P0..P3},  factor 4
return notifyMatchFailure(op, "unsupported physical extf source/result width relation");

而 kLegalCastLayoutPatterns 里 4x widening 登记了 {bits<8>, bits<32>, ls(2), d(2)}。当消费者把 scale 按 EVEN/ODD 拆两半时,x 的 dequant 会被拉成 1 → 2 的 extf(sourceBits=8, sourcePartCount=1, resultPartCount=2),两个分支都不匹配 → op 残留 → VMI-RESIDUAL-OP: failed to convert all VMI ops/types to VPTO。

修法:packedE2M1LaneStride2 那个分支的判据从 isa<F4E2M1x2Type> 放宽到「8-bit 源 + lane_stride == 2 + resultTypes.size() == 2 * sourceParts.size()」(用 {P0, P2}),这与 packed f4 同构(8-bit + lane_stride=2 的有效字节同样落在偶数 lane)。本地改后该 .pto 可正常产出目标文件。

Environment

  • PTOAS:本仓 main(本地 checkout codex/bump-vmi-version-0.1.7 @ 0c16503f1,以及 nightly pin 7ad34a47)均可复现
  • CANN 9.2.0 / Ascend950DT / npusim record -s Ascend950
  • 复现用例来自 TileKernels-vmi cast_back canonical (1,32),(32,1) 路径不受影响

Migrated from GitCode cann/pto-as#11. Original GitCode label: tech-debt.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions