Skip to content

[VMI] expose PLT POST_UPDATE remaining from create_mask for multi-tile vdhist #1009

Description

@peanutchan

Summary

pto.vmi.create_mask(active_lanes, size=...) returns only a mask. For an 8×256 vdhist sweep over a dynamic remaining count (TileLang group_count tail / partial copy tile), that forces absolute per-tile lane counts:

for i in range(8):
    rem_from = n - i * 256          # independent SSA per tile
    mask = pto.vmi.create_mask(rem_from, size=256)
    acc = pto.vmi.vdhist(acc, src[i], mask)

The ISA-intended pattern is a carried remaining scalar with plt_b8(..., POST_UPDATE):

uint32_t rem = n;
for (uint16_t i = 0; i < 8; ++i) {
    auto mask = plt_b8(rem, POST_UPDATE);  // rem updated in place
    dhistv2(hist, src[i], mask, Bin_N0);
}

Today this ideal path is expressible in raw CCE / pto.plt_b8 / pto.make_mask, but not with pto.vmi.vdhist:

  • pto.plt_b8 returns !pto.mask<b8>, which pto.vmi.vdhist rejects (operand #2 must be VMI logical mask type)
  • pto.vmi.create_mask lowers each call independently via plt and discards scalar_out, so remaining cannot chain across tiles

Ask

Please add a VMI-surface API that returns remaining, e.g.:

mask, remained = pto.vmi.create_mask(remained, size=256)  # or create_mask_post_update(...)
# remained usable as next create_mask / loop-carried i32/index

Requirements:

  1. Result mask must be a VMI logical mask accepted by vdhist / other VMI ops
  2. remained must be the PLT POST_UPDATE scalar_out (or equivalent), so an 8-tile unroll can share one remaining counter without recomputing min(256, max(0, n - i*256)) / rematerializing 8 absolute counts
  3. Documented interaction with TileLang T.vmi.create_mask so group_count-style kernels can lower to this path

Microbench (CCE, same 8×256 dhist slice)

Repro (in tilelang fork worktree):

examples/ascend/vmi/plt_post_update_microbench/

  • cce/dhist_plt_compare_cce.cpp — absolute vs POST_UPDATE modes
  • run_cce_launch.py — launch under scripts/sim_dsl.sh
  • abs_create_mask_vmi.py — VMI absolute create_mask(n - i*256) IR reference

Setup: n=1800, outer=64512 tile iterations (64×8). Both modes PASS (identical hist vs golden).

Opsim instr mix (Ascend950PR_9599)

instr absolute calls post_update calls delta
RV_DHISTv2 512 512 0
RV_VLD 512 512 0
RV_PLT 512 512 0
RV_SSUB 512 0 +512
RV_SMIN 512 0 +512
RV_SAND 512 0 +512
RV_SADD 1024 0 +1024
RV_SCMP 576 64 +512
RV_SMOV 129 65 +64
total call_count 5208 2136 ~2.4×

Absolute mode pays a full min/max/sub scalar chain per tile to rebuild the lane count that POST_UPDATE would have carried for free. Wall block time is still DHIST/VLD-dominated in this tiny kernel (~4.28 µs both), but the scalar tax matches what we see in TileLang group_count tail dumps (+PLT/+SMOV per unrolled hist tile).

VMI absolute IR shape

%rem = arith.subi %n, %c256 : index
%mask = pto.vmi.create_mask %rem : index -> !pto.vmi.mask<256xpred>
%acc = pto.vmi.vdhist %acc, %src, %mask ...

repeated per tile — no loop-carried remaining from PLT.

Context

Came up while optimizing TileLang VMI group_count (vdhist over copy tiles with optional tail). Tail-enabled aligned cases show extra per-tile RV_PLT/RV_SMOV vs the no-tail path; expectation is one remaining scalar + POST_UPDATE PLTs across the 8 hist tiles.

Related surface that already does the right thing for non-VMI ops: pto.make_mask / pto.plt_b* returning (mask, remained) (see tcmp_template.py).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions