Add, and infer, a nofree function attribute
This patch adds a function attribute, nofree, to indicate that a function does
not, directly or indirectly, call a memory-deallocation function (e.g., free,
C++'s operator delete).
Reviewers: jdoerfert
Differential Revision: https://reviews.llvm.org/D49165
llvm-svn: 365336
diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll
index af87a28..35de0ce 100644
--- a/llvm/test/Transforms/FunctionAttrs/atomic.ll
+++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll
@@ -21,4 +21,4 @@
}
; CHECK: attributes #0 = { norecurse nounwind readnone ssp uwtable }
-; CHECK: attributes #1 = { norecurse nounwind ssp uwtable }
+; CHECK: attributes #1 = { nofree norecurse nounwind ssp uwtable }
diff --git a/llvm/test/Transforms/FunctionAttrs/nofree.ll b/llvm/test/Transforms/FunctionAttrs/nofree.ll
new file mode 100644
index 0000000..e72ff2f
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/nofree.ll
@@ -0,0 +1,113 @@
+; RUN: opt < %s -functionattrs -S | FileCheck %s
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+; CHECK: define void @_Z4foo1Pi(i32* nocapture readnone %a) local_unnamed_addr #0 {
+define void @_Z4foo1Pi(i32* nocapture readnone %a) local_unnamed_addr #0 {
+entry:
+ tail call void @_Z3extv()
+ ret void
+}
+
+declare void @_Z3extv() local_unnamed_addr
+
+; CHECK: define void @_Z4foo2Pi(i32* nocapture %a) local_unnamed_addr #1 {
+define void @_Z4foo2Pi(i32* nocapture %a) local_unnamed_addr #1 {
+entry:
+ %0 = bitcast i32* %a to i8*
+ tail call void @free(i8* %0) #2
+ ret void
+}
+
+declare void @free(i8* nocapture) local_unnamed_addr #2
+
+; CHECK: define i32 @_Z4foo3Pi(i32* nocapture readonly %a) local_unnamed_addr #3 {
+define i32 @_Z4foo3Pi(i32* nocapture readonly %a) local_unnamed_addr #3 {
+entry:
+ %0 = load i32, i32* %a, align 4
+ ret i32 %0
+}
+
+; CHECK: define double @_Z4foo4Pd(double* nocapture readonly %a) local_unnamed_addr #1 {
+define double @_Z4foo4Pd(double* nocapture readonly %a) local_unnamed_addr #1 {
+entry:
+ %0 = load double, double* %a, align 8
+ %call = tail call double @cos(double %0) #2
+ ret double %call
+}
+
+declare double @cos(double) local_unnamed_addr #2
+
+; CHECK: define noalias i32* @_Z4foo5Pm(i64* nocapture readonly %a) local_unnamed_addr #1 {
+define noalias i32* @_Z4foo5Pm(i64* nocapture readonly %a) local_unnamed_addr #1 {
+entry:
+ %0 = load i64, i64* %a, align 8
+ %call = tail call noalias i8* @malloc(i64 %0) #2
+ %1 = bitcast i8* %call to i32*
+ ret i32* %1
+}
+
+declare noalias i8* @malloc(i64) local_unnamed_addr #2
+
+; CHECK: define noalias i64* @_Z4foo6Pm(i64* nocapture %a) local_unnamed_addr #1 {
+define noalias i64* @_Z4foo6Pm(i64* nocapture %a) local_unnamed_addr #1 {
+entry:
+ %0 = bitcast i64* %a to i8*
+ %1 = load i64, i64* %a, align 8
+ %call = tail call i8* @realloc(i8* %0, i64 %1) #2
+ %2 = bitcast i8* %call to i64*
+ ret i64* %2
+}
+
+declare noalias i8* @realloc(i8* nocapture, i64) local_unnamed_addr #2
+
+; CHECK: define void @_Z4foo7Pi(i32* %a) local_unnamed_addr #1 {
+define void @_Z4foo7Pi(i32* %a) local_unnamed_addr #1 {
+entry:
+ %isnull = icmp eq i32* %a, null
+ br i1 %isnull, label %delete.end, label %delete.notnull
+
+delete.notnull: ; preds = %entry
+ %0 = bitcast i32* %a to i8*
+ tail call void @_ZdlPv(i8* %0) #5
+ br label %delete.end
+
+delete.end: ; preds = %delete.notnull, %entry
+ ret void
+}
+
+declare void @_ZdlPv(i8*) local_unnamed_addr #4
+
+; CHECK: define void @_Z4foo8Pi(i32* %a) local_unnamed_addr #1 {
+define void @_Z4foo8Pi(i32* %a) local_unnamed_addr #1 {
+entry:
+ %isnull = icmp eq i32* %a, null
+ br i1 %isnull, label %delete.end, label %delete.notnull
+
+delete.notnull: ; preds = %entry
+ %0 = bitcast i32* %a to i8*
+ tail call void @_ZdaPv(i8* %0) #5
+ br label %delete.end
+
+delete.end: ; preds = %delete.notnull, %entry
+ ret void
+}
+
+declare void @_ZdaPv(i8*) local_unnamed_addr #4
+
+attributes #0 = { uwtable }
+attributes #1 = { nounwind uwtable }
+attributes #2 = { nounwind }
+attributes #3 = { norecurse nounwind readonly uwtable }
+attributes #4 = { nobuiltin nounwind }
+attributes #5 = { builtin nounwind }
+
+; CHECK: attributes #0 = { uwtable }
+; CHECK: attributes #1 = { nounwind uwtable }
+; CHECK: attributes #2 = { nounwind }
+; CHECK: attributes #3 = { norecurse nounwind readonly uwtable }
+; CHECK: attributes #4 = { nobuiltin nounwind }
+; CHECK: attributes #5 = { builtin nounwind }
+
diff --git a/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll b/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll
index 69808a8..4ad195e 100644
--- a/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll
+++ b/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll
@@ -14,4 +14,4 @@
}
-; CHECK: attributes #0 = { nounwind }
+; CHECK: attributes #0 = { nofree nounwind }
diff --git a/llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll b/llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll
index 0828db3..be190b7 100644
--- a/llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll
+++ b/llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll
@@ -30,7 +30,7 @@
;
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
-; CHECK: Function Attrs: nounwind
+; CHECK: Function Attrs: nofree nounwind
; CHECK-NEXT: define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0)
define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
entry:
@@ -41,7 +41,7 @@
ret i32* %call3
}
-; CHECK: Function Attrs: nounwind
+; CHECK: Function Attrs: nofree nounwind
; CHECK-NEXT: define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0)
define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) {
entry:
@@ -70,7 +70,7 @@
ret i32* %retval.0
}
-; CHECK: Function Attrs: nounwind
+; CHECK: Function Attrs: nofree nounwind
; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0)
define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) {
entry:
@@ -102,7 +102,7 @@
ret i32* %retval.0
}
-; CHECK: Function Attrs: norecurse nounwind
+; CHECK: Function Attrs: nofree norecurse nounwind
; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* readnone %n0, i32* nocapture readonly %r0, i32* returned %w0)
define i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
entry:
@@ -121,7 +121,7 @@
ret i32* %w0
}
-; CHECK: Function Attrs: nounwind
+; CHECK: Function Attrs: nofree nounwind
; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0)
define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) {
entry:
@@ -147,7 +147,7 @@
ret i32* %retval.0
}
-; CHECK: Function Attrs: nounwind
+; CHECK: Function Attrs: nofree nounwind
; CHECK-NEXT: define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0)
define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
entry:
@@ -160,6 +160,6 @@
; for a subset relation.
;
; CHECK-NOT: attributes #
-; CHECK: attributes #{{.*}} = { nounwind }
-; CHECK: attributes #{{.*}} = { norecurse nounwind }
+; CHECK: attributes #{{.*}} = { nofree nounwind }
+; CHECK: attributes #{{.*}} = { nofree norecurse nounwind }
; CHECK-NOT: attributes #