Bug type: crash
Component: EarlyCSE
IR
Bug location: llvm/lib/IR/Instructions.cpp: CmpPredicate::getSwapped
llvm::hash_value
llvm/lib/Transforms/Scalar/EarlyCSE.cpp: getHashValueImpl
Issue title: [EarlyCSE] Assertion `!FoundVal && "Key already in new map?"' failed.
Labels: crash-on-valid
llvm:transforms
Reported by: dtcxzyw

Reproducer:
```
; bin/opt -passes=early-cse reduced.ll -S

define i32 @func_207(i16 %p_208.coerce, i32 %conv, i32 %0, i64 %1, ptr %p) {
entry:
  %conv1 = zext i16 %p_208.coerce to i32
  %conv31 = sext i32 %conv to i64
  %cmp.i = icmp eq i64 %conv31, 0
  %xor = xor i32 %conv, 1
  store i32 %xor, ptr %p, align 4
  %cmp2.i = icmp samesign ugt i32 %conv1, 31
  %shr.i601 = select i1 %cmp2.i, i32 0, i32 %conv1
  %cond.i602 = ashr i32 1, %shr.i601
  %conv6.i603 = trunc i32 %cond.i602 to i16
  %conv245 = trunc i16 %p_208.coerce to i8
  %mul.i628 = mul i8 -107, %conv245
  %conv247 = sext i8 %mul.i628 to i64
  %cond.i629 = call i64 @llvm.smin.i64(i64 0, i64 %conv247)
  %cmp249 = icmp slt i64 %cond.i629, 0
  %conv251 = zext i1 %cmp249 to i64
  %cmp1.i630 = icmp ugt i32 %conv1, 31
  %narrow.i631 = select i1 %cmp1.i630, i32 0, i32 %conv1
  %shr.i632 = zext i32 %narrow.i631 to i64
  %cond.i633 = lshr i64 %conv251, %shr.i632
  %cmp258 = icmp slt i16 %p_208.coerce, 0
  %2 = zext i1 %cmp258 to i16
  %cmp261 = icmp ugt i16 1, %2
  %conv263 = zext i1 %cmp261 to i64
  %cmp344 = icmp eq i16 %p_208.coerce, 0
  %conv345 = zext i1 %cmp344 to i32
  store i32 %conv345, ptr %p, align 4
  %conv351 = sext i32 %0 to i64
  %sub.i641 = call i64 @llvm.ucmp.i64.i64(i64 0, i64 %conv351)
  %conv353 = trunc i64 %sub.i641 to i16
  %3 = mul i16 %conv353, -1
  %conv355 = zext i16 %3 to i64
  %cmp356 = icmp sle i64 1, %conv355
  %conv357 = zext i1 %cmp356 to i32
  %conv359 = trunc i32 %conv357 to i8
  %conv.i650 = sext i8 %conv359 to i32
  %4 = icmp ugt i32 %conv, 0
  %shr.i652 = lshr i32 1, %conv
  %cmp9.i = icmp slt i32 %shr.i652, 1
  %or.cond.i = select i1 %4, i1 false, i1 %cmp9.i
  %shl.i653 = shl i32 %conv.i650, 1
  %5 = trunc i32 %shl.i653 to i8
  %cond.i654 = select i1 %or.cond.i, i8 0, i8 %5
  %conv3612 = sext i8 %cond.i654 to i32
  %conv3623 = trunc i64 %1 to i32
  %6 = or i32 1, %conv3612
  %or.cond.i655 = icmp slt i32 %6, 0
  %cmp3.i = icmp sgt i32 %conv3623, 0
  %or.cond4.i = or i1 %cmp3.i, %or.cond.i655
  %shr.i656 = select i1 %or.cond4.i, i32 0, i32 1
  %cond.i657 = ashr i32 %conv, %shr.i656
  %cmp.i658 = icmp slt i32 %cond.i657, 0
  %shr.i660 = select i1 %cmp.i658, i32 0, i32 1
  %cond.i661 = ashr i32 1, %shr.i660
  %conv365 = trunc i32 %cond.i661 to i16
  %add.i662 = or i16 1, %conv365
  %conv368 = sext i16 %add.i662 to i32
  ret i32 %conv368
}
```
```
opt: /data/zyw/llvm-project/llvm/include/llvm/ADT/DenseMap.h:419: void llvm::DenseMapBase<DerivedT, KeyT, ValueT, KeyInfoT, BucketT>::moveFromOldBuckets(BucketT*, BucketT*) [with DerivedT = llvm::DenseMap<{anonymous}::SimpleValue, llvm::ScopedHashTableVal<{anonymous}::SimpleValue, llvm::Value*>*, llvm::DenseMapInfo<{anonymous}::SimpleValue>, llvm::detail::DenseMapPair<{anonymous}::SimpleValue, llvm::ScopedHashTableVal<{anonymous}::SimpleValue, llvm::Value*>*> >; KeyT = {anonymous}::SimpleValue; ValueT = llvm::ScopedHashTableVal<{anonymous}::SimpleValue, llvm::Value*>*; KeyInfoT = llvm::DenseMapInfo<{anonymous}::SimpleValue>; BucketT = llvm::detail::DenseMapPair<{anonymous}::SimpleValue, llvm::ScopedHashTableVal<{anonymous}::SimpleValue, llvm::Value*>*>]: Assertion `!FoundVal && "Key already in new map?"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.      Program arguments: /data/zyw/llvm-build/bin/opt -passes=early-cse reduced.ll
1.      Running pass "function(early-cse<>)" on module "reduced.ll"
2.      Running pass "early-cse<>" on function "func_207"
 #0 0x00007ffff7def832 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/data/zyw/llvm-build/bin/../lib/libLLVMSupport.so.20.0git+0x1ef832)
 #1 0x00007ffff7dec9ef llvm::sys::RunSignalHandlers() (/data/zyw/llvm-build/bin/../lib/libLLVMSupport.so.20.0git+0x1ec9ef)
 #2 0x00007ffff7decb35 SignalHandler(int) Signals.cpp:0:0
 #3 0x00007ffff7842520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #4 0x00007ffff78969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #5 0x00007ffff7842476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #6 0x00007ffff78287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #7 0x00007ffff782871b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #8 0x00007ffff7839e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #9 0x00007ffff4efa75e llvm::DenseMap<(anonymous namespace)::SimpleValue, llvm::ScopedHashTableVal<(anonymous namespace)::SimpleValue, llvm::Value*>*, llvm::DenseMapInfo<(anonymous namespace)::SimpleValue, void>, llvm::detail::DenseMapPair<(anonymous namespace)::SimpleValue, llvm::ScopedHashTableVal<(anonymous namespace)::SimpleValue, llvm::Value*>*>>::grow(unsigned int) EarlyCSE.cpp:0:0
#10 0x00007ffff4efbd5a llvm::ScopedHashTable<(anonymous namespace)::SimpleValue, llvm::Value*, llvm::DenseMapInfo<(anonymous namespace)::SimpleValue, void>, llvm::RecyclingAllocator<llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator, 4096ul, 4096ul, 128ul>, llvm::ScopedHashTableVal<(anonymous namespace)::SimpleValue, llvm::Value*>, 32ul, 8ul>>::insertIntoScope(llvm::ScopedHashTableScope<(anonymous namespace)::SimpleValue, llvm::Value*, llvm::DenseMapInfo<(anonymous namespace)::SimpleValue, void>, llvm::RecyclingAllocator<llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator, 4096ul, 4096ul, 128ul>, llvm::ScopedHashTableVal<(anonymous namespace)::SimpleValue, llvm::Value*>, 32ul, 8ul>>*, (anonymous namespace)::SimpleValue const&, llvm::Value* const&) EarlyCSE.cpp:0:0
#11 0x00007ffff4efdf19 (anonymous namespace)::EarlyCSE::processNode(llvm::DomTreeNodeBase<llvm::BasicBlock>*) (.isra.0) EarlyCSE.cpp:0:0
#12 0x00007ffff4efeef5 (anonymous namespace)::EarlyCSE::run() EarlyCSE.cpp:0:0
#13 0x00007ffff4f00796 llvm::EarlyCSEPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/data/zyw/llvm-build/bin/../lib/../lib/libLLVMScalarOpts.so.20.0git+0x100796)
#14 0x00007ffff6684345 llvm::detail::PassModel<llvm::Function, llvm::EarlyCSEPass, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/data/zyw/llvm-build/bin/../lib/../lib/libLLVMPasses.so.20.0git+0x84345)
#15 0x00007ffff38ee63d llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/data/zyw/llvm-build/bin/../lib/../lib/libLLVMCore.so.20.0git+0x2ee63d)
#16 0x00007ffff70a9445 llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/data/zyw/llvm-build/bin/../lib/../lib/libLLVMX86CodeGen.so.20.0git+0xa9445)
#17 0x00007ffff38ec628 llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/data/zyw/llvm-build/bin/../lib/../lib/libLLVMCore.so.20.0git+0x2ec628)
#18 0x00007ffff70a9e05 llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/data/zyw/llvm-build/bin/../lib/../lib/libLLVMX86CodeGen.so.20.0git+0xa9e05)
#19 0x00007ffff38ed28d llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/data/zyw/llvm-build/bin/../lib/../lib/libLLVMCore.so.20.0git+0x2ed28d)
#20 0x00007ffff7f9b1eb 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) (/data/zyw/llvm-build/bin/../lib/libLLVMOptDriver.so.20.0git+0x241eb)
#21 0x00007ffff7fa5619 optMain (/data/zyw/llvm-build/bin/../lib/libLLVMOptDriver.so.20.0git+0x2e619)
#22 0x00007ffff7829d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#23 0x00007ffff7829e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#24 0x0000555555555095 _start (/data/zyw/llvm-build/bin/opt+0x1095)
Aborted (core dumped)
```

