Bug type: hang
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp: InstCombinerImpl::foldSelectValueEquivalence
Issue title: [InstCombine] InstCombine gets stuck when simplifying selects
Labels: llvm:instcombine
llvm:hang
Reported by: dtcxzyw

Reduced test case: https://godbolt.org/z/ds9eq96db
```
define i16 @func(i16 noundef %p_12) {
entry:
  %cmp1 = icmp ult i16 %p_12, 2
  %and1 = and i16 %p_12, 1
  %and2 = select i1 %cmp1, i16 %and1, i16 0
  %cmp2 = icmp eq i16 %and2, %p_12
  %and3 = select i1 %cmp2, i16 %and1, i16 0
  ret i16 %and3
}
```
```
ADD:   %and3 = select i1 %cmp2, i16 %and2, i16 0
ADD:   %and2 = select i1 %cmp1, i16 %and1, i16 0
ADD:   %and1 = and i16 %p_12, 1
IC: Visiting:   %and1 = and i16 %p_12, 1
IC: Visiting:   %and2 = select i1 %cmp1, i16 %and1, i16 0
IC: Visiting:   %and3 = select i1 %cmp2, i16 %and2, i16 0
ADD DEFERRED:   %and2 = select i1 %cmp1, i16 %and1, i16 0
ADD DEFERRED:   %cmp2 = icmp eq i16 %and2, %p_12
IC: Mod =   %and3 = select i1 %cmp2, i16 %and2, i16 0
    New =   %and3 = select i1 %cmp2, i16 %and1, i16 0
ADD:   %and3 = select i1 %cmp2, i16 %and1, i16 0
ADD:   %cmp2 = icmp eq i16 %and2, %p_12
ADD:   %and2 = select i1 %cmp1, i16 %and1, i16 0
IC: Visiting:   %and2 = select i1 %cmp1, i16 %and1, i16 0
IC: Visiting:   %cmp2 = icmp eq i16 %and2, %p_12
IC: Visiting:   %and3 = select i1 %cmp2, i16 %and1, i16 0
ADD DEFERRED:   %and1 = and i16 %p_12, 1
ADD DEFERRED:   %and2 = select i1 %cmp1, i16 %and1, i16 0
IC: Mod =   %and3 = select i1 %cmp2, i16 %and1, i16 0
    New =   %and3 = select i1 %cmp2, i16 %and2, i16 0


ADD:   %and3 = select i1 %cmp2, i16 %and2, i16 0
ADD:   %and2 = select i1 %cmp1, i16 %and1, i16 0
ADD:   %and1 = and i16 %p_12, 1
IC: Visiting:   %and1 = and i16 %p_12, 1
IC: Visiting:   %and2 = select i1 %cmp1, i16 %and1, i16 0
IC: Visiting:   %and3 = select i1 %cmp2, i16 %and2, i16 0
ADD DEFERRED:   %and2 = select i1 %cmp1, i16 %and1, i16 0
ADD DEFERRED:   %cmp2 = icmp eq i16 %and2, %p_12
IC: Mod =   %and3 = select i1 %cmp2, i16 %and2, i16 0
    New =   %and3 = select i1 %cmp2, i16 %and1, i16 0
ADD:   %and3 = select i1 %cmp2, i16 %and1, i16 0
ADD:   %cmp2 = icmp eq i16 %and2, %p_12
ADD:   %and2 = select i1 %cmp1, i16 %and1, i16 0
IC: Visiting:   %and2 = select i1 %cmp1, i16 %and1, i16 0
IC: Visiting:   %cmp2 = icmp eq i16 %and2, %p_12
IC: Visiting:   %and3 = select i1 %cmp2, i16 %and1, i16 0
ADD DEFERRED:   %and1 = and i16 %p_12, 1
ADD DEFERRED:   %and2 = select i1 %cmp1, i16 %and1, i16 0
IC: Mod =   %and3 = select i1 %cmp2, i16 %and1, i16 0
    New =   %and3 = select i1 %cmp2, i16 %and2, i16 0

...
```

