Skip to content

bug(vpto): non-zero pto.vci / llvm.hivm.vci base ignored on A5 camodel #1092

Description

@peanutchan

Summary

On Ascend950PR camodel (Bisheng + llvm.hivm.vci), a non-zero scalar base passed to pto.vci is ignored. The hardware/backend always behaves as vci(0) and emits lane ids [0..N), even when LLVM IR correctly carries the base immediate.

This breaks absolute index generation such as vci(i * 64) for multi-vreg expert indexing (e.g. TileLang topk_gate).

Spec expectation

For ASC order:

result[i] = base + i

So pto.vci %c64 on !pto.vreg<64xi32> should yield [64, 65, ..., 127].

Observed

Camodel returns [0, 1, ..., 63].

End-to-end symptom in TileLang topk_gate (E=384): top-1 expert 144 was reported as 16 (144 % 64), and 292 as 36, until indices were materialized as vci(0) + vadds(base).

Minimal repro (legacy VPTO)

// kernel.pto — expected [64..127], observed [0..63]
module attributes {pto.target_arch = "a5", pto.backend = "vpto"} {
  func.func @vci_nonzero_base(%arg0: !pto.ptr<i32, gm>)
      attributes {pto.entry, pto.kernel_kind = #pto.kernel_kind<vector>} {
    %c0_i64 = arith.constant 0 : i64
    %ub = pto.castptr %c0_i64 : i64 -> !pto.ptr<i32, ub>
    %c0 = arith.constant 0 : index
    %c64_i32 = arith.constant 64 : i32
    pto.vecscope {
      %mask = pto.pset_b32 "PAT_ALL" : !pto.mask<b32>
      %idx = pto.vci %c64_i32 {order = "ASC"} : i32 -> !pto.vreg<64xi32>
      pto.vsts %idx, %ub[%c0], %mask
        : !pto.vreg<64xi32>, !pto.ptr<i32, ub>, !pto.mask<b32>
    }
    pto.set_flag["PIPE_V", "PIPE_MTE3", "EVENT_ID0"]
    pto.wait_flag["PIPE_V", "PIPE_MTE3", "EVENT_ID0"]
    %c1 = arith.constant 1 : i64
    %c64 = arith.constant 64 : i64
    %c0b = arith.constant 0 : i64
    pto.set_loop_size_ubtoout %c1, %c1 : i64, i64
    pto.copy_ubuf_to_gm %ub, %arg0, %c0b, %c1, %c64, %c0b, %c64, %c64
      : !pto.ptr<i32, ub>, !pto.ptr<i32, gm>, i64, i64, i64, i64, i64, i64
    pto.barrier #pto.pipe<PIPE_ALL>
    return
  }
}

PTOAS still emits the base into LLVM correctly:

declare <64 x i32> @llvm.hivm.vci.v64s32(i32, i32)
%7 = call <64 x i32> @llvm.hivm.vci.v64s32(i32 64, i32 0)

So this looks like a Bisheng / HIVM intrinsic / camodel bug (or undocumented encoding) rather than PTOAS dropping the operand in IR.

VMI form

pto.vmi.vci %c64 (size 64, contiguous) currently lowers through contiguous iota to a single pto.vci %c64, which hits the same backend bug.

Workaround

Materialize absolute indices as:

lanes = vci(0)
result = vadds(lanes, base)   # or ASC: vci(0)+vadds; DESC: vdup(base)-vci(0)

TileLang ASC already used vci(0) + vadds(i*64) for this reason. A local PTOAS workaround rewrites contiguous VMI iota the same way; ideally the backend honors the intrinsic base so that rewrite can be removed.

Environment

  • Soc: Ascend950PR_9599 camodel via msprof op simulator
  • PTOAS built against llvm-vpto (HIVM) + CANN 9.0.0
  • Reproduced via TileLang examples/ascend/vmi/topk_gate before the vci(0)+vadds rewrite

Ask

  1. Confirm whether llvm.hivm.vci.*(base, order) is supposed to honor a non-zero base.
  2. If yes, fix Bisheng/HIVM lowering / camodel so vci(64) yields [64..127].
  3. If the intrinsic is lane-only by design, please document that and keep the vci(0)+vadds(base) materialization as the supported pattern (and update ISA docs / gym cases that pass non-zero bases).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions