Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp: InstCombinerImpl::foldSelectValueEquivalence
Issue title: [InstCombine] Pointer equality propagated without regard to provenance
Labels: miscompilation
llvm:instcombine
generated by fuzzer
Reported by: dtcxzyw

It is a variant of https://github.com/llvm/llvm-project/issues/115574.
Reproducer: https://alive2.llvm.org/ce/z/FLg923
```
define ptr @src1(ptr %p, ptr %q) {
#0:
  %cmp = icmp eq ptr %p, %q
  %#1 = freeze i1 %cmp
  %select = select i1 %#1, ptr %p, ptr %q
  ret ptr %select
}
=>
define ptr @src1(ptr %p, ptr %q) {
#0:
  %q.fr = freeze ptr %q
  ret ptr %q.fr
}
Transformation doesn't verify!

ERROR: Value mismatch

Example:
ptr %p = pointer(non-local, block_id=1, offset=0) / Address=#x1
ptr %q = pointer(non-local, block_id=0, offset=1) / Address=#x1

Source:
i1 %cmp = #x1 (1)
i1 %#1 = #x1 (1)
ptr %select = pointer(non-local, block_id=1, offset=0) / Address=#x1

SOURCE MEMORY STATE
===================
NON-LOCAL BLOCKS:
Block 0 >	size: 0	align: 1	alloc type: 0	alive: false	address: #x0
Block 1 >	size: 13	align: 1	alloc type: 0	alive: true	address: #x1
Block 2 >	size: 0	align: 1	alloc type: 0	alive: true	address: #xf

Target:
ptr %q.fr = pointer(non-local, block_id=0, offset=1) / Address=#x1

TARGET MEMORY STATE
===================
NON-LOCAL BLOCKS:
Block 0 >	size: 0	align: 1	alloc type: 0	alive: false	address: #x0
Block 1 >	size: 13	align: 1	alloc type: 0	alive: true	address: #x1
Block 2 >	size: 0	align: 1	alloc type: 0	alive: true	address: #xf
Source value: pointer(non-local, block_id=1, offset=0) / Address=#x1
Target value: pointer(non-local, block_id=0, offset=1) / Address=#x1
```

Comments: author: nikic
body: I expect it's due to this special case fold: https://github.com/llvm/llvm-project/blob/99ce20624629921771de2674946bbb2f9707ca5a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp#L2937-L2961

Note that this fold has become useless due to https://github.com/llvm/llvm-project/pull/154336 and should be deleted entirely, I just forgot about it.
author: nikic
body: > I expect it's due to this special case fold: 

Okay, having tried that, it's not this fold. I'll still put up a PR to delete it...
author: dtcxzyw
body: I am preparing for a fix: https://github.com/llvm/llvm-project/compare/main...dtcxzyw:llvm-project:perf/fix-161636


Fix patch:
commit c41611bacb0ca72a283accd7680061421454e152
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Sun Oct 5 14:02:00 2025 +0800

    [InstCombine] Fix pointer replacement in `foldSelectValueEquivalence` (#161701)
    
    Closes https://github.com/llvm/llvm-project/issues/161636.
    
    Compile-time impact (+0.06%):
    https://llvm-compile-time-tracker.com/compare.php?from=c2ef022aa7413ddc9aba48fa6fbe6fbd0cb14e19&to=9a0f0302efc30580136d191e66bac929f08ee25f&stat=instructions%3Au
    I used to disable this fold for pointers, because I cannot construct a
    positive test that is covered by `foldSelectValueEquivalence ` but not
    covered by `simplifySelectWithICmpCond`. But the IR diff shows we still
    benefit from the fold in InstCombine:
    + Bail out on pointers:
    https://github.com/dtcxzyw/llvm-opt-benchmark/pull/2880
    + This patch: https://github.com/dtcxzyw/llvm-opt-benchmark/pull/2882

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 3df448ddde99..8f60e506e8a3 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -17,6 +17,7 @@
 #include "llvm/Analysis/AssumptionCache.h"
 #include "llvm/Analysis/CmpInstAnalysis.h"
 #include "llvm/Analysis/InstructionSimplify.h"
+#include "llvm/Analysis/Loads.h"
 #include "llvm/Analysis/OverflowInstAnalysis.h"
 #include "llvm/Analysis/ValueTracking.h"
 #include "llvm/Analysis/VectorUtils.h"
@@ -42,6 +43,7 @@
 #include "llvm/Support/KnownBits.h"
 #include "llvm/Transforms/InstCombine/InstCombiner.h"
 #include <cassert>
+#include <optional>
 #include <utility>
 
 #define DEBUG_TYPE "instcombine"
@@ -1451,10 +1453,16 @@ Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel,
     return nullptr;
   };
 
