Skip to content

perf(vpto): non-negative ranges for SIMT hardware ID ops + range-driven arith opts (floordivsi/remsi by pow2) - #1497

Draft
jimmychou0 wants to merge 1 commit into
hw-native-sys:masterfrom
jimmychou0:zjm/issue1493-tid-pow2-range
Draft

jimmychou0 wants to merge 1 commit into
hw-native-sys:masterfrom
jimmychou0:zjm/issue1493-tid-pow2-range

Conversation

@jimmychou0

Copy link
Copy Markdown
Contributor

Summary

Closes #1493.

Range-driven arith optimizations for SIMT scalar index math, following the
approach agreed in the issue discussion:

  1. Hardware SIMT ID ops implement InferIntRangeInterface
    get_tid_x/y/z, get_block_idx_x/y/z, get_block_dim_x/y/z,
    get_grid_dim_x/y/z, get_veccoreid, get_laneid report non-negative
    result ranges. Thread indices are tightened to [0, dim-1] when the
    enclosing pto.section.simt<<<...>>> carries static launch dims.
    Deliberately excluded (not provably non-negative): get_clock32/64
    (wraps) and get_lanemask_* (sign bit can be set).
  2. New pto-arith-range-optimize pass wired into the VPTO backend
    pipeline (before VPTOSplitCVModulePass, where the SIMT queries are still
    present). It runs the upstream
    arith::createIntRangeOptimizationsPass() +
    createArithUnsignedWhenEquivalentPass() + canonicalize — no custom
    rewrite rules, so ops whose range cannot be proven are left unchanged
    (the "keep as-is when analysis fails" expectation from the issue).

With the non-negative ranges, the upstream passes rewrite
arith.floordivsi/remsi <tid>, 2^k into udiv/urem, and the LLVM backend
folds those into shifts/masks — eliminating the 9-instruction
signed-division guard chain that currently reaches every SIMT warp.