Comments: author: dtcxzyw
body: Reverting 4a0d53a0b0a58a3c6980a7c551357ac71ba3db10 fixes this issue.
cc @artagnon @nikic Can you double check the implementation of `hash_value(const CmpPredicate&)`?

author: nikic
body: Yeah, the implementation is incorrect for EarlyCSE (which should not take into account poison generating flags). I think we should remove the hash_value implementation and make the caller handler it appropriately.
author: artagnon
body: Not sure I understand. The backtrace points to EarlyCSE.cpp:1558, and the Instruction it crashes on is an `or`. With surrounding context it is:

https://github.com/llvm/llvm-project/blob/main/llvm/lib/Transforms/Scalar/EarlyCSE.cpp#L1392-L1560
author: artagnon
body: So I think this would fix the issue, although I'm not sure I understand the underlying issue:

```diff
diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
index 682c5c3d8c63..8e87f8b9a457 100644
--- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
+++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
@@ -290,7 +290,7 @@ static unsigned getHashValueImpl(SimpleValue Val) {
       Pred = CmpInst::getInversePredicate(Pred);
       std::swap(A, B);
     }
-    return hash_combine(Inst->getOpcode(), Pred, X, Y, A, B);
+    return hash_combine(Inst->getOpcode(), static_cast<CmpInst::Predicate>(Pred), X, Y, A, B);
   }

   if (CastInst *CI = dyn_cast<CastInst>(Inst))
```
author: artagnon
body: We hash two equal predicates, one with with samesign information (leading to a divergent hash), leading to a divergent hash_combine call, and hence a duplicate insertion into DenseMap:

