diff --git a/llvm/include/llvm/Analysis/CallGraph.h b/llvm/include/llvm/Analysis/CallGraph.h index 5b64d857bf71..662ae0e6363c 100644 --- a/llvm/include/llvm/Analysis/CallGraph.h +++ b/llvm/include/llvm/Analysis/CallGraph.h @@ -56,6 +56,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/Function.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/ValueHandle.h" #include "llvm/Pass.h" #include @@ -229,7 +230,8 @@ public: /// \brief Adds a function to the list of functions called by this one. void addCalledFunction(CallSite CS, CallGraphNode *M) { assert(!CS.getInstruction() || !CS.getCalledFunction() || - !CS.getCalledFunction()->isIntrinsic()); + !CS.getCalledFunction()->isIntrinsic() || + !Intrinsic::isLeaf(CS.getCalledFunction()->getIntrinsicID())); CalledFunctions.emplace_back(CS.getInstruction(), M); M->AddRef(); } diff --git a/llvm/include/llvm/IR/Intrinsics.h b/llvm/include/llvm/IR/Intrinsics.h index e12ccace25ca..43b8325107fa 100644 --- a/llvm/include/llvm/IR/Intrinsics.h +++ b/llvm/include/llvm/IR/Intrinsics.h @@ -52,6 +52,11 @@ namespace Intrinsic { /// Returns true if the intrinsic can be overloaded. bool isOverloaded(ID id); + /// Returns true if the intrinsic is a leaf, i.e. it does not make any calls + /// itself. Most intrinsics are leafs, the exceptions being the patchpoint + /// and statepoint intrinsics. These call (or invoke) their "target" argument. + bool isLeaf(ID id); + /// Return the attributes for an intrinsic. AttributeSet getAttributes(LLVMContext &C, ID id); diff --git a/llvm/lib/Analysis/IPA/CallGraph.cpp b/llvm/lib/Analysis/IPA/CallGraph.cpp index ee2763939647..77c36033b47d 100644 --- a/llvm/lib/Analysis/IPA/CallGraph.cpp +++ b/llvm/lib/Analysis/IPA/CallGraph.cpp @@ -79,8 +79,10 @@ void CallGraph::addToCallGraph(Function *F) { CallSite CS(cast(II)); if (CS) { const Function *Callee = CS.getCalledFunction(); - if (!Callee) + if (!Callee || !Intrinsic::isLeaf(Callee->getIntrinsicID())) // Indirect calls of intrinsics are not allowed so no need to check. + // We can be more precise here by using TargetArg returned by + // Intrinsic::isLeaf. Node->addCalledFunction(CS, CallsExternalNode); else if (!Callee->isIntrinsic()) Node->addCalledFunction(CS, getOrInsertFunction(Callee)); diff --git a/llvm/lib/Analysis/IPA/CallGraphSCCPass.cpp b/llvm/lib/Analysis/IPA/CallGraphSCCPass.cpp index 65ba1c7c6c47..6b3e06346269 100644 --- a/llvm/lib/Analysis/IPA/CallGraphSCCPass.cpp +++ b/llvm/lib/Analysis/IPA/CallGraphSCCPass.cpp @@ -217,8 +217,10 @@ bool CGPassManager::RefreshCallGraph(CallGraphSCC &CurSCC, // another value. This can happen when constant folding happens // of well known functions etc. !CallSite(I->first) || - (CallSite(I->first).getCalledFunction() && - CallSite(I->first).getCalledFunction()->isIntrinsic())) { + (CallSite(I->first).getCalledFunction() && + CallSite(I->first).getCalledFunction()->isIntrinsic() && + Intrinsic::isLeaf( + CallSite(I->first).getCalledFunction()->getIntrinsicID()))) { assert(!CheckingMode && "CallGraphSCCPass did not update the CallGraph correctly!"); diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp index bc57dee0af0a..b50ad1262c69 100644 --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -846,6 +846,18 @@ bool Intrinsic::isOverloaded(ID id) { #undef GET_INTRINSIC_OVERLOAD_TABLE } +bool Intrinsic::isLeaf(ID id) { + switch (id) { + default: + return true; + + case Intrinsic::experimental_gc_statepoint: + case Intrinsic::experimental_patchpoint_void: + case Intrinsic::experimental_patchpoint_i64: + return false; + } +} + /// This defines the "Intrinsic::getAttributes(ID id)" method. #define GET_INTRINSIC_ATTRIBUTES #include "llvm/IR/Intrinsics.gen" diff --git a/llvm/test/Analysis/CallGraph/non-leaf-intrinsics.ll b/llvm/test/Analysis/CallGraph/non-leaf-intrinsics.ll new file mode 100644 index 000000000000..ae6ba328e50f --- /dev/null +++ b/llvm/test/Analysis/CallGraph/non-leaf-intrinsics.ll @@ -0,0 +1,32 @@ +; RUN: opt -S -print-callgraph -disable-output < %s 2>&1 | FileCheck %s + +declare void @llvm.experimental.patchpoint.void(i64, i32, i8*, i32, ...) +declare i32 @llvm.experimental.gc.statepoint.p0f_isVoidf(i64, i32, void ()*, i32, i32, ...) + +define private void @f() { + ret void +} + +define void @calls_statepoint(i8 addrspace(1)* %arg) gc "statepoint-example" { +entry: + %cast = bitcast i8 addrspace(1)* %arg to i64 addrspace(1)* + %safepoint_token = call i32 (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @f, i32 0, i32 0, i32 0, i32 5, i32 0, i32 0, i32 0, i32 10, i32 0, i8 addrspace(1)* %arg, i64 addrspace(1)* %cast, i8 addrspace(1)* %arg, i8 addrspace(1)* %arg) + ret void +} + +define void @calls_patchpoint() { +entry: + %c = bitcast void()* @f to i8* + tail call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 1, i32 15, i8* %c, i32 0, i16 65535, i16 -1, i32 65536, i32 2000000000, i32 2147483647, i32 -1, i32 4294967295, i32 4294967296, i64 2147483648, i64 4294967295, i64 4294967296, i64 -1) + ret void +} + + +; CHECK: Call graph node <> +; CHECK: CS<0x0> calls function 'f' + +; CHECK: Call graph node for function: 'calls_statepoint' +; CHECK-NEXT: CS<[[addr_0:[^>]+]]> calls external node + +; CHECK: Call graph node for function: 'calls_patchpoint' +; CHECK-NEXT: CS<[[addr_1:[^>]+]]> calls external node