Poor codegen for AVX-512 `permutex2var_pd`/`_ps` compared to integer permutes for constant indices
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
Floating-point permutex2var intrinsics do not fold to valign for constant indices, unlike their integer counterparts.
I tried this code:
#![crate_type = "lib"]
use std::arch::x86_64::*;
#[unsafe(no_mangle)]
#[target_feature(enable = "avx512f")]
pub unsafe fn slide_ps_512(a: __m512, b: __m512) -> __m512 {
let index: __m512i =
std::mem::transmute([1_i32, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
_mm512_permutex2var_ps(a, index, b)
}
#[unsafe(no_mangle)]
#[target_feature(enable = "avx512f")]
pub unsafe fn slide_epi32_512(a: __m512i, b: __m512i) -> __m512i {
let index: __m512i =
std::mem::transmute([1_i32, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
_mm512_permutex2var_epi32(a, index, b)
}
#[unsafe(no_mangle)]
#[target_feature(enable = "avx512f")]
pub unsafe fn slide_pd_512(a: __m512d, b: __m512d) -> __m512d {
let index: __m512i = std::mem::transmute([1_i64, 2, 3, 4, 5, 6, 7, 8]);
_mm512_permutex2var_pd(a, index, b)
}
#[unsafe(no_mangle)]
#[target_feature(enable = "avx512f")]
pub unsafe fn slide_epi64_512(a: __m512i, b: __m512i) -> __m512i {
let index: __m512i = std::mem::transmute([1_i64, 2, 3, 4, 5, 6, 7, 8]);
_mm512_permutex2var_epi64(a, index, b)
}
#[unsafe(no_mangle)]
#[target_feature(enable = "avx512f,avx512vl")]
pub unsafe fn slide_ps_256(a: __m256, b: __m256) -> __m256 {
let index: __m256i = std::mem::transmute([1_i32, 2, 3, 4, 5, 6, 7, 8]);
_mm256_permutex2var_ps(a, index, b)
}
#[unsafe(no_mangle)]
#[target_feature(enable = "avx512f,avx512vl")]
pub unsafe fn slide_epi32_256(a: __m256i, b: __m256i) -> __m256i {
let index: __m256i = std::mem::transmute([1_i32, 2, 3, 4, 5, 6, 7, 8]);
_mm256_permutex2var_epi32(a, index, b)
}
#[unsafe(no_mangle)]
#[target_feature(enable = "avx512f,avx512vl")]
pub unsafe fn slide_pd_256(a: __m256d, b: __m256d) -> __m256d {
let index: __m256i = std::mem::transmute([1_i64, 2, 3, 4]);
_mm256_permutex2var_pd(a, index, b)
}
#[unsafe(no_mangle)]
#[target_feature(enable = "avx512f,avx512vl")]
pub unsafe fn slide_epi64_256(a: __m256i, b: __m256i) -> __m256i {
let index: __m256i = std::mem::transmute([1_i64, 2, 3, 4]);
_mm256_permutex2var_epi64(a, index, b)
}
Godbolt: https://godbolt.org/z/Phvj8Gf78
I compiled it with:
rustc -O --emit=asm -Ctarget-cpu=icelake-client repro.rs
I expected to see this happen: the constant floating-point permutations should
be recognized as cross-vector slides and lower to one valignd or valignq,
just like the bit-identical integer forms. For example, the integer
epi32 function lowers to:
vmovdqa64 (%rdx), %zmm0
valignd $1, (%rsi), %zmm0, %zmm0
vmovdqa64 %zmm0, (%rdi)
Instead, this happened: the floating functions retain a separate constant
index-vector load and a general two-source permutation. For example,
_mm512_permutex2var_ps lowers to:
vmovaps (%rsi), %zmm0
vmovaps .LCPI7_0(%rip), %zmm1
vpermi2ps (%rdx), %zmm0, %zmm1
vmovaps %zmm1, (%rdi)
The same missed optimization occurs for _mm512_permutex2var_pd,
_mm256_permutex2var_ps, and _mm256_permutex2var_pd. Their corresponding
epi64/epi32 controls all fold to a single valignq/valignd.
Ideally the optimizer would recognize the same constant permutation regardless of whether the intrinsic uses floating or integer vector operands.
Placing bitcasts around the integer intrinsic is not an effective workaround: LLVM canonicalizes that expression back to vpermi2ps/vpermi2pd and retains the constant load. Pre-calculating a large byte-level index vector, casting the vector to i8 and calling _mm512_permutex2var_epi8 does work around this issue.
This is distinct from #156891 which affects 8-bit types with runtime indices, while this affects 32/64-bit floating-point types with const indices.
Meta
rustc --version --verbose:
rustc 1.97.1 (8bab26f4f 2026-07-14)
binary: rustc
commit-hash: 8bab26f4f68e0e26f0bb7960be334d5b520ea452
commit-date: 2026-07-14
host: x86_64-unknown-linux-gnu
release: 1.97.1
LLVM version: 22.1.6
Also reproduced with:
rustc 1.99.0-nightly (eff8269f7 2026-07-18)
binary: rustc
commit-hash: eff8269f797067c30555e77f160ec84c0ed15cd9
commit-date: 2026-07-18
host: x86_64-unknown-linux-gnu
release: 1.99.0-nightly
LLVM version: 22.1.8
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the provided Rust reproducer and compile it using the shown rustc command on an AVX-512 target. Compare the assembly for the floating-point and integer intrinsic entry points, including _mm512_permutex2var_ps/_pd and their epi32/epi64 counterparts. Done means constant floating-point permutations lower to the corresponding valign instruction without a separate index-vector load.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100