Bug type: miscompilation
Component: FunctionAttrs
Bug location: llvm/lib/Transforms/IPO/FunctionAttrs.cpp: isReturnNonNull
Issue title: [FunctionAttrs] Incorrect nonnull inference for non-inbounds GEP
Labels: miscompilation
ipo
Reported by: nikic

FunctionAttrs incorrectly infers that the following function returns nonnull (https://llvm.godbolt.org/z/49Kbq15xn):
```llvm
define ptr @test(ptr nonnull %arg) {
  %res = getelementptr i8, ptr %arg, i64 -8
  ret ptr %res
}
```
This is because the code assumes that the result of a GEP is non-null if the source is non-null here: https://github.com/llvm/llvm-project/blob/d98a78590f4f9e43fdfb69fde7d154a985e4560f/llvm/lib/Transforms/IPO/FunctionAttrs.cpp#L1189 This is not correct for non-inbounds GEPs.


Fix patch:
commit f34d30cdae0f59698f660d5cc8fb993fb3441064
Author: Nikita Popov <npopov@redhat.com>
Date:   Tue May 7 09:47:28 2024 +0900

    [FunctionAttrs] Fix incorrect nonnull inference for non-inbounds GEP (#91180)
    
    For inbounds GEPs, if the source pointer is non-null, the result must
    also be non-null. However, this does not hold for non-inbounds GEPs.
    
    Fixes https://github.com/llvm/llvm-project/issues/91177.

diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 8e11cbf1cee4..26a4508aa151 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -1186,10 +1186,15 @@ static bool isReturnNonNull(Function *F, const SCCNodeSet &SCCNodes,
     switch (RVI->getOpcode()) {
     // Extend the analysis by looking upwards.
     case Instruction::BitCast:
-    case Instruction::GetElementPtr:
     case Instruction::AddrSpaceCast:
       FlowsToReturn.insert(RVI->getOperand(0));
       continue;
+    case Instruction::GetElementPtr:
+      if (cast<GEPOperator>(RVI)->isInBounds()) {
+        FlowsToReturn.insert(RVI->getOperand(0));
+        continue;
+      }
+      return false;
     case Instruction::Select: {
       SelectInst *SI = cast<SelectInst>(RVI);
       FlowsToReturn.insert(SI->getTrueValue());


Regression tests:
file: llvm/test/Transforms/FunctionAttrs/nocapture.ll
commands: opt -passes=function-attrs -S < %s
opt -passes=attributor-light -S < %s
tests: test_name: lookup_bit
test_body: @lookup_table = external global [2 x i1]

; Function Attrs: nounwind memory(none)
define ptr @lookup_bit(ptr %q, i32 %bitno) #0 {
  %tmp = ptrtoint ptr %q to i32
  %tmp2 = lshr i32 %tmp, %bitno
  %bit = and i32 %tmp2, 1
  %lookup = getelementptr [2 x i1], ptr @lookup_table, i32 0, i32 %bit
  ret ptr %lookup
}

attributes #0 = { nounwind memory(none) }

file: llvm/test/Transforms/FunctionAttrs/nonnull.ll
commands: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s
opt -S -passes=attributor-light %s
tests: test_name: pr91177_non_inbounds_gep
test_body: target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

define ptr @pr91177_non_inbounds_gep(ptr nonnull %arg) {
  %res = getelementptr i8, ptr %arg, i64 -8
  ret ptr %res
}

test_name: PR43833_simple
test_body: target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

declare void @sink(ptr)

define void @PR43833_simple(ptr %0, i32 %1) {
  %3 = icmp ne i32 %1, 0
  br i1 %3, label %4, label %7

4:                                                ; preds = %2
  %5 = zext i32 %1 to i64
  %6 = getelementptr inbounds i32, ptr %0, i64 %5
  br label %8

7:                                                ; preds = %8, %2
  ret void

8:                                                ; preds = %8, %4
  %9 = phi i32 [ 1, %4 ], [ %10, %8 ]
  tail call void @sink(ptr %6)
  %10 = add nuw nsw i32 %9, 1
  %11 = icmp eq i32 %10, %1
  br i1 %11, label %7, label %8
}