Skip to content

Disable LLVM's VectorCombine pass on ARM - #9699

Open
abadams wants to merge 2 commits into
mainfrom
abadams/arm_disable_vector_combine
Open

abadams wants to merge 2 commits into
mainfrom
abadams/arm_disable_vector_combine

Conversation

@abadams

@abadams abadams commented Nov 11, 2026

Copy link
Copy Markdown
Member

Copilot says:

This is an alternative to #9698.

On AArch64 with LLVM 22 and later, LLVM's VectorCombine pass (foldShuffleOfCastops) narrows shuffle(zext x) into zext(shuffle x) across the shuffles Halide uses to split vectors into native-width pieces. This produces illegal types like <4 x i8>, which lower to mov/zip/bic sequences instead of ushll2/umull2. For example, u32(u8) * 1000 and a plain u32(u8) both hit this. #9698 works around this in call_intrin, but that fix relies on cost-model details and misses the plain-zext case. LLVM 21 doesn't have the problem.

This PR skips VectorCombine on ARM targets only, using a pass instrumentation callback on Halide's PassBuilder. It doesn't use the global -disable-vector-combine option, so other targets compiled in the same process are unaffected. The change is small and should backport cleanly to releases pinned to an older LLVM.

Tested with LLVM 23:

  • Both cases above now produce ushll/ushll2 + umull(2)/umlal(2), or tbl for the zext-only case.
  • Arm apps codegen (camera_pipe, local_laplacian, lens_blur, resize, hannk, ...) with VectorCombine on vs. off: hot inner loops total +35 instructions out of ~74k, which is noise. The largest differences:
    • local_laplacian −33 and depthwise_conv −13.
    • The elementwise ops +27/+29, mostly scalar fmov/mov.
    • camera_pipe +52, all in the top-level function's buffer-validation prologue (the color-correction loop is 4 instructions shorter).
  • x86 is unchanged. VectorCombine is still clearly useful there (disabling it costs +664 instructions on AVX2), so it stays enabled for x86.
  • correctness_simd_op_check_arm passes, including new sshll2/ushll2 checks.

Checklist

  • Tests added or updated (not required for docs, CI config, or typo fixes)
  • Documentation updated (if public API changed)
  • Python bindings updated (if public API changed)
  • Benchmarks are included here if the change is intended to affect performance.
  • Commits include AI attribution where applicable (see Code of Conduct)

VectorCombine's foldShuffleOfCastops narrows shuffle(zext x) into
zext(shuffle x) across the shuffles Halide uses to split vectors into
native-width pieces. On AArch64 with LLVM 22+ this produces illegal
narrow types (e.g. <4 x i8>) that lower to mov/zip/bic sequences instead
of ushll2/umull2, e.g. for u32(u8) * 1000.

On ARM, VectorCombine's benefit across the apps is negligible (hot inner
loops are net neutral), so skip it there via a pass instrumentation
callback. Other targets are unaffected.

Also add sshll2/ushll2 checks to simd_op_check_arm.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@codecov

codecov Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.95%. Comparing base (f8d34e4) to head (c8047ac).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9699      +/-   ##
==========================================
+ Coverage   72.89%   72.95%   +0.05%     
==========================================
  Files         266      266              
  Lines       83478    83483       +5     
  Branches    20497    20498       +1     
==========================================
+ Hits        60855    60905      +50     
- Misses      16173    16180       +7     
+ Partials     6450     6398      -52     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

LLVM main passes an IRUnitRef rather than an llvm::Any. Take the IR unit
as a generic const reference, which also satisfies clang-tidy.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant