blob: 198b8e788b58c80108666eb8706c551791123721 [file]
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt -S -passes=function-attrs < %s | FileCheck %s
declare noalias ptr @malloc(i64 %size)
declare ptr @not_malloc(i64 %size)
declare void @capture(ptr)
@g = external global i8
define ptr @return_malloc(i64 %size) {
; CHECK-LABEL: define noalias ptr @return_malloc(
; CHECK-SAME: i64 [[SIZE:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr @malloc(i64 %size)
ret ptr %a
}
define ptr @return_not_malloc(i64 %size) {
; CHECK-LABEL: define ptr @return_not_malloc(
; CHECK-SAME: i64 [[SIZE:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @not_malloc(i64 [[SIZE]])
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr @not_malloc(i64 %size)
ret ptr %a
}
define ptr @return_null() {
; CHECK-LABEL: define noalias noundef ptr @return_null(
; CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: ret ptr null
;
ret ptr null
}
define ptr @return_poison() {
; CHECK-LABEL: define noalias ptr @return_poison(
; CHECK-SAME: ) #[[ATTR0]] {
; CHECK-NEXT: ret ptr poison
;
ret ptr poison
}
define ptr @return_alloca() {
; CHECK-LABEL: define noalias noundef nonnull ptr @return_alloca(
; CHECK-SAME: ) #[[ATTR0]] {
; CHECK-NEXT: [[A:%.*]] = alloca i8, align 1
; CHECK-NEXT: ret ptr [[A]]
;
%a = alloca i8
ret ptr %a
}
; noalias arg does not imply noalias return
define ptr @return_noalias_arg(ptr noalias %arg) {
; CHECK-LABEL: define ptr @return_noalias_arg(
; CHECK-SAME: ptr noalias nofree readnone returned captures(ret: address, provenance) [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: ret ptr [[ARG]]
;
ret ptr %arg
}
define ptr @return_global() {
; CHECK-LABEL: define noundef nonnull ptr @return_global(
; CHECK-SAME: ) #[[ATTR0]] {
; CHECK-NEXT: ret ptr @g
;
ret ptr @g
}
define ptr @no_return() {
; CHECK-LABEL: define noalias noundef nonnull ptr @no_return(
; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
; CHECK-NEXT: unreachable
;
unreachable
}
define ptr @return_multiple(i1 %c, i64 %size) {
; CHECK-LABEL: define noalias ptr @return_multiple(
; CHECK-SAME: i1 [[C:%.*]], i64 [[SIZE:%.*]]) {
; CHECK-NEXT: br i1 [[C]], label %[[IF:.*]], label %[[ELSE:.*]]
; CHECK: [[IF]]:
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: ret ptr [[A]]
; CHECK: [[ELSE]]:
; CHECK-NEXT: [[B:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: ret ptr [[B]]
;
br i1 %c, label %if, label %else
if:
%a = call ptr @malloc(i64 %size)
ret ptr %a
else:
%b = call ptr @malloc(i64 %size)
ret ptr %b
}
define ptr @return_select(i1 %c, i64 %size) {
; CHECK-LABEL: define noalias ptr @return_select(
; CHECK-SAME: i1 [[C:%.*]], i64 [[SIZE:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], ptr [[A]], ptr null
; CHECK-NEXT: ret ptr [[SEL]]
;
%a = call ptr @malloc(i64 %size)
%sel = select i1 %c, ptr %a, ptr null
ret ptr %sel
}
define ptr @return_phi(i1 %c, i64 %size) {
; CHECK-LABEL: define noalias ptr @return_phi(
; CHECK-SAME: i1 [[C:%.*]], i64 [[SIZE:%.*]]) {
; CHECK-NEXT: br i1 [[C]], label %[[IF:.*]], label %[[ELSE:.*]]
; CHECK: [[IF]]:
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: br label %[[JOIN:.*]]
; CHECK: [[ELSE]]:
; CHECK-NEXT: br label %[[JOIN]]
; CHECK: [[JOIN]]:
; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[A]], %[[IF]] ], [ null, %[[ELSE]] ]
; CHECK-NEXT: ret ptr [[PHI]]
;
br i1 %c, label %if, label %else
if:
%a = call ptr @malloc(i64 %size)
br label %join
else:
br label %join
join:
%phi = phi ptr [ %a, %if ], [ null, %else ]
ret ptr %phi
}
define ptr @return_phi_wrong(i1 %c, i64 %size) {
; CHECK-LABEL: define ptr @return_phi_wrong(
; CHECK-SAME: i1 [[C:%.*]], i64 [[SIZE:%.*]]) {
; CHECK-NEXT: br i1 [[C]], label %[[IF:.*]], label %[[ELSE:.*]]
; CHECK: [[IF]]:
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: br label %[[JOIN:.*]]
; CHECK: [[ELSE]]:
; CHECK-NEXT: [[B:%.*]] = call ptr @not_malloc(i64 [[SIZE]])
; CHECK-NEXT: br label %[[JOIN]]
; CHECK: [[JOIN]]:
; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[A]], %[[IF]] ], [ [[B]], %[[ELSE]] ]
; CHECK-NEXT: ret ptr [[PHI]]
;
br i1 %c, label %if, label %else
if:
%a = call ptr @malloc(i64 %size)
br label %join
else:
%b = call ptr @not_malloc(i64 %size)
br label %join
join:
%phi = phi ptr [ %a, %if ], [ %b, %else ]
ret ptr %phi
}
define ptr @return_malloc_with_store(i64 %size) {
; CHECK-LABEL: define noalias noundef ptr @return_malloc_with_store(
; CHECK-SAME: i64 [[SIZE:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: store i8 0, ptr [[A]], align 1
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr @malloc(i64 %size)
store i8 0, ptr %a
ret ptr %a
}
define ptr @return_malloc_captured(i64 %size) {
; CHECK-LABEL: define ptr @return_malloc_captured(
; CHECK-SAME: i64 [[SIZE:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: call void @capture(ptr [[A]])
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr @malloc(i64 %size)
call void @capture(ptr %a)
ret ptr %a
}
define ptr @scc1(i1 %c) {
; CHECK-LABEL: define noalias ptr @scc1(
; CHECK-SAME: i1 [[C:%.*]]) {
; CHECK-NEXT: br i1 [[C]], label %[[IF:.*]], label %[[ELSE:.*]]
; CHECK: [[IF]]:
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 4)
; CHECK-NEXT: ret ptr [[A]]
; CHECK: [[ELSE]]:
; CHECK-NEXT: [[B:%.*]] = call ptr @scc2(i1 [[C]])
; CHECK-NEXT: ret ptr [[B]]
;
br i1 %c, label %if, label %else
if:
%a = call ptr @malloc(i64 4)
ret ptr %a
else:
%b = call ptr @scc2(i1 %c)
ret ptr %b
}
define ptr @scc2(i1 %c) {
; CHECK-LABEL: define noalias ptr @scc2(
; CHECK-SAME: i1 [[C:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @scc1(i1 [[C]])
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr @scc1(i1 %c)
ret ptr %a
}
define ptr @return_unknown_call(ptr %fn) {
; CHECK-LABEL: define ptr @return_unknown_call(
; CHECK-SAME: ptr nofree readonly captures(none) [[FN:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr [[FN]]()
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr %fn()
ret ptr %a
}
define ptr @return_unknown_noalias_call(ptr %fn) {
; CHECK-LABEL: define noalias ptr @return_unknown_noalias_call(
; CHECK-SAME: ptr nofree readonly captures(none) [[FN:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call noalias ptr [[FN]]()
; CHECK-NEXT: ret ptr [[A]]
;
%a = call noalias ptr %fn()
ret ptr %a
}
; A null check does not capture the provenance of the result.
define ptr @return_malloc_null_checked(i64 %size) {
; CHECK-LABEL: define noalias ptr @return_malloc_null_checked(
; CHECK-SAME: i64 [[SIZE:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[A]], null
; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
; CHECK: [[THEN]]:
; CHECK-NEXT: ret ptr null
; CHECK: [[ELSE]]:
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr @malloc(i64 %size)
%c = icmp eq ptr %a, null
br i1 %c, label %then, label %else
then:
ret ptr null
else:
ret ptr %a
}
; A null check does not hide the capture by the store to @g.
define ptr @return_malloc_null_checked_captured(i64 %size) {
; CHECK-LABEL: define ptr @return_malloc_null_checked_captured(
; CHECK-SAME: i64 [[SIZE:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[A]], null
; CHECK-NEXT: store ptr [[A]], ptr @g, align 8
; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
; CHECK: [[THEN]]:
; CHECK-NEXT: ret ptr null
; CHECK: [[ELSE]]:
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr @malloc(i64 %size)
%c = icmp eq ptr %a, null
store ptr %a, ptr @g
br i1 %c, label %then, label %else
then:
ret ptr null
else:
ret ptr %a
}
declare void @capture_address_is_null(ptr captures(address_is_null))
declare void @capture_provenance(ptr captures(provenance))
declare void @capture_address_is_null_provenance(ptr captures(address_is_null, provenance))
declare void @capture_read_provenance(ptr captures(read_provenance))
; Only whether the result is null is captured.
define ptr @return_malloc_capture_address_is_null(i64 %size) {
; CHECK-LABEL: define noalias ptr @return_malloc_capture_address_is_null(
; CHECK-SAME: i64 [[SIZE:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: call void @capture_address_is_null(ptr [[A]])
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr @malloc(i64 %size)
call void @capture_address_is_null(ptr %a)
ret ptr %a
}
; The provenance of the result is captured in addition to the null check.
define ptr @return_malloc_null_checked_capture_provenance(i64 %size) {
; CHECK-LABEL: define ptr @return_malloc_null_checked_capture_provenance(
; CHECK-SAME: i64 [[SIZE:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[A]], null
; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
; CHECK: [[THEN]]:
; CHECK-NEXT: ret ptr null
; CHECK: [[ELSE]]:
; CHECK-NEXT: call void @capture_provenance(ptr [[A]])
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr @malloc(i64 %size)
%c = icmp eq ptr %a, null
br i1 %c, label %then, label %else
then:
ret ptr null
else:
call void @capture_provenance(ptr %a)
ret ptr %a
}
; The provenance of the result is captured together with the null check.
define ptr @return_malloc_capture_address_is_null_provenance(i64 %size) {
; CHECK-LABEL: define ptr @return_malloc_capture_address_is_null_provenance(
; CHECK-SAME: i64 [[SIZE:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: call void @capture_address_is_null_provenance(ptr [[A]])
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr @malloc(i64 %size)
call void @capture_address_is_null_provenance(ptr %a)
ret ptr %a
}
; The read provenance of the result is captured in addition to the null check.
define ptr @return_malloc_null_checked_capture_read_provenance(i64 %size) {
; CHECK-LABEL: define ptr @return_malloc_null_checked_capture_read_provenance(
; CHECK-SAME: i64 [[SIZE:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[A]], null
; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
; CHECK: [[THEN]]:
; CHECK-NEXT: ret ptr null
; CHECK: [[ELSE]]:
; CHECK-NEXT: call void @capture_read_provenance(ptr [[A]])
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr @malloc(i64 %size)
%c = icmp eq ptr %a, null
br i1 %c, label %then, label %else
then:
ret ptr null
else:
call void @capture_read_provenance(ptr %a)
ret ptr %a
}
; A null check of a GEP does not capture the provenance of the result.
define ptr @return_malloc_gep_null_checked(i64 %size) {
; CHECK-LABEL: define noalias ptr @return_malloc_gep_null_checked(
; CHECK-SAME: i64 [[SIZE:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[A]], i64 8
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[GEP]], null
; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
; CHECK: [[THEN]]:
; CHECK-NEXT: ret ptr null
; CHECK: [[ELSE]]:
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr @malloc(i64 %size)
%gep = getelementptr i8, ptr %a, i64 8
%c = icmp eq ptr %gep, null
br i1 %c, label %then, label %else
then:
ret ptr null
else:
ret ptr %a
}
; Comparing the result against another pointer only captures its address.
define ptr @return_malloc_compared(i64 %size, ptr %p) {
; CHECK-LABEL: define noalias ptr @return_malloc_compared(
; CHECK-SAME: i64 [[SIZE:%.*]], ptr nofree readnone captures(address) [[P:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[A]], [[P]]
; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
; CHECK: [[THEN]]:
; CHECK-NEXT: ret ptr null
; CHECK: [[ELSE]]:
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr @malloc(i64 %size)
%c = icmp eq ptr %a, %p
br i1 %c, label %then, label %else
then:
ret ptr null
else:
ret ptr %a
}
declare void @capture_address(ptr captures(address))
; Only the address of the result is captured.
define ptr @return_malloc_capture_address(i64 %size) {
; CHECK-LABEL: define noalias ptr @return_malloc_capture_address(
; CHECK-SAME: i64 [[SIZE:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
; CHECK-NEXT: call void @capture_address(ptr [[A]])
; CHECK-NEXT: ret ptr [[A]]
;
%a = call ptr @malloc(i64 %size)
call void @capture_address(ptr %a)
ret ptr %a
}