commit 57bbdbd7ae3698a274edd4dd6ef1b53d9129e552
Author: Alexey Bataev <a.bataev@outlook.com>
Date:   Mon Nov 25 07:56:45 2024 -0800

    [SLP]Relax assertion in mask combine for non-power-of-2 number of elements

    The nodes may contain non-power-of-2 number of elements. Need to relax
    the assertion to avoid possible compiler crash

    Fixes #117517

diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index d033b7c2ef4a..73218f377a06 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -1143,9 +1143,7 @@ static void addMask(SmallVectorImpl<int> &Mask, ArrayRef<int> SubMask,
   assert(
       (!ExtendingManyInputs || SubMask.size() > Mask.size() ||
        // Check if input scalars were extended to match the size of other node.
-       (SubMask.size() == Mask.size() &&
-        std::all_of(std::next(Mask.begin(), Mask.size() / 2), Mask.end(),
-                    [](int Idx) { return Idx == PoisonMaskElem; }))) &&
+       (SubMask.size() == Mask.size() && Mask.back() == PoisonMaskElem)) &&
       "SubMask with many inputs support must be larger than the mask.");
   if (Mask.empty()) {
     Mask.append(SubMask.begin(), SubMask.end());

opt -S --passes=slp-vectorizer -mtriple=x86_64-unknown-linux-gnu -slp-threshold=-10 < %s

[SLPVectorizer] Assertion "SubMask with many inputs support must be larger than the mask." failed.

Reproducer: https://godbolt.org/z/Mzbe1G875
```
; bin/opt -passes=slp-vectorizer test.ll -S
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 i64 @test() {
entry:
  %xor148.2.i = xor i32 0, 0
  %conv193.i = zext i32 %xor148.2.i to i64
  %conv193.1.i = zext i32 %xor148.2.i to i64
  %or194.1.i = or i64 %conv193.i, %conv193.1.i
  %xor148.2.i.1 = xor i32 0, 0
  %conv193.i.1 = zext i32 %xor148.2.i.1 to i64
  %or194.i.1 = or i64 %or194.1.i, %conv193.i.1
  %conv193.1.i.1 = zext i32 %xor148.2.i.1 to i64
  %or194.1.i.1 = or i64 %or194.i.1, %conv193.1.i.1
  %xor148.2.i.2 = xor i32 0, 0
  %conv193.i.2 = zext i32 %xor148.2.i.2 to i64
  %or194.i.2 = or i64 %or194.1.i.1, %conv193.i.2
  %conv193.1.i.2 = zext i32 %xor148.2.i.2 to i64
  %or194.1.i.2 = or i64 %or194.i.2, %conv193.1.i.2
  %xor148.2.i.3 = xor i32 0, 0
  %conv193.i.3 = zext i32 %xor148.2.i.3 to i64
  %or194.i.3 = or i64 %or194.1.i.2, %conv193.i.3
  %conv193.1.i.3 = zext i32 %xor148.2.i.3 to i64
  %or194.1.i.3 = or i64 %or194.i.3, %conv193.1.i.3
  %xor148.2.i.4 = xor i32 0, 0
  %conv193.i.4 = zext i32 %xor148.2.i.4 to i64
  %or194.i.4 = or i64 %or194.1.i.3, %conv193.i.4
  %conv193.1.i.4 = zext i32 %xor148.2.i.4 to i64
  %or194.1.i.4 = or i64 %or194.i.4, %conv193.1.i.4
  %xor148.2.i.5 = xor i32 0, 0
  %conv193.i.5 = zext i32 %xor148.2.i.5 to i64
  %or194.i.5 = or i64 %or194.1.i.4, %conv193.i.5
  %conv193.1.i.5 = zext i32 %xor148.2.i.5 to i64
  %or194.1.i.5 = or i64 %or194.i.5, %conv193.1.i.5
  %xor148.2.i.6 = xor i32 0, 0
  %conv193.i.6 = zext i32 %xor148.2.i.6 to i64
  %or194.i.6 = or i64 %or194.1.i.5, %conv193.i.6
  %or54.i.i.6 = or i32 %xor148.2.i.6, 0
  %conv193.1.i.6 = zext i32 %or54.i.i.6 to i64
  %xor148.2.i.7 = xor i32 0, 0
  %conv193.i.7 = zext i32 %xor148.2.i.7 to i64
  %0 = or i64 %or194.i.6, %conv193.i.7
  %conv193.1.i.7 = zext i32 %xor148.2.i.7 to i64
  %1 = or i64 %0, %conv193.1.i.7
  %or194.1.i.7 = or i64 %1, %conv193.1.i.6
  ret i64 %or194.1.i.7
}
```
```
opt: /root/llvm-project/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp:1143: void addMask(llvm::SmallVectorImpl<int>&, llvm::ArrayRef<int>, bool): Assertion `(!ExtendingManyInputs || SubMask.size() > Mask.size() || (SubMask.size() == Mask.size() && std::all_of(std::next(Mask.begin(), Mask.size() / 2), Mask.end(), [](int Idx) { return Idx == PoisonMaskElem; }))) && "SubMask with many inputs support must be larger than the mask."' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.	Program arguments: /opt/compiler-explorer/clang-assertions-trunk/bin/opt -o /app/output.s -S -passes=slp-vectorizer <source>
1.	Running pass "function(slp-vectorizer)" on module "<source>"
2.	Running pass "slp-vectorizer" on function "test"
 #0 0x00000000051b7378 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x51b7378)
 #1 0x00000000051b4d6c SignalHandler(int) Signals.cpp:0:0
 #2 0x0000751221842520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x00007512218969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #4 0x0000751221842476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #5 0x00007512218287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #6 0x000075122182871b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #7 0x0000751221839e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #8 0x00000000041e91f7 llvm::slpvectorizer::BoUpSLP::ShuffleCostEstimator::finalize(llvm::ArrayRef<int>, llvm::ArrayRef<std::pair<llvm::slpvectorizer::BoUpSLP::TreeEntry const*, unsigned int>>, llvm::ArrayRef<int>, unsigned int, llvm::function_ref<void (llvm::Value*&, llvm::SmallVectorImpl<int>&)>) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x41e91f7)
 #9 0x00000000042256f9 llvm::InstructionCost llvm::slpvectorizer::BoUpSLP::processBuildVector<llvm::slpvectorizer::BoUpSLP::ShuffleCostEstimator, llvm::InstructionCost, llvm::TargetTransformInfo, llvm::ArrayRef<llvm::Value*>, llvm::slpvectorizer::BoUpSLP, llvm::SmallPtrSetImpl<llvm::Value*>>(llvm::slpvectorizer::BoUpSLP::TreeEntry const*, llvm::Type*, llvm::TargetTransformInfo&, llvm::ArrayRef<llvm::Value*>&, llvm::slpvectorizer::BoUpSLP&, llvm::SmallPtrSetImpl<llvm::Value*>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x42256f9)
#10 0x000000000422f707 llvm::slpvectorizer::BoUpSLP::getEntryCost(llvm::slpvectorizer::BoUpSLP::TreeEntry const*, llvm::ArrayRef<llvm::Value*>, llvm::SmallPtrSetImpl<llvm::Value*>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x422f707)
#11 0x00000000042636a1 llvm::slpvectorizer::BoUpSLP::getTreeCost(llvm::ArrayRef<llvm::Value*>) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x42636a1)
#12 0x00000000042691b0 (anonymous namespace)::HorizontalReduction::tryToReduce(llvm::slpvectorizer::BoUpSLP&, llvm::DataLayout const&, llvm::TargetTransformInfo*, llvm::TargetLibraryInfo const&) SLPVectorizer.cpp:0:0
#13 0x000000000426c681 llvm::SLPVectorizerPass::vectorizeHorReduction(llvm::PHINode*, llvm::Instruction*, llvm::BasicBlock*, llvm::slpvectorizer::BoUpSLP&, llvm::SmallVectorImpl<llvm::WeakTrackingVH>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x426c681)
#14 0x0000000004270deb llvm::SLPVectorizerPass::vectorizeRootInstruction(llvm::PHINode*, llvm::Instruction*, llvm::BasicBlock*, llvm::slpvectorizer::BoUpSLP&) (.constprop.0) SLPVectorizer.cpp:0:0
#15 0x00000000042758a6 llvm::SLPVectorizerPass::vectorizeChainsInBlock(llvm::BasicBlock*, llvm::slpvectorizer::BoUpSLP&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x42758a6)
#16 0x000000000427bf86 llvm::SLPVectorizerPass::runImpl(llvm::Function&, llvm::ScalarEvolution*, llvm::TargetTransformInfo*, llvm::TargetLibraryInfo*, llvm::AAResults*, llvm::LoopInfo*, llvm::DominatorTree*, llvm::AssumptionCache*, llvm::DemandedBits*, llvm::OptimizationRemarkEmitter*) (.part.0) SLPVectorizer.cpp:0:0
#17 0x000000000427ca9b llvm::SLPVectorizerPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x427ca9b)
#18 0x0000000002fcc5ce llvm::detail::PassModel<llvm::Function, llvm::SLPVectorizerPass, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x2fcc5ce)
#19 0x0000000004fb6a68 llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4fb6a68)
#20 0x0000000000e3dd4e llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0xe3dd4e)
#21 0x0000000004fb548e llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4fb548e)
#22 0x0000000000e3d3fe llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0xe3d3fe)
#23 0x0000000004fb4ec0 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4fb4ec0)
#24 0x000000000090ce7a llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x90ce7a)
#25 0x00000000008ff519 optMain (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8ff519)
#26 0x0000751221829d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#27 0x0000751221829e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#28 0x00000000008f6d6e _start (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8f6d6e)
Program terminated with signal: SIGSEGV
Compiler returned: 139
```


commit 3d91d9613e294b242d853039209b40a0cb7853f2
Author: Florian Hahn <flo@fhahn.com>
Date:   Wed Jan 24 14:25:54 2024 +0000

    [ConstraintElim] Make sure min/max intrinsic results are not poison.

    The result of umin may be poison and in that case the added constraints
    are not be valid in contexts where poison doesn't cause UB. Only queue
    facts for min/max intrinsics if the result is guaranteed to not be
    poison.

    This could be improved in the future, by only adding the fact when
    solving conditions using the result value.

    Fixes https://github.com/llvm/llvm-project/issues/78621.

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 8f09569d0d9c..7b672e89b67a 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1061,11 +1061,16 @@ void State::addInfoFor(BasicBlock &BB) {
           FactOrCheck::getCheck(DT.getNode(&BB), cast<CallInst>(&I)));
       break;
     // Enqueue the intrinsics to add extra info.
-    case Intrinsic::abs:
     case Intrinsic::umin:
     case Intrinsic::umax:
     case Intrinsic::smin:
     case Intrinsic::smax:
+      // TODO: Check if it is possible to instead only added the min/max facts
+      // when simplifying uses of the min/max intrinsics.
+      if (!isGuaranteedNotToBePoison(&I))
+        break;
+      [[fallthrough]];
+    case Intrinsic::abs:
       WorkList.push_back(FactOrCheck::getInstFact(DT.getNode(&BB), &I));
       break;
     }

opt -passes=constraint-elimination -S %s

define i1 @smin_branchless(i32 %x, i32 %y) {
entry:
  %min = call i32 @llvm.smin.i32(i32 %x, i32 %y)
  %cmp1 = icmp sle i32 %min, %x
  %cmp2 = icmp sgt i32 %min, %x
  %ret = xor i1 %cmp1, %cmp2
  ret i1 %ret
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smin.i32(i32, i32) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

define i1 @umin_poison_call_before_UB(i32 %arg) {
  %icmp = icmp slt i32 %arg, 0
  %shl = shl nuw nsw i32 %arg, 3
  %min = call i32 @llvm.umin.i32(i32 %shl, i32 80)
  call void @fn()
  call void @noundef(i32 noundef %min)
  %cmp2 = shl nuw nsw i32 %arg, 3
  ret i1 %icmp
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umin.i32(i32, i32) #0

declare void @noundef(i32 noundef)

declare void @fn()

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

define i1 @umin_not_used(i32 %arg) {
  %icmp = icmp slt i32 %arg, 0
  %shl = shl nuw nsw i32 %arg, 3
  %1 = call i32 @llvm.umin.i32(i32 %shl, i32 80)
  %cmp2 = shl nuw nsw i32 %arg, 3
  ret i1 %icmp
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umin.i32(i32, i32) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

define i1 @umin_poison_is_UB_via_call(i32 %arg) {
  %icmp = icmp slt i32 %arg, 0
  %shl = shl nuw nsw i32 %arg, 3
  %min = call i32 @llvm.umin.i32(i32 %shl, i32 80)
  call void @noundef(i32 noundef %min)
  %cmp2 = shl nuw nsw i32 %arg, 3
  ret i1 %icmp
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umin.i32(i32, i32) #0

declare void @noundef(i32 noundef)

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

[ConstraintElimination] A miscompile in presence of `shl nuw nsw` and `llvm.umin`

The following commit: https://github.com/llvm/llvm-project/commit/71f56e49ceca75dbf82cbb9537c2545c2d2e51c9
Triggered this miscompile: https://alive2.llvm.org/ce/z/oBg-u8

Now `opt -passes=constraint-elimination -S` turns
```
define i1 @test(i32 %arg) {
  %icmp = icmp slt i32 %arg, 0
  %shl = shl nuw nsw i32 %arg, 3
  %call4 = call i32 @llvm.umin.i32(i32 %shl, i32 80)
  ret i1 %icmp
}

declare i32 @llvm.umin.i32(i32, i32) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
```
into
```
define i1 @test(i32 %arg) {
  %shl = shl nuw nsw i32 %arg, 3
  %call4 = call i32 @llvm.umin.i32(i32 %shl, i32 80)
  ret i1 false
}

declare i32 @llvm.umin.i32(i32, i32) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
```

Given this debug output:
```
opt -passes=constraint-elimination before-constr-elim-renamed.ll -S -debug-only=constraint-elimination,constraint-system
Processing fact to add to the system: icmp ule i32 %call4, %shl
Adding 'icmp ule i32 %call4, %shl'
  constraint: -8 * %arg + %call4 <= 0

---
-1 * %arg <= 0
-8 * %arg + %call4 <= 0
-1 * %call4 <= 0
8 * %arg <= -1
unsat
Adding 'icmp sge i32 %call4, 0'
  constraint: -1 * %call4 <= 0

Adding 'icmp sle i32 %call4, %shl'
  constraint: -8 * %arg + %call4 <= 0

Processing fact to add to the system: icmp ule i32 %call4, 80
Adding 'icmp ule i32 %call4, 80'
  constraint: %call4 <= 80

Adding 'icmp sge i32 %call4, 0'
  constraint: -1 * %call4 <= 0

Adding 'icmp sle i32 %call4, 80'
  constraint: %call4 <= 80

Top of stack : 0 1
CB: 0 1
Processing condition to simplify:   %icmp = icmp slt i32 %arg, 0
Checking   %icmp = icmp slt i32 %arg, 0
---
-1 * %call4 <= 0
-8 * %arg + %call4 <= 0
-1 * %call4 <= 0
%call4 <= 80
-1 * %arg <= 0
sat
---
-1 * %call4 <= 0
-8 * %arg + %call4 <= 0
-1 * %call4 <= 0
%call4 <= 80
%arg <= -1
unsat
Condition icmp sge i32 %arg, 0 implied by dominating constraints
-1 * %call4 <= 0
-8 * %arg + %call4 <= 0
-1 * %call4 <= 0
%call4 <= 80
```
I'm not 100% sure if the aforementioned patch causes the miscompile, of if it merely triggers it on this specific reproducer. The fact that something like `-1 * %call4 <= 0` is reported as a "dominating constraint" seems suspicious to a person who's not familiar with the logic of ConstraintElimination and it doesn't look directly related to that patch. However, if I replace the `shl nuw nsw i32 %arg, 3
` with `mul nuw nsw i32 %arg, 8`, the miscompile doesn't happen: https://alive2.llvm.org/ce/z/EL2bCh.

From the debug log, I suspect that we're using the unsigned constraint system to prove a signed fact for some reason.

cc @fhahn

Okay, this is an interesting case.

We are adding an `icmp ule i32 %call4, %shl` fact, which get transferred as `icmp sge i32 %call4, 0` and `icmp sle i32 %call4, %shl` to the signed system, because `%shl` is known non-negative.

Then we are checking whether a solution of these constraints plus `%arg <= -1` exists, which it does not.

This is "correct" in the sense that *if* that umin call returns a non-poison result, then `icmp slt i32 %arg, 0` is indeed known to be false. But we obviously can't actually make that assumption.

@dtcxzyw @fhahn This makes me think that the way we are currently adding facts for MinMaxIntrinsic (and I guess abs as well) is pretty fundamentally incorrect, because it effectively imposes a constraint that the umin operation returns a well-defined value. Any thoughts on how to fix this without dropping support for them entirely?

(The difference between shl and mul in the reproducer is because ValueTracking apparently currently doesn't know that mul nuw nsw is non-negative, but knows that shl nuw nsw is.)

> @dtcxzyw @fhahn This makes me think that the way we are currently adding facts for MinMaxIntrinsic (and I guess abs as well) is pretty fundamentally incorrect, because it effectively imposes a constraint that the umin operation returns a well-defined value. Any thoughts on how to fix this without dropping support for them entirely?

Can we conservatively check these intrinsics using `isGuaranteedNotToBePoison`?

> If it's not too complicated, I think a better solution would be to only add the intrinsic constraints when the result variable is added to the constraint system.

One way to do that would be to check the uses of the intrinsic and find all the branches that it feeds, and add it as fact for the successors.

> @dtcxzyw Do you plan to submit a PR for this issue?

I will post a patch later.

I've pushed a fix that only adds the facts if the result is guaranteed to not be poison so the mis-compile is fixed. I also added a TODO to improve things if possible.

Reopening so we can pick the fix for the release branch

> @fhahn Why does your fix only do this for min/max but not abs? Can't it have the same problem?

I wasn't able to come up with a problematic test case so far for `abs`, but there's a few more things to try.

> @fhahn I'm not familiar with how this pass works, so I have a question: will this pass still use min/max/etc. to infer facts about the arguments of those intrinsics? I'm just a bit confused by the terminology: I see that it adds "constraints" based on things like `icmp`, which on their own don't actually constrain anything unless you branch on the result (or do something similar).

Constraints for icmps will only be added in branches that the icmp dominates (or code that an assume with an icmp dominates). The intrinsic handling was a bit special in that constraints are added unconditionally.


commit be7239e5a60927f5b4932f995dc4b57423ea8534
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Tue Jul 9 15:08:04 2024 +0800

    [Inline] Remove bitcast handling in `CallAnalyzer::stripAndComputeInBoundsConstantOffsets` (#97988)

    As we are now using opaque pointers, bitcast handling is no longer
    needed.

    Closes https://github.com/llvm/llvm-project/issues/97590.

diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp
index 94aa8ecf6f55..345e5a019520 100644
--- a/llvm/lib/Analysis/InlineCost.cpp
+++ b/llvm/lib/Analysis/InlineCost.cpp
@@ -2644,8 +2644,6 @@ ConstantInt *CallAnalyzer::stripAndComputeInBoundsConstantOffsets(Value *&V) {
       if (!GEP->isInBounds() || !accumulateGEPOffset(*GEP, Offset))
         return nullptr;
       V = GEP->getPointerOperand();
-    } else if (Operator::getOpcode(V) == Instruction::BitCast) {
-      V = cast<Operator>(V)->getOperand(0);
     } else if (GlobalAlias *GA = dyn_cast<GlobalAlias>(V)) {
       if (GA->isInterposable())
         break;

; Check that we don't crash when computing the
; base pointer of `bitcast <1 x ptr> to ptr`

define void @caller(<1 x ptr> %x) {
;
entry:
  %p = bitcast <1 x ptr> %x to ptr
  call void @callee(ptr %p)
  ret void
}

define void @callee(ptr %p) {
;
entry:
  ret void
}

[Inline] Assertion `V->getType()->isPointerTy() && "Unexpected operand type!"' failed.

Reproducer: https://godbolt.org/z/qGc1Kx5YK
```
; bin/opt -passes=inline reduced.ll -S
define i32 @func_120(i1 %tobool626, ptr %g_231, ptr %l_506, i1 %tobool724, ptr %p) {
entry:
  %0 = bitcast i1 %tobool626 to <1 x i1>
  call void @llvm.masked.store.v1i64.p0(<1 x i64> zeroinitializer, ptr %g_231, i32 8, <1 x i1> %0)
  %1 = xor i1 %tobool626, true
  %2 = bitcast i1 %1 to <1 x i1>
  %3 = call <1 x ptr> @llvm.masked.load.v1p0.p0(ptr %g_231, i32 8, <1 x i1> %2, <1 x ptr> poison)
  %4 = bitcast <1 x ptr> %3 to ptr
  br i1 %tobool626, label %common.ret, label %if.else647

common.ret:                                       ; preds = %entry, %lor.end739
  ret i32 0

if.else647:                                       ; preds = %entry
  br i1 %tobool724, label %land.rhs725, label %lor.end739

land.rhs725:                                      ; preds = %if.else647
  %call726 = load i32, ptr %p, align 4
  br label %lor.end739

lor.end739:                                       ; preds = %land.rhs725, %if.else647
  %call745 = call i32 @func_136(ptr %4)
  br label %common.ret
}

define i32 @func_136(ptr nocapture readnone %p_137) {
entry:
  ret i32 0
}

```
```
opt: /root/llvm-project/llvm/lib/Analysis/InlineCost.cpp:2656: llvm::ConstantInt* {anonymous}::CallAnalyzer::stripAndComputeInBoundsConstantOffsets(llvm::Value*&): Assertion `V->getType()->isPointerTy() && "Unexpected operand type!"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.	Program arguments: /opt/compiler-explorer/clang-assertions-trunk/bin/opt -o /app/output.s -S -passes=inline <source>
1.	Running pass "cgscc(inline)" on module "<source>"
 #0 0x0000000004e7cf38 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4e7cf38)
 #1 0x0000000004e7a69c SignalHandler(int) Signals.cpp:0:0
 #2 0x000077b96f042520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x000077b96f0969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #4 0x000077b96f042476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #5 0x000077b96f0287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #6 0x000077b96f02871b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #7 0x000077b96f039e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #8 0x00000000043ea53a (anonymous namespace)::CallAnalyzer::analyze() (.part.0) InlineCost.cpp:0:0
 #9 0x00000000043eb650 llvm::getInlineCost(llvm::CallBase&, llvm::Function*, llvm::InlineParams const&, llvm::TargetTransformInfo&, llvm::function_ref<llvm::AssumptionCache& (llvm::Function&)>, llvm::function_ref<llvm::TargetLibraryInfo const& (llvm::Function&)>, llvm::function_ref<llvm::BlockFrequencyInfo& (llvm::Function&)>, llvm::ProfileSummaryInfo*, llvm::OptimizationRemarkEmitter*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x43eb650)
#10 0x00000000043ebba6 llvm::getInlineCost(llvm::CallBase&, llvm::InlineParams const&, llvm::TargetTransformInfo&, llvm::function_ref<llvm::AssumptionCache& (llvm::Function&)>, llvm::function_ref<llvm::TargetLibraryInfo const& (llvm::Function&)>, llvm::function_ref<llvm::BlockFrequencyInfo& (llvm::Function&)>, llvm::ProfileSummaryInfo*, llvm::OptimizationRemarkEmitter*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x43ebba6)
#11 0x00000000043ed0ab llvm::InlineCost llvm::function_ref<llvm::InlineCost (llvm::CallBase&)>::callback_fn<getDefaultInlineAdvice(llvm::CallBase&, llvm::AnalysisManager<llvm::Function>&, llvm::InlineParams const&)::'lambda2'(llvm::CallBase&)>(long, llvm::CallBase&) InlineAdvisor.cpp:0:0
#12 0x00000000043f310d llvm::shouldInline(llvm::CallBase&, llvm::function_ref<llvm::InlineCost (llvm::CallBase&)>, llvm::OptimizationRemarkEmitter&, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x43f310d)
#13 0x00000000043f4af5 getDefaultInlineAdvice(llvm::CallBase&, llvm::AnalysisManager<llvm::Function>&, llvm::InlineParams const&) InlineAdvisor.cpp:0:0
#14 0x00000000043f4d3b llvm::DefaultInlineAdvisor::getAdviceImpl(llvm::CallBase&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x43f4d3b)
#15 0x00000000043eda71 llvm::InlineAdvisor::getAdvice(llvm::CallBase&, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x43eda71)
#16 0x000000000371e48d llvm::InlinerPass::run(llvm::LazyCallGraph::SCC&, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>&, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x371e48d)
#17 0x0000000002e127ae llvm::detail::PassModel<llvm::LazyCallGraph::SCC, llvm::InlinerPass, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&>::run(llvm::LazyCallGraph::SCC&, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>&, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x2e127ae)
#18 0x000000000431385b llvm::PassManager<llvm::LazyCallGraph::SCC, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&>::run(llvm::LazyCallGraph::SCC&, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>&, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x431385b)
#19 0x0000000002e1154e llvm::detail::PassModel<llvm::LazyCallGraph::SCC, llvm::PassManager<llvm::LazyCallGraph::SCC, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&>, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&>::run(llvm::LazyCallGraph::SCC&, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>&, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x2e1154e)
#20 0x00000000043155b9 llvm::ModuleToPostOrderCGSCCPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x43155b9)
#21 0x0000000002e1152e llvm::detail::PassModel<llvm::Module, llvm::ModuleToPostOrderCGSCCPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x2e1152e)
#22 0x0000000004c780c0 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4c780c0)
#23 0x00000000008d8702 llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8d8702)
#24 0x00000000008cb60c optMain (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8cb60c)
#25 0x000077b96f029d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#26 0x000077b96f029e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#27 0x00000000008c2a6e _start (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8c2a6e)
Program terminated with signal: SIGSEGV
Compiler returned: 139
```
https://github.com/llvm/llvm-project/blob/f1a8f94bba8b090a0d667065e4b8b4fc66b6a5cc/llvm/lib/Analysis/InlineCost.cpp#L2647-L2648
should be removed as we use opaque pointers now.

Related patch: #96878

Reduced reproducer: https://godbolt.org/z/rbjK6ETEW
```
; bin/opt -passes=inline reduced.ll -S
define void @caller(<1 x ptr> %x) {
entry:
  %p = bitcast <1 x ptr> %x to ptr
  call void @callee(ptr %p)
  ret void
}

define void @callee(ptr %p) {
entry:
  ret void
}
```


commit 008df3cf85e9bb1532c079bfd7a7a00e90e0a3c6
Author: Florian Hahn <flo@fhahn.com>
Date:   Fri Jul 19 12:02:25 2024 +0100

    [LV] Check isPredInst instead of isScalarWithPred in uniform analysis. (#98892)

    Any instruction marked as uniform will result in a uniform
    VPReplicateRecipe. If it requires predication, it will be placed in a
    replicate region, even if isScalarWithPredication returns false.

    Check isPredicatedInst instead of isScalarWithPredication to avoid
    generating uniform VPReplicateRecipes placed inside a replicate region.
    This fixes an assertion when using scalable VFs.

    Fixes https://github.com/llvm/llvm-project/issues/80416.
    Fixes https://github.com/llvm/llvm-project/issues/94328.
    Fixes https://github.com/llvm/llvm-project/issues/99625.

    PR: https://github.com/llvm/llvm-project/pull/98892

diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index fbca4cdcbcfc..cceed75aa50b 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -3914,19 +3914,19 @@ void LoopVectorizationCostModel::collectLoopUniforms(ElementCount VF) {
   SetVector<Instruction *> Worklist;

   // Add uniform instructions demanding lane 0 to the worklist. Instructions
-  // that are scalar with predication must not be considered uniform after
+  // that require predication must not be considered uniform after
   // vectorization, because that would create an erroneous replicating region
   // where only a single instance out of VF should be formed.
-  // TODO: optimize such seldom cases if found important, see PR40816.
   auto addToWorklistIfAllowed = [&](Instruction *I) -> void {
     if (isOutOfScope(I)) {
       LLVM_DEBUG(dbgs() << "LV: Found not uniform due to scope: "
                         << *I << "\n");
       return;
     }
-    if (isScalarWithPredication(I, VF)) {
-      LLVM_DEBUG(dbgs() << "LV: Found not uniform being ScalarWithPredication: "
-                        << *I << "\n");
+    if (isPredicatedInst(I)) {
+      LLVM_DEBUG(
+          dbgs() << "LV: Found not uniform due to requiring predication: " << *I
+                 << "\n");
       return;
     }
     LLVM_DEBUG(dbgs() << "LV: Found uniform instruction: " << *I << "\n");
@@ -9516,6 +9516,8 @@ void VPInterleaveRecipe::execute(VPTransformState &State) {
 void VPReplicateRecipe::execute(VPTransformState &State) {
   Instruction *UI = getUnderlyingInstr();
   if (State.Instance) { // Generate a single instance.
+    assert((State.VF.isScalar() || !isUniform()) &&
+           "uniform recipe shouldn't be predicated");
     assert(!State.VF.isScalable() && "Can't scalarize a scalable vector");
     State.ILV->scalarizeInstruction(UI, this, *State.Instance, State);
     // Insert scalar instance packing it into a vector.

opt -p loop-vectorize -mtriple aarch64 -mcpu=neoverse-v1 -S %s

define void @udiv_urem_feeding_gep(i64 %x, ptr %dst, i64 %N) {
entry:
  %mul.1.i = mul i64 %x, %x
  %mul.2.i = mul i64 %mul.1.i, %x
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %div.i = udiv i64 %iv, %mul.2.i
  %rem.i = urem i64 %iv, %mul.2.i
  %div.1.i = udiv i64 %rem.i, %mul.1.i
  %rem.1.i = urem i64 %rem.i, %mul.1.i
  %div.2.i = udiv i64 %rem.1.i, %x
  %rem.2.i = urem i64 %rem.1.i, %x
  %mul.i = mul i64 %x, %div.i
  %add.i = add i64 %mul.i, %div.1.i
  %mul.1.i9 = mul i64 %add.i, %x
  %add.1.i = add i64 %mul.1.i9, %div.2.i
  %mul.2.i11 = mul i64 %add.1.i, %x
  %add.2.i = add i64 %mul.2.i11, %rem.2.i
  %sext.i = shl i64 %add.2.i, 32
  %conv6.i = ashr i64 %sext.i, 32
  %gep = getelementptr i64, ptr %dst, i64 %conv6.i
  store i64 %div.i, ptr %gep, align 4
  %iv.next = add i64 %iv, 1
  %exitcond.not = icmp eq i64 %iv, %N
  br i1 %exitcond.not, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

define void @sdiv_feeding_gep_predicated(ptr %dst, i32 %x, i64 %M, i64 %conv6, i64 %N) {
entry:
  %conv61 = zext i32 %x to i64
  br label %loop

loop:                                             ; preds = %loop.latch, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %c = icmp ule i64 %iv, %M
  br i1 %c, label %then, label %loop.latch

then:                                             ; preds = %loop
  %div18 = sdiv i64 %M, %conv6
  %conv20 = trunc i64 %div18 to i32
  %mul30 = mul i64 %div18, %conv61
  %sub31 = sub i64 %iv, %mul30
  %conv34 = trunc i64 %sub31 to i32
  %mul35 = mul i32 %x, %conv20
  %add36 = add i32 %mul35, %conv34
  %idxprom = sext i32 %add36 to i64
  %gep = getelementptr double, ptr %dst, i64 %idxprom
  store double 0.000000e+00, ptr %gep, align 8
  br label %loop.latch

loop.latch:                                       ; preds = %then, %loop
  %iv.next = add i64 %iv, 1
  %ec = icmp eq i64 %iv.next, %N
  br i1 %ec, label %exit, label %loop

exit:                                             ; preds = %loop.latch
  ret void
}

define void @sdiv_feeding_gep(ptr %dst, i32 %x, i64 %M, i64 %conv6, i64 %N) {
entry:
  %conv61 = zext i32 %x to i64
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %div18 = sdiv i64 %M, %conv6
  %conv20 = trunc i64 %div18 to i32
  %mul30 = mul i64 %div18, %conv61
  %sub31 = sub i64 %iv, %mul30
  %conv34 = trunc i64 %sub31 to i32
  %mul35 = mul i32 %x, %conv20
  %add36 = add i32 %mul35, %conv34
  %idxprom = sext i32 %add36 to i64
  %gep = getelementptr double, ptr %dst, i64 %idxprom
  store double 0.000000e+00, ptr %gep, align 8
  %iv.next = add i64 %iv, 1
  %ec = icmp eq i64 %iv.next, %N
  br i1 %ec, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

[VPlan] Report "Assertion `!State->VF.isScalable() && "VF is assumed to be non scalable."' failed"

The IR is put at the end.

Compile command is `opt -passes=loop-vectorize -prefer-predicate-over-epilogue=predicate-else-scalar-epilogue`

The error is
```
opt: /root/llvm-project/llvm/lib/Transforms/Vectorize/VPlan.cpp:734: virtual void llvm::VPRegionBlock::execute(llvm::VPTransformState*): Assertion `!State->VF.isScalable() && "VF is assumed to be non scalable."' failed.
```

It can be seen at  https://godbolt.org/z/s4bqzdKPP

```
; ModuleID = 'test.cpp'
source_filename = "test.cpp"
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-linux-gnu"

%struct.ident_t = type { i32, i32, i32, i32, ptr }

@0 = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1
@1 = private unnamed_addr constant %struct.ident_t { i32 0, i32 514, i32 0, i32 22, ptr @0 }, align 8
@2 = private unnamed_addr constant %struct.ident_t { i32 0, i32 2, i32 0, i32 22, ptr @0 }, align 8

; Function Attrs: mustprogress nounwind uwtable vscale_range(1,16)
define dso_local void @_Z4testiiPdS_(i32 noundef %nx, i32 noundef %ik, ptr noundef %out, ptr noundef %rspace) local_unnamed_addr #0 {
entry:
  %nx.addr = alloca i32, align 4
  %ik.addr = alloca i32, align 4
  %out.addr = alloca ptr, align 8
  %rspace.addr = alloca ptr, align 8
  store i32 %nx, ptr %nx.addr, align 4
  store i32 %ik, ptr %ik.addr, align 4
  store ptr %out, ptr %out.addr, align 8
  store ptr %rspace, ptr %rspace.addr, align 8
  call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr nonnull @2, i32 4, ptr nonnull @_Z4testiiPdS_.omp_outlined, ptr nonnull %nx.addr, ptr nonnull %ik.addr, ptr nonnull %out.addr, ptr nonnull %rspace.addr)
  ret void
}

; Function Attrs: alwaysinline norecurse nounwind uwtable vscale_range(1,16)
define internal void @_Z4testiiPdS_.omp_outlined(ptr noalias nocapture noundef readonly %.global_tid., ptr noalias nocapture noundef readnone %.bound_tid., ptr noalias nocapture noundef nonnull readonly align 4 dereferenceable(4) %nx, ptr noalias nocapture noundef nonnull readonly align 4 dereferenceable(4) %ik, ptr noalias nocapture noundef nonnull readonly align 8 dereferenceable(8) %out, ptr noalias nocapture noundef nonnull readonly align 8 dereferenceable(8) %rspace) #1 {
entry:
  %.omp.lb = alloca i64, align 8
  %.omp.ub = alloca i64, align 8
  %.omp.stride = alloca i64, align 8
  %.omp.is_last = alloca i32, align 4
  %0 = load i32, ptr %nx, align 4
  %1 = load i32, ptr %ik, align 4
  %cmp = icmp sgt i32 %0, 0
  %cmp8 = icmp sgt i32 %1, 0
  %or.cond = select i1 %cmp, i1 %cmp8, i1 false
  br i1 %or.cond, label %omp.precond.then, label %omp.precond.end

omp.precond.then:                                 ; preds = %entry
  %conv = zext i32 %0 to i64
  %conv6 = zext i32 %1 to i64
  %mul = mul nuw nsw i64 %conv6, %conv
  %sub7 = add nsw i64 %mul, -1
  call void @llvm.lifetime.start.p0(i64 8, ptr nonnull %.omp.lb) #3
  store i64 0, ptr %.omp.lb, align 8
  call void @llvm.lifetime.start.p0(i64 8, ptr nonnull %.omp.ub) #3
  store i64 %sub7, ptr %.omp.ub, align 8
  call void @llvm.lifetime.start.p0(i64 8, ptr nonnull %.omp.stride) #3
  store i64 1, ptr %.omp.stride, align 8
  call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %.omp.is_last) #3
  store i32 0, ptr %.omp.is_last, align 4
  %2 = load i32, ptr %.global_tid., align 4
  call void @__kmpc_for_static_init_8(ptr nonnull @1, i32 %2, i32 33, ptr nonnull %.omp.is_last, ptr nonnull %.omp.lb, ptr nonnull %.omp.ub, ptr nonnull %.omp.stride, i64 1, i64 512)
  %3 = load i64, ptr %.omp.ub, align 8
  %cond60 = call i64 @llvm.smin.i64(i64 %3, i64 %sub7)
  store i64 %cond60, ptr %.omp.ub, align 8
  %4 = load i64, ptr %.omp.lb, align 8
  %cmp12.not61 = icmp sgt i64 %4, %cond60
  br i1 %cmp12.not61, label %omp.dispatch.end, label %omp.inner.for.cond.preheader.lr.ph

omp.inner.for.cond.preheader.lr.ph:               ; preds = %omp.precond.then
  br label %omp.inner.for.cond.preheader

omp.inner.for.cond.preheader:                     ; preds = %omp.inner.for.cond.preheader.lr.ph, %omp.dispatch.inc
  %5 = phi i64 [ %4, %omp.inner.for.cond.preheader.lr.ph ], [ %add42, %omp.dispatch.inc ]
  %cond62 = phi i64 [ %cond60, %omp.inner.for.cond.preheader.lr.ph ], [ %cond, %omp.dispatch.inc ]
  %smax = call i64 @llvm.smax.i64(i64 %cond62, i64 %5)
  %6 = add i64 %smax, 1
  %7 = load ptr, ptr %rspace, align 8
  %8 = load ptr, ptr %out, align 8
  br label %omp.inner.for.body

omp.inner.for.body:                               ; preds = %omp.inner.for.cond.preheader, %omp.inner.for.body
  %.omp.iv.059 = phi i64 [ %5, %omp.inner.for.cond.preheader ], [ %add41, %omp.inner.for.body ]
  %div18 = sdiv i64 %.omp.iv.059, %conv6
  %conv20 = trunc i64 %div18 to i32
  %mul30 = mul nsw i64 %div18, %conv6
  %sub31 = sub nsw i64 %.omp.iv.059, %mul30
  %conv34 = trunc i64 %sub31 to i32
  %mul35 = mul nsw i32 %1, %conv20
  %add36 = add nsw i32 %mul35, %conv34
  %idxprom = sext i32 %add36 to i64
  %arrayidx = getelementptr inbounds double, ptr %7, i64 %idxprom
  %9 = load double, ptr %arrayidx, align 8
  %arrayidx40 = getelementptr inbounds double, ptr %8, i64 %idxprom
  store double %9, ptr %arrayidx40, align 8
  %add41 = add i64 %.omp.iv.059, 1
  %exitcond = icmp ne i64 %add41, %6
  br i1 %exitcond, label %omp.inner.for.body, label %omp.dispatch.inc

omp.dispatch.inc:                                 ; preds = %omp.inner.for.body
  %10 = load i64, ptr %.omp.stride, align 8
  %add42 = add nsw i64 %10, %5
  store i64 %add42, ptr %.omp.lb, align 8
  %add43 = add nsw i64 %10, %cond62
  %cond = call i64 @llvm.smin.i64(i64 %add43, i64 %sub7)
  store i64 %cond, ptr %.omp.ub, align 8
  %cmp12.not = icmp sgt i64 %add42, %cond
  br i1 %cmp12.not, label %omp.dispatch.cond.omp.dispatch.end_crit_edge, label %omp.inner.for.cond.preheader

omp.dispatch.cond.omp.dispatch.end_crit_edge:     ; preds = %omp.dispatch.inc
  br label %omp.dispatch.end

omp.dispatch.end:                                 ; preds = %omp.dispatch.cond.omp.dispatch.end_crit_edge, %omp.precond.then
  call void @__kmpc_for_static_fini(ptr nonnull @1, i32 %2)
  call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %.omp.is_last) #3
  call void @llvm.lifetime.end.p0(i64 8, ptr nonnull %.omp.stride) #3
  call void @llvm.lifetime.end.p0(i64 8, ptr nonnull %.omp.ub) #3
  call void @llvm.lifetime.end.p0(i64 8, ptr nonnull %.omp.lb) #3
  br label %omp.precond.end

