[llvm] Attributor: Do not assume function context in AANoCapture (PR #91462)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Mon May 13 11:42:12 PDT 2024
https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/91462
>From a2f3cc2be427e8d31f53d3d9011c9a2b0c09fe0f Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Wed, 8 May 2024 11:31:58 +0200
Subject: [PATCH 1/3] Attributor: Do not assume function context in AANoCapture
If the calling function has the null_pointer_is_valid attribute,
somehow a null constant reaches here. I'm not sure why exactly,
it doesn't happen for other types of constants.
Fixes #87856
---
.../Transforms/IPO/AttributorAttributes.cpp | 10 ++-
llvm/test/Transforms/Attributor/issue87856.ll | 61 +++++++++++++++++++
2 files changed, 69 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/Transforms/Attributor/issue87856.ll
diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index 41b66aafe7d34..1b3bf3c732ed0 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -5690,6 +5690,9 @@ bool AANoCapture::isImpliedByIR(Attributor &A, const IRPosition &IRP,
return V.use_empty();
// You cannot "capture" null in the default address space.
+ //
+ // FIXME: This should use NullPointerIsDefined to account for the function
+ // attribute.
if (isa<UndefValue>(V) || (isa<ConstantPointerNull>(V) &&
V.getType()->getPointerAddressSpace() == 0)) {
return true;
@@ -5899,10 +5902,13 @@ ChangeStatus AANoCaptureImpl::updateImpl(Attributor &A) {
const Function *F =
isArgumentPosition() ? IRP.getAssociatedFunction() : IRP.getAnchorScope();
- assert(F && "Expected a function!");
- const IRPosition &FnPos = IRPosition::function(*F);
+
+ // TODO: Is the checkForAllUses below useful for constants?
+ if (!F)
+ return indicatePessimisticFixpoint();
AANoCapture::StateType T;
+ const IRPosition &FnPos = IRPosition::function(*F);
// Readonly means we cannot capture through memory.
bool IsKnown;
diff --git a/llvm/test/Transforms/Attributor/issue87856.ll b/llvm/test/Transforms/Attributor/issue87856.ll
new file mode 100644
index 0000000000000..4da29cc4448d4
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/issue87856.ll
@@ -0,0 +1,61 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 4
+; RUN: opt -S -passes=attributor < %s | FileCheck %s
+
+define void @null_ptr_is_valid_call_with_null() #0 {
+; CHECK-LABEL: define void @null_ptr_is_valid_call_with_null(
+; CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: call void @store_as0(ptr nofree noundef writeonly align 4294967296 null) #[[ATTR4:[0-9]+]]
+; CHECK-NEXT: ret void
+;
+ call void @store_as0(ptr null)
+ ret void
+}
+
+define void @null_ptr_is_valid_call_with_undef() #0 {
+; CHECK-LABEL: define void @null_ptr_is_valid_call_with_undef(
+; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: call void @store_as0(ptr undef) #[[ATTR4]]
+; CHECK-NEXT: ret void
+;
+ call void @store_as0(ptr undef)
+ ret void
+}
+
+define void @store_as0(ptr %0) {
+; CHECK-LABEL: define void @store_as0(
+; CHECK-SAME: ptr nocapture nofree noundef nonnull writeonly align 2 dereferenceable(2) [[TMP0:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT: store i16 0, ptr [[TMP0]], align 2
+; CHECK-NEXT: ret void
+;
+ store i16 0, ptr %0, align 2
+ ret void
+}
+
+define void @call_store_as1() {
+; CHECK-LABEL: define void @call_store_as1(
+; CHECK-SAME: ) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT: call void @store_as1(ptr addrspace(1) nocapture nofree noundef writeonly align 4294967296 null) #[[ATTR4]]
+; CHECK-NEXT: ret void
+;
+ call void @store_as1(ptr addrspace(1) null)
+ ret void
+}
+
+define void @store_as1(ptr addrspace(1) %arg) {
+; CHECK-LABEL: define void @store_as1(
+; CHECK-SAME: ptr addrspace(1) nocapture nofree noundef writeonly align 2 dereferenceable_or_null(2) [[ARG:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT: store i16 0, ptr addrspace(1) [[ARG]], align 2
+; CHECK-NEXT: ret void
+;
+ store i16 0, ptr addrspace(1) %arg, align 2
+ ret void
+}
+
+attributes #0 = { null_pointer_is_valid }
+;.
+; CHECK: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(write) }
+; CHECK: attributes #[[ATTR1]] = { mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none) }
+; CHECK: attributes #[[ATTR2]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) }
+; CHECK: attributes #[[ATTR3]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
+; CHECK: attributes #[[ATTR4]] = { nofree nosync nounwind willreturn memory(write) }
+;.
>From 8850006b63b5d5157edc7be63ad270e96f95eed4 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Wed, 8 May 2024 13:45:31 +0200
Subject: [PATCH 2/3] Fuck
---
llvm/test/Transforms/Attributor/fuck.ll | 58 +++++++++++++++++++++++++
1 file changed, 58 insertions(+)
create mode 100644 llvm/test/Transforms/Attributor/fuck.ll
diff --git a/llvm/test/Transforms/Attributor/fuck.ll b/llvm/test/Transforms/Attributor/fuck.ll
new file mode 100644
index 0000000000000..9ff9c15f7afd9
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/fuck.ll
@@ -0,0 +1,58 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+
+ at g = global ptr null ; <ptr> [#uses=1]
+
+define void @test4_1(ptr %x4_1, i1 %c) #0 {
+; CHECK-LABEL: define void @test4_1(
+; CHECK-SAME: ptr nocapture nofree readnone [[X4_1:%.*]], i1 [[C:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[TMP1:%.*]] = call ptr @test4_2(ptr noalias nocapture nofree readnone undef, ptr noalias nofree readnone "no-capture-maybe-returned" [[X4_1]], ptr noalias nocapture nofree readnone undef, i1 noundef [[C]]) #[[ATTR1:[0-9]+]]
+; CHECK-NEXT: store ptr null, ptr @g, align 8
+; CHECK-NEXT: ret void
+;
+; CGSCC: Function Attrs: nofree nosync nounwind memory(write)
+; CGSCC-LABEL: define {{[^@]+}}@test4_1
+; CGSCC-SAME: (ptr nocapture nofree readnone [[X4_1:%.*]], i1 [[C:%.*]]) #[[ATTR10]] {
+; CGSCC-NEXT: [[TMP1:%.*]] = call ptr @test4_2(ptr noalias nocapture nofree readnone undef, ptr noalias nofree readnone "no-capture-maybe-returned" [[X4_1]], ptr noalias nocapture nofree readnone undef, i1 noundef [[C]]) #[[ATTR10]]
+; CGSCC-NEXT: store ptr null, ptr @g, align 8
+; CGSCC-NEXT: ret void
+ call ptr @test4_2(ptr %x4_1, ptr %x4_1, ptr %x4_1, i1 %c)
+ store ptr null, ptr @g
+ ret void
+}
+
+define ptr @test4_2(ptr %x4_2, ptr %y4_2, ptr %z4_2, i1 %c) #0 {
+; CHECK-LABEL: define ptr @test4_2(
+; CHECK-SAME: ptr nocapture nofree readnone [[X4_2:%.*]], ptr nofree readnone returned "no-capture-maybe-returned" [[Y4_2:%.*]], ptr nocapture nofree readnone [[Z4_2:%.*]], i1 noundef [[C:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
+; CHECK: t:
+; CHECK-NEXT: call void @test4_1(ptr noalias nocapture nofree noundef readnone align 4294967296 null, i1 noundef [[C]]) #[[ATTR1]]
+; CHECK-NEXT: store ptr null, ptr @g, align 8
+; CHECK-NEXT: br label [[F]]
+; CHECK: f:
+; CHECK-NEXT: ret ptr [[Y4_2]]
+;
+; CGSCC: Function Attrs: nofree nosync nounwind memory(write)
+; CGSCC-LABEL: define {{[^@]+}}@test4_2
+; CGSCC-SAME: (ptr nocapture nofree readnone [[X4_2:%.*]], ptr nofree readnone returned "no-capture-maybe-returned" [[Y4_2:%.*]], ptr nocapture nofree readnone [[Z4_2:%.*]], i1 noundef [[C:%.*]]) #[[ATTR10]] {
+; CGSCC-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
+; CGSCC: t:
+; CGSCC-NEXT: call void @test4_1(ptr nocapture nofree noundef readnone align 4294967296 null, i1 noundef [[C]]) #[[ATTR10]]
+; CGSCC-NEXT: store ptr null, ptr @g, align 8
+; CGSCC-NEXT: br label [[F]]
+; CGSCC: f:
+; CGSCC-NEXT: ret ptr [[Y4_2]]
+ br i1 %c, label %t, label %f
+t:
+ call void @test4_1(ptr null, i1 %c)
+ store ptr null, ptr @g
+ br label %f
+f:
+ ret ptr %y4_2
+}
+
+
+
+attributes #0 = { null_pointer_is_valid }
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; TUNIT: {{.*}}
>From 05f85af5afb1b61621ae2c05009b4831d11d4152 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Wed, 8 May 2024 13:47:30 +0200
Subject: [PATCH 3/3] xxx - dont reinvent NullPointerIsDefined
---
llvm/lib/Transforms/IPO/AttributorAttributes.cpp | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index 1b3bf3c732ed0..43b130042c8dd 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -5689,12 +5689,15 @@ bool AANoCapture::isImpliedByIR(Attributor &A, const IRPosition &IRP,
if (!IRP.isArgumentPosition())
return V.use_empty();
+ const Function *F = IRP.getAssociatedFunction();
+
// You cannot "capture" null in the default address space.
//
// FIXME: This should use NullPointerIsDefined to account for the function
// attribute.
- if (isa<UndefValue>(V) || (isa<ConstantPointerNull>(V) &&
- V.getType()->getPointerAddressSpace() == 0)) {
+ if (isa<UndefValue>(V) ||
+ (isa<ConstantPointerNull>(V) &&
+ (!F || !NullPointerIsDefined(F, V.getType()->getPointerAddressSpace())))) {
return true;
}
@@ -5712,7 +5715,7 @@ bool AANoCapture::isImpliedByIR(Attributor &A, const IRPosition &IRP,
return true;
}
- if (const Function *F = IRP.getAssociatedFunction()) {
+ if (F) {
// Check what state the associated function can actually capture.
AANoCapture::StateType State;
determineFunctionCaptureCapabilities(IRP, *F, State);
More information about the llvm-commits
mailing list