Bug type: miscompilation
Component: Loads
Bug location: llvm/lib/Analysis/Loads.cpp: isPointerAlwaysReplaceable
llvm::canReplacePointersIfEqual
llvm::canReplacePointersInUseIfEqual
Issue title: [InstSimplify] Folds vector pointer `select (icmp eq <N x ptr>)` to operand and loses provenance
Labels: miscompilation
llvm:analysis
Reported by: cardigan1008

Instsimplify can fold:

```llvm
%cmp = icmp eq <2 x ptr> %a, %b
%sel = select <2 x i1> %cmp, <2 x ptr> %a, %b
```

to `ret <2 x ptr> %b`, which is not always valid for pointers. Even if `%a[0]` and `%b[0]` have the same address , they might have different provenance, so selecting `%a[0]` is not equivalent to returning `%b[0]`.

Alive2 proof: https://alive2.llvm.org/ce/z/JxDjAx

Exposed from https://github.com/llvm/llvm-project/commit/1af627b592dd15bbe58136f902ced46251fc344d, cc @dtcxzyw 

Comments: author: nikic
body: In https://github.com/llvm/llvm-project/blob/393783fff844d8919a090f74e641d78c50bcc7f4/llvm/lib/Analysis/Loads.cpp#L845-L873 we should replace the pointer checks with pointer or vector of pointer checks.

Fix patch:
commit a3c9b72651178e7903c9c4db791b676ab91442a9
Author: hanbeom <kese111@gmail.com>
Date:   Thu Jan 8 09:30:11 2026 +0900

    [IR] Fix canReplacePointersIfEqual to properly validate vector pointers (#174142)
    
    Previously, `canReplacePointersIfEqual` unconditionally returned
    `true` for vectors of pointers (e.g., `<2 x ptr>`) because it only
    checked for scalar pointer types.
    
    This resulted in a failure to perform appropriate verification for
    these types. This patch fixes the logic to ensure they are properly
    validated.
    
    Fixes https://github.com/llvm/llvm-project/issues/174045

diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp
index 73f40f13a262..2e855f918c91 100644
--- a/llvm/lib/Analysis/Loads.cpp
+++ b/llvm/lib/Analysis/Loads.cpp
@@ -835,7 +835,7 @@ static bool isPointerAlwaysReplaceable(const Value *From, const Value *To,
   // optimizations.
   if (isa<ConstantPointerNull>(To))
     return true;
-  if (isa<Constant>(To) &&
+  if (isa<Constant>(To) && To->getType()->isPointerTy() &&
       isDereferenceablePointer(To, Type::getInt8Ty(To->getContext()), DL))
     return true;
   return getUnderlyingObjectAggressive(From) ==
@@ -847,7 +847,7 @@ bool llvm::canReplacePointersInUseIfEqual(const Use &U, const Value *To,
   Type *Ty = To->getType();
   assert(U->getType() == Ty && "values must have matching types");
   // Not a pointer, just return true.
-  if (!Ty->isPointerTy())
+  if (!Ty->isPtrOrPtrVectorTy())
     return true;
 
   // Do not perform replacements in lifetime intrinsic arguments.
@@ -866,7 +866,7 @@ bool llvm::canReplacePointersIfEqual(const Value *From, const Value *To,
                                      const DataLayout &DL) {
   assert(From->getType() == To->getType() && "values must have matching types");
   // Not a pointer, just return true.
-  if (!From->getType()->isPointerTy())
+  if (!From->getType()->isPtrOrPtrVectorTy())
     return true;
 
   return isPointerAlwaysReplaceable(From, To, DL);


Regression tests:
file: llvm/test/Transforms/GVN/condprop.ll
commands: opt < %s -passes=gvn -S
tests: test_name: select_same_obj
test_body: define void @select_same_obj(i1 %c, ptr %p, i64 %x) {
entry:
  %p2 = getelementptr i8, ptr %p, i64 %x
  %p3 = select i1 %c, ptr %p, ptr %p2
  %cmp = icmp eq ptr %p, %p3
  br i1 %cmp, label %if, label %exit

if:                                               ; preds = %entry
  call void @use_ptr(ptr %p)
  call void @use_ptr(ptr %p3)
  ret void

exit:                                             ; preds = %entry
  ret void
}

declare void @use_ptr(ptr)

test_name: select_vector_eq
test_body: define <2 x ptr> @select_vector_eq(<2 x ptr> %a, <2 x ptr> %b) {
  %cmp = icmp eq <2 x ptr> %a, %b
  %sel = select <2 x i1> %cmp, <2 x ptr> %a, <2 x ptr> %b
  ret <2 x ptr> %sel
}

file: llvm/test/Transforms/InstSimplify/select-icmp.ll
commands: opt < %s -passes=instsimplify -S
tests: test_name: ptr_eq_replace_same_underlying_object
test_body: 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: 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
}

test_name: ptr_eq_replace_vector_constant
test_body: define <2 x ptr> @ptr_eq_replace_vector_constant(<2 x ptr> %a) {
  %cmp = icmp eq <2 x ptr> %a, <ptr inttoptr (i64 42 to ptr), ptr inttoptr (i64 88 to ptr)>
  %sel = select <2 x i1> %cmp, <2 x ptr> %a, <2 x ptr> <ptr inttoptr (i64 42 to ptr), ptr inttoptr (i64 88 to ptr)>
  ret <2 x ptr> %sel
}

test_name: icmp_vector_ptr_eq_replace
test_body: define <2 x ptr> @icmp_vector_ptr_eq_replace(<2 x ptr> %a, <2 x ptr> %b) {
  %cmp = icmp eq <2 x ptr> %a, %b
  %sel = select <2 x i1> %cmp, <2 x ptr> %a, <2 x ptr> %b
  ret <2 x ptr> %sel
}

test_name: icmp_ptr_eq_replace_null
test_body: 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_constant
test_body: define ptr @ptr_eq_replace_constant(ptr %a) {
  %cmp = icmp eq ptr %a, inttoptr (i64 42 to ptr)
  %sel = select i1 %cmp, ptr inttoptr (i64 42 to ptr), ptr %a
  ret ptr %sel
}