Bug type: crash
Component: SLPVectorizer
Bug location: llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp: BoUpSLP::tryToVectorizeGatheredLoads
Issue title: Crash in SLPVectorizer.cpp
Labels: llvm:SLPVectorizer
crash
Reported by: psteinfeld

I'm seeing a problem that shows up in the flang compiler when compiling a large, proprietary benchmark. Since the benchmark is proprietary, I can't include the source code in this bug report.

The problem happens in in the file `.../llvm-project/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp` in the function `BoUpSLP::tryToVectorizeGatheredLoads`.

In the code around line 6906, is the statement:

```
ArrayRef<LoadInst *> Slice = ArrayRef(Loads).slice(Cnt, NumElts);
```

The code for `slice()` is in `llvm-project/llvm/include/llvm/ADT/ArrayRef.h`. It contains the line:

```
assert(N+M <= size() && "Invalid specifier");
```

This assertion fails, and the compiler crashes.

I added debug code to `SLPVectorizer.cpp` to trace how the variables `Cnt` and `NumElts` got their values leading up to the point of failure. I've included the output of running the compiler on the test case in the file `log` along with the source code of the modified version of `SLPVectorizer.cpp` in the attached zip file.

At the point of failure, the value of `N` (which corresponds to `Cnt`) is 8. The value of `M` (passed from `NumElts`) is also 8. The call to `size()` returns 14. Since 8 + 8 is not less than or equal to 14, the assert fails and the compiler crashes.

I captured the LLVM IR of the original test case and included it below in the file `x13.f90.ll` in the attached zip file.

If you run this command after downloading `x13.f90.ll`, you'll see the error:

```
#!/bin/bash
../bin/clang -c -O3 -march=native -mllvm -locally-hot-callsite-threshold=4500 x13.f90.ll
```

I also ran the `x13.f90.ll` file through `llvm-reduce` using the following command:

```
llvm-reduce --test pcrash.sh x13.f90.ll
```

This produced a file called `reduced.ll` that I've included in the attached zip file. I've also included the log file `reduced.log` that shows what happend when you run the command:

```
#!/bin/bash
../bin/clang -c -O3 -march=native -mllvm -locally-hot-callsite-threshold=4500 reduced.ll
```

@alexey-bataev, please let me know if someone else should look at this or if I can help out by adding additional debug information.

