Index: llvm/lib/IR/Function.cpp =================================================================== --- llvm/lib/IR/Function.cpp +++ llvm/lib/IR/Function.cpp @@ -1876,6 +1876,10 @@ return NewDecl; } +static bool isPointerCastOperator(const User *U) { + return isa(U) || isa(U); +} + /// hasAddressTaken - returns true if there are any uses of this function /// other than direct calls or invokes to it. Optionally ignores callback /// uses, assume like pointer annotation calls, and references in llvm.used @@ -1897,17 +1901,15 @@ const auto *Call = dyn_cast(FU); if (!Call) { - if (IgnoreAssumeLikeCalls) { - if (const auto *FI = dyn_cast(FU)) { - if (FI->isCast() && !FI->user_empty() && - llvm::all_of(FU->users(), [](const User *U) { - if (const auto *I = dyn_cast(U)) - return I->isAssumeLikeIntrinsic(); - return false; - })) - continue; - } + if (IgnoreAssumeLikeCalls && isPointerCastOperator(FU) && + all_of(FU->users(), [](const User *U) { + if (const auto *I = dyn_cast(U)) + return I->isAssumeLikeIntrinsic(); + return false; + })) { + continue; } + if (IgnoreLLVMUsed && !FU->user_empty()) { const User *FUU = FU; if (isa(FU) && FU->hasOneUse() && @@ -1926,6 +1928,13 @@ *PutOffender = FU; return true; } + + if (IgnoreAssumeLikeCalls) { + if (const auto *I = dyn_cast(Call)) + if (I->isAssumeLikeIntrinsic()) + continue; + } + if (!Call->isCallee(&U) || Call->getFunctionType() != getFunctionType()) { if (IgnoreARCAttachedCall && Call->isOperandBundleOfType(LLVMContext::OB_clang_arc_attachedcall, Index: llvm/test/Analysis/CallGraph/ignore-assumelike-calls.ll =================================================================== --- llvm/test/Analysis/CallGraph/ignore-assumelike-calls.ll +++ llvm/test/Analysis/CallGraph/ignore-assumelike-calls.ll @@ -1,26 +1,63 @@ ; RUN: opt < %s -print-callgraph -disable-output 2>&1 | FileCheck %s ; CHECK: Call graph node <><<{{.*}}>> #uses=0 -; CHECK-NEXT: CS calls function 'cast_only' -; CHECK-NEXT: CS calls function 'llvm.lifetime.start.p0i8' +; CHECK-NEXT: CS calls function 'other_intrinsic_use' +; CHECK-NEXT: CS calls function 'other_cast_intrinsic_use' +; CHECK-NEXT: CS calls function 'llvm.lifetime.start.p0' +; CHECK-NEXT: CS calls function 'llvm.memset.p0.i64' +; CHECK-NEXT: CS calls function 'llvm.memset.p1.i64' ; CHECK-EMPTY: -; CHECK-NEXT: Call graph node for function: 'cast_only'<<{{.*}}>> #uses=1 +; CHECK-NEXT: Call graph node for function: 'addrspacecast_only'<<{{.*}}>> #uses=0 ; CHECK-EMPTY: -; CHECK-NEXT: Call graph node for function: 'llvm.lifetime.start.p0i8'<<{{.*}}>> #uses=1 +; CHECK-NEXT: Call graph node for function: 'bitcast_only'<<{{.*}}>> #uses=0 +; CHECK-EMPTY: +; CHECK-NEXT: Call graph node for function: 'llvm.lifetime.start.p0'<<{{.*}}>> #uses=1 +; CHECK-EMPTY: +; CHECK-NEXT: Call graph node for function: 'llvm.memset.p0.i64'<<{{.*}}>> #uses=1 +; CHECK-EMPTY: +; CHECK-NEXT: Call graph node for function: 'llvm.memset.p1.i64'<<{{.*}}>> #uses=1 +; CHECK-EMPTY: +; CHECK-NEXT: Call graph node for function: 'other_cast_intrinsic_use'<<{{.*}}>> #uses=1 +; CHECK-EMPTY: +; CHECK-NEXT: Call graph node for function: 'other_intrinsic_use'<<{{.*}}>> #uses=1 ; CHECK-EMPTY: ; CHECK-NEXT: Call graph node for function: 'used_by_lifetime'<<{{.*}}>> #uses=0 ; CHECK-EMPTY: +; CHECK-NEXT: Call graph node for function: 'used_by_lifetime_cast'<<{{.*}}>> #uses=0 +; CHECK-EMPTY: define internal void @used_by_lifetime() { entry: - %c = bitcast void()* @used_by_lifetime to i8* - call void @llvm.lifetime.start.p0i8(i64 4, i8* %c) + call void @llvm.lifetime.start.p0(i64 4, ptr @used_by_lifetime) ret void } -define internal void @cast_only() { +define internal void @used_by_lifetime_cast() addrspace(1) { + call void @llvm.lifetime.start.p0(i64 4, ptr addrspacecast (ptr addrspace(1) @used_by_lifetime_cast to ptr)) + ret void +} + +define internal void @bitcast_only() { entry: - %c = bitcast void()* @cast_only to i8* + %c = bitcast ptr @bitcast_only to ptr + ret void +} + +define internal void @addrspacecast_only() addrspace(1) { +entry: + %c = addrspacecast ptr addrspace(1) @addrspacecast_only to ptr + ret void +} + +define internal void @other_intrinsic_use() { + call void @llvm.memset.p0.i64(ptr @other_intrinsic_use, i8 0, i64 1024, i1 false) + ret void +} + +define internal void @other_cast_intrinsic_use() { + call void @llvm.memset.p1.i64(ptr addrspace(1) addrspacecast (ptr @other_cast_intrinsic_use to ptr addrspace(1)), i8 0, i64 1024, i1 false) ret void } -declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) +declare void @llvm.lifetime.start.p0(i64, ptr nocapture) +declare void @llvm.memset.p0.i64(ptr, i8, i64, i1 immarg) +declare void @llvm.memset.p1.i64(ptr addrspace(1), i8, i64, i1 immarg) Index: llvm/test/Transforms/OpenMP/parallel_deletion_cg_update.ll =================================================================== --- llvm/test/Transforms/OpenMP/parallel_deletion_cg_update.ll +++ llvm/test/Transforms/OpenMP/parallel_deletion_cg_update.ll @@ -1,15 +1,14 @@ ; RUN: opt < %s -passes='function(instcombine),cgscc(attributor-cgscc),print-callgraph' -disable-output 2>&1 | FileCheck %s ; CHECK: Call graph node <><<{{.*}}>> #uses=0 -; CHECK: CS calls function 'dead_fork_call' -; CHECK: CS calls function '.omp_outlined..0' -; CHECK: CS calls function '__kmpc_fork_call' -; CHECK: CS calls function 'live_fork_call' -; CHECK: CS calls function '.omp_outlined..1' -; CHECK: CS calls function 'd' +; CHECK-NEXT: CS calls function 'dead_fork_call' +; CHECK-NEXT: CS calls function '__kmpc_fork_call' +; CHECK-NEXT: CS calls function 'live_fork_call' +; CHECK-NEXT: CS calls function '.omp_outlined..1' +; CHECK-NEXT: CS calls function 'd' +; +; CHECK: Call graph node for function: '.omp_outlined..0'<<{{.*}}>> #uses=0 ; -; CHECK: Call graph node for function: '.omp_outlined..0'<<{{.*}}>> #uses=1 -; ; CHECK: Call graph node for function: '.omp_outlined..1'<<{{.*}}>> #uses=3 ; CHECK: CS<{{.*}}> calls function 'd' ;