-  if (Instruction *R = ReplaceOldOpWithNewOp(CmpLHS, CmpRHS))
-    return R;
-  if (Instruction *R = ReplaceOldOpWithNewOp(CmpRHS, CmpLHS))
-    return R;
+  bool CanReplaceCmpLHSWithRHS = canReplacePointersIfEqual(CmpLHS, CmpRHS, DL);
+  if (CanReplaceCmpLHSWithRHS) {
+    if (Instruction *R = ReplaceOldOpWithNewOp(CmpLHS, CmpRHS))
+      return R;
+  }
+  bool CanReplaceCmpRHSWithLHS = canReplacePointersIfEqual(CmpRHS, CmpLHS, DL);
+  if (CanReplaceCmpRHSWithLHS) {
+    if (Instruction *R = ReplaceOldOpWithNewOp(CmpRHS, CmpLHS))
+      return R;
+  }
 
   auto *FalseInst = dyn_cast<Instruction>(FalseVal);
   if (!FalseInst)
@@ -1469,12 +1477,14 @@ Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel,
   // Example:
   // (X == 42) ? 43 : (X + 1) --> (X == 42) ? (X + 1) : (X + 1) --> X + 1
   SmallVector<Instruction *> DropFlags;
-  if (simplifyWithOpReplaced(FalseVal, CmpLHS, CmpRHS, SQ,
-                             /* AllowRefinement */ false,
-                             &DropFlags) == TrueVal ||
-      simplifyWithOpReplaced(FalseVal, CmpRHS, CmpLHS, SQ,
-                             /* AllowRefinement */ false,
-                             &DropFlags) == TrueVal) {
+  if ((CanReplaceCmpLHSWithRHS &&
+       simplifyWithOpReplaced(FalseVal, CmpLHS, CmpRHS, SQ,
+                              /* AllowRefinement */ false,
+                              &DropFlags) == TrueVal) ||
+      (CanReplaceCmpRHSWithLHS &&
+       simplifyWithOpReplaced(FalseVal, CmpRHS, CmpLHS, SQ,
+                              /* AllowRefinement */ false,
+                              &DropFlags) == TrueVal)) {
     for (Instruction *I : DropFlags) {
       I->dropPoisonGeneratingAnnotations();
       Worklist.add(I);


Regression tests:
file: llvm/test/Transforms/InstCombine/select-gep.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: ptr_eq_replace_freeze1
test_body: define ptr @ptr_eq_replace_freeze1(ptr %p, ptr %q) {
  %p.fr = freeze ptr %p
  %q.fr = freeze ptr %q
  %cmp = icmp eq ptr %p.fr, %q.fr
  %select = select i1 %cmp, ptr %p.fr, ptr %q.fr
  ret ptr %select
}

test_name: ptr_eq_replace_freeze2
test_body: define ptr @ptr_eq_replace_freeze2(ptr %p, ptr %q) {
  %gep1 = getelementptr i32, ptr %p, i64 4
  %gep2 = getelementptr i32, ptr %q, i64 4
  %cmp = icmp eq ptr %p, %q
  %cmp.fr = freeze i1 %cmp
  %select = select i1 %cmp.fr, ptr %gep1, ptr %gep2
  ret ptr %select
}

test_name: test7
test_body: define <2 x ptr> @test7(<2 x ptr> %p1, i64 %idx, <2 x i1> %cc) {
  %gep = getelementptr i64, <2 x ptr> %p1, i64 %idx
  %select = select <2 x i1> %cc, <2 x ptr> %p1, <2 x ptr> %gep
  ret <2 x ptr> %select
}