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 @@ -830,9 +830,11 @@ /// 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. + /// Optionally passes back an offending user for diagnostic purposes and + /// ignores callback uses. /// - bool hasAddressTaken(const User** = nullptr) const; + bool hasAddressTaken(const User ** = nullptr, + bool IgnoreCallbackUses = 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/Analysis/CallGraph.cpp b/llvm/lib/Analysis/CallGraph.cpp --- a/llvm/lib/Analysis/CallGraph.cpp +++ b/llvm/lib/Analysis/CallGraph.cpp @@ -77,9 +77,11 @@ void CallGraph::addToCallGraph(Function *F) { CallGraphNode *Node = getOrInsertFunction(F); - // If this function has external linkage or has its address taken, anything - // could call it. - if (!F->hasLocalLinkage() || F->hasAddressTaken()) + bool IgnoreCallbackUses = true; + + // If this function has external linkage or has its address taken and + // it is not a callback, then anything could call it. + if (!F->hasLocalLinkage() || F->hasAddressTaken(nullptr, IgnoreCallbackUses)) ExternalCallingNode->addCalledFunction(nullptr, Node); populateCallGraphNode(Node); 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 @@ -20,6 +20,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/IR/AbstractCallSite.h" #include "llvm/IR/Argument.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/BasicBlock.h" @@ -1484,12 +1485,18 @@ } /// hasAddressTaken - returns true if there are any uses of this function -/// other than direct calls or invokes to it. -bool Function::hasAddressTaken(const User* *PutOffender) const { +/// other than direct calls or invokes to it. Optionally ignores callback +/// uses. +bool Function::hasAddressTaken(const User **PutOffender, + bool IgnoreCallbackUses) const { for (const Use &U : uses()) { const User *FU = U.getUser(); if (isa(FU)) continue; + + if (IgnoreCallbackUses && AbstractCallSite(&U)) + continue; + const auto *Call = dyn_cast(FU); if (!Call) { if (PutOffender) diff --git a/llvm/test/Analysis/CallGraph/ignore-callback-uses.ll b/llvm/test/Analysis/CallGraph/ignore-callback-uses.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Analysis/CallGraph/ignore-callback-uses.ll @@ -0,0 +1,51 @@ +; RUN: opt < %s -print-callgraph -disable-output 2>&1 | FileCheck %s +; CHECK: Call graph node <><<{{.*}}>> #uses=0 +; CHECK-NEXT: CS<{{.*}}> calls function 'f' +; CHECK-NEXT: CS<{{.*}}> calls function '__kmpc_fork_call' +; CHECK-EMPTY: + +%struct.ident_t = type { i32, i32, i32, i32, i8* } + +@0 = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1 +@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 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @f() { +entry: + br label %omp_parallel + +omp_parallel: ; preds = %entry + 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*, ...)*)) + br label %omp.par.exit.split + +omp.par.exit.split: ; preds = %omp_parallel + ret void +} + +; Function Attrs: norecurse nounwind +define internal void @f..omp_par(i32* noalias %tid.addr, i32* noalias %zero.addr) { +omp.par.entry: + %tid.addr.local = alloca i32, align 4 + %0 = load i32, i32* %tid.addr, align 4 + store i32 %0, i32* %tid.addr.local, align 4 + %tid = load i32, i32* %tid.addr.local, align 4 + br label %omp.par.region + +omp.par.exit.split.exitStub: ; preds = %omp.par.outlined.exit + ret void + +omp.par.region: ; preds = %omp.par.entry + br label %omp.par.pre_finalize + +omp.par.pre_finalize: ; preds = %omp.par.region + br label %omp.par.outlined.exit + +omp.par.outlined.exit: ; preds = %omp.par.pre_finalize + br label %omp.par.exit.split.exitStub +} + +; Function Attrs: nounwind +declare !callback !2 void @__kmpc_fork_call(%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) #2 + +!2 = !{!3} +!3 = !{i64 2, i64 -1, i64 -1, i1 true} diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/arg-count-mismatch.ll b/llvm/test/Transforms/Attributor/IPConstantProp/arg-count-mismatch.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/arg-count-mismatch.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/arg-count-mismatch.ll @@ -36,18 +36,27 @@ ; FIXME we should recognize this as UB and make it an unreachable. define dso_local i16 @foo(i16 %a) { -; CHECK-LABEL: define {{[^@]+}}@foo -; CHECK-SAME: (i16 [[A:%.*]]) -; CHECK-NEXT: [[CALL:%.*]] = call i16 @bar() -; CHECK-NEXT: ret i16 [[CALL]] +; NOT_CGSCC_OPM-LABEL: define {{[^@]+}}@foo +; NOT_CGSCC_OPM-SAME: (i16 [[A:%.*]]) +; NOT_CGSCC_OPM-NEXT: [[CALL:%.*]] = call i16 @bar() +; NOT_CGSCC_OPM-NEXT: ret i16 [[CALL]] +; +; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@foo +; IS__CGSCC_OPM-SAME: (i16 [[A:%.*]]) +; IS__CGSCC_OPM-NEXT: [[CALL:%.*]] = call i16 bitcast (i16 (i16, i16)* @bar to i16 (i16)*)(i16 [[A]]) +; IS__CGSCC_OPM-NEXT: ret i16 [[CALL]] ; %call = call i16 bitcast (i16 (i16, i16) * @bar to i16 (i16) *)(i16 %a) ret i16 %call } define internal i16 @bar(i16 %p1, i16 %p2) { -; CHECK-LABEL: define {{[^@]+}}@bar() -; CHECK-NEXT: ret i16 0 +; NOT_CGSCC_OPM-LABEL: define {{[^@]+}}@bar() +; NOT_CGSCC_OPM-NEXT: ret i16 0 +; +; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@bar +; IS__CGSCC_OPM-SAME: (i16 [[P1:%.*]], i16 [[P2:%.*]]) +; IS__CGSCC_OPM-NEXT: ret i16 0 ; ret i16 0 } @@ -107,9 +116,13 @@ } define internal i16 @vararg_no_prop(i16 %p1, i16 %p2, ...) { -; CHECK-LABEL: define {{[^@]+}}@vararg_no_prop -; CHECK-SAME: (i16 returned [[P1:%.*]], i16 [[P2:%.*]], ...) -; CHECK-NEXT: ret i16 7 +; NOT_CGSCC_OPM-LABEL: define {{[^@]+}}@vararg_no_prop +; NOT_CGSCC_OPM-SAME: (i16 returned [[P1:%.*]], i16 [[P2:%.*]], ...) +; NOT_CGSCC_OPM-NEXT: ret i16 7 +; +; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@vararg_no_prop +; IS__CGSCC_OPM-SAME: (i16 [[P1:%.*]], i16 [[P2:%.*]], ...) +; IS__CGSCC_OPM-NEXT: ret i16 [[P1]] ; ret i16 %p1 } diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/arg-type-mismatch.ll b/llvm/test/Transforms/Attributor/IPConstantProp/arg-type-mismatch.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/arg-type-mismatch.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/arg-type-mismatch.ll @@ -18,9 +18,13 @@ } define internal i16 @bar(i16 %p1, i16 %p2) { -; CHECK-LABEL: define {{[^@]+}}@bar -; CHECK-SAME: (i16 [[P1:%.*]], i16 returned [[P2:%.*]]) -; CHECK-NEXT: ret i16 [[P2]] +; NOT_CGSCC_OPM-LABEL: define {{[^@]+}}@bar +; NOT_CGSCC_OPM-SAME: (i16 [[P1:%.*]], i16 returned [[P2:%.*]]) +; NOT_CGSCC_OPM-NEXT: ret i16 [[P2]] +; +; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@bar +; IS__CGSCC_OPM-SAME: (i16 [[P1:%.*]], i16 [[P2:%.*]]) +; IS__CGSCC_OPM-NEXT: ret i16 [[P2]] ; ret i16 %p2 } diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/dangling-block-address.ll b/llvm/test/Transforms/Attributor/IPConstantProp/dangling-block-address.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/dangling-block-address.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/dangling-block-address.ll @@ -19,7 +19,7 @@ ; IS__CGSCC____-SAME: (i32 [[X:%.*]]) ; IS__CGSCC____-NEXT: entry: ; IS__CGSCC____-NEXT: [[B:%.*]] = alloca i32, align 4 -; IS__CGSCC____-NEXT: store volatile i32 -1, i32* [[B]] +; IS__CGSCC____-NEXT: store volatile i32 -1, i32* [[B]], align 4 ; IS__CGSCC____-NEXT: ret void ; entry: @@ -41,9 +41,9 @@ ; IS__CGSCC_OPM: indirectgoto: ; IS__CGSCC_OPM-NEXT: [[INDVAR]] = phi i32 [ [[INDVAR_NEXT]], [[LAB0:%.*]] ], [ 0, [[ENTRY:%.*]] ] ; IS__CGSCC_OPM-NEXT: [[PC_ADDR_0:%.*]] = getelementptr i32, i32* [[PC]], i32 [[INDVAR]] -; IS__CGSCC_OPM-NEXT: [[TMP1_PN:%.*]] = load i32, i32* [[PC_ADDR_0]] +; IS__CGSCC_OPM-NEXT: [[TMP1_PN:%.*]] = load i32, i32* [[PC_ADDR_0]], align 4 ; IS__CGSCC_OPM-NEXT: [[INDIRECT_GOTO_DEST_IN:%.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* @bar.l, i32 0, i32 [[TMP1_PN]] -; IS__CGSCC_OPM-NEXT: [[INDIRECT_GOTO_DEST:%.*]] = load i8*, i8** [[INDIRECT_GOTO_DEST_IN]] +; IS__CGSCC_OPM-NEXT: [[INDIRECT_GOTO_DEST:%.*]] = load i8*, i8** [[INDIRECT_GOTO_DEST_IN]], align 8 ; IS__CGSCC_OPM-NEXT: indirectbr i8* [[INDIRECT_GOTO_DEST]], [label [[LAB0]], label %end] ; entry: diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/pthreads.ll b/llvm/test/Transforms/Attributor/IPConstantProp/pthreads.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/pthreads.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/pthreads.ll @@ -42,27 +42,16 @@ ; IS__TUNIT____-NEXT: [[CALL3:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias nocapture align 536870912 null, i8* (i8*)* nonnull @buz, i8* noalias nofree nonnull readnone align 8 dereferenceable(1) "no-capture-maybe-returned" [[ALLOC2]]) ; IS__TUNIT____-NEXT: ret i32 0 ; -; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@main() -; IS__CGSCC_OPM-NEXT: entry: -; IS__CGSCC_OPM-NEXT: [[ALLOC1:%.*]] = alloca i8, align 8 -; IS__CGSCC_OPM-NEXT: [[ALLOC2:%.*]] = alloca i8, align 8 -; IS__CGSCC_OPM-NEXT: [[THREAD:%.*]] = alloca i64, align 8 -; IS__CGSCC_OPM-NEXT: [[CALL:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias nocapture align 536870912 null, i8* (i8*)* nonnull @foo, i8* noalias nocapture nofree readnone align 536870912 null) -; IS__CGSCC_OPM-NEXT: [[CALL1:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias nocapture align 536870912 null, i8* (i8*)* nonnull @bar, i8* noalias nofree nonnull readnone align 8 dereferenceable(8) bitcast (i8** @GlobalVPtr to i8*)) -; IS__CGSCC_OPM-NEXT: [[CALL2:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias nocapture align 536870912 null, i8* (i8*)* nonnull @baz, i8* noalias nocapture nofree nonnull readnone align 8 dereferenceable(1) [[ALLOC1]]) -; IS__CGSCC_OPM-NEXT: [[CALL3:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias nocapture align 536870912 null, i8* (i8*)* nonnull @buz, i8* noalias nofree nonnull readnone align 8 dereferenceable(1) [[ALLOC2]]) -; IS__CGSCC_OPM-NEXT: ret i32 0 -; -; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@main() -; IS__CGSCC_NPM-NEXT: entry: -; IS__CGSCC_NPM-NEXT: [[ALLOC1:%.*]] = alloca i8, align 8 -; IS__CGSCC_NPM-NEXT: [[ALLOC2:%.*]] = alloca i8, align 8 -; IS__CGSCC_NPM-NEXT: [[THREAD:%.*]] = alloca i64, align 8 -; IS__CGSCC_NPM-NEXT: [[CALL:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias nocapture align 536870912 null, i8* (i8*)* nonnull @foo, i8* noalias nocapture nofree readnone align 536870912 null) -; IS__CGSCC_NPM-NEXT: [[CALL1:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias nocapture align 536870912 null, i8* (i8*)* nonnull @bar, i8* noalias nofree nonnull readnone align 8 dereferenceable(8) bitcast (i8** @GlobalVPtr to i8*)) -; IS__CGSCC_NPM-NEXT: [[CALL2:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias nocapture align 536870912 null, i8* (i8*)* nonnull @baz, i8* noalias nocapture nofree nonnull readnone align 8 dereferenceable(1) [[ALLOC1]]) -; IS__CGSCC_NPM-NEXT: [[CALL3:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias nocapture align 536870912 null, i8* (i8*)* nonnull @buz, i8* noalias nofree nonnull readnone align 8 dereferenceable(1) [[ALLOC2]]) -; IS__CGSCC_NPM-NEXT: ret i32 0 +; IS__CGSCC____-LABEL: define {{[^@]+}}@main() +; IS__CGSCC____-NEXT: entry: +; IS__CGSCC____-NEXT: [[ALLOC1:%.*]] = alloca i8, align 8 +; IS__CGSCC____-NEXT: [[ALLOC2:%.*]] = alloca i8, align 8 +; IS__CGSCC____-NEXT: [[THREAD:%.*]] = alloca i64, align 8 +; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias nocapture align 536870912 null, i8* (i8*)* nonnull @foo, i8* noalias nocapture nofree readnone align 536870912 null) +; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias nocapture align 536870912 null, i8* (i8*)* nonnull @bar, i8* noalias nofree nonnull readnone align 8 dereferenceable(8) bitcast (i8** @GlobalVPtr to i8*)) +; IS__CGSCC____-NEXT: [[CALL2:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias nocapture align 536870912 null, i8* (i8*)* nonnull @baz, i8* noalias nocapture nofree nonnull readnone align 8 dereferenceable(1) [[ALLOC1]]) +; IS__CGSCC____-NEXT: [[CALL3:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias nocapture align 536870912 null, i8* (i8*)* nonnull @buz, i8* noalias nofree nonnull readnone align 8 dereferenceable(1) [[ALLOC2]]) +; IS__CGSCC____-NEXT: ret i32 0 ; entry: %alloc1 = alloca i8, align 8 @@ -93,15 +82,10 @@ ; IS__TUNIT____-NEXT: entry: ; IS__TUNIT____-NEXT: ret i8* bitcast (i8** @GlobalVPtr to i8*) ; -; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@bar -; IS__CGSCC_OPM-SAME: (i8* nofree readnone returned "no-capture-maybe-returned" [[ARG:%.*]]) -; IS__CGSCC_OPM-NEXT: entry: -; IS__CGSCC_OPM-NEXT: ret i8* bitcast (i8** @GlobalVPtr to i8*) -; -; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@bar -; IS__CGSCC_NPM-SAME: (i8* nofree readnone returned "no-capture-maybe-returned" [[ARG:%.*]]) -; IS__CGSCC_NPM-NEXT: entry: -; IS__CGSCC_NPM-NEXT: ret i8* bitcast (i8** @GlobalVPtr to i8*) +; IS__CGSCC____-LABEL: define {{[^@]+}}@bar +; IS__CGSCC____-SAME: (i8* nofree readnone returned "no-capture-maybe-returned" [[ARG:%.*]]) +; IS__CGSCC____-NEXT: entry: +; IS__CGSCC____-NEXT: ret i8* bitcast (i8** @GlobalVPtr to i8*) ; entry: ret i8* %arg diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll b/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll @@ -65,7 +65,7 @@ define void @caller(i1 %C) personality i32 (...)* @__gxx_personality_v0 { ; IS__TUNIT____-LABEL: define {{[^@]+}}@caller ; IS__TUNIT____-SAME: (i1 [[C:%.*]]) #2 personality i32 (...)* @__gxx_personality_v0 -; IS__TUNIT____-NEXT: [[Q:%.*]] = alloca i32 +; IS__TUNIT____-NEXT: [[Q:%.*]] = alloca i32, align 4 ; IS__TUNIT____-NEXT: [[W:%.*]] = call align 4 i32* @incdec(i1 [[C]], i32* noalias nofree nonnull align 4 dereferenceable(4) "no-capture-maybe-returned" [[Q]]) ; IS__TUNIT____-NEXT: [[S1:%.*]] = call { i32, i32 } @foo(i32 1, i32 2) ; IS__TUNIT____-NEXT: [[X1:%.*]] = extractvalue { i32, i32 } [[S1]], 0 @@ -83,7 +83,7 @@ ; ; IS__CGSCC____-LABEL: define {{[^@]+}}@caller ; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #1 personality i32 (...)* @__gxx_personality_v0 -; IS__CGSCC____-NEXT: [[Q:%.*]] = alloca i32 +; IS__CGSCC____-NEXT: [[Q:%.*]] = alloca i32, align 4 ; IS__CGSCC____-NEXT: [[W:%.*]] = call align 4 i32* @incdec(i1 [[C]], i32* noalias nofree nonnull align 4 dereferenceable(4) [[Q]]) ; IS__CGSCC____-NEXT: [[S1:%.*]] = call { i32, i32 } @foo(i32 1, i32 2) ; IS__CGSCC____-NEXT: [[X1:%.*]] = extractvalue { i32, i32 } [[S1]], 0