
When inferring attributes, we should not bail out early on unknown calls (such as virtual calls), as we may still have call-site attributes that can be used for inference. Fixes https://github.com/llvm/llvm-project/issues/150817.
246 lines
6.4 KiB
LLVM
246 lines
6.4 KiB
LLVM
; 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 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 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 readonly captures(none) [[FN:%.*]]) {
|
|
; CHECK-NEXT: [[A:%.*]] = call noalias ptr [[FN]]()
|
|
; CHECK-NEXT: ret ptr [[A]]
|
|
;
|
|
%a = call noalias ptr %fn()
|
|
ret ptr %a
|
|
}
|