diff options
Diffstat (limited to 'llvm/tools/llvm-reduce/deltas/ReduceInlineCallSites.cpp')
-rw-r--r-- | llvm/tools/llvm-reduce/deltas/ReduceInlineCallSites.cpp | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/llvm/tools/llvm-reduce/deltas/ReduceInlineCallSites.cpp b/llvm/tools/llvm-reduce/deltas/ReduceInlineCallSites.cpp new file mode 100644 index 0000000..cfef367 --- /dev/null +++ b/llvm/tools/llvm-reduce/deltas/ReduceInlineCallSites.cpp @@ -0,0 +1,103 @@ +//===- ReduceInlineCallSites.cpp ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ReduceInlineCallSites.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/Utils/Cloning.h" + +using namespace llvm; + +extern cl::OptionCategory LLVMReduceOptions; + +static cl::opt<int> CallsiteInlineThreshold( + "reduce-callsite-inline-threshold", + cl::desc("Number of instructions in a function to unconditionally inline " + "(-1 for inline all)"), + cl::init(5), cl::cat(LLVMReduceOptions)); + +static bool functionHasMoreThanNonTerminatorInsts(const Function &F, + uint64_t NumInsts) { + uint64_t InstCount = 0; + for (const BasicBlock &BB : F) { + for (const Instruction &I : make_range(BB.begin(), std::prev(BB.end()))) { + (void)I; + if (InstCount++ > NumInsts) + return true; + } + } + + return false; +} + +static bool hasOnlyOneCallUse(const Function &F) { + unsigned UseCount = 0; + for (const Use &U : F.uses()) { + const CallBase *CB = dyn_cast<CallBase>(U.getUser()); + if (!CB || !CB->isCallee(&U)) + return false; + if (UseCount++ > 1) + return false; + } + + return UseCount == 1; +} + +// TODO: This could use more thought. +static bool inlineWillReduceComplexity(const Function &Caller, + const Function &Callee) { + // Backdoor to force all possible inlining. + if (CallsiteInlineThreshold < 0) + return true; + + if (!hasOnlyOneCallUse(Callee)) + return false; + + // Permit inlining small functions into big functions, or big functions into + // small functions. + if (!functionHasMoreThanNonTerminatorInsts(Callee, CallsiteInlineThreshold) && + !functionHasMoreThanNonTerminatorInsts(Caller, CallsiteInlineThreshold)) + return true; + + return false; +} + +static void reduceCallSites(Oracle &O, Function &F) { + std::vector<std::pair<CallBase *, InlineFunctionInfo>> CallSitesToInline; + + for (Use &U : F.uses()) { + if (CallBase *CB = dyn_cast<CallBase>(U.getUser())) { + // Ignore callsites with wrong call type. + if (!CB->isCallee(&U)) + continue; + + // We do not consider isInlineViable here. It is overly conservative in + // cases that the inliner should handle correctly (e.g. disallowing inline + // of of functions with indirectbr). Some of the other cases are for other + // correctness issues which we do need to worry about here. + + // TODO: Should we delete the function body? + InlineFunctionInfo IFI; + if (CanInlineCallSite(*CB, IFI).isSuccess() && + inlineWillReduceComplexity(*CB->getFunction(), F) && !O.shouldKeep()) + CallSitesToInline.emplace_back(CB, std::move(IFI)); + } + } + + // TODO: InlineFunctionImpl will implicitly perform some simplifications / + // optimizations which we should be able to opt-out of. + for (auto [CB, IFI] : CallSitesToInline) + InlineFunctionImpl(*CB, IFI); +} + +void llvm::reduceInlineCallSitesDeltaPass(Oracle &O, ReducerWorkItem &Program) { + for (Function &F : Program.getModule()) { + if (!F.isDeclaration()) + reduceCallSites(O, F); + } +} |