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:
- Result mask must be a VMI logical mask accepted by
vdhist / other VMI ops
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
- 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=64 → 512 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).
Summary
pto.vmi.create_mask(active_lanes, size=...)returns only a mask. For an 8×256vdhistsweep over a dynamic remaining count (TileLang group_count tail / partial copy tile), that forces absolute per-tile lane counts:The ISA-intended pattern is a carried remaining scalar with
plt_b8(..., POST_UPDATE):Today this ideal path is expressible in raw CCE /
pto.plt_b8/pto.make_mask, but not withpto.vmi.vdhist:pto.plt_b8returns!pto.mask<b8>, whichpto.vmi.vdhistrejects (operand #2 must be VMI logical mask type)pto.vmi.create_masklowers each call independently viapltand discardsscalar_out, so remaining cannot chain across tilesAsk
Please add a VMI-surface API that returns remaining, e.g.:
Requirements:
vdhist/ other VMI opsremainedmust be the PLTPOST_UPDATEscalar_out (or equivalent), so an 8-tile unroll can share one remaining counter without recomputingmin(256, max(0, n - i*256))/ rematerializing 8 absolute countsT.vmi.create_maskso group_count-style kernels can lower to this pathMicrobench (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 modesrun_cce_launch.py— launch underscripts/sim_dsl.shabs_create_mask_vmi.py— VMI absolutecreate_mask(n - i*256)IR referenceSetup:
n=1800,outer=64→512tile iterations (64×8). Both modes PASS (identical hist vs golden).Opsim instr mix (
Ascend950PR_9599)Absolute mode pays a full
min/max/subscalar chain per tile to rebuild the lane count thatPOST_UPDATEwould 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/+SMOVper unrolled hist tile).VMI absolute IR shape
repeated per tile — no loop-carried remaining from PLT.
Context
Came up while optimizing TileLang VMI
group_count(vdhistover copy tiles with optional tail). Tail-enabled aligned cases show extra per-tileRV_PLT/RV_SMOVvs 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)(seetcmp_template.py).