Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp: InstCombinerImpl::foldGEPICmp
Issue title: [InstCombine] Missing flag check when folding comparison of geps with the same base pointer
Labels: miscompilation
llvm:instcombine
generated by fuzzer
Reported by: dtcxzyw

Alive2: https://alive2.llvm.org/ce/z/P5XbMx
```

----------------------------------------
define i1 @src(ptr %ptr, i8 %idx1, i8 %idx2) {
#0:
  %gep1 = gep ptr %ptr, 4 x i8 %idx1
  %gep2 = gep ptr %ptr, 4 x i8 %idx2
  %cmp = icmp eq ptr %gep1, %gep2, offsetonly
  ret i1 %cmp
}
=>
define i1 @tgt(ptr %ptr, i8 %idx1, i8 %idx2) {
#0:
  %cmp = icmp eq i8 %idx1, %idx2
  ret i1 %cmp
}
Transformation doesn't verify!

ERROR: Value mismatch

Example:
ptr %ptr = null
i8 %idx1 = #x00 (0)
i8 %idx2 = #x40 (64)

Source:
ptr %gep1 = null
ptr %gep2 = null
i1 %cmp = #x1 (1)

SOURCE MEMORY STATE
===================
NON-LOCAL BLOCKS:
Block 0 >	size: 0	align: 1	alloc type: 0	alive: false	address: 0
Block 1 >	size: 0	align: 1	alloc type: 0	alive: true	address: 1

Target:
i1 %cmp = #x0 (0)
Source value: #x1 (1)
Target value: #x0 (0)


----------------------------------------
define i1 @src1(ptr %ptr, i8 %idx1, i8 %idx2) {
#0:
  %gep1 = gep nuw ptr %ptr, 4 x i8 %idx1
  %gep2 = gep nuw ptr %ptr, 4 x i8 %idx2
  %cmp = icmp eq ptr %gep1, %gep2, offsetonly
  ret i1 %cmp
}
=>
define i1 @tgt1(ptr %ptr, i8 %idx1, i8 %idx2) {
#0:
  %cmp = icmp eq i8 %idx1, %idx2
  ret i1 %cmp
}
Transformation seems to be correct!


----------------------------------------
define i1 @src2(ptr %ptr, i8 %idx1, i8 %idx2) {
#0:
  %gep1 = gep nusw ptr %ptr, 4 x i8 %idx1
  %gep2 = gep nusw ptr %ptr, 4 x i8 %idx2
  %cmp = icmp eq ptr %gep1, %gep2, offsetonly
  ret i1 %cmp
}
=>
define i1 @tgt2(ptr %ptr, i8 %idx1, i8 %idx2) {
#0:
  %cmp = icmp eq i8 %idx1, %idx2
  ret i1 %cmp
}
Transformation seems to be correct!

Summary:
  2 correct transformations
  1 incorrect transformations
  0 failed-to-prove transformations
  0 Alive2 errors
```


Fix patch:
commit 9725595f3acc0c1aaa354e15ac4ee2b1f8ff4cc9
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Sat Feb 1 20:41:15 2025 +0800

    [InstCombine] Check nowrap flags when folding comparison of GEPs with the same base pointer (#121892)
    
    Alive2: https://alive2.llvm.org/ce/z/P5XbMx
    Closes https://github.com/llvm/llvm-project/issues/121890
    
    TODO: It is still safe to perform this transform without nowrap flags if
    the corresponding scale factor is 1 byte:
    https://alive2.llvm.org/ce/z/J-JCJd

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 9e9739ac2440..a8a5cb2b20d7 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -834,8 +834,9 @@ Instruction *InstCombinerImpl::foldGEPICmp(GEPOperator *GEPLHS, Value *RHS,
         return replaceInstUsesWith(
             I, // No comparison is needed here.
             ConstantInt::get(I.getType(), ICmpInst::isTrueWhenEqual(Cond)));
-
-      else if (NumDifferences == 1 && CanFold(NW)) {
+      // If two GEPs only differ by an index, compare them.
+      // Note that nowrap flags are always needed when comparing two indices.
+      else if (NumDifferences == 1 && NW != GEPNoWrapFlags::none()) {
         Value *LHSV = GEPLHS->getOperand(DiffOperand);
         Value *RHSV = GEPRHS->getOperand(DiffOperand);
         return NewICmp(NW, LHSV, RHSV);


Regression tests:
file: llvm/test/Transforms/InstCombine/opaque-ptr.ll
commands: opt -S -passes='instcombine<no-verify-fixpoint>' < %s
tests: test_name: cmp_gep_same_base_same_type_nusw
test_body: target datalayout = "p:8:8:8"
define i1 @cmp_gep_same_base_same_type_nusw(ptr %ptr, i8%idx1, i8%idx2) {
  %gep1 = getelementptr nusw i32, ptr %ptr, i8%idx1
  %gep2 = getelementptr nusw i32, ptr %ptr, i8%idx2
  %cmp = icmp eq ptr %gep1, %gep2
  ret i1 %cmp
}

test_name: cmp_gep_same_base_same_type_maywrap
test_body: target datalayout = "p:8:8:8"
define i1 @cmp_gep_same_base_same_type_maywrap(ptr %ptr, i8%idx1, i8%idx2) {
  %gep1 = getelementptr i32, ptr %ptr, i8%idx1
  %gep2 = getelementptr i32, ptr %ptr, i8%idx2
  %cmp = icmp eq ptr %gep1, %gep2
  ret i1 %cmp
}

test_name: cmp_gep_same_base_same_type_nuw
test_body: target datalayout = "p:8:8:8"
define i1 @cmp_gep_same_base_same_type_nuw(ptr %ptr, i8%idx1, i8%idx2) {
  %gep1 = getelementptr nuw i32, ptr %ptr, i8%idx1
  %gep2 = getelementptr nuw i32, ptr %ptr, i8%idx2
  %cmp = icmp eq ptr %gep1, %gep2
  ret i1 %cmp
}

test_name: cmp_gep_same_base_different_type
test_body: target datalayout = "p:8:8:8"
define i1 @cmp_gep_same_base_different_type(ptr %ptr, i8%idx1, i8%idx2) {
  %gep1 = getelementptr inbounds i32, ptr %ptr, i8%idx1
  %gep2 = getelementptr inbounds i64, ptr %ptr, i8%idx2
  %cmp = icmp ult ptr %gep1, %gep2
  ret i1 %cmp
}

test_name: cmp_gep_same_base_same_type
test_body: target datalayout = "p:8:8:8"
define i1 @cmp_gep_same_base_same_type(ptr %ptr, i8%idx1, i8%idx2) {
  %gep1 = getelementptr inbounds i32, ptr %ptr, i8%idx1
  %gep2 = getelementptr inbounds i32, ptr %ptr, i8%idx2
  %cmp = icmp ult ptr %gep1, %gep2
  ret i1 %cmp
}