Bug type: miscompilation
Component: GVNSink
Bug location: llvm/lib/Transforms/Scalar/GVNSink.cpp: GVNSink::analyzeInstructionForSinking
Issue title: GVNSink produces incorrect codegen with respect to GEPs
Labels: bug
miscompilation
llvm:GVN
Reported by: PiJoules

```llvm
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "thumbv7em-none-unknown-eabi"

%"struct.std::pair" = type <{ i32, %struct.substruct, [2 x i8] }>
%struct.substruct = type { i8, i8 }
%"struct.std::random_access_iterator_tag" = type { i8 }

define linkonce_odr dso_local void @__advance(ptr noundef nonnull align 4 dereferenceable(4) %__i, i32 noundef %__n) local_unnamed_addr {
entry:
  %0 = call i1 @llvm.is.constant.i32(i32 %__n)
  %cmp = icmp eq i32 %__n, 1
  %or.cond = and i1 %0, %cmp
  br i1 %or.cond, label %if.then, label %if.else

if.then:                                          ; preds = %entry
  %1 = load ptr, ptr %__i, align 4
  %incdec.ptr = getelementptr inbounds i8, ptr %1, i32 8
  store ptr %incdec.ptr, ptr %__i, align 4
  br label %if.end6

if.else:                                          ; preds = %entry
  %2 = call i1 @llvm.is.constant.i32(i32 %__n)
  %cmp2 = icmp eq i32 %__n, -1
  %or.cond7 = and i1 %2, %cmp2
  br i1 %or.cond7, label %if.then3, label %if.else5

if.then3:                                         ; preds = %if.else
  %3 = load ptr, ptr %__i, align 4
  %incdec.ptr4 = getelementptr inbounds i8, ptr %3, i32 -8
  store ptr %incdec.ptr4, ptr %__i, align 4
  br label %if.end6

if.else5:                                         ; preds = %if.else
  %4 = load ptr, ptr %__i, align 4
  %add.ptr = getelementptr inbounds %"struct.std::pair", ptr %4, i32 %__n
  store ptr %add.ptr, ptr %__i, align 4
  br label %if.end6

if.end6:                                          ; preds = %if.then3, %if.else5, %if.then
  ret void
}
```

The above IR when piped through gvn-sink via `./bin/opt < /tmp/repro.ll -passes=gvn-sink -S` produces:

```llvm
; ModuleID = '<stdin>'
source_filename = "<stdin>"
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "thumbv7em-none-unknown-eabi"

define linkonce_odr dso_local void @__advance(ptr noundef nonnull align 4 dereferenceable(4) %__i, i32 noundef %__n) local_unnamed_addr {
entry:
  %0 = call i1 @llvm.is.constant.i32(i32 %__n)
  %cmp = icmp eq i32 %__n, 1
  %or.cond = and i1 %0, %cmp
  br i1 %or.cond, label %if.then, label %if.else

if.then:                                          ; preds = %entry
  br label %if.end6

if.else:                                          ; preds = %entry
  %1 = call i1 @llvm.is.constant.i32(i32 %__n)
  %cmp2 = icmp eq i32 %__n, -1
  %or.cond7 = and i1 %1, %cmp2
  br i1 %or.cond7, label %if.then3, label %if.else5

if.then3:                                         ; preds = %if.else
  br label %if.end6

if.else5:                                         ; preds = %if.else
  br label %if.end6

if.end6:                                          ; preds = %if.else5, %if.then3, %if.then
  %.sink1 = phi i32 [ 8, %if.then ], [ -8, %if.then3 ], [ %__n, %if.else5 ]
  %2 = load ptr, ptr %__i, align 4
  %incdec.ptr = getelementptr inbounds i8, ptr %2, i32 %.sink1
  store ptr %incdec.ptr, ptr %__i, align 4
  ret void
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare i1 @llvm.is.constant.i32(i32) #0

attributes #0 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
```

The GEP in the result is incorrect. Prior, the GEPs were
```llvm
  %incdec.ptr = getelementptr inbounds i8, ptr %1, i32 8
  %incdec.ptr4 = getelementptr inbounds i8, ptr %3, i32 -8
  %add.ptr = getelementptr inbounds %"struct.std::pair", ptr %4, i32 %__n
```
but now it's
```llvm
%incdec.ptr = getelementptr inbounds i8, ptr %2, i32 %.sink1
```
This results in an incorrect offset for `%add.ptr = getelementptr inbounds %"struct.std::pair", ptr %4, i32 %__n` since we'd now just add `%.sink1` bytes (where `%.sink1 = %__n` which is the second argument), whereas before we'd add `%__n * 8` bytes (where 8 is the sizeof `%"struct.std::pair"`).

