Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp: foldGEPOfPhi
Issue title: [InstCombine] GEPNoWrapFlags is propagated incorrectly
Labels: miscompilation
llvm:instcombine
Reported by: dtcxzyw

Alive2: https://alive2.llvm.org/ce/z/3ME7S7
```
define ptr @src(i1 %c, ptr %p) {
  br i1 %c, label %if, label %else

if:
  %gep1 = getelementptr inbounds i32, ptr %p, i64 1
  br label %join

else:
  %gep2 = getelementptr i64, ptr %p, i64 2
  br label %join

join:
  %phi = phi ptr [ %gep1, %if ], [ %gep2, %else ]
  %gep = getelementptr i32, ptr %phi, i64 1
  ret ptr %gep
}
```
```

----------------------------------------
define ptr @src(i1 %c, ptr %p) {
#0:
  br i1 %c, label %if, label %else

else:
  %gep2 = gep ptr %p, 8 x i64 2
  br label %join

if:
  %gep1 = gep inbounds ptr %p, 4 x i64 1
  br label %join

join:
  %phi = phi ptr [ %gep1, %if ], [ %gep2, %else ]
  %gep = gep ptr %phi, 4 x i64 1
  ret ptr %gep
}
=>
define ptr @src(i1 %c, ptr %p) {
#0:
  br i1 %c, label %if, label %else

else:
  br label %join

if:
  br label %join

join:
  %#1 = phi i64 [ 4, %if ], [ 16, %else ]
  %#2 = gep inbounds nuw ptr %p, 1 x i64 %#1
  %gep = gep ptr %#2, 1 x i64 4
  ret ptr %gep
}
Transformation doesn't verify!

ERROR: Target is more poisonous than source

Example:
i1 %c = #x0 (0)
ptr %p = null

Source:
  >> Jump to %else
ptr %gep2 = pointer(non-local, block_id=0, offset=16) / Address=#x0000000000000010
  >> Jump to %join
ptr %phi = pointer(non-local, block_id=0, offset=16) / Address=#x0000000000000010
ptr %gep = pointer(non-local, block_id=0, offset=20) / Address=#x0000000000000014

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:
  >> Jump to %else
  >> Jump to %join
i64 %#1 = #x0000000000000010 (16)
ptr %#2 = poison
ptr %gep = poison
Source value: pointer(non-local, block_id=0, offset=20) / Address=#x0000000000000014
Target value: poison

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



Fix patch:
commit a4d92400a6db9566d84cb4b900149e36e117f452
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Fri Jan 3 23:19:57 2025 +0800

    [InstCombine] Fix GEPNoWrapFlags propagation in `foldGEPOfPhi` (#121572)
    
    Closes https://github.com/llvm/llvm-project/issues/121459.

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 934156f04f7f..f63de1f0d410 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2782,6 +2782,7 @@ static Instruction *foldGEPOfPhi(GetElementPtrInst &GEP, PHINode *PN,
   // loop iteration).
   if (Op1 == &GEP)
     return nullptr;
+  GEPNoWrapFlags NW = Op1->getNoWrapFlags();
 
   int DI = -1;
 
@@ -2838,6 +2839,8 @@ static Instruction *foldGEPOfPhi(GetElementPtrInst &GEP, PHINode *PN,
         }
       }
     }
+
+    NW &= Op2->getNoWrapFlags();
   }
 
   // If not all GEPs are identical we'll have to create a new PHI node.
@@ -2847,6 +2850,8 @@ static Instruction *foldGEPOfPhi(GetElementPtrInst &GEP, PHINode *PN,
     return nullptr;
 
   auto *NewGEP = cast<GetElementPtrInst>(Op1->clone());
+  NewGEP->setNoWrapFlags(NW);
+
   if (DI == -1) {
     // All the GEPs feeding the PHI are identical. Clone one down into our
     // BB so that it can be merged with the current GEP.


Regression tests:
file: llvm/test/Transforms/InstCombine/opaque-ptr.ll
commands: opt -S -passes='instcombine<no-verify-fixpoint>' < %s
tests: test_name: gep_of_phi_of_gep_different_type
test_body: define ptr @gep_of_phi_of_gep_different_type(i1 %c, ptr %p) {
  br i1 %c, label %if, label %else

if:                                               ; preds = %0
  %gep1 = getelementptr i32, ptr %p, i64 1
  br label %join

else:                                             ; preds = %0
  %gep2 = getelementptr i64, ptr %p, i64 2
  br label %join

join:                                             ; preds = %else, %if
  %phi = phi ptr [ %gep1, %if ], [ %gep2, %else ]
  %gep = getelementptr i32, ptr %phi, i64 1
  ret ptr %gep
}

test_name: gep_of_phi_of_gep_flags2
test_body: define ptr @gep_of_phi_of_gep_flags2(i1 %c, ptr %p) {
  br i1 %c, label %if, label %else

if:                                               ; preds = %0
  %gep1 = getelementptr nuw i32, ptr %p, i64 1
  br label %join

else:                                             ; preds = %0
  %gep2 = getelementptr nuw i32, ptr %p, i64 2
  br label %join

join:                                             ; preds = %else, %if
  %phi = phi ptr [ %gep1, %if ], [ %gep2, %else ]
  %gep = getelementptr i32, ptr %phi, i64 1
  ret ptr %gep
}

test_name: gep_of_phi_of_gep_flags1
test_body: define ptr @gep_of_phi_of_gep_flags1(i1 %c, ptr %p) {
  br i1 %c, label %if, label %else

if:                                               ; preds = %0
  %gep1 = getelementptr inbounds i32, ptr %p, i64 1
  br label %join

else:                                             ; preds = %0
  %gep2 = getelementptr i32, ptr %p, i64 2
  br label %join

join:                                             ; preds = %else, %if
  %phi = phi ptr [ %gep1, %if ], [ %gep2, %else ]
  %gep = getelementptr i32, ptr %phi, i64 1
  ret ptr %gep
}