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

perf(vpto): non-negative ranges for SIMT hardware ID ops + range-driven arith opts (floordivsi/remsi by pow2)#1497
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

Resolved: ci-sim runtime is back within its soft budget.

  • Latest vpto-sim-validation runtime: 1h 9m 39s
  • Soft budget: 1h 30m
  • Workflow run

The previous duration warning is resolved. This status is advisory only.

@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
…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.

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.
Both forms pin the value to [0, 2^32) for downstream folding.

Building on those range facts, a second step narrows the element-offset
chains feeding pto.load/pto.store/pto.addptr to i32 whenever a local
recursive range evaluation proves the chain stays in [0, 2^32). The
default lowering widens these chains to index/i64 because loop
induction variables are index-typed, so each mul/add lowers as a 64-bit
operation although A5 scalar units compute in 32 bits. Two rewrites
fall out of one traversal: a fully provable chain replaces the offset
operand outright (converted back with index_castui), and a provable
operand under an unprovable parent -- e.g. `w * 2048` beneath
`+ block_idx`, where the runtime block count blocks the proof -- is
still swapped for its i32 mirror widened back with a zero-extend.

The evaluator is local rather than a dataflow-framework analysis on
purpose: the rewrite needs operand-level subchain decisions (narrow
only the provable operand of an unprovable parent, e.g. `w * 2048`
beneath `+ block_idx`) that a global sparse range analysis does not
drive on its own, and the chain grammar is closed, so local evaluation
is both sound and precise for it; anything unprovable keeps the wide
form. Mirrored div/rem ops are emitted unsigned so the shift-friendly
udiv/urem forms established by the int-range phase are preserved. andi
is deliberately excluded from the grammar: the chains it feeds (e.g.
the ping-pong UB buffer address `(w & 1) * 4096`) sit on the MTE
address path, where the extra trunc/zext pair costs more than the i64
mul and measurably regresses the kernel.

On the SIMT-VF per-token cast-to-fp8 example (8192x8192) the narrowed
kernel measures level with the AscendC-backend build of the same
program on a quiet A5 board (0.291 ms vs 0.288 ms, both within the
run-to-run noise band), with numerics unchanged; the narrowing makes
the PTO addressing arithmetic match AscendC's all-int32 forms, removing
the 64-bit address-math divergence previously visible in trace
comparisons.

Three 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; the guarded-LICM pipeline test
now expects the narrowed IV chain. A new test covers both narrowing
outcomes. Closes hw-native-sys#1493.
@jimmychou0
jimmychou0 force-pushed the zjm/issue1493-tid-pow2-range branch from 316068e to 9d19881 Compare September 13, 2026 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant