llvm / llvm/llvm-project

[VectorCombine] Re-vectorize matching scalar ops on multiple extracted elements

Open
#216,612 2 comments 0 reactions 0 assignees View on GitHub
llvm:vectorcombine missed-optimization
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

LLVM can miss opportunities where multiple elements are extracted from the same vector, undergo the same scalar ops, and are later consumed independently or assembled back into vector form.

For example, using zext:

```llvm
define <4 x i64> @src(<4 x i64> %A, <4 x i16> %V) {
entry:
%x0 = extractelement <4 x i16> %V, i32 0
%z0 = zext i16 %x0 to i64
%r0 = insertelement <4 x i64> %A, i64 %z0, i32 0

%x1 = extractelement <4 x i16> %V, i32 1
%z1 = zext i16 %x1 to i64
%r1 = insertelement <4 x i64> %r0, i64 %z1, i32 2

ret <4 x i64> %r1
}
````

The two scalar casts can instead be factored into one vector cast:

```llvm
define <4 x i64> @tgt(<4 x i64> %A, <4 x i16> %V) {
entry:
%wide = zext <4 x i16> %V to <4 x i64>

%z0 = extractelement <4 x i64> %wide, i32 0
%r0 = insertelement <4 x i64> %A, i64 %z0, i32 0

%z1 = extractelement <4 x i64> %wide, i32 1
%r1 = insertelement <4 x i64> %r0, i64 %z1, i32 2

ret <4 x i64> %r1
}
```

Alive2: https://alive2.llvm.org/ce/z/iJ7EQH

The underlying transformation is more generally:

```text
cast(extractelement V, i)
cast(extractelement V, j)
...

->

W = vector_cast V
extractelement W, i
extractelement W, j
...
```

when enough matching scalar casts from the same source vector exist for the vector form to be profitable.

For a single use, moving:

```text
cast(extractelement V, i)
```

to:

```text
extractelement(vector_cast V, i)
```

may not be profitable because the cast is performed across the whole vector.

With multiple extracted lanes, however, the vector cast is shared:

```text
N * (extract + scalar cast)

vs.

vector cast + N * extract
```

The surrounding users can also expose additional simplifications. In the motivating example, the extracted/casted values are inserted into another vector. Once the casts are represented in vector form, the extract/insert construction can be expressed as a `shufflevector`.

For the motivating case, after normal LLVM optimization:

```text
source: 7 non-terminator IR instructions, TTI cost 9
candidate: 4 non-terminator IR instructions, TTI cost 5
```

So the larger pattern is effectively:

```text
extract -> zext -> insert
extract -> zext -> insert

->

vector zext
+
shufflevector
```

This seems closely related to VectorCombine's existing responsibility for optimizing scalar/vector interactions, but current transforms appear not to factor the same scalar cast across multiple `extractelement`s from one existing vector in this case.

I think an initial implementation could be deliberately narrow and consider casts such as:

```text
zext
sext
trunc
```

with constant `extractelement` indices and compatible source/destination types.

Profitability should be determined using TTI rather than assuming that two uses are always sufficient.

A possible broader generalization would be to matching lane-wise scalar operations:

```text
OP(extractelement V, i)
OP(extractelement V, j)
```

becoming:

```text
W = vector OP V
extractelement W, i
extractelement W, j
```

when profitable. That starts to overlap more substantially with SLP/re-vectorization, so it may be better considered separately.

The motivating opportunity here is narrower: avoid scalarizing multiple lanes of an already-existing vector solely to perform the same cast independently.

I have not measured how frequently this exact pattern occurs in production IR. The motivating example was reduced from LLVM regression-test-derived IR; this report is primarily about the missing scalar/vector factoring opportunity and the resulting improvement under LLVM's existing IR cost model.

Assisted by gpt-5.6-sol

Contributor guide

Open the contributing guide

Research direction

Start with VectorCombine's existing scalar/vector interaction transforms and reproduce the motivating IR from the issue. Check how TTI evaluates the repeated scalar casts versus a shared vector cast, then verify that the narrow constant-index zext, sext, or trunc transformation preserves the stated improvement and exposes the surrounding simplification.

Written by the indexing model from the issue text.

Assessment

Domain
compilers
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.