Comments: author: dtcxzyw
body: Another reproducer:  https://godbolt.org/z/7MEYTe9cq
```
declare void @use(i32,i32)

define void @func(ptr %p) {
  %1 = load i32, ptr %p, align 4
  %2 = sext i32 %1 to i64
  %3 = icmp slt i64 %2, 2
  %4 = zext i1 %3 to i32
  %5 = and i32 %4, %1
  %6 = icmp eq i32 %5, %1
  %7 = zext i1 %6 to i32
  %8 = and i32 %7, %1
  call void @use(i32 %8, i32 noundef %1)
  ret void
}
```

author: dtcxzyw
body: Third reproducer: https://godbolt.org/z/8f8b4xved
```
define i8 @func(i32 noundef %p_38) {
entry:
  %conv393 = trunc i32 %p_38 to i8
  %0 = call i8 @llvm.bitreverse.i8(i8 %conv393)
  %conv394 = zext i8 %0 to i32
  %cmp395 = icmp eq i32 %conv394, %p_38
  %conv397 = zext i1 %cmp395 to i8
  %mul.i = mul i8 %conv393, %conv397
  ret i8 %mul.i
}
```
author: dtcxzyw
body: > Third reproducer: https://godbolt.org/z/8f8b4xved
> 
> ```
> define i8 @func(i32 noundef %p_38) {
> entry:
>   %conv393 = trunc i32 %p_38 to i8
>   %0 = call i8 @llvm.bitreverse.i8(i8 %conv393)
>   %conv394 = zext i8 %0 to i32
>   %cmp395 = icmp eq i32 %conv394, %p_38
>   %conv397 = zext i1 %cmp395 to i8
>   %mul.i = mul i8 %conv393, %conv397
>   ret i8 %mul.i
> }
> ```

A simpler testcase:
```
define i8 @func(i32 noundef %x) {
entry:
  %trunc = trunc i32 %x to i8
  %rev = call i8 @llvm.bitreverse.i8(i8 %trunc)
  %ext = zext i8 %rev to i32
  %cmp = icmp eq i32 %ext, %x
  %sel = select i1 %cmp, i8 %trunc, i8 0
  ret i8 %sel
}
```

@nikic I have no idea how to fix it in `foldSelectValueEquivalence`/`simplifyWithOpReplaced`. `simplifyWithOpReplaced` may replace the input `x` with a more complicated value `f(x)`.

author: dtcxzyw
body: @nikic Ping.

Fix patch:
commit 9f45c5e1a65a1abf4920b617d36ed05e73c04bea
Author: Nikita Popov <npopov@redhat.com>
Date:   Wed Mar 6 09:33:51 2024 +0100

    [InstCombine] Fix infinite loop in select equivalence fold (#84036)
    
    When replacing with a non-constant, it's possible that the result of the
    simplification is actually more complicated than the original, and may
    result in an infinite combine loop.
    
    Mitigate the issue by requiring that either the replacement or
    simplification result is constant, which should ensure that it's
    simpler. While this check is crude, it does not appear to cause
    optimization regressions in real-world code in practice.
    
    Fixes https://github.com/llvm/llvm-project/issues/83127.

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index c47bc33df070..aee18d770f72 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1285,7 +1285,11 @@ Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel,
       isGuaranteedNotToBeUndefOrPoison(CmpRHS, SQ.AC, &Sel, &DT)) {
     if (Value *V = simplifyWithOpReplaced(TrueVal, CmpLHS, CmpRHS, SQ,
                                           /* AllowRefinement */ true))
-      return replaceOperand(Sel, Swapped ? 2 : 1, V);
+      // Require either the replacement or the simplification result to be a
+      // constant to avoid infinite loops.
+      // FIXME: Make this check more precise.
+      if (isa<Constant>(CmpRHS) || isa<Constant>(V))
+        return replaceOperand(Sel, Swapped ? 2 : 1, V);
 
     // Even if TrueVal does not simplify, we can directly replace a use of
     // CmpLHS with CmpRHS, as long as the instruction is not used anywhere
@@ -1303,7 +1307,8 @@ Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel,
       isGuaranteedNotToBeUndefOrPoison(CmpLHS, SQ.AC, &Sel, &DT))
     if (Value *V = simplifyWithOpReplaced(TrueVal, CmpRHS, CmpLHS, SQ,
                                           /* AllowRefinement */ true))
