Skip to content

Emit masked gathers for predicated non-dense vector loads - #9684

Open
derek-gerstmann wants to merge 1 commit into
mainfrom
dg/predicated_gather
Open

derek-gerstmann wants to merge 1 commit into
mainfrom
dg/predicated_gather

Conversation

@derek-gerstmann

@derek-gerstmann derek-gerstmann commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Issue

Predicated loads that aren't dense or stride -1 were scalarized through Call::if_then_else, which emits a conditional branch per lane. Vectorized loops with TailStrategy::GuardWithIf emit predicated gathers in their tails, so pipelines with many table lookups produced tens of thousands of tiny basic blocks (e.g. at 64 lanes on AVX-512), causing LLVM to spend hundreds of seconds in SLPVectorizerPass and LICM.

Changes

  • src/CodeGen_LLVM.cpp:
    • Emit llvm.masked.gather (builder->CreateMaskedGather) in CodeGen_LLVM::codegen_predicated_load for fixed-width vector types (isa<FixedVectorType>(llvm_type_of(op->type))) so the backend either selects a native gather or scalarizes the intrinsic late after the mid-level optimizer has run.
    • Guard the constant-zero index shortcut in CodeGen_LLVM::codegen_buffer_pointer with !index->getType()->isVectorTy() so constant-zero vector indices still return a vector of pointers rather than a scalar base pointer.
  • test/correctness/predicated_gather.cpp, test/correctness/CMakeLists.txt:
    • Add correctness_predicated_gather testing predicated gathers across vector widths ({3, 4, 16, 64}), strides ({1, 3}), constant-zero vector indices (clamp(i, 0, 0)), and element types (float, uint8_t, int16_t, double), and verifying that llvm.masked.gather is emitted on x86.

Breaking changes

None.

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)

Co-authored-by: Bob Gardner
Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com

Predicated loads that aren't dense or stride -1 were scalarized through
if_then_else, which emits a branch per lane. Vectorized loops with
TailStrategy::GuardWithIf get predicated gathers in their tails, so a pipeline
with many table lookups produced tens of thousands of tiny basic blocks, and
LLVM's SLP vectorizer and LICM spent a very long time on them. One Photoshop
generator took ~27 minutes to compile for AVX-512 (64-lane vectors), 826s of
it in SLPVectorizerPass.

Use llvm.masked.gather instead, for fixed-width vectors. The backend selects
a native gather where it is legal and profitable, and otherwise scalarizes it
late, after the mid-level optimizer has run. That generator now compiles in
83s for AVX-512 and 107s for AVX2 (previously 339s).

Also guard the constant-zero index shortcut in CodeGen_LLVM::codegen_buffer_pointer
with !index->getType()->isVectorTy() so vector zero indices still produce a vector
of pointers for CreateMaskedGather.

Co-authored-by: Bob Gardner <bgardner@adobe.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment thread src/CodeGen_LLVM.cpp
add_streaming_load_metadata(gather);
}
value = gather;
} else { // It's not dense vector load, we need to scalarize it

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What actually reaches the else case now? Scalarizing non-fixed-width vectors sounds impossible.

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants