Bug type: crash
Component: LoopVectorize
Bug location: llvm/lib/Transforms/Vectorize/LoopVectorize.cpp: VPRecipeBuilder::tryToWiden
Issue title: [clang] Assertion failed in Vectorize
Labels: vectorizers
confirmed
crash
Reported by: cardigan1008

When I compiled this code with O2 flag, it crashed:

```c
#include <stdint.h>
static uint64_t
safe_sub_func_uint64_t_u_u(uint64_t ui1, uint64_t ui2)
{
  return ui1 - ui2;
}
long a;
static int8_t c;
void b(long d, int f) {
  int e = 0;
  for (; e < 64; e++)
    if (e - f)
      a |= e >> f;
    else
      a |= d & e;
}
uint32_t g() {
  b(6, c);
  for (;; c = safe_sub_func_uint64_t_u_u(c, 4))
    ;
}
```

The crash is:
```
clang: /root/llvm-project/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp:7383: llvm::VectorizationFactor llvm::LoopVectorizationPlanner::computeBestVF(): Assertion `(BestFactor.Width == LegacyVF.Width || planContainsAdditionalSimplifications(getPlanFor(BestFactor.Width), CostCtx, OrigLoop)) && " VPlan cost model and legacy cost model disagreed"' failed.
```

Details can be found here: https://godbolt.org/z/7733x1hvP

Comments: author: shafik
body: This looks like a regression in trunk: https://godbolt.org/z/eo7WG1e66
author: hiraditya
body: Probably a recent change caused this? cc: @ayalz @fhahn 

Fix patch:
commit 6d6eea92e36c301e34a7ec11b2a40e3080f79f53
Author: Florian Hahn <flo@fhahn.com>
Date:   Wed Oct 2 13:45:48 2024 +0100

    [LV] Use SCEV to simplify wide binop operand to constant.
    
    The legacy cost model uses SCEV to determine if the second operand of a
    binary op is a constant. Update the VPlan construction logic to mirror
    the current legacy behavior, to fix a difference in the cost models.
    
    Fixes https://github.com/llvm/llvm-project/issues/109528.
    Fixes https://github.com/llvm/llvm-project/issues/110440.

diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index e1f9f29ac6d9..de3b981a4fe3 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -8420,21 +8420,28 @@ VPWidenRecipe *VPRecipeBuilder::tryToWiden(Instruction *I,
   case Instruction::Sub:
   case Instruction::Xor:
   case Instruction::Freeze:
-    if (I->getOpcode() == Instruction::Mul) {
-      // Simplify operands of multiplications using SCEV. This is needed at the
-      // moment to match the behavior of the legacy cost-model.
-      // TODO: Generalize to any opcode and move to VPlan transformation.
-      SmallVector<VPValue *> NewOps(Operands);
+    SmallVector<VPValue *> NewOps(Operands);
+    if (Instruction::isBinaryOp(I->getOpcode())) {
+      // The legacy cost model uses SCEV to check if some of the operands are
+      // constants. To match the legacy cost model's behavior, use SCEV to try
+      // to replace operands with constants.
       ScalarEvolution &SE = *PSE.getSE();
-      for (unsigned I = 0; I < Operands.size(); ++I) {
-        Value *V = NewOps[I]->getUnderlyingValue();
-        if (!isa<Constant>(V) && SE.isSCEVable(V->getType()))
-          if (auto *C = dyn_cast<SCEVConstant>(PSE.getSE()->getSCEV(V)))
-            NewOps[I] = Plan.getOrAddLiveIn(C->getValue());
-      }
-      return new VPWidenRecipe(*I, make_range(NewOps.begin(), NewOps.end()));
+      auto GetConstantViaSCEV = [this, &SE](VPValue *Op) {
+        Value *V = Op->getUnderlyingValue();
+        if (isa<Constant>(V) || !SE.isSCEVable(V->getType()))
+          return Op;
+        auto *C = dyn_cast<SCEVConstant>(SE.getSCEV(V));
+        if (!C)
+          return Op;
+        return Plan.getOrAddLiveIn(C->getValue());
+      };
+      // For Mul, the legacy cost model checks both operands.
+      if (I->getOpcode() == Instruction::Mul)
+        NewOps[0] = GetConstantViaSCEV(NewOps[0]);
+      // For other binops, the legacy cost model only checks the second operand.
+      NewOps[1] = GetConstantViaSCEV(NewOps[1]);
     }
-    return new VPWidenRecipe(*I, make_range(Operands.begin(), Operands.end()));
+    return new VPWidenRecipe(*I, make_range(NewOps.begin(), NewOps.end()));
   };
 }
 


Regression tests:
file: llvm/test/Transforms/LoopVectorize/X86/cost-constant-known-via-scev.ll
commands: opt -p loop-vectorize -S %s
tests: test_name: test_foldable_live_in_via_scev
test_body: 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_foldable_live_in_via_scev() {
entry:
  %conv = zext i16 -6 to i64
  %add = add nsw i64 %conv, -65528
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i32 [ 1, %entry ], [ %iv.next, %loop ]
  %red = phi i64 [ 1, %entry ], [ %mul, %loop ]
  %mul = mul nsw i64 %red, %add
  %iv.next = add nsw i32 %iv, 1
  %ec = icmp eq i32 %iv.next, 100
  br i1 %ec, label %exit, label %loop

exit:                                             ; preds = %loop
  %ret = phi i64 [ %mul, %loop ]
  ret i64 %ret
}

test_name: second_lshr_operand_zero_via_scev
test_body: 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 @second_lshr_operand_zero_via_scev() {
entry:
  %ext.0 = sext i8 0 to i32
  br label %loops

loops:                                            ; preds = %loops, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loops ]
  %red = phi i64 [ 0, %entry ], [ %red.next, %loops ]
  %c = icmp eq i64 %iv, 0
  %and = and i64 %iv, 0
  %0 = trunc i64 %iv to i32
  %shr = lshr i32 %0, %ext.0
  %conv.1 = zext i32 %shr to i64
  %red.next.v = select i1 %c, i64 %and, i64 %conv.1
  %red.next = or i64 %red.next.v, %red
  %iv.next = add i64 %iv, 1
  %ec = icmp eq i64 %iv.next, 0
  br i1 %ec, label %exit, label %loops

exit:                                             ; preds = %loops
  %res = phi i64 [ %red.next, %loops ]
  ret i64 %res
}