llvm / llvm/llvm-project

[LoopVectorize] `-vector-library` selects a 512-bit veclib call (`amd_vrd8_log`) on an AVX2-only target

Open
#204,930 4 comments 0 reactions 0 assignees View on GitHub
miscompilation vectorizers
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

Disclaimer: AI generated. Found during other work and manually prompted. No auto-discovery / bounty /karma seeking entreprise here (pinky swear).

## Summary

With `-vector-library=AMDLIBM` on a target whose maximum legal vector width is 256-bit (e.g. `znver1`, `-march=x86-64-v3`), a loop over `float` whose body computes `log` in `double` is vectorized at VF=8. The resulting `<8 x double>` `log` is lowered to `amd_vrd8_log` — an 8-wide-double (512-bit) routine that executes AVX-512 instructions — so the binary SIGILLs at runtime on a CPU without AVX-512. A pure-`double` loop in the same translation unit is correctly capped at `amd_vrd4_log` (256-bit), so the width gate exists but is not applied to a wider-element library call that arises inside a narrower-element loop.

## Reproducer

`repro.c`:

```c
#include

// float loop, but log() runs in double -> <8 x double> at VF=8.
void f(float *out, const float *in, long n) {
for (long i = 0; i < n; i++)
out[i] = (float) log((double) in[i]);
}

// Control: pure-double loop.
void g(double *out, const double *in, long n) {
for (long i = 0; i < n; i++)
out[i] = log(in[i]);
}
```

Compile to asm:

```
clang -O3 -march=znver1 -fno-math-errno -fveclib=AMDLIBM -S repro.c -o repro.s
grep -oE 'callq\s+amd_vr[a-z0-9_]+' repro.s | sort -u
```

Or via `opt` on the IR:

```
clang -O0 -Xclang -disable-O0-optnone -march=znver1 -fno-math-errno -emit-llvm -S repro.c -o repro.ll
opt -passes='default' -vector-library=AMDLIBM repro.ll -S | grep -oE 'amd_vr[a-z0-9_]+'
```

## Expected vs actual

| function | expected on AVX2 (max 256-bit) | actual |
|---|---|---|
| `g` (pure double) | `amd_vrd4_log` (256-bit) | `amd_vrd4_log` — OK |
| `f` (float loop, double log) | `amd_vrd4_log` / native split | `amd_vrd8_log` (512-bit, AVX-512 only) — BUG |

## Confirmed crash on real hardware

On an AMD Ryzen 5 2400G (znver1, AVX2, no AVX-512), the binary above SIGILLs in an AVX-512 instruction:

```
Program received signal SIGILL, Illegal instruction.
0x00007ffff7db94ea in amd_vrd8_log_zn4 () from libalm.so
=> 0x7ffff7db94ea : vmovapd %zmm0,%zmm6
#0 amd_vrd8_log_zn4 ()
#1 f ()
```

`amd_vrd8_log` is a real AMDLIBM symbol; it dispatches only to AVX-512 implementations (`_avx512`/`_zn4`/`_zn5`) — there is no 256-bit variant of an 8-wide-double routine, so any call to it on a sub-AVX-512 target is unconditionally illegal.

## Notes

Reproduced with clang/LLVM 22.1.8 (current release) via the `-fveclib=AMDLIBM` driver flag; not yet checked against trunk.

Contributor guide

Open the contributing guide

Research direction

Start by running the repro.c clang and opt commands with -vector-library=AMDLIBM, then inspect the LoopVectorize vector-library width handling for the float loop whose log call becomes <8 x double>. Done means an AVX2-only target no longer emits amd_vrd8_log for f, while the pure-double control remains capped at amd_vrd4_log.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
compilers, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.