Nikita Popov ef51514c38
[FunctionAttrs] Don't bail out on unknown calls (#150958)
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.
2025-07-29 11:45:31 +02:00

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
}