-      return replaceOperand(Sel, Swapped ? 2 : 1, V);
+      if (isa<Constant>(CmpLHS) || isa<Constant>(V))
+        return replaceOperand(Sel, Swapped ? 2 : 1, V);
 
   auto *FalseInst = dyn_cast<Instruction>(FalseVal);
   if (!FalseInst)


Regression tests:
file: llvm/test/Transforms/InstCombine/select.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: select_replacement_shift_noundef
test_body: target datalayout = "e-p:64:64-p1:16:16-p2:32:32:32-p3:64:64:64"

define i8 @select_replacement_shift_noundef(i8 %x, i8 %y, i8 %z) {
  %shr = lshr exact i8 %x, 1
  call void @use_i8(i8 noundef %shr)
  %cmp = icmp eq i8 %shr, %y
  %shl = shl i8 %y, 1
  %sel = select i1 %cmp, i8 %shl, i8 %z
  ret i8 %sel
}

declare void @use_i8(i8)

test_name: select_replacement_loop4
test_body: target datalayout = "e-p:64:64-p1:16:16-p2:32:32:32-p3:64:64:64"

define i16 @select_replacement_loop4(i16 noundef %p_12) {
  %cmp1 = icmp ult i16 %p_12, 2
  %and1 = and i16 %p_12, 1
  %and2 = select i1 %cmp1, i16 %and1, i16 0
  %cmp2 = icmp eq i16 %and2, %p_12
  %and3 = select i1 %cmp2, i16 %and1, i16 0
  ret i16 %and3
}

test_name: select_replacement_loop3
test_body: target datalayout = "e-p:64:64-p1:16:16-p2:32:32:32-p3:64:64:64"

define i8 @select_replacement_loop3(i32 noundef %x) {
  %trunc = trunc i32 %x to i8
  %rev = call i8 @llvm.bitreverse.i8(i8 %trunc)
  %ext = zext i8 %rev to i32
  %cmp = icmp eq i32 %ext, %x
  %sel = select i1 %cmp, i8 %trunc, i8 0
  ret i8 %sel
}

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

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

test_name: select_replacement_sub
test_body: target datalayout = "e-p:64:64-p1:16:16-p2:32:32:32-p3:64:64:64"

define i8 @select_replacement_sub(i8 %x, i8 %y, i8 %z) {
  %cmp = icmp eq i8 %x, %y
  %sub = sub i8 %x, %y
  %sel = select i1 %cmp, i8 %sub, i8 %z
  ret i8 %sel
}

test_name: select_replacement_gep_inbounds
test_body: target datalayout = "e-p:64:64-p1:16:16-p2:32:32:32-p3:64:64:64"

define ptr @select_replacement_gep_inbounds(ptr %base, i64 %offset) {
  %cmp = icmp eq i64 %offset, 0
  %gep = getelementptr inbounds i8, ptr %base, i64 %offset
  %sel = select i1 %cmp, ptr %base, ptr %gep
  ret ptr %sel
}

test_name: select_replacement_loop2
test_body: target datalayout = "e-p:64:64-p1:16:16-p2:32:32:32-p3:64:64:64"

define i32 @select_replacement_loop2(i32 %arg, i32 %arg2) {
  %div = udiv i32 %arg, %arg2
  %mul = mul nsw i32 %div, %arg2
  %cmp = icmp eq i32 %mul, %arg
  %sel = select i1 %cmp, i32 %div, i32 undef
  ret i32 %sel
}