| ; 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 | |
| } |