[llvm] [Inliner] Fix bug when propagating poison generating return attributes (PR #66036)

via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 28 13:51:36 PDT 2023


https://github.com/goldsteinn updated https://github.com/llvm/llvm-project/pull/66036

>From 70117ad4dbdd14936adaec87cf079971a530b7d8 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Mon, 4 Sep 2023 16:31:47 -0500
Subject: [PATCH 1/2] [Inliner] Add some additional tests for progagating
 attributes before inlining; NFC

---
 .../Inline/access-attributes-prop.ll          | 498 ++++++++++++++++++
 .../Inline/ret_attr_align_and_noundef.ll      | 339 ++++++++++++
 2 files changed, 837 insertions(+)
 create mode 100644 llvm/test/Transforms/Inline/access-attributes-prop.ll
 create mode 100644 llvm/test/Transforms/Inline/ret_attr_align_and_noundef.ll

diff --git a/llvm/test/Transforms/Inline/access-attributes-prop.ll b/llvm/test/Transforms/Inline/access-attributes-prop.ll
new file mode 100644
index 000000000000000..3b4a59897c5694a
--- /dev/null
+++ b/llvm/test/Transforms/Inline/access-attributes-prop.ll
@@ -0,0 +1,498 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes
+; RUN: opt -S -passes=inline %s | FileCheck %s
+; RUN: opt -S -passes='cgscc(inline)' %s | FileCheck %s
+; RUN: opt -S -passes='module-inline' %s | FileCheck %s
+
+declare void @bar1(ptr %p)
+declare void @bar2(ptr %p, ptr %p2)
+
+define dso_local void @foo1_rdonly(ptr readonly %p) {
+; CHECK-LABEL: define {{[^@]+}}@foo1_rdonly
+; CHECK-SAME: (ptr readonly [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @bar1(ptr %p)
+  ret void
+}
+
+define dso_local void @foo1(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@foo1
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @bar1(ptr %p)
+  ret void
+}
+
+define dso_local void @foo1_bar_aligned64_deref512(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@foo1_bar_aligned64_deref512
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr align 64 dereferenceable(512) [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @bar1(ptr align 64 dereferenceable(512) %p)
+  ret void
+}
+
+define dso_local void @foo1_bar_aligned512_deref_or_null512(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@foo1_bar_aligned512_deref_or_null512
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr align 512 dereferenceable_or_null(512) [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @bar1(ptr align 512 dereferenceable_or_null(512) %p)
+  ret void
+}
+
+define dso_local void @foo2(ptr %p, ptr %p2) {
+; CHECK-LABEL: define {{[^@]+}}@foo2
+; CHECK-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @bar2(ptr %p, ptr %p)
+  ret void
+}
+
+define dso_local void @foo2_2(ptr %p, ptr %p2) {
+; CHECK-LABEL: define {{[^@]+}}@foo2_2
+; CHECK-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {
+; CHECK-NEXT:    call void @bar2(ptr [[P2]], ptr [[P2]])
+; CHECK-NEXT:    ret void
+;
+  call void @bar2(ptr %p2, ptr %p2)
+  ret void
+}
+
+define dso_local void @foo2_3(ptr %p, ptr readnone %p2) {
+; CHECK-LABEL: define {{[^@]+}}@foo2_3
+; CHECK-SAME: (ptr [[P:%.*]], ptr readnone [[P2:%.*]]) {
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[P2]])
+; CHECK-NEXT:    ret void
+;
+  call void @bar2(ptr %p, ptr %p2)
+  ret void
+}
+
+define dso_local void @buz1_wronly(ptr %p) writeonly {
+; CHECK: Function Attrs: memory(write)
+; CHECK-LABEL: define {{[^@]+}}@buz1_wronly
+; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @bar1(ptr %p)
+  ret void
+}
+
+define dso_local void @buz1(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@buz1
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @bar1(ptr %p)
+  ret void
+}
+
+define dso_local void @buz1_wronly_fail_alloca(ptr %p) writeonly {
+; CHECK: Function Attrs: memory(write)
+; CHECK-LABEL: define {{[^@]+}}@buz1_wronly_fail_alloca
+; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[A]])
+; CHECK-NEXT:    ret void
+;
+  %a = alloca i32, align 4
+  call void @bar2(ptr %p, ptr %a)
+  ret void
+}
+
+define dso_local void @buz1_fail_alloca(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@buz1_fail_alloca
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[A]])
+; CHECK-NEXT:    ret void
+;
+  %a = alloca i32, align 4
+  call void @bar2(ptr %p, ptr %a)
+  ret void
+}
+
+define dso_local void @buz1_wronly_partially_okay_alloca(ptr %p) writeonly {
+; CHECK: Function Attrs: memory(write)
+; CHECK-LABEL: define {{[^@]+}}@buz1_wronly_partially_okay_alloca
+; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[A]])
+; CHECK-NEXT:    ret void
+;
+  call void @bar1(ptr %p)
+  %a = alloca i32, align 4
+  call void @bar2(ptr %p, ptr %a)
+  ret void
+}
+
+define dso_local void @buz1_partially_okay_alloca(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@buz1_partially_okay_alloca
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[A]])
+; CHECK-NEXT:    ret void
+;
+  call void @bar1(ptr %p)
+  %a = alloca i32, align 4
+  call void @bar2(ptr %p, ptr %a)
+  ret void
+}
+
+define dso_local void @foo2_through_obj(ptr %p, ptr %p2) {
+; CHECK-LABEL: define {{[^@]+}}@foo2_through_obj
+; CHECK-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {
+; CHECK-NEXT:    [[PP:%.*]] = getelementptr i8, ptr [[P]], i64 9
+; CHECK-NEXT:    [[P2P:%.*]] = getelementptr i8, ptr [[P2]], i64 123
+; CHECK-NEXT:    call void @bar2(ptr [[P2P]], ptr [[PP]])
+; CHECK-NEXT:    ret void
+;
+  %pp = getelementptr i8, ptr %p, i64 9
+  %p2p = getelementptr i8, ptr %p2, i64 123
+  call void @bar2(ptr %p2p, ptr %pp)
+  ret void
+}
+
+define void @prop_param_func_decl(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_func_decl
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1_rdonly(ptr %p)
+  ret void
+}
+
+define void @prop_param_callbase_def(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_callbase_def
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1(ptr readonly %p)
+  call void @bar1(ptr %p)
+  ret void
+}
+
+define void @prop_param_callbase_def_2x(ptr %p, ptr %p2) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_callbase_def_2x
+; CHECK-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo2(ptr readonly %p, ptr %p)
+  ret void
+}
+
+define void @prop_param_callbase_def_2x_2(ptr %p, ptr %p2) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_callbase_def_2x_2
+; CHECK-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {
+; CHECK-NEXT:    [[PP_I:%.*]] = getelementptr i8, ptr [[P]], i64 9
+; CHECK-NEXT:    [[P2P_I:%.*]] = getelementptr i8, ptr [[P2]], i64 123
+; CHECK-NEXT:    call void @bar2(ptr [[P2P_I]], ptr [[PP_I]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo2_through_obj(ptr readonly %p, ptr writeonly %p2)
+  ret void
+}
+
+define void @prop_param_callbase_def_2x_incompat(ptr %p, ptr %p2) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_callbase_def_2x_incompat
+; CHECK-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {
+; CHECK-NEXT:    [[PP_I:%.*]] = getelementptr i8, ptr [[P]], i64 9
+; CHECK-NEXT:    [[P2P_I:%.*]] = getelementptr i8, ptr [[P]], i64 123
+; CHECK-NEXT:    call void @bar2(ptr [[P2P_I]], ptr [[PP_I]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo2_through_obj(ptr readnone %p, ptr readonly %p)
+  ret void
+}
+
+define void @prop_param_callbase_def_2x_incompat_2(ptr %p, ptr %p2) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_callbase_def_2x_incompat_2
+; CHECK-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo2(ptr readonly %p, ptr readnone %p)
+  ret void
+}
+
+define void @prop_param_callbase_def_2x_incompat_3(ptr %p, ptr %p2) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_callbase_def_2x_incompat_3
+; CHECK-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo2_2(ptr readonly %p, ptr readnone %p)
+  ret void
+}
+
+define void @prop_param_callbase_def_1x_partial(ptr %p, ptr %p2) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_callbase_def_1x_partial
+; CHECK-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo2(ptr readonly %p, ptr %p)
+  ret void
+}
+
+define void @prop_param_callbase_def_1x_partial_2(ptr %p, ptr %p2) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_callbase_def_1x_partial_2
+; CHECK-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo2_2(ptr readonly %p, ptr %p)
+  ret void
+}
+
+define void @prop_param_callbase_def_1x_partial_3(ptr %p, ptr %p2) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_callbase_def_1x_partial_3
+; CHECK-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo2_3(ptr readonly %p, ptr %p)
+  ret void
+}
+
+define void @prop_deref(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_deref
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1(ptr dereferenceable(16) %p)
+  ret void
+}
+
+define void @prop_deref_or_null(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_deref_or_null
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1(ptr dereferenceable_or_null(256) %p)
+  ret void
+}
+
+define void @prop_param_nonnull_and_align(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_nonnull_and_align
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1(ptr nonnull align 32 %p)
+  ret void
+}
+
+define void @prop_param_deref_align_no_update(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_deref_align_no_update
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr align 64 dereferenceable(512) [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1_bar_aligned64_deref512(ptr align 4 dereferenceable(64) %p)
+  ret void
+}
+
+define void @prop_param_deref_align_update(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_deref_align_update
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr align 64 dereferenceable(512) [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1_bar_aligned64_deref512(ptr align 128 dereferenceable(1024) %p)
+  ret void
+}
+
+define void @prop_param_deref_or_null_update(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_deref_or_null_update
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr align 512 dereferenceable_or_null(512) [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1_bar_aligned512_deref_or_null512(ptr dereferenceable_or_null(1024) %p)
+  ret void
+}
+
+define void @prop_param_deref_or_null_no_update(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_deref_or_null_no_update
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr align 512 dereferenceable_or_null(512) [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1_bar_aligned512_deref_or_null512(ptr dereferenceable_or_null(32) %p)
+  ret void
+}
+
+define void @prop_fn_decl(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_fn_decl
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @buz1_wronly(ptr %p)
+  call void @bar1(ptr %p)
+  ret void
+}
+
+define void @prop_cb_def_wr(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_cb_def_wr
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @buz1(ptr %p) writeonly
+  call void @bar1(ptr %p)
+  ret void
+}
+
+define void @prop_fn_decl_fail_alloca(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_fn_decl_fail_alloca
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[A_I:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 4, ptr [[A_I]])
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[A_I]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 4, ptr [[A_I]])
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @buz1_wronly_fail_alloca(ptr %p)
+  call void @bar1(ptr %p)
+  ret void
+}
+
+define void @prop_cb_def_wr_fail_alloca(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_cb_def_wr_fail_alloca
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[A_I:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 4, ptr [[A_I]])
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[A_I]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 4, ptr [[A_I]])
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @buz1_fail_alloca(ptr %p) writeonly
+  call void @bar1(ptr %p)
+  ret void
+}
+
+define void @prop_fn_decl_partially_okay_alloca(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_fn_decl_partially_okay_alloca
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[A_I:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 4, ptr [[A_I]])
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[A_I]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 4, ptr [[A_I]])
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @buz1_wronly_partially_okay_alloca(ptr %p)
+  call void @bar1(ptr %p)
+  ret void
+}
+
+define void @prop_cb_def_wr_partially_okay_alloca(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_cb_def_wr_partially_okay_alloca
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[A_I:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 4, ptr [[A_I]])
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    call void @bar2(ptr [[P]], ptr [[A_I]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 4, ptr [[A_I]])
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @buz1_partially_okay_alloca(ptr %p) writeonly
+  call void @bar1(ptr %p)
+  ret void
+}
+
+define void @prop_cb_def_readonly(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_cb_def_readonly
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1(ptr %p) readonly
+  ret void
+}
+
+define void @prop_cb_def_readnone(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_cb_def_readnone
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1(ptr %p) readnone
+  ret void
+}
+
+define void @prop_cb_def_argmem_readonly_fail(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_cb_def_argmem_readonly_fail
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1(ptr %p) memory(argmem:read)
+  ret void
+}
+
+define void @prop_cb_def_inaccessible_none(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_cb_def_inaccessible_none
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1(ptr %p) memory(inaccessiblemem:none)
+  ret void
+}
+
+define void @prop_cb_def_inaccessible_none_argmem_none(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_cb_def_inaccessible_none_argmem_none
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1(ptr %p) memory(inaccessiblemem:none, argmem:none)
+  ret void
+}
+
+define void @prop_cb_def_willreturn(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_cb_def_willreturn
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1(ptr %p) willreturn
+  ret void
+}
+
+define void @prop_cb_def_mustprogress(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_cb_def_mustprogress
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @bar1(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+  call void @foo1(ptr %p) mustprogress
+  ret void
+}
diff --git a/llvm/test/Transforms/Inline/ret_attr_align_and_noundef.ll b/llvm/test/Transforms/Inline/ret_attr_align_and_noundef.ll
new file mode 100644
index 000000000000000..f7dccc75ba4e029
--- /dev/null
+++ b/llvm/test/Transforms/Inline/ret_attr_align_and_noundef.ll
@@ -0,0 +1,339 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
+; RUN: opt -S -passes=inline %s | FileCheck %s
+; RUN: opt -S -passes='cgscc(inline)' %s | FileCheck %s
+; RUN: opt -S -passes='module-inline' %s | FileCheck %s
+
+declare ptr @foo()
+declare void @use.ptr(ptr) willreturn nounwind
+declare void @bar()
+declare void @baz()
+declare ptr @llvm.ptrmask.p0.i64(ptr, i64)
+declare i1 @val()
+
+define ptr @callee0123() {
+; CHECK-LABEL: define ptr @callee0123() {
+; CHECK-NEXT:    [[R:%.*]] = call ptr @foo()
+; CHECK-NEXT:    ret ptr [[R]]
+;
+  %r = call ptr @foo()
+  ret ptr %r
+}
+
+define ptr @caller0() {
+; CHECK-LABEL: define ptr @caller0() {
+; CHECK-NEXT:    [[R_I:%.*]] = call dereferenceable(16) ptr @foo()
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call dereferenceable(16) ptr @callee0123()
+  ret ptr %r
+}
+
+define ptr @caller1() {
+; CHECK-LABEL: define ptr @caller1() {
+; CHECK-NEXT:    [[R_I:%.*]] = call ptr @foo()
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call align(16) ptr @callee0123()
+  ret ptr %r
+}
+
+define ptr @caller2() {
+; CHECK-LABEL: define ptr @caller2() {
+; CHECK-NEXT:    [[R_I:%.*]] = call ptr @foo()
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call noundef ptr @callee0123()
+  ret ptr %r
+}
+
+define ptr @caller3() {
+; CHECK-LABEL: define ptr @caller3() {
+; CHECK-NEXT:    [[R_I:%.*]] = call dereferenceable_or_null(32) ptr @foo()
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call dereferenceable_or_null(32) ptr @callee0123()
+  ret ptr %r
+}
+
+define ptr @caller_0123_dornull() {
+; CHECK-LABEL: define ptr @caller_0123_dornull() {
+; CHECK-NEXT:    [[R_I:%.*]] = call dereferenceable_or_null(16) ptr @foo()
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call noundef align(32) dereferenceable_or_null(16) ptr @callee0123()
+  ret ptr %r
+}
+
+define ptr @caller_0123_d() {
+; CHECK-LABEL: define ptr @caller_0123_d() {
+; CHECK-NEXT:    [[R_I:%.*]] = call dereferenceable(16) ptr @foo()
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call noundef align(32) dereferenceable(16) ptr @callee0123()
+  ret ptr %r
+}
+
+define ptr @callee4() {
+; CHECK-LABEL: define ptr @callee4() {
+; CHECK-NEXT:    [[R:%.*]] = call ptr @foo()
+; CHECK-NEXT:    call void @bar()
+; CHECK-NEXT:    ret ptr [[R]]
+;
+  %r = call ptr @foo()
+  call void @bar()
+  ret ptr %r
+}
+
+define ptr @caller4_fail() {
+; CHECK-LABEL: define ptr @caller4_fail() {
+; CHECK-NEXT:    [[R_I:%.*]] = call ptr @foo()
+; CHECK-NEXT:    call void @bar()
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call noundef align(256) ptr @callee4()
+  ret ptr %r
+}
+
+define ptr @callee5() {
+; CHECK-LABEL: define ptr @callee5() {
+; CHECK-NEXT:    [[R:%.*]] = call align 64 ptr @foo()
+; CHECK-NEXT:    ret ptr [[R]]
+;
+  %r = call align(64) ptr @foo()
+  ret ptr %r
+}
+
+define ptr @caller5_fail() {
+; CHECK-LABEL: define ptr @caller5_fail() {
+; CHECK-NEXT:    [[R_I:%.*]] = call align 64 ptr @foo()
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call noundef align(32) ptr @callee5()
+  ret ptr %r
+}
+
+define ptr @caller5_okay() {
+; CHECK-LABEL: define ptr @caller5_okay() {
+; CHECK-NEXT:    [[R_I:%.*]] = call align 64 ptr @foo()
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call noundef align(128) ptr @callee5()
+  ret ptr %r
+}
+
+define ptr @callee6() {
+; CHECK-LABEL: define ptr @callee6() {
+; CHECK-NEXT:    [[R:%.*]] = call dereferenceable(16) ptr @foo()
+; CHECK-NEXT:    ret ptr [[R]]
+;
+  %r = call dereferenceable(16) ptr @foo()
+  ret ptr %r
+}
+
+define ptr @caller6_fail() {
+; CHECK-LABEL: define ptr @caller6_fail() {
+; CHECK-NEXT:    [[R_I:%.*]] = call dereferenceable(8) ptr @foo()
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call dereferenceable(8) ptr @callee6()
+  ret ptr %r
+}
+
+define ptr @caller6_okay() {
+; CHECK-LABEL: define ptr @caller6_okay() {
+; CHECK-NEXT:    [[R_I:%.*]] = call dereferenceable(32) ptr @foo()
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call dereferenceable(32) ptr @callee6()
+  ret ptr %r
+}
+
+define ptr @callee7() {
+; CHECK-LABEL: define ptr @callee7() {
+; CHECK-NEXT:    [[R:%.*]] = call dereferenceable_or_null(16) ptr @foo()
+; CHECK-NEXT:    ret ptr [[R]]
+;
+  %r = call dereferenceable_or_null(16) ptr @foo()
+  ret ptr %r
+}
+
+define ptr @caller7_fail() {
+; CHECK-LABEL: define ptr @caller7_fail() {
+; CHECK-NEXT:    [[R_I:%.*]] = call dereferenceable_or_null(8) ptr @foo()
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call dereferenceable_or_null(8) ptr @callee7()
+  ret ptr %r
+}
+
+define ptr @caller7_okay() {
+; CHECK-LABEL: define ptr @caller7_okay() {
+; CHECK-NEXT:    [[R_I:%.*]] = call dereferenceable_or_null(32) ptr @foo()
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call dereferenceable_or_null(32) ptr @callee7()
+  ret ptr %r
+}
+
+define ptr @callee8() {
+; CHECK-LABEL: define ptr @callee8() {
+; CHECK-NEXT:    [[R:%.*]] = call ptr @foo()
+; CHECK-NEXT:    ret ptr [[R]]
+;
+  %r = call ptr @foo()
+  ret ptr %r
+}
+
+define ptr @caller8_okay_use_after_poison_anyways() {
+; CHECK-LABEL: define ptr @caller8_okay_use_after_poison_anyways() {
+; CHECK-NEXT:    [[R_I:%.*]] = call nonnull ptr @foo()
+; CHECK-NEXT:    call void @use.ptr(ptr [[R_I]])
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call nonnull ptr @callee8()
+  call void @use.ptr(ptr %r)
+  ret ptr %r
+}
+
+define ptr @callee9() {
+; CHECK-LABEL: define ptr @callee9() {
+; CHECK-NEXT:    [[R:%.*]] = call noundef ptr @foo()
+; CHECK-NEXT:    ret ptr [[R]]
+;
+  %r = call noundef ptr @foo()
+  ret ptr %r
+}
+
+define ptr @caller9_fail_creates_ub() {
+; CHECK-LABEL: define ptr @caller9_fail_creates_ub() {
+; CHECK-NEXT:    [[R_I:%.*]] = call noundef nonnull ptr @foo()
+; CHECK-NEXT:    call void @use.ptr(ptr [[R_I]])
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call nonnull ptr @callee9()
+  call void @use.ptr(ptr %r)
+  ret ptr %r
+}
+
+define ptr @caller9_okay_is_ub_anyways() {
+; CHECK-LABEL: define ptr @caller9_okay_is_ub_anyways() {
+; CHECK-NEXT:    [[R_I:%.*]] = call noundef nonnull ptr @foo()
+; CHECK-NEXT:    call void @use.ptr(ptr [[R_I]])
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call noundef nonnull ptr @callee9()
+  call void @use.ptr(ptr %r)
+  ret ptr %r
+}
+
+define ptr @callee10() {
+; CHECK-LABEL: define ptr @callee10() {
+; CHECK-NEXT:    [[R:%.*]] = call ptr @foo()
+; CHECK-NEXT:    call void @use.ptr(ptr [[R]])
+; CHECK-NEXT:    ret ptr [[R]]
+;
+  %r = call ptr @foo()
+  call void @use.ptr(ptr %r)
+  ret ptr %r
+}
+
+define ptr @caller10_fail_maybe_poison() {
+; CHECK-LABEL: define ptr @caller10_fail_maybe_poison() {
+; CHECK-NEXT:    [[R_I:%.*]] = call nonnull ptr @foo()
+; CHECK-NEXT:    call void @use.ptr(ptr [[R_I]])
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call nonnull ptr @callee10()
+  ret ptr %r
+}
+
+define ptr @caller10_okay_will_be_ub() {
+; CHECK-LABEL: define ptr @caller10_okay_will_be_ub() {
+; CHECK-NEXT:    [[R_I:%.*]] = call nonnull ptr @foo()
+; CHECK-NEXT:    call void @use.ptr(ptr [[R_I]])
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call noundef nonnull ptr @callee10()
+  ret ptr %r
+}
+
+define noundef ptr @caller10_okay_will_be_ub_todo() {
+; CHECK-LABEL: define noundef ptr @caller10_okay_will_be_ub_todo() {
+; CHECK-NEXT:    [[R_I:%.*]] = call nonnull ptr @foo()
+; CHECK-NEXT:    call void @use.ptr(ptr [[R_I]])
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call nonnull ptr @callee10()
+  ret ptr %r
+}
+
+define ptr @callee11() {
+; CHECK-LABEL: define ptr @callee11() {
+; CHECK-NEXT:    [[R:%.*]] = call ptr @foo()
+; CHECK-NEXT:    [[COND:%.*]] = call i1 @val() #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    br i1 [[COND]], label [[TRUE:%.*]], label [[FALSE:%.*]]
+; CHECK:       True:
+; CHECK-NEXT:    call void @baz() #[[ATTR0]]
+; CHECK-NEXT:    ret ptr [[R]]
+; CHECK:       False:
+; CHECK-NEXT:    call void @bar() #[[ATTR0]]
+; CHECK-NEXT:    [[COND2:%.*]] = call i1 @val()
+; CHECK-NEXT:    ret ptr [[R]]
+;
+  %r = call ptr @foo()
+  %cond = call i1 @val() nounwind willreturn
+  br i1 %cond, label %True, label %False
+True:
+  call void @baz() nounwind willreturn
+  ret ptr %r
+False:
+  call void @bar() nounwind willreturn
+  %cond2 = call i1 @val()
+  ret ptr %r
+}
+
+define ptr @caller11_todo() {
+; CHECK-LABEL: define ptr @caller11_todo() {
+; CHECK-NEXT:    [[R_I:%.*]] = call ptr @foo()
+; CHECK-NEXT:    [[COND_I:%.*]] = call i1 @val() #[[ATTR0]]
+; CHECK-NEXT:    br i1 [[COND_I]], label [[TRUE_I:%.*]], label [[FALSE_I:%.*]]
+; CHECK:       True.i:
+; CHECK-NEXT:    call void @baz() #[[ATTR0]]
+; CHECK-NEXT:    br label [[CALLEE11_EXIT:%.*]]
+; CHECK:       False.i:
+; CHECK-NEXT:    call void @bar() #[[ATTR0]]
+; CHECK-NEXT:    [[COND2_I:%.*]] = call i1 @val()
+; CHECK-NEXT:    br label [[CALLEE11_EXIT]]
+; CHECK:       callee11.exit:
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call nonnull ptr @callee11()
+  ret ptr %r
+}
+
+define ptr @callee12() {
+; CHECK-LABEL: define ptr @callee12() {
+; CHECK-NEXT:    [[P:%.*]] = call ptr @foo()
+; CHECK-NEXT:    [[COND:%.*]] = call i1 @val() #[[ATTR0]]
+; CHECK-NEXT:    [[PP:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr [[P]], i64 -4)
+; CHECK-NEXT:    [[R:%.*]] = select i1 [[COND]], ptr [[P]], ptr [[PP]]
+; CHECK-NEXT:    ret ptr [[R]]
+;
+  %p = call ptr @foo()
+  %cond = call i1 @val() nounwind willreturn
+  %pp = call ptr @llvm.ptrmask.p0.i64(ptr %p, i64 -4)
+  %r = select i1 %cond, ptr %p, ptr %pp
+  ret ptr %r
+}
+
+define ptr @caller12_todo() {
+; CHECK-LABEL: define ptr @caller12_todo() {
+; CHECK-NEXT:    [[P_I:%.*]] = call ptr @foo()
+; CHECK-NEXT:    [[COND_I:%.*]] = call i1 @val() #[[ATTR0]]
+; CHECK-NEXT:    [[PP_I:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr [[P_I]], i64 -4)
+; CHECK-NEXT:    [[R_I:%.*]] = select i1 [[COND_I]], ptr [[P_I]], ptr [[PP_I]]
+; CHECK-NEXT:    ret ptr [[R_I]]
+;
+  %r = call nonnull ptr @callee12()
+  ret ptr %r
+}

>From 76331bcc0dc4057b0647180dbbd42443d5fdf091 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Tue, 19 Sep 2023 17:59:54 -0500
Subject: [PATCH 2/2] [Inliner] Fix bug when propagating poison generating
 return attributes

Poison generating return attributes can't be propagated the same as
others, as they can change the behavior of other uses and/or create UB
where it otherwise wouldn't have occurred.

For example:
```
define nonnull ptr @foo() {
    %p = call ptr @bar()
    call void @use(ptr %p)
    ret ptr %p
}
```

If we inline `@foo` and propagate `nonnull` to `@bar`, it could change
the behavior of `@use` as instead of taking `null`, `@use` will
now be passed `poison`.

This can be even worth in a case like:
```
define nonnull ptr @foo() {
    %p = call noundef ptr @bar()
    ret ptr %p
}
```

Where propagating `nonnull` to `@bar` will cause UB on `null` return
of `@bar` (`noundef` + `poison`) where it previously wouldn't
have occurred.

To fix this, we only propagate poison generating return attributes if
either 1) The only use of the callsite to propagate too is return and
the callsite to propagate too doesn't have `noundef`. Or 2) the
callsite to be be inlined has `noundef`.

The former case ensures no new UB or `poison` values will be
added. The latter is UB anyways if the value is `poison` so we can go
ahead without worrying about behavior changes.
---
 llvm/lib/Transforms/Utils/InlineFunction.cpp  | 73 +++++++++++++++++--
 .../Inline/ret_attr_align_and_noundef.ll      |  6 +-
 2 files changed, 69 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp
index 56bc90a7c935292..548f949277952ca 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -1344,25 +1344,36 @@ static bool MayContainThrowingOrExitingCallAfterCB(CallBase *Begin,
       ++BeginIt, End->getIterator(), InlinerAttributeWindow + 1);
 }
 
-static AttrBuilder IdentifyValidAttributes(CallBase &CB) {
+// Only allow these white listed attributes to be propagated back to the
+// callee. This is because other attributes may only be valid on the call
+// itself, i.e. attributes such as signext and zeroext.
+
+// Attributes that are always okay to propagate as if they are violated its
+// immediate UB.
+static AttrBuilder IdentifyValidUBGeneratingAttributes(CallBase &CB) {
   AttrBuilder Valid(CB.getContext());
-  // Only allow these white listed attributes to be propagated back to the
-  // callee. This is because other attributes may only be valid on the call
-  // itself, i.e. attributes such as signext and zeroext.
   if (auto DerefBytes = CB.getRetDereferenceableBytes())
     Valid.addDereferenceableAttr(DerefBytes);
   if (auto DerefOrNullBytes = CB.getRetDereferenceableOrNullBytes())
     Valid.addDereferenceableOrNullAttr(DerefOrNullBytes);
   if (CB.hasRetAttr(Attribute::NoAlias))
     Valid.addAttribute(Attribute::NoAlias);
+  return Valid;
+}
+
+// Attributes that need additional checks as propagating them may change
+// behavior or cause new UB.
+static AttrBuilder IdentifyValidPoisonGeneratingAttributes(CallBase &CB) {
+  AttrBuilder Valid(CB.getContext());
   if (CB.hasRetAttr(Attribute::NonNull))
     Valid.addAttribute(Attribute::NonNull);
   return Valid;
 }
 
 static void AddReturnAttributes(CallBase &CB, ValueToValueMapTy &VMap) {
-  AttrBuilder Valid = IdentifyValidAttributes(CB);
-  if (!Valid.hasAttributes())
+  AttrBuilder ValidUB = IdentifyValidUBGeneratingAttributes(CB);
+  AttrBuilder ValidPG = IdentifyValidPoisonGeneratingAttributes(CB);
+  if (!ValidUB.hasAttributes() && !ValidPG.hasAttributes())
     return;
   auto *CalledFunction = CB.getCalledFunction();
   auto &Context = CalledFunction->getContext();
@@ -1406,7 +1417,55 @@ static void AddReturnAttributes(CallBase &CB, ValueToValueMapTy &VMap) {
     // existing attribute value (i.e. attributes such as dereferenceable,
     // dereferenceable_or_null etc). See AttrBuilder::merge for more details.
     AttributeList AL = NewRetVal->getAttributes();
-    AttributeList NewAL = AL.addRetAttributes(Context, Valid);
+    AttributeList NewAL = AL.addRetAttributes(Context, ValidUB);
+    // Attributes that may generate poison returns are a bit tricky. If we
+    // propagate them, other uses of the callsite might have their behavior
+    // change or cause UB (if they have noundef) b.c of the new potential
+    // poison.
+    // Take the following three cases:
+    //
+    // 1)
+    // define nonnull ptr @foo() {
+    //   %p = call ptr @bar()
+    //   call void @use(ptr %p) willreturn nounwind
+    //   ret ptr %p
+    // }
+    //
+    // 2)
+    // define noundef nonnull ptr @foo() {
+    //   %p = call ptr @bar()
+    //   call void @use(ptr %p) willreturn nounwind
+    //   ret ptr %p
+    // }
+    //
+    // 3)
+    // define nonnull ptr @foo() {
+    //   %p = call noundef ptr @bar()
+    //   ret ptr %p
+    // }
+    //
+    // In case 1, we can't propagate nonnull because poison value in @use may
+    // change behavior or trigger UB.
+    // In case 2, we don't need to be concerned about propagating nonnull, as
+    // any new poison at @use will trigger UB anyways.
+    // In case 3, we can never propagate nonnull because it may create UB due to
+    // the noundef on @bar.
+    if (ValidPG.hasAttributes()) {
+      // Three checks.
+      // If the callsite has `noundef`, then a poison due to violating the
+      // return attribute will create UB anyways so we can always propagate.
+      // Otherwise, if the return value (callee to be inlined) has `noundef`, we
+      // can't propagate as a new poison return will cause UB.
+      // Finally, check if the return value has no uses whose behavior may
+      // change/may cause UB if we potentially return poison. At the moment this
+      // is implemented overly conservatively with a single-use check.
+      // TODO: Update the single-use check to iterate through uses and only bail
+      // if we have a potentially dangerous use.
+
+      if (CB.hasRetAttr(Attribute::NoUndef) ||
+          (RetVal->hasOneUse() && !RetVal->hasRetAttr(Attribute::NoUndef)))
+        NewAL = NewAL.addRetAttributes(Context, ValidPG);
+    }
     NewRetVal->setAttributes(NewAL);
   }
 }
diff --git a/llvm/test/Transforms/Inline/ret_attr_align_and_noundef.ll b/llvm/test/Transforms/Inline/ret_attr_align_and_noundef.ll
index f7dccc75ba4e029..ddb94ef3fe4e070 100644
--- a/llvm/test/Transforms/Inline/ret_attr_align_and_noundef.ll
+++ b/llvm/test/Transforms/Inline/ret_attr_align_and_noundef.ll
@@ -206,7 +206,7 @@ define ptr @callee9() {
 
 define ptr @caller9_fail_creates_ub() {
 ; CHECK-LABEL: define ptr @caller9_fail_creates_ub() {
-; CHECK-NEXT:    [[R_I:%.*]] = call noundef nonnull ptr @foo()
+; CHECK-NEXT:    [[R_I:%.*]] = call noundef ptr @foo()
 ; CHECK-NEXT:    call void @use.ptr(ptr [[R_I]])
 ; CHECK-NEXT:    ret ptr [[R_I]]
 ;
@@ -239,7 +239,7 @@ define ptr @callee10() {
 
 define ptr @caller10_fail_maybe_poison() {
 ; CHECK-LABEL: define ptr @caller10_fail_maybe_poison() {
-; CHECK-NEXT:    [[R_I:%.*]] = call nonnull ptr @foo()
+; CHECK-NEXT:    [[R_I:%.*]] = call ptr @foo()
 ; CHECK-NEXT:    call void @use.ptr(ptr [[R_I]])
 ; CHECK-NEXT:    ret ptr [[R_I]]
 ;
@@ -259,7 +259,7 @@ define ptr @caller10_okay_will_be_ub() {
 
 define noundef ptr @caller10_okay_will_be_ub_todo() {
 ; CHECK-LABEL: define noundef ptr @caller10_okay_will_be_ub_todo() {
-; CHECK-NEXT:    [[R_I:%.*]] = call nonnull ptr @foo()
+; CHECK-NEXT:    [[R_I:%.*]] = call ptr @foo()
 ; CHECK-NEXT:    call void @use.ptr(ptr [[R_I]])
 ; CHECK-NEXT:    ret ptr [[R_I]]
 ;



More information about the llvm-commits mailing list