Skip to content

Commit 205dc09

Browse files
committed
[CallGraph] Ignore callback uses
Summary: Ignore callback uses when adding a callback function in the CallGraph. Callback functions are typically created when outlining, e.g. for OpenMP, so they have internal scope and linkage. They should not be added to the ExternalCallingNode since they are only callable by the specified caller function at creation time. A CGSCC pass, such as OpenMPOpt, may need to update the CallGraph by adding a new outlined callback function. Without ignoring callback uses, adding breaks CGSCC pass restrictions and results to a broken CallGraph. Reviewers: jdoerfert Subscribers: hiraditya, sstefan1, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D83370
1 parent 03640ee commit 205dc09

File tree

4 files changed

+69
-7
lines changed

4 files changed

+69
-7
lines changed

llvm/include/llvm/IR/Function.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -830,9 +830,11 @@ class Function : public GlobalObject, public ilist_node<Function> {
830830

831831
/// hasAddressTaken - returns true if there are any uses of this function
832832
/// other than direct calls or invokes to it, or blockaddress expressions.
833-
/// Optionally passes back an offending user for diagnostic purposes.
833+
/// Optionally passes back an offending user for diagnostic purposes and
834+
/// ignores callback uses.
834835
///
835-
bool hasAddressTaken(const User** = nullptr) const;
836+
bool hasAddressTaken(const User ** = nullptr,
837+
bool IgnoreCallbackUses = false) const;
836838

837839
/// isDefTriviallyDead - Return true if it is trivially safe to remove
838840
/// this function definition from the module (because it isn't externally

llvm/lib/Analysis/CallGraph.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,11 @@ bool CallGraph::invalidate(Module &, const PreservedAnalyses &PA,
7777
void CallGraph::addToCallGraph(Function *F) {
7878
CallGraphNode *Node = getOrInsertFunction(F);
7979

80-
// If this function has external linkage or has its address taken, anything
81-
// could call it.
82-
if (!F->hasLocalLinkage() || F->hasAddressTaken())
80+
bool IgnoreCallbackUses = true;
81+
82+
// If this function has external linkage or has its address taken and
83+
// it is not a callback, then anything could call it.
84+
if (!F->hasLocalLinkage() || F->hasAddressTaken(nullptr, IgnoreCallbackUses))
8385
ExternalCallingNode->addCalledFunction(nullptr, Node);
8486

8587
populateCallGraphNode(Node);

llvm/lib/IR/Function.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "llvm/ADT/SmallVector.h"
2121
#include "llvm/ADT/StringExtras.h"
2222
#include "llvm/ADT/StringRef.h"
23+
#include "llvm/IR/AbstractCallSite.h"
2324
#include "llvm/IR/Argument.h"
2425
#include "llvm/IR/Attributes.h"
2526
#include "llvm/IR/BasicBlock.h"
@@ -1484,12 +1485,18 @@ Optional<Function *> Intrinsic::remangleIntrinsicFunction(Function *F) {
14841485
}
14851486

14861487
/// hasAddressTaken - returns true if there are any uses of this function
1487-
/// other than direct calls or invokes to it.
1488-
bool Function::hasAddressTaken(const User* *PutOffender) const {
1488+
/// other than direct calls or invokes to it. Optionally ignores callback
1489+
/// uses.
1490+
bool Function::hasAddressTaken(const User **PutOffender,
1491+
bool IgnoreCallbackUses) const {
14891492
for (const Use &U : uses()) {
14901493
const User *FU = U.getUser();
14911494
if (isa<BlockAddress>(FU))
14921495
continue;
1496+
1497+
if (IgnoreCallbackUses && AbstractCallSite(&U))
1498+
continue;
1499+
14931500
const auto *Call = dyn_cast<CallBase>(FU);
14941501
if (!Call) {
14951502
if (PutOffender)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
; RUN: opt < %s -print-callgraph -disable-output 2>&1 | FileCheck %s
2+
; CHECK: Call graph node <<null function>><<{{.*}}>> #uses=0
3+
; CHECK-NEXT: CS<{{.*}}> calls function 'f'
4+
; CHECK-NEXT: CS<{{.*}}> calls function '__kmpc_fork_call'
5+
; CHECK-EMPTY:
6+
7+
%struct.ident_t = type { i32, i32, i32, i32, i8* }
8+
9+
@0 = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1
10+
@1 = private unnamed_addr global %struct.ident_t { i32 0, i32 2, i32 0, i32 0, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @0, i32 0, i32 0) }, align 8
11+
12+
; Function Attrs: noinline nounwind optnone uwtable
13+
define dso_local void @f() {
14+
entry:
15+
br label %omp_parallel
16+
17+
omp_parallel: ; preds = %entry
18+
call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* @1, i32 0, void (i32*, i32*, ...)* bitcast (void (i32*, i32*)* @f..omp_par to void (i32*, i32*, ...)*))
19+
br label %omp.par.exit.split
20+
21+
omp.par.exit.split: ; preds = %omp_parallel
22+
ret void
23+
}
24+
25+
; Function Attrs: norecurse nounwind
26+
define internal void @f..omp_par(i32* noalias %tid.addr, i32* noalias %zero.addr) {
27+
omp.par.entry:
28+
%tid.addr.local = alloca i32, align 4
29+
%0 = load i32, i32* %tid.addr, align 4
30+
store i32 %0, i32* %tid.addr.local, align 4
31+
%tid = load i32, i32* %tid.addr.local, align 4
32+
br label %omp.par.region
33+
34+
omp.par.exit.split.exitStub: ; preds = %omp.par.outlined.exit
35+
ret void
36+
37+
omp.par.region: ; preds = %omp.par.entry
38+
br label %omp.par.pre_finalize
39+
40+
omp.par.pre_finalize: ; preds = %omp.par.region
41+
br label %omp.par.outlined.exit
42+
43+
omp.par.outlined.exit: ; preds = %omp.par.pre_finalize
44+
br label %omp.par.exit.split.exitStub
45+
}
46+
47+
; Function Attrs: nounwind
48+
declare !callback !2 void @__kmpc_fork_call(%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) #2
49+
50+
!2 = !{!3}
51+
!3 = !{i64 2, i64 -1, i64 -1, i1 true}

0 commit comments

Comments
 (0)