[llvm] e04b7ce - [X86] Remove TuningFastVariableCrossLaneShuffle from X86_64V4Tuning (#212178)

via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 25 00:01:22 PDT 2026


Author: Rohan Shenoy
Date: 2026-08-25T12:31:16+05:30
New Revision: e04b7cec1ea54c908f2c9c576464f366b7858bba

URL: https://github.com/llvm/llvm-project/commit/e04b7cec1ea54c908f2c9c576464f366b7858bba
DIFF: https://github.com/llvm/llvm-project/commit/e04b7cec1ea54c908f2c9c576464f366b7858bba.diff

LOG: [X86] Remove TuningFastVariableCrossLaneShuffle from X86_64V4Tuning (#212178)

## 1. Background

This flag pushes the compiler toward a shuffle variant that pulls its
permutation pattern from memory, even when that pattern is a
compile-time constant that could just be baked straight into the
instruction. Skipping the load shows gains whenever the pattern is fixed
like that.

We propose dropping it from the `x86-64-v4` tuning list as both SPEC and
the microbenchmark below show a real improvement in runtime in both
Intel and AMD machines.

## 2. SPEC impact

We ran SPEC CPU2017 `base` tuning, `refrate`. No regressions observed in
any of the other benchmarks in the suite, and **noticable gains were
observed in 557.xz_r**
**Config:** clang `23.0.0git`, two builds from the same upstream tree
(baseline vs. `TuningFastVariableCrossLaneShuffle` removed). `-O3 -flto
-march=x86-64-v4`.

#### 557.xz_r results


| Machine | Copies | Iterations | Patched vs Baseline score difference |
| ---------------------------------- | ------ | ---------- | --------- |
| Rocket Lake (Intel Core i5-11600K) | 10     | 3          | **+1.3%** |
| Granite Rapids (Intel Xeon 6980P)  | 256    | 3          | **+1.5%** |
| Genoa (AMD EPYC 9654, Zen4)        | 256    | 3          | **+3.9%** |
| Turin (AMD EPYC 9755, Zen5)        | 512    | 3          | **+2.9%** |


## 3. Microbenchmark impact

**Microkernel** (derived from
`llvm/test/CodeGen/X86/pr77459.ll`:`reverse_cmp_v8i1`, which was
modified in this commit.)
_Created with the help of Claude Code._

```c
#include <immintrin.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>

__attribute__((noinline))
uint8_t reverse_cmp_v8i1(__m128i a0, __m128i a1) {
    __mmask8 m = _mm_cmpeq_epi16_mask(a0, a1);
    return __builtin_bitreverse8((uint8_t)m);
}

static double now_ns(void) {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return (double)ts.tv_sec * 1e9 + (double)ts.tv_nsec;
}

/* Precomputed OUTSIDE the timed region: 64 genuinely distinct inputs,
 * padded to 96 so the inner 32 reads never need a wraparound check. */
#define ARR_SIZE 64
static __m128i inputs[ARR_SIZE + 32];

int main(void) {
    __m128i a1 = _mm_set_epi16(1, 2, 3, 0, 5, 6, 7, 8);
    volatile uint8_t sink = 0;
    long N = 40000000L;

    for (int j = 0; j < ARR_SIZE + 32; j++) {
        int k = j % ARR_SIZE;
        inputs[j] = _mm_set_epi16(k, k+1, k+2, k+3, k+4, k+5, k+6, k+7);
    }

    reverse_cmp_v8i1(inputs[0], a1); /* warm up */

    size_t idx = 0;
    double t0 = now_ns();
    for (long i = 0; i < N; i++) {
        __m128i *p = &inputs[idx];
        uint8_t r0  = reverse_cmp_v8i1(p[0],  a1); 
        uint8_t r1  = reverse_cmp_v8i1(p[1],  a1);
        uint8_t r2  = reverse_cmp_v8i1(p[2],  a1);
        uint8_t r3  = reverse_cmp_v8i1(p[3],  a1);
        uint8_t r4  = reverse_cmp_v8i1(p[4],  a1);
        uint8_t r5  = reverse_cmp_v8i1(p[5],  a1);
        uint8_t r6  = reverse_cmp_v8i1(p[6],  a1);
        uint8_t r7  = reverse_cmp_v8i1(p[7],  a1);
        uint8_t r8  = reverse_cmp_v8i1(p[8],  a1);
        uint8_t r9  = reverse_cmp_v8i1(p[9],  a1);
        uint8_t r10 = reverse_cmp_v8i1(p[10], a1);
        uint8_t r11 = reverse_cmp_v8i1(p[11], a1);
        uint8_t r12 = reverse_cmp_v8i1(p[12], a1);
        uint8_t r13 = reverse_cmp_v8i1(p[13], a1);
        uint8_t r14 = reverse_cmp_v8i1(p[14], a1);
        uint8_t r15 = reverse_cmp_v8i1(p[15], a1);
        uint8_t r16 = reverse_cmp_v8i1(p[16], a1);
        uint8_t r17 = reverse_cmp_v8i1(p[17], a1);
        uint8_t r18 = reverse_cmp_v8i1(p[18], a1);
        uint8_t r19 = reverse_cmp_v8i1(p[19], a1);
        uint8_t r20 = reverse_cmp_v8i1(p[20], a1);
        uint8_t r21 = reverse_cmp_v8i1(p[21], a1);
        uint8_t r22 = reverse_cmp_v8i1(p[22], a1);
        uint8_t r23 = reverse_cmp_v8i1(p[23], a1);
        uint8_t r24 = reverse_cmp_v8i1(p[24], a1);
        uint8_t r25 = reverse_cmp_v8i1(p[25], a1);
        uint8_t r26 = reverse_cmp_v8i1(p[26], a1);
        uint8_t r27 = reverse_cmp_v8i1(p[27], a1);
        uint8_t r28 = reverse_cmp_v8i1(p[28], a1);
        uint8_t r29 = reverse_cmp_v8i1(p[29], a1);
        uint8_t r30 = reverse_cmp_v8i1(p[30], a1);
        uint8_t r31 = reverse_cmp_v8i1(p[31], a1);
        sink = r0^r1^r2^r3^r4^r5^r6^r7^r8^r9^r10^r11^r12^r13^r14^r15
             ^r16^r17^r18^r19^r20^r21^r22^r23^r24^r25^r26^r27^r28^r29^r30^r31;
        idx += 1;
        if (idx >= ARR_SIZE) idx = 0; 
    }
    double t1 = now_ns();

    printf("reverse_cmp_v8i1: %.4f ns/call (sink=%u)\n",
           (t1 - t0) / (N * 32), (unsigned)sink);
    return 0;
}
```

**Config:** same two clang `23.0.0git` builds, compiled using `-O3
-march=x86-64-v4`


| Machine                     | Baseline | Patched  | Speedup    |
| --------------------------- | -------- | -------- | ---------- |
| Rocket Lake (i5-11600K)     | 1.318 ns | 0.866 ns | **+34.3%** |
| Ice Lake (Xeon 8380)        | 1.901 ns | 1.248 ns | **+34.4%** |
| Sapphire Rapids (8490H)     | 1.828 ns | 1.307 ns | **+28.5%** |
| Emerald Rapids (8592+)      | 2.205 ns | 1.533 ns | **+30.5%** |
| Granite Rapids (Xeon 6980P) | 1.200 ns | 0.867 ns | **+27.8%** |
| Genoa (EPYC 9654, Zen4)     | 1.475 ns | 1.196 ns | **+18.9%** |
| Turin (EPYC 9755, Zen5)     | 0.981 ns | 0.981 ns | ~0% |


**Codegen change**:

```asm
; baseline
vpcmpeqw    %xmm1, %xmm0, %k0
vpmovm2d    %k0, %ymm0
vmovdqa     .LCPI0_0(%rip), %ymm1     # ymm1 = [7,6,5,4,3,2,1,0]
vpermd      %ymm0, %ymm1, %ymm0
vmovmskps   %ymm0, %eax

; patched
vpcmpeqw    %xmm1, %xmm0, %k0
vpmovm2d    %k0, %ymm0
vpshufd     $27, %ymm0, %ymm0            
vpermq      $78, %ymm0, %ymm0           
vmovmskps   %ymm0, %eax
```

Added: 
    

Modified: 
    llvm/lib/Target/X86/X86.td
    llvm/test/CodeGen/X86/pr77459.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/X86/X86.td b/llvm/lib/Target/X86/X86.td
index 5a36549a61bc4..991370e736c29 100644
--- a/llvm/lib/Target/X86/X86.td
+++ b/llvm/lib/Target/X86/X86.td
@@ -1012,7 +1012,6 @@ def ProcessorFeatures {
     TuningFastScalarFSQRT,
     TuningFastVectorFSQRT,
     TuningFast15ByteNOP,
-    TuningFastVariableCrossLaneShuffle,
     TuningFastVariablePerLaneShuffle,
     TuningPrefer256Bit,
     TuningFastGather,

diff  --git a/llvm/test/CodeGen/X86/pr77459.ll b/llvm/test/CodeGen/X86/pr77459.ll
index 5fdc71cf43071..be1ab64c33c6e 100644
--- a/llvm/test/CodeGen/X86/pr77459.ll
+++ b/llvm/test/CodeGen/X86/pr77459.ll
@@ -98,8 +98,8 @@ define i8 @reverse_cmp_v8i1(<8 x i16> %a0, <8 x i16> %a1) {
 ; AVX512:       # %bb.0:
 ; AVX512-NEXT:    vpcmpeqw %xmm1, %xmm0, %k0
 ; AVX512-NEXT:    vpmovm2d %k0, %ymm0
-; AVX512-NEXT:    vmovdqa {{.*#+}} ymm1 = [7,6,5,4,3,2,1,0]
-; AVX512-NEXT:    vpermd %ymm0, %ymm1, %ymm0
+; AVX512-NEXT:    vpshufd {{.*#+}} ymm0 = ymm0[3,2,1,0,7,6,5,4]
+; AVX512-NEXT:    vpermq {{.*#+}} ymm0 = ymm0[2,3,0,1]
 ; AVX512-NEXT:    vmovmskps %ymm0, %eax
 ; AVX512-NEXT:    # kill: def $al killed $al killed $eax
 ; AVX512-NEXT:    vzeroupper


        


More information about the llvm-commits mailing list