```cpp
  SelectPatternFlavor SPF;
  Value *Cond, *A, *B;
  if (matchSelectWithOptionalNotCond(Inst, Cond, A, B, SPF)) {
    // Hash min/max (cmp + select) to allow for commuted operands.
    // Min/max may also have non-canonical compare predicate (eg, the compare for
    // smin may use 'sgt' rather than 'slt'), and non-canonical operands in the
    // compare.
    // TODO: We should also detect FP min/max.
    if (SPF == SPF_SMIN || SPF == SPF_SMAX ||
        SPF == SPF_UMIN || SPF == SPF_UMAX) {
      if (A > B)
        std::swap(A, B);
      return hash_combine(Inst->getOpcode(), SPF, A, B);
    }

    // Hash general selects to allow matching commuted true/false operands.

    // If we do not have a compare as the condition, just hash in the condition.
    CmpPredicate Pred;
    Value *X, *Y;
    if (!match(Cond, m_Cmp(Pred, m_Value(X), m_Value(Y))))
      return hash_combine(Inst->getOpcode(), Cond, A, B);

    // Similar to cmp normalization (above) - canonicalize the predicate value:
    // select (icmp Pred, X, Y), A, B --> select (icmp InvPred, X, Y), B, A
    if (CmpInst::getInversePredicate(Pred) < Pred) {
      Pred = CmpInst::getInversePredicate(Pred);
      std::swap(A, B);
    }
    return hash_combine(Inst->getOpcode(), static_cast<CmpInst::Predicate>(Pred), X, Y, A, B); /// fixed
  }
```

