diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h --- a/llvm/include/llvm/IR/Function.h +++ b/llvm/include/llvm/IR/Function.h @@ -873,13 +873,13 @@ /// hasAddressTaken - returns true if there are any uses of this function /// other than direct calls or invokes to it, or blockaddress expressions. /// Optionally passes back an offending user for diagnostic purposes, - /// ignores callback uses, assume like pointer annotation calls, and - /// references in llvm.used and llvm.compiler.used variables. - /// - bool hasAddressTaken(const User ** = nullptr, - bool IgnoreCallbackUses = false, + /// ignores callback uses, assume like pointer annotation calls, + /// references in llvm.used and llvm.compiler.used variables and + /// bitcast callees (where callee operand is a result of inline bitcast). + bool hasAddressTaken(const User ** = nullptr, bool IgnoreCallbackUses = false, bool IgnoreAssumeLikeCalls = false, - bool IngoreLLVMUsed = false) const; + bool IngoreLLVMUsed = false, + bool IgnoreBitCastCallees = false) const; /// isDefTriviallyDead - Return true if it is trivially safe to remove /// this function definition from the module (because it isn't externally diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -1580,12 +1580,13 @@ /// 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 -/// and llvm.compiler.used variables. +/// uses, assume like pointer annotation calls, references in llvm.used +/// and llvm.compiler.used variables and bitcast callees (where callee +/// operand is a result of inline bitcast). bool Function::hasAddressTaken(const User **PutOffender, bool IgnoreCallbackUses, - bool IgnoreAssumeLikeCalls, - bool IgnoreLLVMUsed) const { + bool IgnoreAssumeLikeCalls, bool IgnoreLLVMUsed, + bool IgnoreBitCastCallees) const { for (const Use &U : uses()) { const User *FU = U.getUser(); if (isa(FU)) @@ -1597,6 +1598,16 @@ continue; } + if (IgnoreBitCastCallees) { + if (isa(FU) && isa(FU) && + llvm::all_of(FU->uses(), [](const Use &U) { + if (const CallBase *CB = dyn_cast(U.getUser())) + return CB->isCallee(&U); + return false; + })) + continue; + } + const auto *Call = dyn_cast(FU); if (!Call) { if (IgnoreAssumeLikeCalls) { diff --git a/llvm/lib/Target/AMDGPU/AMDGPUAnnotateKernelFeatures.cpp b/llvm/lib/Target/AMDGPU/AMDGPUAnnotateKernelFeatures.cpp --- a/llvm/lib/Target/AMDGPU/AMDGPUAnnotateKernelFeatures.cpp +++ b/llvm/lib/Target/AMDGPU/AMDGPUAnnotateKernelFeatures.cpp @@ -356,7 +356,6 @@ bool AMDGPUAnnotateKernelFeatures::runOnSCC(CallGraphSCC &SCC) { bool Changed = false; - for (CallGraphNode *I : SCC) { // Build a list of CallGraphNodes from most number of uses to least if (I->getNumReferences()) diff --git a/llvm/test/Analysis/CallGraph/bitcast-call-argument-callee.ll b/llvm/test/Analysis/CallGraph/bitcast-call-argument-callee.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Analysis/CallGraph/bitcast-call-argument-callee.ll @@ -0,0 +1,30 @@ +; RUN: opt < %s -print-callgraph -disable-output 2>&1 | FileCheck %s + +; CHECK: Call graph node <><<{{.*}}>> #uses=0 +; CHECK-NEXT: CS calls function 'foo' +; CHECK-EMPTY: +; CHECK-NEXT: Call graph node for function: 'bar'<<{{.*}}>> #uses=1 +; CHECK-EMPTY: +; CHECK-NEXT: Call graph node for function: 'foo'<<{{.*}}>> #uses=1 +; CHECK-EMPTY: +; CHECK-NEXT: Call graph node for function: 'test'<<{{.*}}>> #uses=0 +; CHECK-NEXT: CS<{{.*}}> calls function 'bar' +; CHECK-NEXT: CS<{{.*}}> calls external node + +define internal i32 @foo() { +entry: + ret i32 5 +} + +define internal i32 @bar(float()* %arg) { + ret i32 5 +} + +define internal i32 @test() { + %v1 = call i32 @bar(float()* bitcast (i32()* @foo to float()*)) + %v2 = call float bitcast (i32()* @foo to float()*)() + %v3 = fptoui float %v2 to i32 + %v4 = add i32 %v1, %v3 + ret i32 %v4 +} + diff --git a/llvm/test/Analysis/CallGraph/bitcast-call-argument.ll b/llvm/test/Analysis/CallGraph/bitcast-call-argument.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Analysis/CallGraph/bitcast-call-argument.ll @@ -0,0 +1,27 @@ +; RUN: opt < %s -print-callgraph -disable-output 2>&1 | FileCheck %s + +; CHECK: Call graph node <><<{{.*}}>> #uses=0 +; CHECK-NEXT: CS calls function 'foo' +; CHECK-EMPTY: +; CHECK-NEXT: Call graph node for function: 'bar'<<{{.*}}>> #uses=1 +; CHECK-EMPTY: +; CHECK-NEXT: Call graph node for function: 'foo'<<{{.*}}>> #uses=1 +; CHECK-EMPTY: +; CHECK-NEXT: Call graph node for function: 'test'<<{{.*}}>> #uses=0 +; CHECK-NEXT: CS<{{.*}}> calls function 'bar' + +define internal i32 @foo() { +entry: + ret i32 5 +} + +define internal i32 @bar(float()* %arg) { + ret i32 5 +} + +define internal i32 @test() { + %v1 = call i32 @bar(float()* bitcast (i32()* @foo to float()*)) + %v2 = add i32 %v1, 6 + ret i32 %v2 +} + diff --git a/llvm/test/Analysis/CallGraph/ignore-bitcast-callees.ll b/llvm/test/Analysis/CallGraph/ignore-bitcast-callees.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Analysis/CallGraph/ignore-bitcast-callees.ll @@ -0,0 +1,24 @@ +; RUN: opt < %s -print-callgraph -disable-output 2>&1 | FileCheck %s +; CHECK: Call graph node <><<{{.*}}>> #uses=0 +; CHECK-NEXT: CS calls function 'foo' +; CHECK-EMPTY: +; CHECK-NEXT: Call graph node for function: 'foo'<<{{.*}}>> #uses=1 +; CHECK-EMPTY: +; CHECK-NEXT: Call graph node for function: 'test_bitcast_callees'<<{{.*}}>> #uses=0 +; CHECK-NEXT: CS<{{.*}}> calls external node +; CHECK-NEXT: CS<{{.*}}> calls external node + +define internal i32 @foo() { +entry: + ret i32 5 +} + +define internal float @test_bitcast_callees() { + %v1 = call float bitcast (i32()* @foo to float()*)() + %v2 = fadd float %v1, 1.0 + %v3 = call i8 bitcast (i32()* @foo to i8()*)() + %v4 = uitofp i8 %v3 to float + %v5 = fadd float %v2, %v4 + ret float %v5 +} +