Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp: CommonPointerBase::compute
Issue title: [InstCombine] Miscompilation in foldGEPICmp
Labels: miscompilation
llvm:instcombine
generated by fuzzer
Reported by: dtcxzyw

Reproducer: https://alive2.llvm.org/ce/z/SLybDz
```
define i1 @gep_mugtiple_ugt_inbounds_nusw(ptr %base, i64 %idx, i64 %idx2) {
  %gep1 = getelementptr inbounds i8, ptr %base, i64 %idx
  %gep2 = getelementptr nusw i8, ptr %gep1, i64 %idx2
  %cmp = icmp ugt ptr %gep2, %base
  ret i1 %cmp
}
```
```
=>
define i1 @gep_mugtiple_ugt_inbounds_nusw(ptr %base, i64 %idx, i64 %idx2) {
#0:
  %#1 = add i64 %idx, %idx2
  %cmp = icmp sgt i64 %#1, 0
  ret i1 %cmp
}
Transformation doesn't verify!

ERROR: Value mismatch

Example:
ptr %base = pointer(non-local, block_id=1, offset=0) / Address=#x000000000000000c
i64 %idx = #x7ffffffffffffffc (9223372036854775804)
i64 %idx2 = #x0000000000000005 (5)

Source:
ptr %gep1 = pointer(non-local, block_id=1, offset=9223372036854775804) / Address=#x8000000000000008
ptr %gep2 = pointer(non-local, block_id=1, offset=-9223372036854775807) / Address=#x800000000000000d
i1 %cmp = #x1 (1)

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

Target:
i64 %#1 = #x8000000000000001 (9223372036854775809, -9223372036854775807)
i1 %cmp = #x0 (0)
Source value: #x1 (1)
Target value: #x0 (0)
```
cc @nikic 

Comments: author: nikic
body: Probably CommonPointerBase needs to use intersectForOffsetAdd.

Fix patch:
commit dcdbb39786a56181e1612354ecb8ecf5a7b0f449
Author: Nikita Popov <npopov@redhat.com>
Date:   Wed Sep 10 15:57:27 2025 +0200

    [InstCombine] Use intersectForOffsetAdd() in CommonPointerBase (#157851)
    
    Transforms using this helper will add up all the offsets, so we should
    use intersectForOffsetAdd() instead of plain intersection.
    
    Annoyingly, this requires specially handling the first GEP to avoid
    losing flags in that case.
    
    Fixes https://github.com/llvm/llvm-project/issues/157714.

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index d934638c15e7..f9155cc66031 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -2115,6 +2115,7 @@ CommonPointerBase CommonPointerBase::compute(Value *LHS, Value *RHS) {
   }
 
   // Find common base and collect RHS GEPs.
+  bool First = true;
   while (true) {
     if (Ptrs.contains(RHS)) {
       Base.Ptr = RHS;
@@ -2123,7 +2124,12 @@ CommonPointerBase CommonPointerBase::compute(Value *LHS, Value *RHS) {
 
     if (auto *GEP = dyn_cast<GEPOperator>(RHS)) {
       Base.RHSGEPs.push_back(GEP);
-      Base.RHSNW &= GEP->getNoWrapFlags();
+      if (First) {
+        First = false;
+        Base.RHSNW = GEP->getNoWrapFlags();
+      } else {
+        Base.RHSNW = Base.RHSNW.intersectForOffsetAdd(GEP->getNoWrapFlags());
+      }
       RHS = GEP->getPointerOperand();
     } else {
       // No common base.
@@ -2132,13 +2138,19 @@ CommonPointerBase CommonPointerBase::compute(Value *LHS, Value *RHS) {
   }
 
   // Collect LHS GEPs.
+  First = true;
   while (true) {
     if (LHS == Base.Ptr)
       break;
 
     auto *GEP = cast<GEPOperator>(LHS);
     Base.LHSGEPs.push_back(GEP);
-    Base.LHSNW &= GEP->getNoWrapFlags();
+    if (First) {
+      First = false;
+      Base.LHSNW = GEP->getNoWrapFlags();
+    } else {
+      Base.LHSNW = Base.LHSNW.intersectForOffsetAdd(GEP->getNoWrapFlags());
+    }
     LHS = GEP->getPointerOperand();
   }
 


Regression tests:
file: llvm/test/Transforms/InstCombine/icmp-gep.ll
commands: opt -passes=instcombine -S  < %s
tests: test_name: gep_mugtiple_ugt_not_all_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_mugtiple_ugt_not_all_nuw(ptr %base, i64 %idx, i64 %idx2) {
  %gep1 = getelementptr nuw i32, ptr %base, i64 %idx
  %gep2 = getelementptr i32, ptr %gep1, i64 %idx2
  %cmp = icmp ugt ptr %gep2, %base
  ret i1 %cmp
}

test_name: gep_mugtiple_ugt_inbounds_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_mugtiple_ugt_inbounds_nusw(ptr %base, i64 %idx, i64 %idx2) {
  %gep1 = getelementptr inbounds i32, ptr %base, i64 %idx
  %gep2 = getelementptr nusw i32, ptr %gep1, i64 %idx2
  %cmp = icmp ugt ptr %gep2, %base
  ret i1 %cmp
}