Skip to content

feat(vpto): canonicalize integer-backed castptr for post-update addressing (#591) - #1353

Draft
mouliangyu wants to merge 9 commits into
hw-native-sys:mainfrom
mouliangyu:feature/vpto-integer-address-canonicalization
Draft

feat(vpto): canonicalize integer-backed castptr for post-update addressing (#591)#1353
mouliangyu wants to merge 9 commits into
hw-native-sys:mainfrom
mouliangyu:feature/vpto-integer-address-canonicalization

Conversation

@mouliangyu

Copy link
Copy Markdown
Collaborator

背景

Issue #591:vpto 在 rolled scf.for 里逐迭代重新物化 castptr(base + iv*stride) 的整数地址算术,泄漏成标量 RV_SADD/RV_SMOVK 上 vector pipe,序列化 load(WHT N=512 时 1640 个泄漏点,2.45× 退化)。

方案

两个新 pass,把地址规范成现有 post-update consumer(VPTOSoftPostUpdate)能直接消费的形态:

  1. pto-canonicalize-integer-address(规则 R,核心)
    castptr(byte_address)castptr(规范根) + addptr(element 商)

    • 仅 zero-origin integral 空间(A5 UB);
    • 精确商((B−R)/E)+ 位宽 round-trip proof;
    • 原子根例外:系数 1 的运行时基址参数保留为根(C15);
    • 拒绝:不可整除、多原子/非单位系数原子、pointer-derived integer、非 64 位输入;
    • 商直接物化在 index 域(供 SoftPostUpdate 分析),规范根提升到循环外;
    • 规则要求商非平凡 → 单趟收敛到规范形(幂等)。
  2. pto-absorb-addptr(规则 F / C11 fold)
    op(addptr(base, A), O)op(base, A+O),合法性仅来自 VPTOAddressSemanticsOpInterface(Element 单位、非 post-update、index 加法)。Bisheng 只有 operation-offset 形态才出 post-update。

配套:

  • pto.vstsx2 缺失的 VPTOAddressSemanticsOpInterface(与 vldsx2 对称);
  • getElementBytes 提升为共享 getPTOElementBytes(Type/Value)
  • ptoas 管线在 VPTOSoftPostUpdate 前接入两个 pass;
  • lit 测试:C01-C15 canonicalization 矩阵(含拒绝类与幂等双 RUN)+ absorb fold;
  • 设计文档:docs/designs/vpto-integer-address-canonicalization-design-zh.md
  • 复现文件:test/dsl/issue_591_repro.pto

端到端验证

issue_591_repro.ptocastptr(iv*4096) 的 rolled 循环)经两个新 pass 后:

%base = pto.castptr %c0_i64 : i64 -> !pto.ptr<f16, ub>   // 根(循环外)
scf.for %t = ... {
  %off = arith.muli %t, %c2048 : index                    // 商(element)
  %lo = pto.vldsx2 %base[%off], ...                       // offset 在 operation 上
}

→ SoftPostUpdate → @llvm.hivm.vldsx2.post.v128f16(ptr, 4096, ...)(phi 携带)→ bisheng device .text 与 VLDS post-update 形态逐字节一致,循环体内零标量地址 op(VLDI+SADD 形态为 0x128 字节,post-update 为 0x120 字节)。

验证

  • ninja ptobc PTOASCompiler pto-test-opt 构建通过(-Werror);
  • lit:integer_address_canonicalization.pto(RUN1 + IDEMPOTENT 双 RUN)、absorb_addptr_offset.pto 全部通过(pto-test-opt + FileCheck);
  • C15 原子根(castptr(%param))实测:根 + operation offset → vldsx2.post,与 castptr(0) 根字节级同构;根 + addptr(未 fold)→ 非 post(见设计文档 §6.5)。

测试命令

llvm-lit -sv test/lit/vpto/integer_address_canonicalization.pto
llvm-lit -sv test/lit/vpto/absorb_addptr_offset.pto

…ssing

Issue #591: vpto re-materializes castptr(base + iv*stride) address arithmetic
inside rolled scf.for loops, leaking scalar RV_SADD/RV_SMOVK ops onto the
vector pipe. Add two passes that normalize the address form so the existing
post-update consumer can strength-reduce it:

- pto-canonicalize-integer-address: castptr(byte_addr) ->
  castptr(canonical root) + addptr(element quotient) in zero-origin (UB)
  spaces, with exact-division proof, atom-root handling, and
  pointer-derived rejection; converges to the normal form in one pass.
- pto-absorb-addptr: op(addptr(base, A), O) -> op(base, A + O) using only
  VPTOAddressSemanticsOpInterface (Element-unit offsets).

Also adds the missing VPTOAddressSemanticsOpInterface on pto.vstsx2,
promotes getElementBytes into a shared helper, wires both passes before
VPTOSoftPostUpdate in the ptoas pipeline, and adds lit coverage (C01-C15
canonicalization matrix + absorb fold). End-to-end, the issue-591 repro now
emits llvm.hivm.vldsx2.post and a VLDS post-update device body with zero
scalar address ops in the tile loop.

Design: docs/designs/vpto-integer-address-canonicalization-design-zh.md
Review found two issues in pto-canonicalize-integer-address:

- Hoisting castptr(R) above an enclosing scf.for when the atom root R is
  defined inside the loop produced IR that violates SSA dominance. Only
  hoist when R is loop-invariant for every skipped loop; otherwise keep the
  base in place (legal IR, post-update simply does not fire).
- The root selection accepted a -1 coefficient while the design contract
  only allows a +1 atom; reject negative-coefficient atoms.

Adds lit coverage for the loop-local atom root, negative-coefficient
rejection, nested-loop hoisting, and vstsx2 addptr absorption.
…proof

Design C14 requires the quotient to round-trip losslessly into index; the
first implementation approximates this with the sufficient condition
inputWidth == index width. Narrower inputs are conservatively rejected
until PTO defines the zero/sign extension semantics of castptr into the
64-bit address space. Align the rejection message and the design doc with
this approximation instead of claiming a full round-trip proof.
castptr i32 inputs today lower straight to inttoptr i32 -> ptr addrspace(6)
(no width extension) and compile through bisheng; the narrow-to-64-bit
extension is an LLVM inttoptr implementation convention, not a PTO semantic
contract. Record this measured fact to justify keeping the 64-bit-only
approximation in the canonicalization rewrite.
… convention)

