Index: include/llvm/Analysis/CallGraph.h =================================================================== --- include/llvm/Analysis/CallGraph.h +++ 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 @@ /// \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(); } Index: include/llvm/IR/Intrinsics.h =================================================================== --- include/llvm/IR/Intrinsics.h +++ include/llvm/IR/Intrinsics.h @@ -52,6 +52,11 @@ /// 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); Index: lib/Analysis/IPA/CallGraph.cpp =================================================================== --- lib/Analysis/IPA/CallGraph.cpp +++ lib/Analysis/IPA/CallGraph.cpp @@ -79,8 +79,10 @@ 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)); Index: lib/Analysis/IPA/CallGraphSCCPass.cpp =================================================================== --- lib/Analysis/IPA/CallGraphSCCPass.cpp +++ lib/Analysis/IPA/CallGraphSCCPass.cpp @@ -217,8 +217,10 @@ // 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!"); Index: lib/IR/Function.cpp =================================================================== --- lib/IR/Function.cpp +++ lib/IR/Function.cpp @@ -846,6 +846,18 @@ #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" Index: test/Analysis/CallGraph/non-leaf-intrinsics.ll =================================================================== --- /dev/null +++ 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