These are the duplicates:

```llvm
  %cmp2.i = icmp samesign ugt i32 %conv1, 31
  %cmp1.i630 = icmp ugt i32 %conv1, 31
```

Fix patch:
commit 5528388e3664c6d7d292f20a739f1bf1c8ef768d
Author: Ramkumar Ramachandra <ramkumar.ramachandra@codasip.com>
Date:   Fri Dec 13 22:06:39 2024 +0000

    EarlyCSE: fix CmpPredicate duplicate-hashing (#119902)
    
    Strip hash_value() for CmpPredicate, as different callers have different
    hashing use-cases. In this case, there is just one caller, namely
    EarlyCSE, which calls hash_combine() on a CmpPredicate, which used to
    call hash_combine() on a CmpInst::Predicate prior to 4a0d53a
    (PatternMatch: migrate to CmpPredicate). This has uncovered a bug where
    two icmp instructions differing in just the fact that one of them has
    the samesign flag on it are hashed differently, leading to divergent
    hashing, and a crash. Fix this crash by dropping samesign information on
    icmp instructions before hashing them, preserving the former behavior.
    
    Fixes #119893.

diff --git a/llvm/include/llvm/IR/CmpPredicate.h b/llvm/include/llvm/IR/CmpPredicate.h
index ce78e4311f9f..9aa1449465f5 100644
--- a/llvm/include/llvm/IR/CmpPredicate.h
+++ b/llvm/include/llvm/IR/CmpPredicate.h
@@ -71,13 +71,7 @@ public:
 
   /// Get the swapped predicate of a CmpInst.
   static CmpPredicate getSwapped(const CmpInst *Cmp);
-
-  /// Provided to facilitate storing a CmpPredicate in data structures that
-  /// require hashing.
-  friend hash_code hash_value(const CmpPredicate &Arg); // NOLINT
 };
-
-[[nodiscard]] hash_code hash_value(const CmpPredicate &Arg);
 } // namespace llvm
 
 #endif
diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index d1da02c744f1..2d6fe40f4c1d 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -3946,10 +3946,6 @@ CmpPredicate CmpPredicate::getSwapped(const CmpInst *Cmp) {
   return getSwapped(get(Cmp));
 }
 
-hash_code llvm::hash_value(const CmpPredicate &Arg) { // NOLINT
-  return hash_combine(Arg.Pred, Arg.HasSameSign);
-}
-
 //===----------------------------------------------------------------------===//
 //                        SwitchInst Implementation
 //===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
index 682c5c3d8c63..3a0ae6b01a11 100644
--- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
+++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
@@ -290,7 +290,8 @@ static unsigned getHashValueImpl(SimpleValue Val) {
       Pred = CmpInst::getInversePredicate(Pred);
       std::swap(A, B);
     }
-    return hash_combine(Inst->getOpcode(), Pred, X, Y, A, B);
+    return hash_combine(Inst->getOpcode(),
+                        static_cast<CmpInst::Predicate>(Pred), X, Y, A, B);
   }
 
   if (CastInst *CI = dyn_cast<CastInst>(Inst))


Regression tests:
file: llvm/test/Transforms/EarlyCSE/pr119893.ll
commands: opt -passes=early-cse -S %s
tests: test_name: samesign_hash_bug
test_body: define i32 @samesign_hash_bug(i16 %v) {
  %zext = zext i16 %v to i32
  %icmp.samesign = icmp samesign ugt i32 %zext, 31
  %select.icmp.samesign = select i1 %icmp.samesign, i32 0, i32 1
  %ashr = ashr i32 0, %select.icmp.samesign
  %icmp = icmp ugt i32 %zext, 31
  %select.icmp = select i1 %icmp, i32 1, i32 0
  %ret = add i32 %ashr, %select.icmp
  ret i32 %ret
}