Index: include/llvm/Analysis/LazyCallGraph.h =================================================================== --- include/llvm/Analysis/LazyCallGraph.h +++ include/llvm/Analysis/LazyCallGraph.h @@ -202,7 +202,7 @@ friend class LazyCallGraph::RefSCC; LazyCallGraph *G; - Function &F; + Function *F; // We provide for the DFS numbering and Tarjan walk lowlink numbers to be // stored directly within the node. These are both '-1' when nodes are part @@ -229,6 +229,20 @@ /// Internal helper to remove the edge to the given function. void removeEdgeInternal(Function &ChildF); + /// Internal helper to directly replace the function with a new one. + /// + /// This is used to facilitate tranfsormations which need to replace the + /// formal Function object but directly move the body and users from one to + /// the other. + void replaceFunction(Function &NewF); + + /// Internal helper to replace an edge key with a new one. + /// + /// This should be used when the function for a particular node in the + /// graph gets replaced and we are updating all of the edges to that node + /// to use the new function as the key. + void replaceEdgeKey(Function &OldTarget, Function &NewTarget); + void clear() { Edges.clear(); EdgeIndexMap.clear(); @@ -236,7 +250,7 @@ /// Print the name of this node's function. friend raw_ostream &operator<<(raw_ostream &OS, const Node &N) { - return OS << N.F.getName(); + return OS << N.F->getName(); } /// Dump the name of this node's function to stderr. @@ -245,7 +259,7 @@ public: LazyCallGraph &getGraph() const { return *G; } - Function &getFunction() const { return F; } + Function &getFunction() const { return *F; } edge_iterator begin() const { return edge_iterator(Edges.begin(), Edges.end()); @@ -789,6 +803,17 @@ /// already existing edges. void insertTrivialRefEdge(Node &SourceN, Node &TargetN); + /// Directly replace a node's function with a new function. + /// + /// This should be used when moving the body and users of a function to + /// a new formal function object but not otherwise changing the call graph + /// structure in any way. + /// + /// It requires that the old function in the provided node have zero uses + /// and the new function must have calls and references to it establishing + /// an equivalent graph. + void replaceNodeFunction(Node &N, Function &NewF); + ///@} }; Index: include/llvm/Transforms/IPO/ArgumentPromotion.h =================================================================== --- /dev/null +++ include/llvm/Transforms/IPO/ArgumentPromotion.h @@ -0,0 +1,31 @@ +//===- ArgumentPromotion.h - Promote by-reference arguments -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_IPO_ARGUMENTPROMOTION_H +#define LLVM_TRANSFORMS_IPO_ARGUMENTPROMOTION_H + +#include "llvm/Analysis/CGSCCPassManager.h" +#include "llvm/Analysis/LazyCallGraph.h" + +namespace llvm { + +/// Argument promotion pass. +/// +/// This pass walks the functions in each SCC and for each one tries to +/// transform it and all of its callers to replace indirect arguments with +/// direct (by-value) arguments. +class ArgumentPromotionPass : public PassInfoMixin { +public: + PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &UR); +}; + +} + +#endif Index: lib/Analysis/LazyCallGraph.cpp =================================================================== --- lib/Analysis/LazyCallGraph.cpp +++ lib/Analysis/LazyCallGraph.cpp @@ -34,7 +34,7 @@ } LazyCallGraph::Node::Node(LazyCallGraph &G, Function &F) - : G(&G), F(F), DFSNumber(0), LowLink(0) { + : G(&G), F(&F), DFSNumber(0), LowLink(0) { DEBUG(dbgs() << " Adding functions called by '" << F.getName() << "' to the graph.\n"); @@ -108,6 +108,24 @@ EdgeIndexMap.erase(IndexMapI); } +void LazyCallGraph::Node::replaceFunction(Function &NewF) { + assert(F != &NewF && "Must not replace a function with itself!"); + F = &NewF; +} + +void LazyCallGraph::Node::replaceEdgeKey(Function &OldTarget, Function &NewTarget) { + auto IndexMapI = EdgeIndexMap.find(&OldTarget); + assert(IndexMapI != EdgeIndexMap.end() && "Old target not in the edge set!"); + int Index = IndexMapI->second; + assert(&Edges[Index].getFunction() == &NewTarget && + "Edge's target hasn't been updated yet!"); + // Ok, everything is in order, so remove the old key and insert a new one. + EdgeIndexMap.erase(IndexMapI); + bool Inserted = EdgeIndexMap.insert({&NewTarget, Index}).second; + (void)Inserted; + assert(Inserted && "Didn't actually insert a new key!"); +} + #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void LazyCallGraph::Node::dump() const { dbgs() << *this << '\n'; @@ -1518,6 +1536,88 @@ handleTrivialEdgeInsertion(SourceN, TargetN); } +void LazyCallGraph::RefSCC::replaceNodeFunction(Node &N, Function &NewF) { +#ifndef NDEBUG + // Check that the RefSCC is still valid when we finish. + auto ExitVerifier = make_scope_exit([this] { verify(); }); + + assert(G->lookupRefSCC(N) == this && + "Cannot replace the function of a node outside this RefSCC."); + + assert(G->NodeMap.find(&NewF) == G->NodeMap.end() && + "Must not have already walked the new function!'"); + assert(G->EntryIndexMap.find(&NewF) == G->EntryIndexMap.end() && + "Must not have already walked the new function!'"); + + // It is important that this replacement not introduce graph changes so we + // insist that the caller has already removed every use of the original + // function and that all uses of the new function correspond to existing + // edges in the graph. The common and expected way to use this is when + // replacing the function itself in the IR without changing the call graph + // shape and just updating the analysis based on that. + Function &OldF = N.getFunction(); + assert(&OldF != &NewF && "Cannot replace a function with itself!"); + assert(OldF.use_empty() && + "Must have moved all uses from the old function to the new!"); + assert(llvm::find(G->RefSCCEntryNodes, &OldF) == G->RefSCCEntryNodes.end() && + "Must not replace a function which hasn't yet been processed into the " + "SCC graph!"); +#endif + + N.replaceFunction(NewF); + + // Update various call graph maps. + G->NodeMap.erase(&OldF); + G->NodeMap[&NewF] = &N; + auto EntryI = G->EntryIndexMap.find(&OldF); + if (EntryI != G->EntryIndexMap.end()) { + int Index = EntryI->second; + G->EntryIndexMap.erase(EntryI); + G->EntryIndexMap[&NewF] = Index; + } + + // Update all of the edges from other nodes in the call graph to this + // function. + SmallPtrSet Updated; + SmallPtrSet Visited; + SmallVector Worklist(NewF.user_begin(), NewF.user_end()); + for (User *U : Worklist) + Visited.insert(U); + while (!Worklist.empty()) { + User *U = Worklist.pop_back_val(); + // If the user is a constant, we just recurse down through its users. + if (auto *C = dyn_cast(U)) { + for (User *CU : C->users()) + if (Visited.insert(CU).second) + Worklist.push_back(CU); + continue; + } + + // We don't care what kind of instruction this is, but it must be an + // instruction at the bottom of the recursive walk through the users of the + // constants. + auto *I = cast(U); + + // Find the function and make sure we haven't processed it yet. + Function &UserF = *I->getParent()->getParent(); + if (!Updated.insert(&UserF).second) + // Already handled this node. + continue; + + Node *UserN = G->lookup(UserF); + if (!UserN) + // No need to update a function that hasn't been put into a node. + continue; + + assert(UserN->lookup(OldF) && + "No edge from a caller to the replacement node!"); + assert(UserN->lookup(OldF)->getNode() == &N && + "Edge from a caller doesn't point to the node!"); + + UserN->replaceEdgeKey(OldF, NewF); + } +} + void LazyCallGraph::insertEdge(Node &SourceN, Function &Target, Edge::Kind EK) { assert(SCCMap.empty() && DFSStack.empty() && "This method cannot be called after SCCs have been formed!"); Index: lib/Passes/PassBuilder.cpp =================================================================== --- lib/Passes/PassBuilder.cpp +++ lib/Passes/PassBuilder.cpp @@ -61,6 +61,7 @@ #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/GCOVProfiler.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" +#include "llvm/Transforms/IPO/ArgumentPromotion.h" #include "llvm/Transforms/IPO/ConstantMerge.h" #include "llvm/Transforms/IPO/CrossDSOCFI.h" #include "llvm/Transforms/IPO/DeadArgumentElimination.h" Index: lib/Passes/PassRegistry.def =================================================================== --- lib/Passes/PassRegistry.def +++ lib/Passes/PassRegistry.def @@ -85,6 +85,7 @@ #ifndef CGSCC_PASS #define CGSCC_PASS(NAME, CREATE_PASS) #endif +CGSCC_PASS("argpromotion", ArgumentPromotionPass()) CGSCC_PASS("invalidate", InvalidateAllAnalysesPass()) CGSCC_PASS("function-attrs", PostOrderFunctionAttrsPass()) CGSCC_PASS("inline", InlinerPass()) Index: lib/Transforms/IPO/ArgumentPromotion.cpp =================================================================== --- lib/Transforms/IPO/ArgumentPromotion.cpp +++ lib/Transforms/IPO/ArgumentPromotion.cpp @@ -29,7 +29,9 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Transforms/IPO/ArgumentPromotion.h" #include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Analysis/AliasAnalysis.h" @@ -37,6 +39,7 @@ #include "llvm/Analysis/BasicAliasAnalysis.h" #include "llvm/Analysis/CallGraph.h" #include "llvm/Analysis/CallGraphSCCPass.h" +#include "llvm/Analysis/LazyCallGraph.h" #include "llvm/Analysis/Loads.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/IR/CFG.h" @@ -67,9 +70,11 @@ /// DoPromotion - This method actually performs the promotion of the specified /// arguments, and returns the new function. At this point, we know that it's /// safe to do so. -static CallGraphNode * +static Function * doPromotion(Function *F, SmallPtrSetImpl &ArgsToPromote, - SmallPtrSetImpl &ByValArgsToTransform, CallGraph &CG) { + SmallPtrSetImpl &ByValArgsToTransform, + Optional> + ReplaceCallSite) { // Start by computing a new prototype for the function, which is the same as // the old function, but has modified arguments. @@ -207,9 +212,6 @@ F->getParent()->getFunctionList().insert(F->getIterator(), NF); NF->takeName(F); - // Get a new callgraph node for NF. - CallGraphNode *NF_CGN = CG.getOrInsertFunction(NF); - // Loop over all of the callers of the function, transforming the call sites // to pass in the loaded pointers. // @@ -334,8 +336,8 @@ AttributesVec.clear(); // Update the callgraph to know that the callsite has been transformed. - CallGraphNode *CalleeNode = CG[Call->getParent()->getParent()]; - CalleeNode->replaceCallEdge(CS, CallSite(New), NF_CGN); + if (ReplaceCallSite) + (*ReplaceCallSite)(CS, CallSite(New)); if (!Call->use_empty()) { Call->replaceAllUsesWith(New); @@ -463,18 +465,7 @@ std::advance(I2, ArgIndices.size()); } - NF_CGN->stealCalledFunctionsFrom(CG[F]); - - // Now that the old function is dead, delete it. If there is a dangling - // reference to the CallgraphNode, just leave the dead function around for - // someone else to nuke. - CallGraphNode *CGN = CG[F]; - if (CGN->getNumReferences() == 0) - delete CG.removeFunctionFromModule(CGN); - else - F->setLinkage(Function::ExternalLinkage); - - return NF_CGN; + return NF; } /// AllCallersPassInValidPointerForArgument - Return true if we can prove that @@ -818,14 +809,13 @@ /// example, all callers are direct). If safe to promote some arguments, it /// calls the DoPromotion method. /// -static CallGraphNode * -promoteArguments(CallGraphNode *CGN, CallGraph &CG, - function_ref AARGetter, - unsigned MaxElements) { - Function *F = CGN->getFunction(); - +static Function * +promoteArguments(Function *F, function_ref AARGetter, + unsigned MaxElements, + Optional> + ReplaceCallSite) { // Make sure that it is local to this module. - if (!F || !F->hasLocalLinkage()) + if (!F->hasLocalLinkage()) return nullptr; // Don't promote arguments for variadic functions. Adding, removing, or @@ -950,7 +940,52 @@ if (ArgsToPromote.empty() && ByValArgsToTransform.empty()) return nullptr; - return doPromotion(F, ArgsToPromote, ByValArgsToTransform, CG); + return doPromotion(F, ArgsToPromote, ByValArgsToTransform, ReplaceCallSite); +} + +PreservedAnalyses ArgumentPromotionPass::run(LazyCallGraph::SCC &C, + CGSCCAnalysisManager &AM, + LazyCallGraph &CG, + CGSCCUpdateResult &UR) { + bool Changed = false, LocalChange; + + // Iterate until we stop promoting from this SCC. + do { + LocalChange = false; + + for (LazyCallGraph::Node &N : C) { + Function &OldF = N.getFunction(); + + FunctionAnalysisManager &FAM = + AM.getResult(C, CG).getManager(); + // FIXME: This lambda must only be used with this function. We should + // skip the lambda and just get the AA results directly. + auto AARGetter = [&](Function &F) -> AAResults & { + assert(&F == &OldF && "Called with an unexpected function!"); + return FAM.getResult(F); + }; + + Function *NewF = promoteArguments(&OldF, AARGetter, 3u, None); + if (!NewF) + continue; + LocalChange = true; + + // Directly substitute the functions in the call graph. Note that this + // requires the old function to be completely dead and completely + // replaced by the new function. It does no call graph updates, it merely + // swaps out the particular function mapped to a particular node in the + // graph. + C.getOuterRefSCC().replaceNodeFunction(N, *NewF); + OldF.eraseFromParent(); + } + + Changed |= LocalChange; + } while (LocalChange); + + if (!Changed) + return PreservedAnalyses::all(); + + return PreservedAnalyses::none(); } namespace { @@ -1019,9 +1054,31 @@ LocalChange = false; // Attempt to promote arguments from all functions in this SCC. for (CallGraphNode *OldNode : SCC) { - if (CallGraphNode *NewNode = - promoteArguments(OldNode, CG, AARGetter, MaxElements)) { + Function *OldF = OldNode->getFunction(); + if (!OldF) + continue; + + auto ReplaceCallSite = [&](CallSite OldCS, CallSite NewCS) { + Function *Caller = OldCS.getInstruction()->getParent()->getParent(); + CallGraphNode *NewCalleeNode = + CG.getOrInsertFunction(NewCS.getCalledFunction()); + CallGraphNode *CallerNode = CG[Caller]; + CallerNode->replaceCallEdge(OldCS, NewCS, NewCalleeNode); + }; + + if (Function *NewF = + promoteArguments(OldF, AARGetter, MaxElements, {ReplaceCallSite})) { LocalChange = true; + + // Update the call graph for the newly promoted function. + CallGraphNode *NewNode = CG.getOrInsertFunction(NewF); + NewNode->stealCalledFunctionsFrom(OldNode); + if (OldNode->getNumReferences() == 0) + delete CG.removeFunctionFromModule(OldNode); + else + OldF->setLinkage(Function::ExternalLinkage); + + // And updat ethe SCC we're iterating as well. SCC.ReplaceNode(OldNode, NewNode); } } Index: test/Transforms/ArgumentPromotion/aggregate-promote.ll =================================================================== --- test/Transforms/ArgumentPromotion/aggregate-promote.ll +++ test/Transforms/ArgumentPromotion/aggregate-promote.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -argpromotion -instcombine -S | not grep load +; RUN: opt < %s -passes='cgscc(argpromotion,function(instcombine))' -S | not grep load target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" %QuadTy = type { i32, i32, i32, i32 } Index: test/Transforms/ArgumentPromotion/attrs.ll =================================================================== --- test/Transforms/ArgumentPromotion/attrs.ll +++ test/Transforms/ArgumentPromotion/attrs.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -argpromotion -S | grep zeroext +; RUN: opt < %s -passes=argpromotion -S | grep zeroext %struct.ss = type { i32, i64 } Index: test/Transforms/ArgumentPromotion/byval-2.ll =================================================================== --- test/Transforms/ArgumentPromotion/byval-2.ll +++ test/Transforms/ArgumentPromotion/byval-2.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -S | FileCheck %s ; Arg promotion eliminates the struct argument. ; FIXME: Should it eliminate the i32* argument? Index: test/Transforms/ArgumentPromotion/byval.ll =================================================================== --- test/Transforms/ArgumentPromotion/byval.ll +++ test/Transforms/ArgumentPromotion/byval.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -S | FileCheck %s target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" Index: test/Transforms/ArgumentPromotion/callgraph-update.ll =================================================================== --- test/Transforms/ArgumentPromotion/callgraph-update.ll +++ test/Transforms/ArgumentPromotion/callgraph-update.ll @@ -1,4 +1,6 @@ ; RUN: opt < %s -argpromotion -simplifycfg -constmerge | llvm-dis +; RUN: opt < %s -passes='cgscc(argpromotion,function(simplify-cfg)),constmerge' | llvm-dis + target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128" target triple = "i386-apple-darwin10.0" Index: test/Transforms/ArgumentPromotion/chained.ll =================================================================== --- test/Transforms/ArgumentPromotion/chained.ll +++ test/Transforms/ArgumentPromotion/chained.ll @@ -1,4 +1,6 @@ ; RUN: opt < %s -argpromotion -instcombine -S | not grep load +; RUN: opt < %s -passes='cgscc(argpromotion,function(instcombine))' -S | not grep load + target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" @G1 = constant i32 0 ; [#uses=1] Index: test/Transforms/ArgumentPromotion/control-flow.ll =================================================================== --- test/Transforms/ArgumentPromotion/control-flow.ll +++ test/Transforms/ArgumentPromotion/control-flow.ll @@ -1,5 +1,7 @@ ; RUN: opt < %s -argpromotion -S | \ ; RUN: not grep "load i32* null" +; RUN: opt < %s -passes=argpromotion -S | \ +; RUN: not grep "load i32* null" define internal i32 @callee(i1 %C, i32* %P) { br i1 %C, label %T, label %F Index: test/Transforms/ArgumentPromotion/control-flow2.ll =================================================================== --- test/Transforms/ArgumentPromotion/control-flow2.ll +++ test/Transforms/ArgumentPromotion/control-flow2.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -S | FileCheck %s ; CHECK: load i32, i32* %A target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" Index: test/Transforms/ArgumentPromotion/crash.ll =================================================================== --- test/Transforms/ArgumentPromotion/crash.ll +++ test/Transforms/ArgumentPromotion/crash.ll @@ -1,4 +1,5 @@ -; RUN: opt -inline -argpromotion < %s +; RUN: opt -S -inline -argpromotion < %s +; RUN: opt -S -passes=inline,argpromotion < %s ; rdar://7879828 define void @foo() personality i32 (...)* @__gxx_personality_v0 { Index: test/Transforms/ArgumentPromotion/dbg.ll =================================================================== --- test/Transforms/ArgumentPromotion/dbg.ll +++ test/Transforms/ArgumentPromotion/dbg.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -S | FileCheck %s declare void @sink(i32) Index: test/Transforms/ArgumentPromotion/fp80.ll =================================================================== --- test/Transforms/ArgumentPromotion/fp80.ll +++ test/Transforms/ArgumentPromotion/fp80.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -S | FileCheck %s target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" Index: test/Transforms/ArgumentPromotion/inalloca.ll =================================================================== --- test/Transforms/ArgumentPromotion/inalloca.ll +++ test/Transforms/ArgumentPromotion/inalloca.ll @@ -1,4 +1,5 @@ ; RUN: opt %s -argpromotion -sroa -S | FileCheck %s +; RUN: opt %s -passes='argpromotion,function(sroa)' -S | FileCheck %s target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" Index: test/Transforms/ArgumentPromotion/pr27568.ll =================================================================== --- test/Transforms/ArgumentPromotion/pr27568.ll +++ test/Transforms/ArgumentPromotion/pr27568.ll @@ -1,4 +1,5 @@ ; RUN: opt -S -argpromotion < %s | FileCheck %s +; RUN: opt -S -passes=argpromotion < %s | FileCheck %s target triple = "x86_64-pc-windows-msvc" define internal void @callee(i8*) { Index: test/Transforms/ArgumentPromotion/reserve-tbaa.ll =================================================================== --- test/Transforms/ArgumentPromotion/reserve-tbaa.ll +++ test/Transforms/ArgumentPromotion/reserve-tbaa.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -argpromotion -S +; RUN: opt < %s -passes=argpromotion -S ; PR17906 ; When we promote two arguments in a single function with different types, Index: test/Transforms/ArgumentPromotion/sret.ll =================================================================== --- test/Transforms/ArgumentPromotion/sret.ll +++ test/Transforms/ArgumentPromotion/sret.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -S | FileCheck %s target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-windows-msvc" Index: test/Transforms/ArgumentPromotion/tail.ll =================================================================== --- test/Transforms/ArgumentPromotion/tail.ll +++ test/Transforms/ArgumentPromotion/tail.ll @@ -1,4 +1,5 @@ ; RUN: opt %s -argpromotion -S -o - | FileCheck %s +; RUN: opt %s -passes=argpromotion -S -o - | FileCheck %s ; PR14710 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" Index: test/Transforms/ArgumentPromotion/variadic.ll =================================================================== --- test/Transforms/ArgumentPromotion/variadic.ll +++ test/Transforms/ArgumentPromotion/variadic.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -S | FileCheck %s ; Unused arguments from variadic functions cannot be eliminated as that changes ; their classiciation according to the SysV amd64 ABI. Clang and other frontends Index: unittests/Analysis/LazyCallGraphTest.cpp =================================================================== --- unittests/Analysis/LazyCallGraphTest.cpp +++ unittests/Analysis/LazyCallGraphTest.cpp @@ -2134,4 +2134,83 @@ EXPECT_TRUE(GRC.isParentOf(FRC)); } +TEST(LazyCallGraphTest, ReplaceNodeFunction) { + LLVMContext Context; + // A graph with several different kinds of edges pointing at a particular + // function. + std::unique_ptr M = parseAssembly( + Context, "define void @a(i8** %ptr) {\n" + "entry:\n" + " store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n" + " ret void\n" + "}\n" + "define void @b(i8** %ptr) {\n" + "entry:\n" + " store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n" + " store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n" + " call void @d(i8** %ptr)" + " ret void\n" + "}\n" + "define void @c(i8** %ptr) {\n" + "entry:\n" + " call void @d(i8** %ptr)" + " call void @d(i8** %ptr)" + " store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n" + " ret void\n" + "}\n" + "define void @d(i8** %ptr) {\n" + "entry:\n" + " store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n" + " call void @c(i8** %ptr)" + " call void @d(i8** %ptr)" + " store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n" + " ret void\n" + "}\n"); + LazyCallGraph CG(*M); + + // Force the graph to be fully expanded. + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC &RC1 = *I++; + LazyCallGraph::RefSCC &RC2 = *I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + ASSERT_EQ(2u, RC1.size()); + LazyCallGraph::SCC &C1 = RC1[0]; + LazyCallGraph::SCC &C2 = RC1[1]; + + LazyCallGraph::Node &AN = *CG.lookup(lookupFunction(*M, "a")); + LazyCallGraph::Node &BN = *CG.lookup(lookupFunction(*M, "b")); + LazyCallGraph::Node &CN = *CG.lookup(lookupFunction(*M, "c")); + LazyCallGraph::Node &DN = *CG.lookup(lookupFunction(*M, "d")); + EXPECT_EQ(&C1, CG.lookupSCC(DN)); + EXPECT_EQ(&C1, CG.lookupSCC(CN)); + EXPECT_EQ(&C2, CG.lookupSCC(BN)); + EXPECT_EQ(&RC1, CG.lookupRefSCC(DN)); + EXPECT_EQ(&RC1, CG.lookupRefSCC(CN)); + EXPECT_EQ(&RC1, CG.lookupRefSCC(BN)); + EXPECT_EQ(&RC2, CG.lookupRefSCC(AN)); + + // Now we need to build a new function 'e' with the same signature as 'd'. + Function &D = DN.getFunction(); + Function &E = *Function::Create(D.getFunctionType(), D.getLinkage(), "e"); + D.getParent()->getFunctionList().insert(D.getIterator(), &E); + + // Change each use of 'd' to use 'e'. This is particularly easy as they have + // the same type. + D.replaceAllUsesWith(&E); + + // Splice the body of the old function into the new one. + E.getBasicBlockList().splice(E.begin(), D.getBasicBlockList()); + // And fix up the one argument. + D.arg_begin()->replaceAllUsesWith(&*E.arg_begin()); + E.arg_begin()->takeName(&*D.arg_begin()); + + // Now replace the function in the graph. + RC1.replaceNodeFunction(DN, E); + + EXPECT_EQ(&E, &DN.getFunction()); + EXPECT_EQ(&DN, CN[E].getNode()); + EXPECT_EQ(&DN, BN[E].getNode()); +} + }