diff --git a/llvm/include/llvm/Analysis/CallGraph.h b/llvm/include/llvm/Analysis/CallGraph.h --- a/llvm/include/llvm/Analysis/CallGraph.h +++ b/llvm/include/llvm/Analysis/CallGraph.h @@ -175,13 +175,21 @@ public: /// A pair of the calling instruction (a call or invoke) /// and the call graph node being called. - using CallRecord = std::pair; + /// Call graph node may have two types of call records which represent an edge + /// in the call graph - reference or a call edge. Reference edges are not + /// associated with any call instruction and are created with the first field + /// set to `None`, while real call edges have instruction address in this + /// field. Therefore, all real call edges are expected to have a value in the + /// first field and it is not supposed to be `nullptr`. + /// Reference edges, for example, are used for connecting broker function + /// caller to the callback function for callback call sites. + using CallRecord = std::pair, CallGraphNode *>; public: using CalledFunctionsVector = std::vector; /// Creates a node for the specified function. - inline CallGraphNode(Function *F) : F(F) {} + inline CallGraphNode(CallGraph *CG, Function *F) : CG(CG), F(F) {} CallGraphNode(const CallGraphNode &) = delete; CallGraphNode &operator=(const CallGraphNode &) = delete; @@ -243,7 +251,8 @@ assert(!Call || !Call->getCalledFunction() || !Call->getCalledFunction()->isIntrinsic() || !Intrinsic::isLeaf(Call->getCalledFunction()->getIntrinsicID())); - CalledFunctions.emplace_back(Call, M); + CalledFunctions.emplace_back( + Call ? Optional(Call) : Optional(), M); M->AddRef(); } @@ -279,6 +288,7 @@ private: friend class CallGraph; + CallGraph *CG; Function *F; std::vector CalledFunctions; diff --git a/llvm/include/llvm/IR/AbstractCallSite.h b/llvm/include/llvm/IR/AbstractCallSite.h --- a/llvm/include/llvm/IR/AbstractCallSite.h +++ b/llvm/include/llvm/IR/AbstractCallSite.h @@ -221,6 +221,27 @@ } }; +/// Apply function Func to each CB's callback call site. +template +void forEachCallbackCallSite(const CallBase &CB, UnaryFunction Func) { + SmallVector CallbackUses; + AbstractCallSite::getCallbackUses(CB, CallbackUses); + for (const Use *U : CallbackUses) { + AbstractCallSite ACS(U); + assert(ACS && ACS.isCallbackCall() && "must be a callback call"); + Func(ACS); + } +} + +/// Apply function Func to each CB's callback function. +template +void forEachCallbackFunction(const CallBase &CB, UnaryFunction Func) { + forEachCallbackCallSite(CB, [&Func](AbstractCallSite &ACS) { + if (Function *Callback = ACS.getCalledFunction()) + Func(Callback); + }); +} + } // end namespace llvm #endif // LLVM_IR_ABSTRACTCALLSITE_H 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 @@ -10,6 +10,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Config/llvm-config.h" +#include "llvm/IR/AbstractCallSite.h" #include "llvm/IR/Function.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" @@ -31,7 +32,7 @@ CallGraph::CallGraph(Module &M) : M(M), ExternalCallingNode(getOrInsertFunction(nullptr)), - CallsExternalNode(std::make_unique(nullptr)) { + CallsExternalNode(std::make_unique(this, nullptr)) { // Add every interesting function to the call graph. for (Function &F : M) if (!isDbgInfoIntrinsic(F.getIntrinsicID())) @@ -44,6 +45,11 @@ CallsExternalNode(std::move(Arg.CallsExternalNode)) { Arg.FunctionMap.clear(); Arg.ExternalCallingNode = nullptr; + + // Update parent CG for all call graph's nodes. + CallsExternalNode->CG = this; + for (auto &P : FunctionMap) + P.second->CG = this; } CallGraph::~CallGraph() { @@ -99,6 +105,11 @@ Node->addCalledFunction(Call, CallsExternalNode.get()); else if (!Callee->isIntrinsic()) Node->addCalledFunction(Call, getOrInsertFunction(Callee)); + + // Add reference to callback functions. + forEachCallbackFunction(*Call, [=](Function *CB) { + Node->addCalledFunction(nullptr, getOrInsertFunction(CB)); + }); } } } @@ -178,7 +189,7 @@ return CGN.get(); assert((!F || F->getParent() == &M) && "Function not in current module!"); - CGN = std::make_unique(const_cast(F)); + CGN = std::make_unique(this, const_cast(F)); return CGN.get(); } @@ -214,10 +225,15 @@ void CallGraphNode::removeCallEdgeFor(CallBase &Call) { for (CalledFunctionsVector::iterator I = CalledFunctions.begin(); ; ++I) { assert(I != CalledFunctions.end() && "Cannot find callsite to remove!"); - if (I->first == &Call) { + if (I->first && *I->first == &Call) { I->second->DropRef(); *I = CalledFunctions.back(); CalledFunctions.pop_back(); + + // Remove all references to callback functions if there are any. + forEachCallbackFunction(Call, [=](Function *CB) { + removeOneAbstractEdgeTo(CG->getOrInsertFunction(CB)); + }); return; } } @@ -242,7 +258,7 @@ for (CalledFunctionsVector::iterator I = CalledFunctions.begin(); ; ++I) { assert(I != CalledFunctions.end() && "Cannot find callee to remove!"); CallRecord &CR = *I; - if (CR.second == Callee && CR.first == nullptr) { + if (CR.second == Callee && !CR.first) { Callee->DropRef(); *I = CalledFunctions.back(); CalledFunctions.pop_back(); @@ -258,11 +274,19 @@ CallGraphNode *NewNode) { for (CalledFunctionsVector::iterator I = CalledFunctions.begin(); ; ++I) { assert(I != CalledFunctions.end() && "Cannot find callsite to remove!"); - if (I->first == &Call) { + if (I->first && *I->first == &Call) { I->second->DropRef(); I->first = &NewCall; I->second = NewNode; NewNode->AddRef(); + + // Refresh callback references. + forEachCallbackFunction(Call, [=](Function *CB) { + removeOneAbstractEdgeTo(CG->getOrInsertFunction(CB)); + }); + forEachCallbackFunction(NewCall, [=](Function *CB) { + addCalledFunction(nullptr, CG->getOrInsertFunction(CB)); + }); return; } } diff --git a/llvm/lib/Analysis/CallGraphSCCPass.cpp b/llvm/lib/Analysis/CallGraphSCCPass.cpp --- a/llvm/lib/Analysis/CallGraphSCCPass.cpp +++ b/llvm/lib/Analysis/CallGraphSCCPass.cpp @@ -227,9 +227,15 @@ // Get the set of call sites currently in the function. for (CallGraphNode::iterator I = CGN->begin(), E = CGN->end(); I != E; ) { + // Skip "reference" call records that do not have call instruction. + if (!I->first) { + ++I; + continue; + } + // If this call site is null, then the function pass deleted the call // entirely and the WeakTrackingVH nulled it out. - auto *Call = dyn_cast_or_null(I->first); + auto *Call = dyn_cast_or_null(*I->first); if (!Call || // If we've already seen this call site, then the FunctionPass RAUW'd // one call with another, which resulted in two "uses" in the edge diff --git a/llvm/lib/Transforms/IPO/SyntheticCountsPropagation.cpp b/llvm/lib/Transforms/IPO/SyntheticCountsPropagation.cpp --- a/llvm/lib/Transforms/IPO/SyntheticCountsPropagation.cpp +++ b/llvm/lib/Transforms/IPO/SyntheticCountsPropagation.cpp @@ -109,7 +109,7 @@ Optional Res = None; if (!Edge.first) return Res; - CallBase &CB = *cast(Edge.first); + CallBase &CB = *cast(*Edge.first); Function *Caller = CB.getCaller(); auto &BFI = FAM.getResult(*Caller); diff --git a/llvm/lib/Transforms/Utils/CallGraphUpdater.cpp b/llvm/lib/Transforms/Utils/CallGraphUpdater.cpp --- a/llvm/lib/Transforms/Utils/CallGraphUpdater.cpp +++ b/llvm/lib/Transforms/Utils/CallGraphUpdater.cpp @@ -149,7 +149,7 @@ CG->getOrInsertFunction(NewCS.getCalledFunction()); CallGraphNode *CallerNode = (*CG)[Caller]; if (llvm::none_of(*CallerNode, [&OldCS](const CallGraphNode::CallRecord &CR) { - return CR.first == &OldCS; + return CR.first && *CR.first == &OldCS; })) return false; CallerNode->replaceCallEdge(OldCS, NewCS, NewCalleeNode); diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp --- a/llvm/lib/Transforms/Utils/InlineFunction.cpp +++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -1291,7 +1291,11 @@ } for (; I != E; ++I) { - const Value *OrigCall = I->first; + // Skip 'refererence' call records. + if (!I->first) + continue; + + const Value *OrigCall = *I->first; ValueToValueMapTy::iterator VMI = VMap.find(OrigCall); // Only copy the edge if the call was inlined! diff --git a/llvm/test/Analysis/CallGraph/callback-calls.ll b/llvm/test/Analysis/CallGraph/callback-calls.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Analysis/CallGraph/callback-calls.ll @@ -0,0 +1,20 @@ +; RUN: opt < %s -print-callgraph -disable-output 2>&1 | FileCheck %s +; RUN: opt < %s -passes=print-callgraph -disable-output 2>&1 | FileCheck %s + +; CHECK: Call graph node for function: 'caller' +; CHECK-NEXT: CS<{{.*}}> calls function 'broker' +; CHECK-NEXT: CS calls function 'callback' + +define void @caller(i32* %arg) { + call void @broker(void (i32*)* @callback, i32* %arg) + ret void +} + +define void @callback(i32* %arg) { + ret void +} + +declare !callback !0 void @broker(void (i32*)*, i32*) + +!0 = !{!1} +!1 = !{i64 0, i64 1, i1 false} diff --git a/llvm/test/Analysis/CallGraph/non-leaf-intrinsics.ll b/llvm/test/Analysis/CallGraph/non-leaf-intrinsics.ll --- a/llvm/test/Analysis/CallGraph/non-leaf-intrinsics.ll +++ b/llvm/test/Analysis/CallGraph/non-leaf-intrinsics.ll @@ -23,7 +23,7 @@ ; CHECK: Call graph node <> -; CHECK: CS<0x0> calls function 'f' +; CHECK: CS calls function 'f' ; CHECK: Call graph node for function: 'calls_patchpoint' ; CHECK-NEXT: CS<[[addr_1:[^>]+]]> calls external node diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.ll b/llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.ll @@ -58,7 +58,7 @@ ; IS__CGSCC_OPM-NEXT: store i32 [[N]], i32* [[N_ADDR]], align 4 ; IS__CGSCC_OPM-NEXT: store float 3.000000e+00, float* [[P]], align 4 ; IS__CGSCC_OPM-NEXT: store i32 7, i32* [[N_ADDR]], align 4 -; IS__CGSCC_OPM-NEXT: call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* nonnull align 8 dereferenceable(24) @1, i32 3, void (i32*, i32*, ...)* bitcast (void (i32*, i32*, i32*, float*, i64)* @.omp_outlined. to void (i32*, i32*, ...)*), i32* nonnull align 4 dereferenceable(4) [[N_ADDR]], float* nonnull align 4 dereferenceable(4) [[P]], i64 4617315517961601024) +; IS__CGSCC_OPM-NEXT: call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* nonnull align 8 dereferenceable(24) @1, i32 3, void (i32*, i32*, ...)* bitcast (void (i32*, i32*, i32*, float*, i64)* @.omp_outlined. to void (i32*, i32*, ...)*), i32* nocapture nonnull readonly align 4 dereferenceable(4) [[N_ADDR]], float* nocapture nonnull readonly align 4 dereferenceable(4) [[P]], i64 4617315517961601024) ; IS__CGSCC_OPM-NEXT: ret void ; ; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@foo 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 @@ -47,10 +47,10 @@ ; 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 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* nonnull 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* nocapture nonnull 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* nonnull align 8 dereferenceable(1) [[ALLOC2]]) +; 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() @@ -94,7 +94,7 @@ ; IS__TUNIT____-NEXT: ret i8* bitcast (i8** @GlobalVPtr to i8*) ; ; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@bar -; IS__CGSCC_OPM-SAME: (i8* nofree nonnull readnone returned align 8 dereferenceable(8) "no-capture-maybe-returned" [[ARG:%.*]]) +; 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*) ; diff --git a/llvm/test/Transforms/Attributor/callbacks.ll b/llvm/test/Transforms/Attributor/callbacks.ll --- a/llvm/test/Transforms/Attributor/callbacks.ll +++ b/llvm/test/Transforms/Attributor/callbacks.ll @@ -49,7 +49,7 @@ ; IS__CGSCC_OPM-NEXT: [[TMP0:%.*]] = bitcast i32* [[B]] to i8* ; IS__CGSCC_OPM-NEXT: store i32 42, i32* [[B]], align 32 ; IS__CGSCC_OPM-NEXT: store i32* [[B]], i32** [[C]], align 64 -; IS__CGSCC_OPM-NEXT: call void (i32*, i32*, void (i32*, i32*, ...)*, ...) @t0_callback_broker(i32* noalias nocapture align 536870912 null, i32* nonnull align 128 dereferenceable(4) [[PTR]], void (i32*, i32*, ...)* bitcast (void (i32*, i32*, i32*, i64, i32**)* @t0_callback_callee to void (i32*, i32*, ...)*), i32* align 256 [[A]], i64 99, i32** nonnull align 64 dereferenceable(8) [[C]]) +; IS__CGSCC_OPM-NEXT: call void (i32*, i32*, void (i32*, i32*, ...)*, ...) @t0_callback_broker(i32* noalias nocapture align 536870912 null, i32* nonnull align 128 dereferenceable(4) [[PTR]], void (i32*, i32*, ...)* bitcast (void (i32*, i32*, i32*, i64, i32**)* @t0_callback_callee to void (i32*, i32*, ...)*), i32* align 256 [[A]], i64 99, i32** nocapture nonnull readonly align 64 dereferenceable(8) [[C]]) ; IS__CGSCC_OPM-NEXT: ret void ; ; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@t0_caller @@ -140,7 +140,7 @@ ; IS__TUNIT_NPM-NEXT: ret void ; ; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@t1_caller -; IS__CGSCC_OPM-SAME: (i32* noalias align 256 [[A:%.*]]) +; IS__CGSCC_OPM-SAME: (i32* noalias nocapture align 256 [[A:%.*]]) ; IS__CGSCC_OPM-NEXT: entry: ; IS__CGSCC_OPM-NEXT: [[B:%.*]] = alloca i32, align 32 ; IS__CGSCC_OPM-NEXT: [[C:%.*]] = alloca i32*, align 64 @@ -148,7 +148,7 @@ ; IS__CGSCC_OPM-NEXT: [[TMP0:%.*]] = bitcast i32* [[B]] to i8* ; IS__CGSCC_OPM-NEXT: store i32 42, i32* [[B]], align 32 ; IS__CGSCC_OPM-NEXT: store i32* [[B]], i32** [[C]], align 64 -; IS__CGSCC_OPM-NEXT: call void (i32*, i32*, void (i32*, i32*, ...)*, ...) @t1_callback_broker(i32* noalias nocapture align 536870912 null, i32* nocapture nonnull align 128 dereferenceable(4) [[PTR]], void (i32*, i32*, ...)* nocapture bitcast (void (i32*, i32*, i32*, i64, i32**)* @t1_callback_callee to void (i32*, i32*, ...)*), i32* align 256 [[A]], i64 99, i32** nonnull align 64 dereferenceable(8) [[C]]) +; IS__CGSCC_OPM-NEXT: call void (i32*, i32*, void (i32*, i32*, ...)*, ...) @t1_callback_broker(i32* noalias nocapture align 536870912 null, i32* nocapture nonnull align 128 dereferenceable(4) [[PTR]], void (i32*, i32*, ...)* nocapture bitcast (void (i32*, i32*, i32*, i64, i32**)* @t1_callback_callee to void (i32*, i32*, ...)*), i32* nocapture align 256 [[A]], i64 99, i32** nocapture nonnull readonly align 64 dereferenceable(8) [[C]]) ; IS__CGSCC_OPM-NEXT: ret void ; ; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@t1_caller @@ -238,7 +238,7 @@ ; IS__TUNIT_NPM-NEXT: ret void ; ; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@t2_caller -; IS__CGSCC_OPM-SAME: (i32* noalias align 256 [[A:%.*]]) +; IS__CGSCC_OPM-SAME: (i32* noalias nocapture align 256 [[A:%.*]]) ; IS__CGSCC_OPM-NEXT: entry: ; IS__CGSCC_OPM-NEXT: [[B:%.*]] = alloca i32, align 32 ; IS__CGSCC_OPM-NEXT: [[C:%.*]] = alloca i32*, align 64 @@ -246,7 +246,7 @@ ; IS__CGSCC_OPM-NEXT: [[TMP0:%.*]] = bitcast i32* [[B]] to i8* ; IS__CGSCC_OPM-NEXT: store i32 42, i32* [[B]], align 32 ; IS__CGSCC_OPM-NEXT: store i32* [[B]], i32** [[C]], align 64 -; IS__CGSCC_OPM-NEXT: call void (i32*, i32*, void (i32*, i32*, ...)*, ...) @t2_callback_broker(i32* noalias nocapture align 536870912 null, i32* nocapture nonnull align 128 dereferenceable(4) [[PTR]], void (i32*, i32*, ...)* nocapture bitcast (void (i32*, i32*, i32*, i64, i32**)* @t2_callback_callee to void (i32*, i32*, ...)*), i32* align 256 [[A]], i64 99, i32** nonnull align 64 dereferenceable(8) [[C]]) +; IS__CGSCC_OPM-NEXT: call void (i32*, i32*, void (i32*, i32*, ...)*, ...) @t2_callback_broker(i32* noalias nocapture align 536870912 null, i32* nocapture nonnull align 128 dereferenceable(4) [[PTR]], void (i32*, i32*, ...)* nocapture bitcast (void (i32*, i32*, i32*, i64, i32**)* @t2_callback_callee to void (i32*, i32*, ...)*), i32* nocapture align 256 [[A]], i64 99, i32** nocapture nonnull readonly align 64 dereferenceable(8) [[C]]) ; IS__CGSCC_OPM-NEXT: ret void ; ; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@t2_caller @@ -340,7 +340,7 @@ ; IS__TUNIT_NPM-NEXT: ret void ; ; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@t3_caller -; IS__CGSCC_OPM-SAME: (i32* noalias align 256 [[A:%.*]]) +; IS__CGSCC_OPM-SAME: (i32* noalias nocapture align 256 [[A:%.*]]) ; IS__CGSCC_OPM-NEXT: entry: ; IS__CGSCC_OPM-NEXT: [[B:%.*]] = alloca i32, align 32 ; IS__CGSCC_OPM-NEXT: [[C:%.*]] = alloca i32*, align 64 @@ -348,8 +348,8 @@ ; IS__CGSCC_OPM-NEXT: [[TMP0:%.*]] = bitcast i32* [[B]] to i8* ; IS__CGSCC_OPM-NEXT: store i32 42, i32* [[B]], align 32 ; IS__CGSCC_OPM-NEXT: store i32* [[B]], i32** [[C]], align 64 -; IS__CGSCC_OPM-NEXT: call void (i32*, i32*, void (i32*, i32*, ...)*, ...) @t3_callback_broker(i32* noalias nocapture align 536870912 null, i32* nocapture nonnull align 128 dereferenceable(4) [[PTR]], void (i32*, i32*, ...)* nocapture bitcast (void (i32*, i32*, i32*, i64, i32**)* @t3_callback_callee to void (i32*, i32*, ...)*), i32* align 256 [[A]], i64 99, i32** nonnull align 64 dereferenceable(8) [[C]]) -; IS__CGSCC_OPM-NEXT: call void (i32*, i32*, void (i32*, i32*, ...)*, ...) @t3_callback_broker(i32* noalias nocapture align 536870912 null, i32* nocapture nonnull align 128 dereferenceable(4) [[PTR]], void (i32*, i32*, ...)* nocapture bitcast (void (i32*, i32*, i32*, i64, i32**)* @t3_callback_callee to void (i32*, i32*, ...)*), i32* align 256 [[A]], i64 99, i32** nonnull align 64 dereferenceable(8) [[C]]) +; IS__CGSCC_OPM-NEXT: call void (i32*, i32*, void (i32*, i32*, ...)*, ...) @t3_callback_broker(i32* noalias nocapture align 536870912 null, i32* nocapture nonnull align 128 dereferenceable(4) [[PTR]], void (i32*, i32*, ...)* nocapture bitcast (void (i32*, i32*, i32*, i64, i32**)* @t3_callback_callee to void (i32*, i32*, ...)*), i32* nocapture align 256 [[A]], i64 99, i32** nocapture nonnull readonly align 64 dereferenceable(8) [[C]]) +; IS__CGSCC_OPM-NEXT: call void (i32*, i32*, void (i32*, i32*, ...)*, ...) @t3_callback_broker(i32* noalias nocapture align 536870912 null, i32* nocapture nonnull align 128 dereferenceable(4) [[PTR]], void (i32*, i32*, ...)* nocapture bitcast (void (i32*, i32*, i32*, i64, i32**)* @t3_callback_callee to void (i32*, i32*, ...)*), i32* nocapture align 256 [[A]], i64 99, i32** nocapture nonnull readonly align 64 dereferenceable(8) [[C]]) ; IS__CGSCC_OPM-NEXT: ret void ; ; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@t3_caller