[GVN] store/partial-load forwarding of a vector type can spread poison to extra lanes
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
GVN seems to detect the load from a stored pointer, but since the sizes are different, it performs a bitcast-trunc-bitcast conversion. For vectors, this means that a poison in any lane becomes a poison in all lanes, even if the poison value was previously never observed by the return
Minimal repro: Running `opt -passes=gvn` on this:
```llvm
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
define <2 x i64> @do_stuff(<4 x i64> %v2, ptr %p) {
store <4 x i64> %v2, ptr %p, align 8
%1 = load <2 x i64>, ptr %p, align 8
ret <2 x i64> %1
}
```
->
```llvm
define <2 x i64> @do_stuff(<4 x i64> %v2, ptr %p) {
store <4 x i64> %v2, ptr %p, align 8
%1 = bitcast <4 x i64> %v2 to i256
%2 = trunc i256 %1 to i128
%3 = bitcast i128 %2 to <2 x i64>
ret <2 x i64> %3
}
```
Godbolt link: https://godbolt.org/z/E3EWMr13q
Alive case: https://alive2.llvm.org/ce/z/Mu4rFZ
```
Example:
<4 x i64> %v2 = < #x0000000000000003 (3), #x0000000000000003 (3), poison, #x0000000000000003 (3) >
Source:
<2 x i64> %#1 = < #x0000000000000003 (3), #x0000000000000003 (3) >
Target value: < poison, poison >
```
I believe it comes down to `getStoreValueForLoadHelper` here:
https://github.com/llvm/llvm-project/blob/d4676e649ba86d7c389e1afe6d9ed4d8443e23ef/llvm/lib/Transforms/Utils/VNCoercion.cpp#L321
The code seems old, I wasn't able to find a version of GVN w/o the issue to bisect on
I have confirmed this occurs on the latest trunk, b6059b6ce21886c3f8644355ae9017cf3ba04aad
For triage: this was found by a fuzzer for testing SIMD optimisations, not in real-world code. I'm also not sure of if there is a concrete end-to-end miscompile resulting from this, just the Alive validation failing
Contributor guide
Assessment
This issue has not been assessed yet.