-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[Attributor] Support nested conditional branches #168532
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[Attributor] Support nested conditional branches #168532
Conversation
The attributor can infer the alignment of %p at the call-site in this example [1]: ``` define void @f(ptr align 8 %p, i1 %c1, i1 %c2) { entry: br i1 %c1, label %bb.1, label %exit bb.1: call void (...) @llvm.fake.use(ptr %p) br label %exit exit: ret void } ``` but not when there's an additional conditional branch: ``` define void @f(ptr align 8 %p, i1 %c1, i1 %c2) { entry: br i1 %c1, label %bb.1, label %exit bb.1: br i1 %c2, label %bb.2, label %exit bb.2: call void (...) @llvm.fake.use(ptr %p) br label %exit exit: ret void } ``` unless `-attributor-annotate-decl-cs` is enabled. This patch extends `followUsesInMBEC` to handle such recursive branches. n.b. admittedly I wrote this patch before discovering inferring the alignment in this example is already possible with `-attributor-annotate-decl-cs`, I came to realise this once writing the tests, but this seems like a gap regardless looking at existing FIXMEs, plus the alignment can now be inferred in this particular example without the flag. [1] https://godbolt.org/z/aKoc75so5
|
@llvm/pr-subscribers-llvm-transforms Author: Cullen Rhodes (c-rhodes) ChangesThe attributor can infer the alignment of %p at the call-site in this but not when there's an additional conditional branch: unless n.b. admittedly I wrote this patch before discovering inferring the [1] https://godbolt.org/z/aKoc75so5 Patch is 49.01 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/168532.diff 6 Files Affected:
diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index a6ac7610a2c7a..e806a02a1f58f 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -665,7 +665,10 @@ static void followUsesInMBEC(AAType &AA, Attributor &A, StateType &S,
return;
SmallVector<const BranchInst *, 4> BrInsts;
+ SmallPtrSet<const Instruction *, 16> Visited;
auto Pred = [&](const Instruction *I) {
+ if (!Visited.insert(I).second)
+ return false;
if (const BranchInst *Br = dyn_cast<BranchInst>(I))
if (Br->isConditional())
BrInsts.push_back(Br);
@@ -684,28 +687,10 @@ static void followUsesInMBEC(AAType &AA, Attributor &A, StateType &S,
// ParentS_m = ChildS_{m, 1} /\ ChildS_{m, 2} /\ ... /\ ChildS_{m, n_m}
//
// Known State |= ParentS_1 \/ ParentS_2 \/... \/ ParentS_m
- //
- // FIXME: Currently, recursive branches are not handled. For example, we
- // can't deduce that ptr must be dereferenced in below function.
- //
- // void f(int a, int c, int *ptr) {
- // if(a)
- // if (b) {
- // *ptr = 0;
- // } else {
- // *ptr = 1;
- // }
- // else {
- // if (b) {
- // *ptr = 0;
- // } else {
- // *ptr = 1;
- // }
- // }
- // }
Explorer->checkForAllContext(&CtxI, Pred);
- for (const BranchInst *Br : BrInsts) {
+ while (!BrInsts.empty()) {
+ const BranchInst *Br = BrInsts.pop_back_val();
StateType ParentState;
// The known state of the parent state is a conjunction of children's
@@ -714,15 +699,18 @@ static void followUsesInMBEC(AAType &AA, Attributor &A, StateType &S,
for (const BasicBlock *BB : Br->successors()) {
StateType ChildState;
-
size_t BeforeSize = Uses.size();
- followUsesInContext(AA, A, *Explorer, &BB->front(), Uses, ChildState);
+ const Instruction *I = &BB->front();
+ followUsesInContext(AA, A, *Explorer, I, Uses, ChildState);
// Erase uses which only appear in the child.
for (auto It = Uses.begin() + BeforeSize; It != Uses.end();)
It = Uses.erase(It);
ParentState &= ChildState;
+
+ // Check for recursive conditional branches.
+ Explorer->checkForAllContext(I, Pred);
}
// Use only known state.
diff --git a/llvm/test/Transforms/Attributor/dereferenceable-1.ll b/llvm/test/Transforms/Attributor/dereferenceable-1.ll
index 5bff2a2e6b208..246a8c42ba912 100644
--- a/llvm/test/Transforms/Attributor/dereferenceable-1.ll
+++ b/llvm/test/Transforms/Attributor/dereferenceable-1.ll
@@ -555,12 +555,10 @@ cont2:
; *ptr = 4;
; }
; }
-;
-; FIXME: %ptr should be dereferenceable(4)
define dso_local void @rec-branch-1(i32 %a, i32 %b, i32 %c, ptr %ptr) {
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
; CHECK-LABEL: define {{[^@]+}}@rec-branch-1
-; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]], ptr nofree writeonly captures(none) [[PTR:%.*]]) #[[ATTR3]] {
+; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]], ptr nofree nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR:%.*]]) #[[ATTR3]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[A]], 0
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_ELSE3:%.*]], label [[IF_THEN:%.*]]
@@ -630,11 +628,10 @@ if.end8: ; preds = %if.then5, %if.else6
; rec-branch-2(1, 1, 1, ptr);
; }
; }
-; FIXME: %ptr should be dereferenceable(4)
define dso_local void @rec-branch-2(i32 %a, i32 %b, i32 %c, ptr %ptr) {
; CHECK: Function Attrs: nofree nosync nounwind memory(argmem: write)
; CHECK-LABEL: define {{[^@]+}}@rec-branch-2
-; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]], ptr nofree writeonly captures(none) [[PTR:%.*]]) #[[ATTR5:[0-9]+]] {
+; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]], ptr nofree nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR:%.*]]) #[[ATTR5:[0-9]+]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[A]], 0
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_ELSE3:%.*]], label [[IF_THEN:%.*]]
@@ -654,7 +651,7 @@ define dso_local void @rec-branch-2(i32 %a, i32 %b, i32 %c, ptr %ptr) {
; CHECK-NEXT: store i32 3, ptr [[PTR]], align 4
; CHECK-NEXT: br label [[IF_END8]]
; CHECK: if.else6:
-; CHECK-NEXT: tail call void @rec-branch-2(i32 noundef 1, i32 noundef 1, i32 noundef 1, ptr nofree writeonly captures(none) [[PTR]]) #[[ATTR8:[0-9]+]]
+; CHECK-NEXT: tail call void @rec-branch-2(i32 noundef 1, i32 noundef 1, i32 noundef 1, ptr nofree nonnull writeonly align 4 captures(none) dereferenceable(4) [[PTR]]) #[[ATTR8:[0-9]+]]
; CHECK-NEXT: br label [[IF_END8]]
; CHECK: if.end8:
; CHECK-NEXT: ret void
diff --git a/llvm/test/Transforms/Attributor/nonnull.ll b/llvm/test/Transforms/Attributor/nonnull.ll
index 2ff8a3fa3a688..57a6d09af64fa 100644
--- a/llvm/test/Transforms/Attributor/nonnull.ll
+++ b/llvm/test/Transforms/Attributor/nonnull.ll
@@ -32,16 +32,27 @@ define ptr @test2(ptr nonnull %p) {
}
define ptr @test2A(i1 %c, ptr %ret) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write)
-; CHECK-LABEL: define {{[^@]+}}@test2A
-; CHECK-SAME: (i1 noundef [[C:%.*]], ptr nofree nonnull readnone returned "no-capture-maybe-returned" [[RET:%.*]]) #[[ATTR2:[0-9]+]] {
-; CHECK-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]]
-; CHECK: A:
-; CHECK-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR16:[0-9]+]] [ "nonnull"(ptr [[RET]]) ]
-; CHECK-NEXT: ret ptr [[RET]]
-; CHECK: B:
-; CHECK-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR16]] [ "nonnull"(ptr [[RET]]) ]
-; CHECK-NEXT: ret ptr [[RET]]
+; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write)
+; TUNIT-LABEL: define {{[^@]+}}@test2A
+; TUNIT-SAME: (i1 noundef [[C:%.*]], ptr nofree nonnull readnone returned "no-capture-maybe-returned" [[RET:%.*]]) #[[ATTR2:[0-9]+]] {
+; TUNIT-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]]
+; TUNIT: A:
+; TUNIT-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR15:[0-9]+]] [ "nonnull"(ptr [[RET]]) ]
+; TUNIT-NEXT: ret ptr [[RET]]
+; TUNIT: B:
+; TUNIT-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR15]] [ "nonnull"(ptr [[RET]]) ]
+; TUNIT-NEXT: ret ptr [[RET]]
+;
+; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write)
+; CGSCC-LABEL: define {{[^@]+}}@test2A
+; CGSCC-SAME: (i1 noundef [[C:%.*]], ptr nofree nonnull readnone returned "no-capture-maybe-returned" [[RET:%.*]]) #[[ATTR2:[0-9]+]] {
+; CGSCC-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]]
+; CGSCC: A:
+; CGSCC-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR16:[0-9]+]] [ "nonnull"(ptr [[RET]]) ]
+; CGSCC-NEXT: ret ptr [[RET]]
+; CGSCC: B:
+; CGSCC-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR16]] [ "nonnull"(ptr [[RET]]) ]
+; CGSCC-NEXT: ret ptr [[RET]]
;
br i1 %c, label %A, label %B
A:
@@ -53,16 +64,27 @@ B:
}
define ptr @test2B(i1 %c, ptr %ret) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write)
-; CHECK-LABEL: define {{[^@]+}}@test2B
-; CHECK-SAME: (i1 noundef [[C:%.*]], ptr nofree nonnull readnone returned dereferenceable(4) "no-capture-maybe-returned" [[RET:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]]
-; CHECK: A:
-; CHECK-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR16]] [ "dereferenceable"(ptr [[RET]], i32 4) ]
-; CHECK-NEXT: ret ptr [[RET]]
-; CHECK: B:
-; CHECK-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR16]] [ "dereferenceable"(ptr [[RET]], i32 4) ]
-; CHECK-NEXT: ret ptr [[RET]]
+; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write)
+; TUNIT-LABEL: define {{[^@]+}}@test2B
+; TUNIT-SAME: (i1 noundef [[C:%.*]], ptr nofree nonnull readnone returned dereferenceable(4) "no-capture-maybe-returned" [[RET:%.*]]) #[[ATTR2]] {
+; TUNIT-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]]
+; TUNIT: A:
+; TUNIT-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR15]] [ "dereferenceable"(ptr [[RET]], i32 4) ]
+; TUNIT-NEXT: ret ptr [[RET]]
+; TUNIT: B:
+; TUNIT-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR15]] [ "dereferenceable"(ptr [[RET]], i32 4) ]
+; TUNIT-NEXT: ret ptr [[RET]]
+;
+; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write)
+; CGSCC-LABEL: define {{[^@]+}}@test2B
+; CGSCC-SAME: (i1 noundef [[C:%.*]], ptr nofree nonnull readnone returned dereferenceable(4) "no-capture-maybe-returned" [[RET:%.*]]) #[[ATTR2]] {
+; CGSCC-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]]
+; CGSCC: A:
+; CGSCC-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR16]] [ "dereferenceable"(ptr [[RET]], i32 4) ]
+; CGSCC-NEXT: ret ptr [[RET]]
+; CGSCC: B:
+; CGSCC-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR16]] [ "dereferenceable"(ptr [[RET]], i32 4) ]
+; CGSCC-NEXT: ret ptr [[RET]]
;
br i1 %c, label %A, label %B
A:
@@ -273,13 +295,21 @@ define ptr @test9(ptr %a, i64 %n) {
; ATTRIBUTOR_OPM: define ptr @test10
; ATTRIBUTOR_NPM: define nonnull ptr @test10
define ptr @test10(ptr %a, i64 %n) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write)
-; CHECK-LABEL: define {{[^@]+}}@test10
-; CHECK-SAME: (ptr nofree readnone "no-capture-maybe-returned" [[A:%.*]], i64 [[N:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT: [[CMP:%.*]] = icmp ne i64 [[N]], 0
-; CHECK-NEXT: call void @llvm.assume(i1 noundef [[CMP]]) #[[ATTR16]]
-; CHECK-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]]
-; CHECK-NEXT: ret ptr [[B]]
+; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write)
+; TUNIT-LABEL: define {{[^@]+}}@test10
+; TUNIT-SAME: (ptr nofree readnone "no-capture-maybe-returned" [[A:%.*]], i64 [[N:%.*]]) #[[ATTR2]] {
+; TUNIT-NEXT: [[CMP:%.*]] = icmp ne i64 [[N]], 0
+; TUNIT-NEXT: call void @llvm.assume(i1 noundef [[CMP]]) #[[ATTR15]]
+; TUNIT-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]]
+; TUNIT-NEXT: ret ptr [[B]]
+;
+; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write)
+; CGSCC-LABEL: define {{[^@]+}}@test10
+; CGSCC-SAME: (ptr nofree readnone "no-capture-maybe-returned" [[A:%.*]], i64 [[N:%.*]]) #[[ATTR2]] {
+; CGSCC-NEXT: [[CMP:%.*]] = icmp ne i64 [[N]], 0
+; CGSCC-NEXT: call void @llvm.assume(i1 noundef [[CMP]]) #[[ATTR16]]
+; CGSCC-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]]
+; CGSCC-NEXT: ret ptr [[B]]
;
%cmp = icmp ne i64 %n, 0
call void @llvm.assume(i1 %cmp)
@@ -392,50 +422,22 @@ declare nonnull ptr @nonnull()
define internal ptr @f1(ptr %arg) {
-; FIXME: missing nonnull It should be nonnull @f1(ptr nonnull readonly %arg)
-; TUNIT: Function Attrs: nofree nosync nounwind memory(argmem: read)
-; TUNIT-LABEL: define {{[^@]+}}@f1
-; TUNIT-SAME: (ptr nofree readonly [[ARG:%.*]]) #[[ATTR6:[0-9]+]] {
-; TUNIT-NEXT: bb:
-; TUNIT-NEXT: [[TMP:%.*]] = icmp eq ptr [[ARG]], null
-; TUNIT-NEXT: br i1 [[TMP]], label [[BB9:%.*]], label [[BB1:%.*]]
-; TUNIT: bb1:
-; TUNIT-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARG]], align 4
-; TUNIT-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0
-; TUNIT-NEXT: br i1 [[TMP3]], label [[BB6:%.*]], label [[BB4:%.*]]
-; TUNIT: bb4:
-; TUNIT-NEXT: [[TMP5:%.*]] = getelementptr inbounds i32, ptr [[ARG]], i64 1
-; TUNIT-NEXT: [[TMP5B:%.*]] = tail call ptr @f3(ptr nofree nonnull readonly [[TMP5]]) #[[ATTR17:[0-9]+]]
-; TUNIT-NEXT: [[TMP5C:%.*]] = getelementptr inbounds i32, ptr [[TMP5B]], i64 -1
-; TUNIT-NEXT: br label [[BB9]]
-; TUNIT: bb6:
-; TUNIT-NEXT: [[TMP7:%.*]] = tail call ptr @f2(ptr nofree nonnull readonly align 4 dereferenceable(4) [[ARG]]) #[[ATTR17]]
-; TUNIT-NEXT: ret ptr [[TMP7]]
-; TUNIT: bb9:
-; TUNIT-NEXT: [[TMP10:%.*]] = phi ptr [ [[TMP5C]], [[BB4]] ], [ inttoptr (i64 4 to ptr), [[BB:%.*]] ]
-; TUNIT-NEXT: ret ptr [[TMP10]]
-;
-; CGSCC: Function Attrs: nofree nosync nounwind memory(argmem: read)
+; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: read)
; CGSCC-LABEL: define {{[^@]+}}@f1
-; CGSCC-SAME: (ptr nofree readonly [[ARG:%.*]]) #[[ATTR5:[0-9]+]] {
+; CGSCC-SAME: (ptr nofree nonnull readonly align 4 captures(none) dereferenceable(4) [[ARG:%.*]]) #[[ATTR5:[0-9]+]] {
; CGSCC-NEXT: bb:
-; CGSCC-NEXT: [[TMP:%.*]] = icmp eq ptr [[ARG]], null
-; CGSCC-NEXT: br i1 [[TMP]], label [[BB9:%.*]], label [[BB1:%.*]]
+; CGSCC-NEXT: br label [[BB1:%.*]]
; CGSCC: bb1:
-; CGSCC-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARG]], align 4
+; CGSCC-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARG]], align 4, !invariant.load [[META0:![0-9]+]]
; CGSCC-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0
; CGSCC-NEXT: br i1 [[TMP3]], label [[BB6:%.*]], label [[BB4:%.*]]
; CGSCC: bb4:
-; CGSCC-NEXT: [[TMP5:%.*]] = getelementptr inbounds i32, ptr [[ARG]], i64 1
-; CGSCC-NEXT: [[TMP5B:%.*]] = tail call ptr @f3(ptr nofree nonnull readonly [[TMP5]]) #[[ATTR17:[0-9]+]]
-; CGSCC-NEXT: [[TMP5C:%.*]] = getelementptr inbounds i32, ptr [[TMP5B]], i64 -1
-; CGSCC-NEXT: br label [[BB9]]
+; CGSCC-NEXT: [[TMP5C:%.*]] = getelementptr inbounds i32, ptr undef, i64 -1
+; CGSCC-NEXT: br label [[BB9:%.*]]
; CGSCC: bb6:
-; CGSCC-NEXT: [[TMP7:%.*]] = tail call ptr @f2(ptr nofree nonnull readonly align 4 dereferenceable(4) [[ARG]]) #[[ATTR17]]
-; CGSCC-NEXT: ret ptr [[TMP7]]
+; CGSCC-NEXT: ret ptr undef
; CGSCC: bb9:
-; CGSCC-NEXT: [[TMP10:%.*]] = phi ptr [ [[TMP5C]], [[BB4]] ], [ inttoptr (i64 4 to ptr), [[BB:%.*]] ]
-; CGSCC-NEXT: ret ptr [[TMP10]]
+; CGSCC-NEXT: ret ptr undef
;
bb:
@@ -463,19 +465,11 @@ bb9: ; preds = %bb4, %bb
}
define internal ptr @f2(ptr %arg) {
-; TUNIT: Function Attrs: nofree nosync nounwind memory(argmem: read)
-; TUNIT-LABEL: define {{[^@]+}}@f2
-; TUNIT-SAME: (ptr nofree nonnull readonly align 4 dereferenceable(4) [[ARG:%.*]]) #[[ATTR6]] {
-; TUNIT-NEXT: bb:
-; TUNIT-NEXT: [[TMP:%.*]] = tail call ptr @f1(ptr nofree readonly [[ARG]]) #[[ATTR17]]
-; TUNIT-NEXT: ret ptr [[TMP]]
-;
-; CGSCC: Function Attrs: nofree nosync nounwind memory(argmem: read)
+; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none)
; CGSCC-LABEL: define {{[^@]+}}@f2
-; CGSCC-SAME: (ptr nofree nonnull readonly align 4 dereferenceable(4) [[ARG:%.*]]) #[[ATTR5]] {
+; CGSCC-SAME: (ptr noalias nofree nonnull readnone align 4 captures(none) dereferenceable(4) [[ARG:%.*]]) #[[ATTR6:[0-9]+]] {
; CGSCC-NEXT: bb:
-; CGSCC-NEXT: [[TMP:%.*]] = tail call ptr @f1(ptr nofree readonly [[ARG]]) #[[ATTR17]]
-; CGSCC-NEXT: ret ptr [[TMP]]
+; CGSCC-NEXT: ret ptr undef
;
bb:
%tmp = tail call ptr @f1(ptr %arg)
@@ -484,19 +478,17 @@ bb:
define dso_local noalias ptr @f3(ptr %arg) {
; FIXME: missing nonnull. It should be nonnull @f3(ptr nonnull readonly %arg)
-; TUNIT: Function Attrs: nofree nosync nounwind memory(argmem: read)
+; TUNIT: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none)
; TUNIT-LABEL: define {{[^@]+}}@f3
-; TUNIT-SAME: (ptr nofree readonly [[ARG:%.*]]) #[[ATTR6]] {
+; TUNIT-SAME: (ptr nofree readnone captures(none) [[ARG:%.*]]) #[[ATTR3]] {
; TUNIT-NEXT: bb:
-; TUNIT-NEXT: [[TMP:%.*]] = call ptr @f1(ptr nofree readonly [[ARG]]) #[[ATTR17]]
-; TUNIT-NEXT: ret ptr [[TMP]]
+; TUNIT-NEXT: ret ptr undef
;
-; CGSCC: Function Attrs: nofree nosync nounwind memory(argmem: read)
+; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
; CGSCC-LABEL: define {{[^@]+}}@f3
-; CGSCC-SAME: (ptr nofree readonly [[ARG:%.*]]) #[[ATTR5]] {
+; CGSCC-SAME: (ptr nofree readnone captures(none) [[ARG:%.*]]) #[[ATTR1]] {
; CGSCC-NEXT: bb:
-; CGSCC-NEXT: [[TMP:%.*]] = call ptr @f1(ptr nofree readonly [[ARG]]) #[[ATTR17]]
-; CGSCC-NEXT: ret ptr [[TMP]]
+; CGSCC-NEXT: ret ptr undef
;
bb:
; FIXME: missing nonnull. It should be @f1(ptr nonnull readonly %arg)
@@ -529,26 +521,26 @@ declare void @fun3(ptr, ptr, ptr) #1
define void @f16(ptr %a, ptr %b, i8 %c) {
; TUNIT: Function Attrs: mustprogress nounwind willreturn
; TUNIT-LABEL: define {{[^@]+}}@f16
-; TUNIT-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR8:[0-9]+]] {
+; TUNIT-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR7:[0-9]+]] {
; TUNIT-NEXT: [[CMP:%.*]] = icmp eq i8 [[C]], 0
; TUNIT-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; TUNIT: if.then:
-; TUNIT-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr nonnull [[B]]) #[[ATTR7:[0-9]+]]
+; TUNIT-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr nonnull [[B]]) #[[ATTR6:[0-9]+]]
; TUNIT-NEXT: ret void
; TUNIT: if.else:
-; TUNIT-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr [[B]]) #[[ATTR7]]
+; TUNIT-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr [[B]]) #[[ATTR6]]
; TUNIT-NEXT: ret void
;
; CGSCC: Function Attrs: mustprogress nounwind willreturn
; CGSCC-LABEL: define {{[^@]+}}@f16
-; CGSCC-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR7:[0-9]+]] {
+; CGSCC-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR8:[0-9]+]] {
; CGSCC-NEXT: [[CMP:%.*]] = icmp eq i8 [[C]], 0
; CGSCC-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CGSCC: if.then:
-; CGSCC-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr nonnull [[B]]) #[[ATTR6:[0-9]+]]
+; CGSCC-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr nonnull [[B]]) #[[ATTR7:[0-9]+]]
; CGSCC-NEXT: ret void
; CGSCC: if.else:
-; CGSCC-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr [[B]]) #[[ATTR6]]
+; CGSCC-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr [[B]]) #[[ATTR7]]
; CGSCC-NEXT: ret void
;
%cmp = icmp eq i8 %c, 0
@@ -571,32 +563,32 @@ define void @f17(ptr %a, i8 %c) {
;
; TUNIT: Function Attrs: mustprogress nounwind willreturn
; TUNIT-LABEL: define {{[^@]+}}@f17
-; TUNIT-SAME: (ptr nonnull [[A:%.*]], i8 [[C:%.*]]) #[[ATTR8]] {
+; TUNIT-SAME: (ptr nonnull [[A:%.*]], i8 [[C:%.*]]) #[[ATTR7]] {
; TUNIT-NEXT: [[CMP:%.*]] = icmp eq i8 [[C]], 0
; TUNIT-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; TUNIT: if.then:
-; TUNIT-NEXT: tail call void @fun0() #[[ATTR7]]
+; TUNIT-NEXT: tail call void @fun0() #[[ATTR6]]
; TUNIT-NEXT: br label [[CONT:%.*]]
; TUNIT: if.else:
-; TUNIT-NEXT: tail call void @fun0() #[[ATTR7]]
+; TUNIT-NEXT: tail call void @fun0() #[[ATTR6]]
; TUNIT-NEXT: br label [[CONT]]
; TUNIT: cont:
-; TUNIT-NEXT: tail call void @fun1(ptr nonnull [[A]]) #[[ATTR7]]
+; TUNIT-NEXT: tail call void @fun1(ptr nonnull [[A]]) #[[ATTR6]]
; TUNIT-NEXT: ret void
;
; CGSCC: Function Attrs: mustprogress nounwind willreturn
; CGSCC-LABEL: define {{[^@]+}}@f17
-; CGSCC-SAME: (ptr nonnull [[A:%.*]], i8 [[C:%.*]]) #[[ATTR7]] {
+; CGSCC-SAME: (ptr nonnull [[A:%.*]], i8 [[C:%.*]]) #[[ATTR8]] {
; CGSCC-NEXT: [[CMP:%.*]] = icmp eq i8 [[C]], 0
; CGSCC-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CGSCC: if.then:
-; CGSCC-NEXT: tail call void @fun0() #[[ATTR6]]
+; CGSCC-NEXT: tail call void @fun0() #[[ATTR7]]
; CGSCC-NEXT: br label [[CONT:%.*]]
; CGSCC: if.else:
-; CGSCC-NEXT: tail call void @fun0() #[[ATTR6]]
+; CGSCC-NEXT: tail call void @fun0() #[[ATTR7]]
; CGSCC-NEXT: br label [[CONT]]
; CGSCC: cont:
-; CGSCC-NEXT: tail call void @fun1(ptr nonnull [[A]]) #[[ATTR6]]
+; CGSCC-NEXT: tail call void @fun1(ptr nonnull [[A]]) #[[ATTR7]]
; CGSCC-NEXT: ret void
;
%cmp = icmp eq i8 %c, 0
@@ -625,50 +617,50 @@ cont:
define void @f18(ptr ...
[truncated]
|
You can test this locally with the following command:git diff -U0 --pickaxe-regex -S '([^a-zA-Z0-9#_-]undef([^a-zA-Z0-9_-]|$)|UndefValue::get)' 'HEAD~1' HEAD llvm/lib/Transforms/IPO/AttributorAttributes.cpp llvm/test/Transforms/Attributor/dereferenceable-1.ll llvm/test/Transforms/Attributor/nonnull.ll llvm/test/Transforms/Attributor/value-simplify-pointer-info.ll llvm/test/Transforms/Attributor/willreturn.ll llvm/test/Transforms/FunctionAttrs/nonnull.llThe following files introduce new uses of undef:
Undef is now deprecated and should only be used in the rare cases where no replacement is possible. For example, a load of uninitialized memory yields In tests, avoid using For example, this is considered a bad practice: define void @fn() {
...
br i1 undef, ...
}Please use the following instead: define void @fn(i1 %cond) {
...
br i1 %cond, ...
}Please refer to the Undefined Behavior Manual for more information. |
🐧 Linux x64 Test Results
|
shiltian
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
The attributor can infer the alignment of %p at the call-site in this
example [1]:
but not when there's an additional conditional branch:
unless
-attributor-annotate-decl-csis enabled. This patch extendsfollowUsesInMBECto handle such recursive branches.n.b. admittedly I wrote this patch before discovering inferring the
alignment in this example is already possible with
-attributor-annotate-decl-cs, I came to realise this once writing thetests, but this seems like a gap regardless looking at existing FIXMEs,
plus the alignment can now be inferred in this particular example
without the flag.
[1] https://godbolt.org/z/aKoc75so5