Skip to content

[REFACTOR][ARITH] Phase out arith/scalable_expression; arith no longer proves over scalable vectors#19638

Merged
tlopex merged 7 commits into
apache:mainfrom
tqchen:tvm-phase-out-scalable-expr
May 29, 2026
Merged

[REFACTOR][ARITH] Phase out arith/scalable_expression; arith no longer proves over scalable vectors#19638
tlopex merged 7 commits into
apache:mainfrom
tqchen:tvm-phase-out-scalable-expr

Conversation

@tqchen
Copy link
Copy Markdown
Member

@tqchen tqchen commented May 28, 2026

Summary

Phase out src/arith/scalable_expression.{h,cc}. The arith layer no longer attempts to prove anything about scalable vectors — proofs that depended on Target::Current() are removed. Scalable vectors remain a first-class concept; arith just doesn't reason about their lengths.

Use-site summary

Only 16 call sites total across 7 symbols (9 live, 7 proof-related).

Symbol Live callers (kept) Proof callers (deleted) New home
ExtractVscaleFactor 4 × arith/rewrite_simplify.cc + 2 × tirx/ir/expr.cc file-local in each
IsVScaleCall 1 × tirx/op/op.cc + 1 × tirx/transform/vectorize_loop.cc inline at use sites
ContainsVscaleCall 4 × arith/rewrite_simplify.cc + 1 × s_tir/schedule/ir_comparator.cc inline at use sites
TargetHasVLA 2 × tirx/transform/vectorize_loop.cc analyzer.cc + const_int_bound.cc local in vectorize_loop.cc
GetVScaleValues 1 × target/llvm/codegen_aarch64.cc analyzer.cc + const_int_bound.cc inlined at codegen_aarch64
CanProveVscaleExpressionFromKnownValues analyzer.cc DELETE
SubstituteVScaleWithKnownValue internal only DELETE

Changes (6 commits)

  1. Move ExtractVscaleFactor to file-local anonymous-namespace helpers in rewrite_simplify.cc and tirx/ir/expr.cc. Function is small; per-file duplication is cleaner than a shared header.
  2. Inline IsVScaleCall / ContainsVscaleCall / TargetHasVLA at call sites (1-3 line predicates, anonymous-namespace per consumer .cc).
  3. Drop the scalable-vector proof scaffolding from arith/analyzer.cc (substitution-proof loop) and arith/const_int_bound.cc (vscale branch). vscale() calls fall back to Everything() — no special bound narrowing.
  4. Delete scalable_expression.{h,cc}. Inline the GetVScaleValues body at codegen_aarch64.cc (computes max_val = vector_width / 8 floor-rounded to a power of two for the LLVM vscale_range attribute).
  5. Mark pytest.mark.xfail on 19 tests that relied on the deleted substitution-proof loop.
  6. pre-commit line-length cleanup.

Compatibility / intentional regression

This is a hard break for any consumer of the deleted symbols. They were already in a private header (src/arith/scalable_expression.h, not under include/).

19 tests that proved vscale-bearing inequalities on SVE / RVV are xfailed. The proofs were target-dependent and the new policy is that arith does not attempt them.

tqchen added 6 commits May 28, 2026 11:13
…rite_simplify, tirx/ir/expr)

ExtractVscaleFactor is a target-free pattern recognizer (matches
`multiplier * vscale()`) that does not belong in the arith layer.

Add an anonymous-namespace ExtractVscaleFactor in src/arith/rewrite_simplify.cc
(verbatim body using the existing pattern_match machinery) covering the 4
simplify call sites. Add an equivalent file-local helper in src/tirx/ir/expr.cc
(raw AST walk, no pattern_match dependency) covering the 2 Ramp/Broadcast
constructor call sites. Both files no longer include scalable_expression.h
for this function.

The function is small enough that two-file duplication is cleaner than a
shared cross-subsystem header.
… at call sites

IsVScaleCall (3-line predicate on tirx::builtin::vscale()) and
ContainsVscaleCall (1-line wrapper over CheckContains::ExprContains) are
tirx-level recognizers, not arith-level. TargetHasVLA belongs in the one
transform that actually needs it.

Each consumer file now declares a file-local anonymous-namespace helper
containing the same logic, and drops the arith::scalable_expression.h include
for these symbols:

- src/arith/rewrite_simplify.cc  (ContainsVscaleCall x4, IsVScaleCall used internally)
- src/tirx/op/op.cc              (IsVScaleCall x1)
- src/tirx/transform/vectorize_loop.cc (IsVScaleCall x1, TargetHasVLA x2)
- src/s_tir/schedule/ir_comparator.cc (ContainsVscaleCall x1)

vectorize_loop.cc also receives the TargetHasVLA file-local helper here,
since it is the only non-proof consumer and is already having its include
rewritten in this commit.
…r/const_int_bound