[Archive.zip](https://github.com/user-attachments/files/17805978/Archive.zip)



Fix patch:
commit ad9c0b369e86e75d56e229f294782a4eaf527226
Author: Alexey Bataev <a.bataev@outlook.com>
Date:   Mon Nov 18 13:49:04 2024 -0800

    [SLP]Check if the gathered loads form full vector before attempting build it
    
    Need to check that the number of gathered loads in the slice forms the
    build vector to avoid compiler crash.
    
    Fixes #116691

diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 918d7663548f..dc0dffd9fcbf 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -6815,16 +6815,7 @@ void BoUpSLP::tryToVectorizeGatheredLoads(
         // Check if it is profitable to try vectorizing gathered loads. It is
         // profitable if we have more than 3 consecutive loads or if we have
         // less but all users are vectorized or deleted.
-        bool AllowToVectorize =
-            NumElts >= 3 ||
-            any_of(ValueToGatherNodes.at(Slice.front()),
-                   [=](const TreeEntry *TE) {
-                     return TE->Scalars.size() == 2 &&
-                            ((TE->Scalars.front() == Slice.front() &&
-                              TE->Scalars.back() == Slice.back()) ||
-                             (TE->Scalars.front() == Slice.back() &&
-                              TE->Scalars.back() == Slice.front()));
-                   });
+        bool AllowToVectorize = false;
         // Check if it is profitable to vectorize 2-elements loads.
         if (NumElts == 2) {
           bool IsLegalBroadcastLoad = TTI->isLegalBroadcastLoad(
@@ -6861,6 +6852,19 @@ void BoUpSLP::tryToVectorizeGatheredLoads(
             return true;
           };
           AllowToVectorize = CheckIfAllowed(Slice);
+        } else {
+          AllowToVectorize =
+              (NumElts >= 3 ||
+               any_of(ValueToGatherNodes.at(Slice.front()),
+                      [=](const TreeEntry *TE) {
+                        return TE->Scalars.size() == 2 &&
+                               ((TE->Scalars.front() == Slice.front() &&
+                                 TE->Scalars.back() == Slice.back()) ||
+                                (TE->Scalars.front() == Slice.back() &&
+                                 TE->Scalars.back() == Slice.front()));
+                      })) &&
+              hasFullVectorsOrPowerOf2(*TTI, Slice.front()->getType(),
+                                       Slice.size());
         }
         if (AllowToVectorize) {
           SmallVector<Value *> PointerOps;
@@ -6903,7 +6907,8 @@ void BoUpSLP::tryToVectorizeGatheredLoads(
       }
       // Mark masked gathers candidates as vectorized, if any.
       for (unsigned Cnt : MaskedGatherVectorized) {
-        ArrayRef<LoadInst *> Slice = ArrayRef(Loads).slice(Cnt, NumElts);
+        ArrayRef<LoadInst *> Slice = ArrayRef(Loads).slice(
+            Cnt, std::min<unsigned>(NumElts, Loads.size() - Cnt));
         ArrayRef<Value *> Values(
             reinterpret_cast<Value *const *>(Slice.begin()), Slice.size());
         Results.emplace_back(Values, LoadsState::ScatterVectorize);


Regression tests:
file: llvm/test/Transforms/SLPVectorizer/X86/gathered-loads-non-full-reg.ll
commands: opt -S --passes=slp-vectorizer -mtriple=x86_64-unknown-linux -mcpu=cascadelake < %s
tests: test_name: <module>
test_body: 
@solid_ = external global [608 x i8]

define void @test(ptr noalias %0) {
;
.lr.ph1019:
  %1 = getelementptr i8, ptr %0, i64 8
  %2 = getelementptr i8, ptr %0, i64 32
  %3 = getelementptr i8, ptr %0, i64 128
  %4 = getelementptr i8, ptr %0, i64 200
  %5 = getelementptr i8, ptr %0, i64 208
  %6 = getelementptr i8, ptr %0, i64 232
  %7 = getelementptr i8, ptr %0, i64 288
  %8 = getelementptr i8, ptr %0, i64 320
  %9 = getelementptr i8, ptr %0, i64 304
  %10 = getelementptr i8, ptr %0, i64 424
  %11 = getelementptr i8, ptr %0, i64 480
  %12 = getelementptr i8, ptr %0, i64 504
  %13 = getelementptr i8, ptr %0, i64 632
  %14 = getelementptr i8, ptr %0, i64 720
  %15 = load double, ptr %1, align 8
  %16 = load double, ptr %2, align 8
  %17 = fadd double %16, %15
  %18 = load double, ptr %3, align 8
  %19 = load double, ptr %4, align 8
  %20 = load double, ptr %5, align 8
  %21 = load double, ptr %6, align 8
  %22 = fadd double %21, %20
  %23 = load double, ptr %7, align 8
  %24 = load double, ptr %8, align 8
  %25 = load double, ptr %9, align 8
  %26 = load double, ptr %10, align 8
  %27 = load double, ptr %11, align 8
  %28 = load double, ptr %12, align 8
  %29 = fadd double %28, %27
  %30 = fmul double %22, %18
  %31 = fmul double %30, 0.000000e+00
  %32 = fsub double 0.000000e+00, %25
  %33 = fmul double %32, 0.000000e+00
  %34 = fadd double %33, 0.000000e+00
  %35 = fmul double %34, 0.000000e+00
  %36 = fmul double %29, %26
  %37 = fmul double %36, 0.000000e+00
  %38 = fadd double %37, 0.000000e+00
  %39 = fsub double %17, %19
  %40 = fmul double %39, %23
  %41 = fmul double %40, 0.000000e+00
  %42 = load double, ptr %0, align 8
  %43 = load double, ptr %13, align 8
  %44 = fmul double %43, %31
  %45 = load double, ptr %14, align 8
  %46 = fmul double %35, 0.000000e+00
  %47 = fadd double %44, 0.000000e+00
  %48 = fmul double %45, %38
  %49 = fmul double %45, %41
  store double %46, ptr getelementptr inbounds (i8, ptr @solid_, i64 384), align 8
  store double %47, ptr getelementptr inbounds (i8, ptr @solid_, i64 408), align 8
  store double %48, ptr getelementptr inbounds (i8, ptr @solid_, i64 392), align 8
  store double %49, ptr getelementptr inbounds (i8, ptr @solid_, i64 400), align 8
  %.neg965 = fmul double %48, %24
  %reass.add993 = fadd double %.neg965, 0.000000e+00
  %50 = fadd double %42, %reass.add993
  %51 = fsub double 0.000000e+00, %50
  store double %51, ptr getelementptr inbounds (i8, ptr @solid_, i64 296), align 8
  %.neg969 = fmul double %49, 0.000000e+00
  %reass.add996 = fadd double %.neg969, 0.000000e+00
  %52 = fadd double %45, %reass.add996
  %53 = fsub double 0.000000e+00, %52
  store double %53, ptr getelementptr inbounds (i8, ptr @solid_, i64 304), align 8
  ret void
}