Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp: InstCombinerImpl::foldGEPICmp
Issue title: [InstCombine] Incorrect fold of the comparison of GEPs
Labels: miscompilation
llvm:instcombine
Reported by: dtcxzyw

Alive2: https://alive2.llvm.org/ce/z/HRsqeW
```
define i1 @test(ptr %x, ptr %y) {
  %gep1 = getelementptr i8, ptr %x, i32 1
  %gep2 = getelementptr i8, ptr %y, i32 1
  %cmp = icmp ult ptr %gep1, %gep2
  ret i1 %cmp
}
```
```

----------------------------------------
define i1 @test(ptr %x, ptr %y) {
#0:
  %gep1 = gep ptr %x, 1 x i32 1
  %gep2 = gep ptr %y, 1 x i32 1
  %cmp = icmp ult ptr %gep1, %gep2
  ret i1 %cmp
}
=>
define i1 @test(ptr %x, ptr %y) {
#0:
  %cmp = icmp ult ptr %x, %y
  ret i1 %cmp
}
Transformation doesn't verify!

ERROR: Value mismatch

Example:
ptr %x = pointer(non-local, block_id=0, offset=-1) / Address=#xf
ptr %y = null

Source:
ptr %gep1 = null
ptr %gep2 = pointer(non-local, block_id=0, offset=1) / Address=#x1
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: 1	align: 2	alloc type: 0	alive: true	address: 2
Block 2 >	size: 0	align: 1	alloc type: 0	alive: true	address: 3

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

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


Fix patch:
commit fac46469977da9c4e9c6eeaac21103c971190577
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Sat Jan 4 17:23:57 2025 +0800

    [InstCombine] Check no wrap flags before folding icmp of GEPs with same indices (#121628)
    
    Alive2: https://alive2.llvm.org/ce/z/Dr3Sbe
    Closes https://github.com/llvm/llvm-project/issues/121581.

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index d6fdade25559..8b23583c5106 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -747,6 +747,8 @@ Instruction *InstCombinerImpl::foldGEPICmp(GEPOperator *GEPLHS, Value *RHS,
                         ConstantExpr::getPointerBitCastOrAddrSpaceCast(
                             cast<Constant>(RHS), Base->getType()));
   } else if (GEPOperator *GEPRHS = dyn_cast<GEPOperator>(RHS)) {
+    GEPNoWrapFlags NW = GEPLHS->getNoWrapFlags() & GEPRHS->getNoWrapFlags();
+
     // If the base pointers are different, but the indices are the same, just
     // compare the base pointer.
     if (PtrBase != GEPRHS->getOperand(0)) {
@@ -764,7 +766,8 @@ Instruction *InstCombinerImpl::foldGEPICmp(GEPOperator *GEPLHS, Value *RHS,
 
       // If all indices are the same, just compare the base pointers.
       Type *BaseType = GEPLHS->getOperand(0)->getType();
-      if (IndicesTheSame && CmpInst::makeCmpResultType(BaseType) == I.getType())
+      if (IndicesTheSame &&
+          CmpInst::makeCmpResultType(BaseType) == I.getType() && CanFold(NW))
         return new ICmpInst(Cond, GEPLHS->getOperand(0), GEPRHS->getOperand(0));
 
       // If we're comparing GEPs with two base pointers that only differ in type
@@ -804,7 +807,6 @@ Instruction *InstCombinerImpl::foldGEPICmp(GEPOperator *GEPLHS, Value *RHS,
       return transformToIndexedCompare(GEPLHS, RHS, Cond, DL, *this);
     }
 
-    GEPNoWrapFlags NW = GEPLHS->getNoWrapFlags() & GEPRHS->getNoWrapFlags();
     if (GEPLHS->getNumOperands() == GEPRHS->getNumOperands() &&
         GEPLHS->getSourceElementType() == GEPRHS->getSourceElementType()) {
       // If the GEPs only differ by one index, compare it.


Regression tests:
file: llvm/test/Transforms/InstCombine/icmp-gep.ll
commands: opt -passes=instcombine -S  < %s
tests: test_name: pointer_icmp_aligned_with_offset_negative
test_body: target datalayout = "e-p:64:64:64-p1:16:16:16-p2:32:32:32-p3:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"

define i1 @pointer_icmp_aligned_with_offset_negative(ptr align 8 %a, ptr align 8 %a2) {
  %gep = getelementptr i8, ptr %a, i64 8
  %cmp = icmp eq ptr %gep, %a2
  ret i1 %cmp
}

test_name: gep_diff_base_same_indices_nuw_nusw
test_body: target datalayout = "e-p:64:64:64-p1:16:16:16-p2:32:32:32-p3:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"

define i1 @gep_diff_base_same_indices_nuw_nusw(ptr %x, ptr %y, i64 %z) {
  %gep1 = getelementptr nuw i8, ptr %x, i64 %z
  %gep2 = getelementptr nusw i8, ptr %y, i64 %z
  %cmp = icmp ult ptr %gep1, %gep2
  ret i1 %cmp
}

test_name: gep_diff_base_same_indices
test_body: target datalayout = "e-p:64:64:64-p1:16:16:16-p2:32:32:32-p3:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"

define i1 @gep_diff_base_same_indices(ptr %x, ptr %y, i64 %z) {
  %gep1 = getelementptr i8, ptr %x, i64 %z
  %gep2 = getelementptr i8, ptr %y, i64 %z
  %cmp = icmp ult ptr %gep1, %gep2
  ret i1 %cmp
}

test_name: gep_diff_base_same_indices_nusw
test_body: target datalayout = "e-p:64:64:64-p1:16:16:16-p2:32:32:32-p3:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"

define i1 @gep_diff_base_same_indices_nusw(ptr %x, ptr %y, i64 %z) {
  %gep1 = getelementptr nusw i8, ptr %x, i64 %z
  %gep2 = getelementptr nusw i8, ptr %y, i64 %z
  %cmp = icmp ult ptr %gep1, %gep2
  ret i1 %cmp
}

test_name: gep_diff_base_same_indices_nuw
test_body: target datalayout = "e-p:64:64:64-p1:16:16:16-p2:32:32:32-p3:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"

define i1 @gep_diff_base_same_indices_nuw(ptr %x, ptr %y, i64 %z) {
  %gep1 = getelementptr nuw i8, ptr %x, i64 %z
  %gep2 = getelementptr nuw i8, ptr %y, i64 %z
  %cmp = icmp ult ptr %gep1, %gep2
  ret i1 %cmp
}