omp.precond.end:                                  ; preds = %omp.dispatch.end, %entry
  ret void
}

; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #2

; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #2

; Function Attrs: nounwind
declare void @__kmpc_for_static_init_8(ptr, i32, i32, ptr, ptr, ptr, ptr, i64, i64) local_unnamed_addr #3

; Function Attrs: nounwind
declare void @__kmpc_for_static_fini(ptr, i32) local_unnamed_addr #3

; Function Attrs: nounwind
declare void @__kmpc_fork_call(ptr, i32, ptr, ...) local_unnamed_addr #3

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i64 @llvm.smin.i64(i64, i64) #4

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i64 @llvm.smax.i64(i64, i64) #4

attributes #0 = { mustprogress nounwind uwtable vscale_range(1,16) "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+fullfp16,+neon,+sve,+v8a,-fmv" }
attributes #1 = { alwaysinline norecurse nounwind uwtable vscale_range(1,16) "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+fullfp16,+neon,+sve,+v8a,-fmv" }
attributes #2 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #3 = { nounwind }
attributes #4 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

```

Simpler test case is
```c
extern const int npy;
extern const int nx;
double* rspace;

void recip2real(double* out, const double factor)
{
#ifdef _OPENMP
#pragma omp parallel for collapse(2)
#endif
        for (int ix = 0; ix < nx; ++ix)
        {
            for (int ipy = 0; ipy < npy; ++ipy) {
                out[ix * npy + ipy] += factor * rspace[ix * npy + ipy];
            }
        }
}
```

Command is `clang -O3 -march=armv9-a -fopenmp -mllvm -prefer-predicate-over-epilogue=predicate-else-scalar-epilogue -S`

It can be seen at   https://godbolt.org/z/6MGsecEr5

I don't know much about VPlan, and as far as I understand by tracing the code,

### first, it fails at the following VPlan
```
VPlan 'Initial VPlan for VF={vscale x 1,vscale x 2},UF>=1' {
vp<%2> = original trip-count

ph:
  EMIT vp<%2> = EXPAND SCEV (1 + (-1 * %5) + ((-1 + ((zext i32 %0 to i64) * (sext i32 %1 to i64)))<nsw> smin %4))
No successors

vector.ph:
  EMIT vp<%3> = TC > VF ? TC - VF : 0 vp<%2>
  EMIT vp<%4> = VF * Part + ir<0>
  EMIT vp<%5> = active lane mask vp<%4>, vp<%2>
Successor(s): vector loop

<x1> vector loop: {
  vector.body:
    EMIT vp<%6> = CANONICAL-INDUCTION
    ACTIVE-LANE-MASK-PHI vp<%7> = phi vp<%5>, vp<%27>
    vp<%8>    = DERIVED-IV ir<%5> + vp<%6> * ir<1>
    vp<%9>    = SCALAR-STEPS vp<%8>, ir<1>
  Successor(s): pred.sdiv

  <xVFxUF> pred.sdiv: {
    pred.sdiv.entry:
      BRANCH-ON-MASK vp<%7>
    Successor(s): pred.sdiv.if, pred.sdiv.continue

    pred.sdiv.if:
      CLONE ir<%div24> = sdiv vp<%9>, ir<%conv6>
    Successor(s): pred.sdiv.continue

    pred.sdiv.continue:
      PHI-PREDICATED-INSTRUCTION vp<%11> = ir<%div24>
    No successors
  }
  Successor(s): omp.inner.for.body.0

  omp.inner.for.body.0:
    CLONE ir<%conv26> = trunc vp<%11>
    CLONE ir<%mul36> = mul nsw vp<%11>, ir<%conv6>
    CLONE ir<%sub37> = sub nsw vp<%9>, ir<%mul36>
    CLONE ir<%conv40> = trunc ir<%sub37>
    CLONE ir<%mul41> = mul nsw ir<%1>, ir<%conv26>
    CLONE ir<%add42> = add nsw ir<%mul41>, ir<%conv40>
    CLONE ir<%idxprom> = sext ir<%add42>
    CLONE ir<%arrayidx> = getelementptr inbounds ir<%6>, ir<%idxprom>
    WIDEN ir<%8> = load ir<%arrayidx>, vp<%7>
    WIDEN ir<%mul43> = fmul contract ir<%2>, ir<%8>
    CLONE ir<%arrayidx47> = getelementptr inbounds ir<%7>, ir<%idxprom>
    WIDEN ir<%9> = load ir<%arrayidx47>, vp<%7>
    WIDEN ir<%add48> = fadd contract ir<%mul43>, ir<%9>
    WIDEN store ir<%arrayidx47>, ir<%add48>, vp<%7>
    EMIT vp<%25> = VF * UF + vp<%6>
    EMIT vp<%26> = VF * Part + vp<%6>
    EMIT vp<%27> = active lane mask vp<%26>, vp<%3>
    EMIT vp<%28> = not vp<%27>
    EMIT branch-on-cond vp<%28>
  No successors
}
Successor(s): middle.block
```

VPRegion `pred.sdiv` fails at assertion
```c++
void VPRegionBlock::execute(VPTransformState *State) {
...
  if (!isReplicator()) {
...
    return;
  }
...
  for (...) {
    assert(!State->VF.isScalable() && "VF is assumed to be non scalable.");
  }
}
```
I think VPRegion `pred.sdiv` should have `isReplicator() == false` or `pred.sdiv` just should not exist.

### second, I find VPRegion `pred.sdiv` is splitted from `WorkList`
```c++
static void addReplicateRegions(VPlan &Plan) {
  SmallVector<VPReplicateRecipe *> WorkList;
  for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(
           vp_depth_first_deep(Plan.getEntry()))) {
    for (VPRecipeBase &R : *VPBB)
      if (auto *RepR = dyn_cast<VPReplicateRecipe>(&R)) {
        if (RepR->isPredicated())
          WorkList.push_back(RepR);
      }
  }
...
}
```
by VPRecipe `CLONE ir<%div24> = sdiv ir<%.omp.iv.065>, ir<%conv6>, vp<%7>`, where the instruction is `%div24 = sdiv i64 %.omp.iv.065, %conv6`
I think this VPRecipe should have `isPredicated() == false` here, so it will not be splitted.

### third, I find this VPRecipe is created here
```c++
VPRecipeOrVPValueTy VPRecipeBuilder::handleReplication(...) {
  bool IsUniform = LoopVectorizationPlanner::getDecisionAndClampRange(
      [&](ElementCount VF) { return CM.isUniformAfterVectorization(I, VF); },
      Range);

  bool IsPredicated = CM.isPredicatedInst(I);
...
  VPValue *BlockInMask = nullptr;
  if (!IsPredicated) {
    // Finalize the recipe for Instr, first if it is not predicated.
    LLVM_DEBUG(dbgs() << "LV: Scalarizing:" << *I << "\n");
  } else {
    LLVM_DEBUG(dbgs() << "LV: Scalarizing and predicating:" << *I << "\n");
    // Instructions marked for predication are replicated and a mask operand is
    // added initially. Masked replicate recipes will later be placed under an
    // if-then construct to prevent side-effects. Generate recipes to compute
    // the block mask for this region.
    BlockInMask = createBlockInMask(I->getParent(), Plan);
  }

  auto *Recipe = new VPReplicateRecipe(I, Plan.mapToVPValues(I->operands()),
                                       IsUniform, BlockInMask);
  return toVPRecipeResult(Recipe);
}
```
I notice that instruction `I` (`%div24 = sdiv i64 %.omp.iv.065, %conv6`) do not need to vectorize, because function `isScalarAfterVectorization` returns true and `I` is just used to calculate the index.
It seems reasonable that a `scalar` instruction does not need `Predicated`.

### So I try the following modification,
```diff
diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index 907b8ce002e8..76a5704a61c5 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -9819,7 +9819,9 @@ VPRecipeOrVPValueTy VPRecipeBuilder::handleReplication(Instruction *I,
       [&](ElementCount VF) { return CM.isUniformAfterVectorization(I, VF); },
       Range);