LLVM LangRef defines inttoptr width conversion explicitly: narrower
integers are zero-extended, wider are truncated. The PTO gap is only that
PTO does not document inheriting that semantics; the real LLVM undefined
behavior is provenance (inttoptr pointers based on no pointer are UB to
dereference), which the zero-origin UB model and this rewrite do not
change. Update the measured-evidence note and the C14 approximation note
accordingly.
…roof

Implement design C14 instead of the 64-bit-only approximation: narrower
integer inputs (i32 etc.) are accepted when the byte expression provably
does not wrap in its own width, using the LLVM inttoptr zero-extension
semantics recorded in design section 2.2. Constant expressions are proved
via foldPTOConstant; loop-carried expressions via PTOValueEvolutionAnalysis
getRange (non-negative, signed upper bound with headroom). Quotient leaves
materialize into index with index_castui (zero-extend). Wider-than-index
inputs still truncate and are rejected.

Known boundary: the analysis Mul range is currently index-only, so i32
loop recurrences such as muli(iv32, 4096) still return PossibleWrap and are
rejected even when mathematically non-wrapping; extending the analysis to
non-index arithmetic is future work.
Add two host-validation cases (run_host_vpto_validation.sh, dav_3510
simulator) measuring the rolled-vs-unrolled tile loop on the affine
castptr(t*4096) pattern:

- rolled (canonicalized): 0 RV_SADD/RV_SMOVK in the tile loop, 16 RV_VLDS
  post-update loads, kernal ticks 3959
- unrolled (baseline): 2 scalar addr ops in prologue, 15 RV_VLDS/1 RV_VLDI,
  kernal ticks 3957

rolled is 1.0005x of unrolled (target <= 1.1x) and rvec busy is 70 vs 71,
so design section 9 acceptance is met. Record the measured numbers in the
design doc, including the known boundary that VPTOSoftPostUpdate does not
convert dynamic-trip-count loops on this build (missing hw-native-sys hw-native-sys#1330;
cases use a constant trip count).
…tests

Full vpto lit run (545 tests) exposed two interaction bugs with the
default-on pipeline:

- absorb folded arbitrary addptr chains over user pointers, destroying
  VPTOSoftPostUpdate's sequential base-chain post-update structure
  (soft_postupdate_sequential-base-chain). Scope the fold to addptr whose
  base is an integer-backed castptr — the shape produced by
  pto-canonicalize-integer-address; chains are SoftPostUpdate's job.
- canonicalize/absorb changed the emitted shape of tests that previously
  kept integer castptr forms (auto_vecscope_infer_shared_ptr_capture:
  castptr(2048) -> castptr(0)+addptr(1024); soft_postupdate_delta-nonzero-lb:
  addptr recurrence absorbed into op offset). Update their CHECKs to the new
  canonical form.

Also extends coverage: C04 (i8 identity quotient), C06 (trunc rejection),
subtraction input, non-linear (srem) rejection, and a negative test that
chained addptr over a user pointer is NOT absorbed. After the fix the whole
test/lit/vpto suite passes except the out-of-tree baseline scratch file.

lit: 544/545 passed (only failure: untracked integer_address_forms_baseline
scratch, not part of this change).
The fold now applies only when the addptr base is an integer-backed
castptr (the canonical shape); arbitrary addptr chains stay with
VPTOSoftPostUpdate's sequential base-chain handling.
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.

1 participant