Validation

  • test/lit/pto/issue1493_tid_pow2_range.pto: the issue's repro pattern
    (tid % 32 == 0 guard + (tid // 32) * 64 addressing inside
    pto.section.simt) asserts the emitted VPTO LLVM IR contains
    udiv/urem and no sdiv/srem.
  • End-to-end on A5 (real device, per-token FP8 quantization kernel from the
    issue, 8192x8192):
    • unmodified .pto input now emits urem/udiv (guard chain gone);
    • final aicore .text is byte-identical to the hand-verified
      and/lshr rewrite from the issue (1112 vs 1192 bytes, −20 instruction
      words);
    • numerics pass (amax + fp8 outputs match the reference);
    • latency improves to match the hand-verified variant (0.4939 ms vs
      0.4936 ms baseline on the same box, consistent with the 0.3–0.5%
      measured on three CANN versions).
  • Full ninja build of ptoas with the change on x86 CANN 9.1.0.

@jimmychou0

Copy link
Copy Markdown
Contributor Author

Review 意见已全部修复(ad1ad0551):

  • P1 license 头:PTOArithRangeOptimize.cpp 已换成与 PTOCanonicalizeIR.cpp 相同的 PR386 OAT.3 标准头。
  • P1 FileCheck 顺序:已改为 urem 在前、udiv 在后,并且用真实 FileCheck 在发射 IR 上跑过(PASS)。另注意到两个 kernel 的指令顺序不同(scf.if 内外布局差异),当前顺序按第一个 kernel 的实际布局断言。
  • P2 full-range 退化:确认属实——fromUnsigned(0, 0xFFFFFFFF) 的 signed 区间因符号位翻转退化为 [INT32_MIN, INT32_MAX],非负信息完全丢失。已把 11 个非 tid op 及 simtThreadDimUpperBound 回退值全部改为 int32 max(0x7fffffff),helper 里的死 clamp 改为 isNegative() 时 clamp 到 getSignedMaxValue,注释同步更新。lit 补了 get_block_idx_x 用例(无 section.simt dim 上界,走 [0, INT32_SIGNED_MAX] 回退路径),同样 PASS。
  • P3 lit 头:已换 PR386 OAT.3。

验证:238b 全量重建 0 error;lit 两个用例(tid + block_idx)FileCheck 实跑 PASS;真实 kernel 端到端 .text 仍与手改 and/lshr 版逐字节相同(md5 6e851189,P2 修复未改变 tid 路径产出);真机数值 AMAX+FP8 PASS,延迟 0.4919ms。

@jimmychou0
jimmychou0 force-pushed the zjm/issue1493-tid-pow2-range branch 4 times, most recently from 58a0c3c to e20e481 Compare September 10, 2026 08:48
@jimmychou0

Copy link
Copy Markdown
Contributor Author

新增:block query 的 AIC 编排路径(非 SIMT entry)现在也走 32 位 tpe intrinsic + zext,与 SIMT 路径和 CCE 前端语义一致(__builtin_cce_get_block_idx/get_block_num 是 int32_t)。此前 AIC 路径声明 i64 的 GET.BLOCK.IDX/NUM,下游 LLVM 拿不到值宽信息。两个 emitter(默认 + CANN900)同步修改,simt_block_query_vpto_llvm.pto 更新为统一形态。144 LLVM 19.1.7 全套 lit 1863 通过 / 0 失败。

@jimmychou0
jimmychou0 force-pushed the zjm/issue1493-tid-pow2-range branch 6 times, most recently from 55af82a to 9dec823 Compare September 11, 2026 07:06
@github-actions github-actions Bot added the ci-slow this pr make ci too slow, makesure your pr doesn't cause this label Sep 11, 2026
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Warning: @jimmychou0, ci-sim exceeded its soft runtime budget.

  • vpto-sim-validation runtime: 4h 4m 7s
  • Soft budget: 1h 30m
  • Job conclusion: failure
  • Workflow run

This warning is advisory only and does not affect required checks. Please inspect the step timings for an unexpected regression.

@jimmychou0
jimmychou0 force-pushed the zjm/issue1493-tid-pow2-range branch 2 times, most recently from 9b001be to 316068e Compare September 13, 2026 10:39
@github-actions github-actions Bot removed the ci-slow this pr make ci too slow, makesure your pr doesn't cause this label Sep 13, 2026
@jimmychou0
jimmychou0 force-pushed the zjm/issue1493-tid-pow2-range branch 4 times, most recently from 896f871 to 0ceed82 Compare September 15, 2026 08:39
…en arith opts

Implement InferIntRangeInterface on the provably non-negative hardware
ID queries and run the upstream arith integer-range optimizations
early in the VPTO backend pipeline, so floordivsi/remsi by a power of
two on thread/block indices reach the LLVM backend as udiv/urem instead
of the signed-division guard chain.

Covers both dialects: the VPTO SIMT queries (get_tid_x/y/z with
[0, dim-1] from section.simt launch dims, get_block_idx_x/y/z,
get_block_dim_*, get_grid_dim_*, get_veccoreid, get_laneid) and the
PTO-level i64 runtime queries (get_block_idx, get_subblock_idx [0,1],
get_block_num, get_subblock_num) used by cube-side orchestrators.
pto.get_block_idx reports [0, INT32_MAX]: the value is a 32-bit
quantity on the hardware and the LLVM lowering rounds it through i32 on
every path, so the tight claim matches what the lowered code actually
computes. Counts stay at [0, INT64_SIGNED_MAX] so `cmpi sge
block_num, 1` guards remain dynamic.

Block queries are rounded through i32 on all paths (both emitters),
matching the CCE frontend where __builtin_cce_get_block_idx/get_block_num
are int32_t: inside simt_entry functions the query calls the 32-bit tpe
intrinsic and zero-extends to the i64 PTO result; the AIC orchestrator
path must keep the 64-bit intrinsic (the tpe form is only legal in
simt_entry), so it truncates the result to i32 and zero-extends back.

Building on those range facts, a second step narrows element-offset
chains feeding pto.load/pto.store into i32 whenever a local recursive
range evaluation proves the whole chain stays in [0, 2^32) — the chain
is converted back with arith.index_castui at the offset operand, so the
narrowed arithmetic includes any term that only ADDS the block index
(e.g. `w*2048 + (tid/32)*64 + block_idx`), matching the all-int32
addressing the AscendC frontend emits. andi and pto.addptr chains are
deliberately excluded: the MTE copy addresses they feed are
strength-reduced by the backend into per-iteration recurrences, and
inserting trunc/zext into those chains measurably regresses the kernel.

On the SIMT-VF per-token cast-to-fp8 example (8192x8192) the narrowed
kernel measures ~1.5% faster than both the un-narrowed build and the
AscendC-backend build of the same program on a quiet A5 board
(0.283 ms vs 0.288 ms across interleaved A/B rounds), with numerics
unchanged.

The evaluator keeps signed and unsigned semantics apart: a signed
index_cast is only value-preserving when it does not widen with the sign
bit reachable, and the signed div/rem flavors require the whole proven
window (numerator and divisor) to keep the sign bit clear. Divisors are
additionally required to fit u32 so the i32 mirror cannot truncate them
into a different value or a division by zero. --pto-disable-offset-narrowing
turns just the narrowing step off for field triage.

Four existing lit tests are updated for the new canonical forms
(extsi->extui on tid, floordivsi->divui on block queries, cmpi
sge->uge on non-negative operands, tpe i32 callees on AIC queries);
one tautological guard whose loop bound equals the guard bound is
decoupled so the guard stays dynamic; a new test covers both the
subchain and full-chain (block-index add included) narrowing outcomes
plus the [0,1] subblock fold, the excluded wrapping/lanemask queries
(which must keep sdiv/srem), the remaining ranged queries (laneid,
veccoreid, block_dim, grid_dim), a signed widening cast with the sign bit
reachable, divisors of 2^32 and 2^32+1, and the tautological-guard fold.
The micro-ISA entries for laneid/veccoreid/block_dim/grid_dim now state
their ranges so the non-negative claims have a written source.
Closes hw-native-sys#1493.
@jimmychou0
jimmychou0 force-pushed the zjm/issue1493-tid-pow2-range branch from 0ceed82 to c9b0039 Compare September 15, 2026 11:01
@github-actions github-actions Bot added the ci-slow this pr make ci too slow, makesure your pr doesn't cause this label Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-slow this pr make ci too slow, makesure your pr doesn't cause this

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[perf] SIMT 标量索引数学中的 floordivsi/remsi 除 2 的幂应规范化为 and/shr

1 participant