-  bool IsPredicated = CM.isPredicatedInst(I);
+  bool IsPredicated = LoopVectorizationPlanner::getDecisionAndClampRange(
+      [&](ElementCount VF) { return CM.isPredicatedInst(I) && !CM.isScalarAfterVectorization(I, VF); },
+      Range);

   // Even if the instruction is not marked as uniform, there are certain
   // intrinsic calls that can be effectively treated as such, so we check for
```

Just my guess, I'm not sure if it is correct direction.

Ping @fhahn

I think I misunderstand something, and the changes above may be wrong.

And I have another way. In instruction `%div24 = sdiv i64 %.omp.iv.065, %conv6`, `%conv6` is invariant in loop, so it seems we do not need `Predicated`.

I try the following patch,
```diff
diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index c7c19ef456c7..f294703e1529 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -3856,21 +3856,21 @@ bool LoopVectorizationCostModel::isPredicatedInst(Instruction *I) const {
         !Legal->blockNeedsPredication(I->getParent()))
       return false;
     return true;
   }
   case Instruction::UDiv:
   case Instruction::SDiv:
   case Instruction::SRem:
   case Instruction::URem:
     // TODO: We can use the loop-preheader as context point here and get
     // context sensitive reasoning
-    return !isSafeToSpeculativelyExecute(I);
+    return !isSafeToSpeculativelyExecute(I) && !Legal->isInvariant(I->getOperand(1));
   case Instruction::Call:
     return Legal->isMaskRequired(I);
   }
 }

 std::pair<InstructionCost, InstructionCost>
 LoopVectorizationCostModel::getDivRemSpeculationCost(Instruction *I,
                                                     ElementCount VF) const {
   assert(I->getOpcode() == Instruction::UDiv ||
          I->getOpcode() == Instruction::SDiv ||

```

ping @sdesmalen-arm @davemgreen @paulwalker-arm

@eastB233 unfortunately I don't think this change is correct, e.g. consider https://github.com/llvm/llvm-project/blob/967eba07549d64f15e7a91e798aa46214704f62b/llvm/test/Transforms/LoopVectorize/X86/divs-with-tail-folding.ll#L251 when the sdiv/udiv is executed conditionally in the loop


commit f68b0e36997322eeda8fd199ea80deb1b49c5410
Author: Antonio Frighetto <me@antoniofrighetto.com>
Date:   Wed Dec 4 10:15:11 2024 +0100

    [AggressiveInstCombine] Use APInt and avoid truncation when folding loads

    A miscompilation issue has been addressed with improved handling.

    Fixes: https://github.com/llvm/llvm-project/issues/118467.

diff --git a/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp b/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp
index b5b561797f75..45ee2d472a11 100644
--- a/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp
+++ b/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp
@@ -803,8 +803,7 @@ static bool foldConsecutiveLoads(Instruction &I, const DataLayout &DL,
     APInt Offset1(DL.getIndexTypeSizeInBits(Load1Ptr->getType()), 0);
     Load1Ptr = Load1Ptr->stripAndAccumulateConstantOffsets(
         DL, Offset1, /* AllowNonInbounds */ true);
-    Load1Ptr = Builder.CreatePtrAdd(Load1Ptr,
-                                    Builder.getInt32(Offset1.getZExtValue()));
+    Load1Ptr = Builder.CreatePtrAdd(Load1Ptr, Builder.getInt(Offset1));
   }
   // Generate wider load.
   NewLoad = Builder.CreateAlignedLoad(WiderType, Load1Ptr, LI1->getAlign(),

opt < %s -passes=aggressive-instcombine -mtriple x86_64-none-eabi -mattr=avx2 -data-layout="e-n64" -S

opt < %s -passes=aggressive-instcombine -mtriple x86_64-none-eabi -mattr=avx2 -data-layout="E-n64" -S

define i32 @loadcombine_consecutive_idx_64(ptr %data) {
entry:
  %arrayidx = getelementptr inbounds nuw i8, ptr %data, i64 2149675577
  %val = load i8, ptr %arrayidx, align 1
  %conv = zext i8 %val to i32
  %arrayidx.2 = getelementptr inbounds nuw i8, ptr %data, i64 2149675576
  %val.2 = load i8, ptr %arrayidx.2, align 1
  %conv.2 = zext i8 %val.2 to i32
  %shl = shl nuw nsw i32 %conv, 8
  %or = or disjoint i32 %shl, %conv.2
  ret i32 %or
}

Invalid LLVM IR code generated on x86-64 with a very simple sample (crash the generated executable code)

Below is a very simple program that's loading 16 bits table value into a 32 bits variable from a 8 bits table.
On **x86-64** with **-O2** or **-O3** flags, this program works fine with LLVM 16.0.0 and below, but since LLVM 17.0.0, the LLVM IR and the generated assembly code are using a wrong calculated offset on the table (doesn't occurs on LLVM armv8 whatever the version).
As you can see below, the offset in the table which is supposed to be **2149675576** (**0x80217238**) has its 32 upper bits inverted for **-2145291720** (**0xFFFFFFFF80217238**).

```c

#include <stdint.h>

// Load 16 bits into 32 bits value from data offset 0x80217238

uint32_t Test(const uint8_t* data)
{
    uint32_t a, b, c;
	b = 0xFFFF8022 << 16;
    b += 0xFFFFE808;
    a = data[b + 0xFFFF8A31];
    c = data[b + 0xFFFF8A30];
    c &= ~0x0000FF00;
    c |= ((a << 8) | (a >> 24)) & 0x0000FF00;
	return c;
}

```

LLVM IR with LLVM 16.0.0 (from Compiler Explorer)
```ll
define dso_local noundef i32 @Test(unsigned char const*)(ptr nocapture noundef readonly %data) local_unnamed_addr {
entry:
  %arrayidx4 = getelementptr inbounds i8, ptr %data, i64 2149675576
  %0 = load i16, ptr %arrayidx4, align 1
  %1 = zext i16 %0 to i32
  ret i32 %1
}

declare void @llvm.dbg.value(metadata, metadata, metadata) #1
```
LLVM IR with LLVM 19.1.0 and trunk (from Compiler Explorer)
```ll
define dso_local noundef range(i32 0, 65536) i32 @Test(unsigned char const*)(ptr nocapture noundef readonly %data) local_unnamed_addr {
entry:
  %0 = getelementptr i8, ptr %data, i64 -2145291720
  %1 = load i16, ptr %0, align 1
  %2 = zext i16 %1 to i32
  ret i32 %2
}
```

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

> opt-bisect suggests AggressiveInstCominer. There were two separate loads that got merged. The resulting GEP had an index of `i32 -2145291720` with trunk and `i64 2149675576` with clang 16. Those would be the same number if GEP zero extended indices instead of sign extending.

See my PR for 19.x