Bug type: miscompilation
Component: InstructionSimplify
Bug location: llvm/lib/Analysis/InstructionSimplify.cpp: simplifySelectWithICmpCond
Issue title: [InstSimplify] wrong folding of pointer comparison `select (icmp eq ptr ...)`
Labels: miscompilation
llvm:analysis
Reported by: bongjunj

https://github.com/llvm/llvm-project/blob/c93e001ca695e905cb965b36d63f7a348d1dd809/llvm/lib/Analysis/InstructionSimplify.cpp#L148-L167

Alive2 report: https://alive2.llvm.org/ce/z/aux2zY

```llvm
----------------------------------------
define ptr @smin_test8.2(ptr %a, ptr %b, ptr %c) {
#0:
  %cmp1 = icmp eq ptr %a, %b
  %umin1 = select i1 %cmp1, ptr %a, ptr %b
  %cmp2 = icmp slt ptr %b, %c
  %umin2 = select i1 %cmp2, ptr %b, ptr %c
  %cmp3 = icmp ult ptr %umin2, %a
  %umin3 = select i1 %cmp3, ptr %umin2, ptr %a
  %cmp4 = icmp slt ptr %c, %umin3
  %res = select i1 %cmp4, ptr %umin1, ptr %umin3
  ret ptr %res
}
=>
define ptr @smin_test8.2(ptr %a, ptr %b, ptr %c) {
#0:
  %cmp2 = icmp slt ptr %b, %c
  %umin2 = select i1 %cmp2, ptr %b, ptr %c
  %cmp3 = icmp ult ptr %umin2, %a
  %umin3 = select i1 %cmp3, ptr %umin2, ptr %a
  %cmp4 = icmp slt ptr %c, %umin3
  %res = select i1 %cmp4, ptr %b, ptr %umin3
  ret ptr %res
}
Transformation doesn't verify!

ERROR: Value mismatch

Example:
ptr %a = pointer(non-local, block_id=0, offset=1) / Address=#x1
ptr %b = pointer(non-local, block_id=1, offset=0) / Address=#x1
ptr %c = pointer(non-local, block_id=0, offset=-8) / Address=#x8

Source:
i1 %cmp1 = #x1 (1)
ptr %umin1 = pointer(non-local, block_id=0, offset=1) / Address=#x1
i1 %cmp2 = #x0 (0)
ptr %umin2 = pointer(non-local, block_id=0, offset=-8) / Address=#x8
i1 %cmp3 = #x0 (0)
ptr %umin3 = pointer(non-local, block_id=0, offset=1) / Address=#x1
i1 %cmp4 = #x1 (1)
ptr %res = pointer(non-local, block_id=0, offset=1) / Address=#x1

SOURCE MEMORY STATE
===================
NON-LOCAL BLOCKS:
Block 0 >	size: 0	align: 1	alloc type: 0	alive: false	address: 0
Block 1 >	size: 1	align: 1	alloc type: 0	alive: true	address: 1
Block 2 >	size: 1	align: 1	alloc type: 0	alive: true	address: 2
Block 3 >	size: 3	align: 1	alloc type: 0	alive: true	address: 8

Target:
i1 %cmp2 = #x0 (0)
ptr %umin2 = pointer(non-local, block_id=0, offset=-8) / Address=#x8
i1 %cmp3 = #x0 (0)
ptr %umin3 = pointer(non-local, block_id=0, offset=1) / Address=#x1
i1 %cmp4 = #x1 (1)
ptr %res = pointer(non-local, block_id=1, offset=0) / Address=#x1
Source value: pointer(non-local, block_id=0, offset=1) / Address=#x1
Target value: pointer(non-local, block_id=1, offset=0) / Address=#x1

Summary:
  0 correct transformations
  1 incorrect transformations
  0 failed-to-prove transformations
  0 Alive2 errors
```


Fix patch:
commit 1af627b592dd15bbe58136f902ced46251fc344d
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Sun Feb 2 19:04:23 2025 +0800

    [InstSimplify] Add additional checks when substituting pointers (#125385)
    
    Compile-time impact:
    https://llvm-compile-time-tracker.com/compare.php?from=d09b521624f263b5f1296f8d4771836b97e600cb&to=e437ba2cb83bb965e13ef00727671896f03ff84f&stat=instructions:u
    IR diff looks acceptable.
    Closes https://github.com/llvm/llvm-project/issues/115574

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 21c937530cd8..3cbc4107433e 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -27,6 +27,7 @@
 #include "llvm/Analysis/CmpInstAnalysis.h"
 #include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/Analysis/InstSimplifyFolder.h"
+#include "llvm/Analysis/Loads.h"
 #include "llvm/Analysis/LoopAnalysisManager.h"
 #include "llvm/Analysis/MemoryBuiltins.h"
 #include "llvm/Analysis/OverflowInstAnalysis.h"
@@ -4731,12 +4732,16 @@ static Value *simplifySelectWithICmpCond(Value *CondVal, Value *TrueVal,
   // the arms of the select. See if substituting this value into the arm and
   // simplifying the result yields the same value as the other arm.
   if (Pred == ICmpInst::ICMP_EQ) {
-    if (Value *V = simplifySelectWithEquivalence({{CmpLHS, CmpRHS}}, TrueVal,
-                                                 FalseVal, Q, MaxRecurse))
-      return V;
-    if (Value *V = simplifySelectWithEquivalence({{CmpRHS, CmpLHS}}, TrueVal,
-                                                 FalseVal, Q, MaxRecurse))
-      return V;
+    if (CmpLHS->getType()->isIntOrIntVectorTy() ||
+        canReplacePointersIfEqual(CmpLHS, CmpRHS, Q.DL))
+      if (Value *V = simplifySelectWithEquivalence({{CmpLHS, CmpRHS}}, TrueVal,
+                                                   FalseVal, Q, MaxRecurse))
+        return V;
+    if (CmpLHS->getType()->isIntOrIntVectorTy() ||
+        canReplacePointersIfEqual(CmpRHS, CmpLHS, Q.DL))
+      if (Value *V = simplifySelectWithEquivalence({{CmpRHS, CmpLHS}}, TrueVal,
+                                                   FalseVal, Q, MaxRecurse))
+        return V;
 
     Value *X;
     Value *Y;


Regression tests:
file: llvm/test/Transforms/InstSimplify/select-icmp.ll
commands: opt < %s -passes=instsimplify -S
tests: test_name: icmp_ptr_eq_replace_null
test_body: target datalayout = "p:8:8:8"

define ptr @icmp_ptr_eq_replace_null(ptr %a) {
  %cmp = icmp eq ptr %a, null
  %sel = select i1 %cmp, ptr null, ptr %a
  ret ptr %sel
}

test_name: ptr_eq_replace_same_underlying_object
test_body: target datalayout = "p:8:8:8"

define ptr @ptr_eq_replace_same_underlying_object(ptr %st, i64 %i, i64 %j) {
  %a = getelementptr inbounds i8, ptr %st, i64 %i
  %b = getelementptr inbounds i8, ptr %st, i64 %j
  %cmp = icmp eq ptr %a, %b
  %sel = select i1 %cmp, ptr %a, ptr %b
  ret ptr %sel
}

test_name: icmp_ptr_eq_replace
test_body: target datalayout = "p:8:8:8"

define ptr @icmp_ptr_eq_replace(ptr %a, ptr %b) {
  %cmp = icmp eq ptr %a, %b
  %sel = select i1 %cmp, ptr %a, ptr %b
  ret ptr %sel
}