The arith layer no longer attempts to prove anything about expressions
containing vscale() calls. This is intentional: reasoning about
scalable-vector lengths at compile time requires Target::Current(), a
runtime-mutable global, which does not belong in the arith analysis layer.

Changes:
- src/arith/analyzer.cc: delete the substitution-proof block (lines 234-250)
  that iterated over known vscale values via TargetHasVLA + GetVScaleValues +
  CanProveVscaleExpressionFromKnownValues. vscale-bearing expressions now
  fall through to `return false` in CanProve. Drop scalable_expression.h include.
- src/arith/const_int_bound.cc: delete the vscale() branch in VisitExpr_(CallNode*)
  (lines 427-430) that returned MakeBound(1, max_val) via GetVScaleValues.
  vscale() calls now fall back to Everything(op->dtype). Drop scalable_expression.h
  and the Target::Current() call.

Behavioural regression: on VLA targets (SVE / RVV), the analyzer no longer
narrows vscale() bounds or proves vscale-bearing inequalities by substitution.
The simplifications in rewrite_simplify.cc that call CanProve on vscale
expressions (min/max vscale comparison blocks) will fire less often. This is
the intended policy change.
…ng users

All consumers of the now-empty arith/scalable_expression.{h,cc} have been
migrated in earlier commits. This commit performs the final deletion and
handles the two remaining legitimate non-proof callers.

src/target/llvm/codegen_aarch64.cc: inline GetVScaleValues at the one
non-proof call site (SetTargetAttributes). The function computed
max_val = largest power-of-two ≤ vector_width/8 from
target.llvm_get_vector_width. The inlined version uses a bit-shift loop
instead of std::pow, avoids building an intermediate vector, and guards
the attribute with max_val > 0.

src/tirx/transform/vectorize_loop.cc: TargetHasVLA and IsVScaleCall are
now file-local anonymous-namespace helpers (added in the recognizer commit).
No further changes needed here.

Delete src/arith/scalable_expression.h and src/arith/scalable_expression.cc.
CMake's file glob drops the .cc automatically; no CMakeLists.txt edit needed.

Post-deletion sweep confirms no remaining references to the deleted symbols
outside the expected file-local helpers.
…of loop

Removing the CanProveVscaleExpressionFromKnownValues substitution loop from
arith::CanProve and removing const_int_bound's vscale narrow weakens the
analyzer for scalable-vector expressions. Tests that relied on these proof
paths now fall short.

Affected test groups:
- TestScalableIndex (test_arith_rewrite_simplify.py): 13 min/max/floordiv/
  floormod simplifications that called CanProve on vscale-bearing predicates
  (e.g. vscale()*4 >= 0) are no longer provable. Marked xfail.
- test_simplify_vscale_comparison_with_sve_target (test_arith_simplify.py):
  4 direct tests of CanProve on vscale expressions with an SVE target.
  Marked xfail.
- test_simplify_vscale_comparison_without_sve_target (test_arith_simplify.py):
  Checked for LOG(WARNING) that was in the deleted proof block. Marked xfail.
- test_rvv_fast_softmax_vectorizes_exp (test_cpu_reduction.py): end-to-end
  RVV codegen test that required vscale bounds narrowing to produce scalable
  vector ops in the scheduled LLVM IR. Marked xfail.

All of these regressions are intentional: the policy change is that arith
no longer attempts to reason about scalable-vector lengths at the target level.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request removes the target-dependent proof loop for vscale-bearing expressions (CanProveVscaleExpressionFromKnownValues) and deletes the scalable_expression utility files. The helper functions are localized to the files that still require them, and several tests that relied on target-dependent vscale proofs are marked as expected failures. The review feedback suggests preventing potential undefined behavior in a bit-shift loop in codegen_aarch64.cc, adding an explicit cast to avoid narrowing conversion warnings in rewrite_simplify.cc, and removing a redundant namespace qualification in expr.cc.

Comment thread src/target/llvm/codegen_aarch64.cc Outdated
Comment thread src/arith/rewrite_simplify.cc
Comment thread src/tirx/ir/expr.cc
@tqchen tqchen force-pushed the tvm-phase-out-scalable-expr branch from aa028ad to e9f441f Compare May 28, 2026 18:34
…ector_width query

Outside an active target context Target::Current() returns an
undefined Target; passing it to llvm_get_vector_width_fn would
crash. Skip the vscale_range attribute when no target is active.
@tqchen tqchen force-pushed the tvm-phase-out-scalable-expr branch from e9f441f to 7647e45 Compare May 28, 2026 22:41
@tlopex tlopex merged commit 6b4b866 into apache:main May 29, 2026
10 checks passed
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.

2 participants