The before ASM is:
```asm
__advance:
        ldr     r2, [r0]
        add.w   r1, r2, r1, lsl #3
        str     r1, [r0]
        bx      lr
```
but the after asm is:
```asm
__advance:
        ldr     r2, [r0]
        add     r1, r2
        str     r1, [r0]
        bx      lr
```

The GEP should not be a candidate for sinking.


Fix patch:
commit 1c979ab7e59feb03c58cac631a93143b0c776c20
Author: AdityaK <hiraditya@msn.com>
Date:   Tue Apr 30 11:31:45 2024 -0700

    [GVNSink] Fix incorrect codegen with respect to GEPs #85333 (#88440)
    
    As mentioned in #68882 and
    https://discourse.llvm.org/t/rfc-replacing-getelementptr-with-ptradd/68699
    
    Gep arithmetic isn't consistent with different types. GVNSink didn't
    realize this and sank all geps
    as long as their operands can be wired via PHIs
    in a post-dominator.
    
    Fixes: #85333

diff --git a/llvm/lib/Transforms/Scalar/GVNSink.cpp b/llvm/lib/Transforms/Scalar/GVNSink.cpp
index d4907326eb0a..1d2577e1da33 100644
--- a/llvm/lib/Transforms/Scalar/GVNSink.cpp
+++ b/llvm/lib/Transforms/Scalar/GVNSink.cpp
@@ -719,12 +719,11 @@ GVNSink::analyzeInstructionForSinking(LockstepReverseIterator &LRI,
   // try and continue making progress.
   Instruction *I0 = NewInsts[0];
 
-  // If all instructions that are going to participate don't have the same
-  // number of operands, we can't do any useful PHI analysis for all operands.
-  auto hasDifferentNumOperands = [&I0](Instruction *I) {
-    return I->getNumOperands() != I0->getNumOperands();
+  auto isNotSameOperation = [&I0](Instruction *I) {
+    return !I0->isSameOperationAs(I);
   };
-  if (any_of(NewInsts, hasDifferentNumOperands))
+
+  if (any_of(NewInsts, isNotSameOperation))
     return std::nullopt;
 
   for (unsigned OpNum = 0, E = I0->getNumOperands(); OpNum != E; ++OpNum) {


Regression tests:
file: llvm/test/Transforms/GVNSink/different-gep-types.ll
commands: opt -passes=gvn-sink -S %s
tests: test_name: bar
test_body: target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"

%"struct.std::pair" = type <{ i32, %struct.substruct, [2 x i8] }>
%struct.substruct = type { i8, i8 }

define void @bar(ptr noundef nonnull align 4 dereferenceable(4) %__i, i32 noundef %__n) {
entry:
  %cmp = icmp eq i32 %__n, 1
  br i1 %cmp, label %if.then, label %if.else

if.then:                                          ; preds = %entry
  %0 = load ptr, ptr %__i, align 4
  %incdec.ptr4 = getelementptr inbounds i8, ptr %0, i32 -8
  br label %if.end6

if.else:                                          ; preds = %entry
  %1 = load ptr, ptr %__i, align 4
  %add.ptr = getelementptr inbounds %"struct.std::pair", ptr %1, i32 %__n
  br label %if.end6

if.end6:                                          ; preds = %if.else, %if.then
  %incdec.ptr.sink = phi ptr [ %incdec.ptr4, %if.then ], [ %add.ptr, %if.else ]
  store ptr %incdec.ptr.sink, ptr %__i, align 4
  ret void
}

test_name: foo
test_body: target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"

define void @foo(ptr noundef nonnull align 4 dereferenceable(4) %__i, i32 noundef %__n) {
entry:
  %cmp = icmp eq i32 %__n, 1
  br i1 %cmp, label %if.then, label %if.else

if.then:                                          ; preds = %entry
  %0 = load ptr, ptr %__i, align 4
  %incdec.ptr = getelementptr inbounds i8, ptr %0, i32 8
  store ptr %incdec.ptr, ptr %__i, align 4
  br label %if.end6

if.else:                                          ; preds = %entry
  %cmp2 = icmp eq i32 %__n, -1
  br i1 %cmp2, label %if.then3, label %if.else5

if.then3:                                         ; preds = %if.else
  %1 = load ptr, ptr %__i, align 4
  %incdec.ptr4 = getelementptr inbounds i8, ptr %1, i32 -8
  store ptr %incdec.ptr4, ptr %__i, align 4
  br label %if.end6

if.else5:                                         ; preds = %if.else
  %2 = load ptr, ptr %__i, align 4
  %add.ptr = getelementptr inbounds i8, ptr %2, i32 -4
  store ptr %add.ptr, ptr %__i, align 4
  br label %if.end6

if.end6:                                          ; preds = %if.else5, %if.then3, %if.then
  ret void
}