| //===- ReduceArguments.cpp - Specialized Delta Pass -----------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements a function which calls the Generic Delta pass in order |
| // to reduce uninteresting Arguments from declared and defined functions. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ReduceArguments.h" |
| #include "Delta.h" |
| #include "Utils.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/IR/Constants.h" |
| #include "llvm/IR/FMF.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/Intrinsics.h" |
| #include "llvm/IR/Operator.h" |
| #include <set> |
| #include <vector> |
| |
| using namespace llvm; |
| |
| static bool callingConvRequiresArgument(const Function &F, |
| const Argument &Arg) { |
| switch (F.getCallingConv()) { |
| case CallingConv::X86_INTR: |
| // If there are any arguments, the first one must by byval. |
| return Arg.getArgNo() == 0 && F.arg_size() != 1; |
| default: |
| return false; |
| } |
| |
| llvm_unreachable("covered calling conv switch"); |
| } |
| |
| /// Goes over OldF calls and replaces them with a call to NewF |
| static void replaceFunctionCalls(Function &OldF, Function &NewF, |
| const std::set<int> &ArgIndexesToKeep) { |
| LLVMContext &Ctx = OldF.getContext(); |
| |
| const auto &Users = OldF.users(); |
| for (auto I = Users.begin(), E = Users.end(); I != E; ) |
| if (auto *CI = dyn_cast<CallInst>(*I++)) { |
| // Skip uses in call instructions where OldF isn't the called function |
| // (e.g. if OldF is an argument of the call). |
| if (CI->getCalledFunction() != &OldF) |
| continue; |
| SmallVector<Value *, 8> Args; |
| SmallVector<AttrBuilder, 8> ArgAttrs; |
| |
| for (auto ArgI = CI->arg_begin(), E = CI->arg_end(); ArgI != E; ++ArgI) { |
| unsigned ArgIdx = ArgI - CI->arg_begin(); |
| if (ArgIndexesToKeep.count(ArgIdx)) { |
| Args.push_back(*ArgI); |
| ArgAttrs.emplace_back(Ctx, CI->getParamAttributes(ArgIdx)); |
| } |
| } |
| |
| SmallVector<OperandBundleDef, 2> OpBundles; |
| CI->getOperandBundlesAsDefs(OpBundles); |
| |
| CallInst *NewCI = CallInst::Create(&NewF, Args, OpBundles); |
| NewCI->setCallingConv(CI->getCallingConv()); |
| |
| AttrBuilder CallSiteAttrs(Ctx, CI->getAttributes().getFnAttrs()); |
| NewCI->setAttributes( |
| AttributeList::get(Ctx, AttributeList::FunctionIndex, CallSiteAttrs)); |
| NewCI->addRetAttrs(AttrBuilder(Ctx, CI->getRetAttributes())); |
| |
| unsigned AttrIdx = 0; |
| for (auto ArgI = NewCI->arg_begin(), E = NewCI->arg_end(); ArgI != E; |
| ++ArgI, ++AttrIdx) |
| NewCI->addParamAttrs(AttrIdx, ArgAttrs[AttrIdx]); |
| |
| if (auto *FPOp = dyn_cast<FPMathOperator>(NewCI)) |
| cast<Instruction>(FPOp)->setFastMathFlags(CI->getFastMathFlags()); |
| |
| NewCI->copyMetadata(*CI); |
| |
| if (!CI->use_empty()) |
| CI->replaceAllUsesWith(NewCI); |
| ReplaceInstWithInst(CI, NewCI); |
| } |
| } |
| |
| /// Returns whether or not this function should be considered a candidate for |
| /// argument removal. Currently, functions with no arguments and intrinsics are |
| /// not considered. Intrinsics aren't considered because their signatures are |
| /// fixed. |
| static bool shouldRemoveArguments(const Function &F) { |
| return !F.arg_empty() && !F.isIntrinsic(); |
| } |
| |
| static bool allFuncUsersRewritable(const Function &F) { |
| for (const Use &U : F.uses()) { |
| const CallBase *CB = dyn_cast<CallBase>(U.getUser()); |
| if (!CB || !CB->isCallee(&U)) |
| continue; |
| |
| // TODO: Handle all CallBase cases. |
| if (!isa<CallInst>(CB)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /// Removes out-of-chunk arguments from functions, and modifies their calls |
| /// accordingly. It also removes allocations of out-of-chunk arguments. |
| static void extractArgumentsFromModule(Oracle &O, ReducerWorkItem &WorkItem) { |
| Module &Program = WorkItem.getModule(); |
| std::vector<Argument *> InitArgsToKeep; |
| std::vector<Function *> Funcs; |
| |
| // Get inside-chunk arguments, as well as their parent function |
| for (auto &F : Program) { |
| if (!shouldRemoveArguments(F)) |
| continue; |
| if (!allFuncUsersRewritable(F)) |
| continue; |
| Funcs.push_back(&F); |
| for (auto &A : F.args()) { |
| if (callingConvRequiresArgument(F, A) || O.shouldKeep()) |
| InitArgsToKeep.push_back(&A); |
| } |
| } |
| |
| // We create a vector first, then convert it to a set, so that we don't have |
| // to pay the cost of rebalancing the set frequently if the order we insert |
| // the elements doesn't match the order they should appear inside the set. |
| std::set<Argument *> ArgsToKeep(InitArgsToKeep.begin(), InitArgsToKeep.end()); |
| |
| for (auto *F : Funcs) { |
| ValueToValueMapTy VMap; |
| std::vector<WeakVH> InstToDelete; |
| for (auto &A : F->args()) |
| if (!ArgsToKeep.count(&A)) { |
| // By adding undesired arguments to the VMap, CloneFunction will remove |
| // them from the resulting Function |
| VMap[&A] = getDefaultValue(A.getType()); |
| for (auto *U : A.users()) |
| if (auto *I = dyn_cast<Instruction>(*&U)) |
| InstToDelete.push_back(I); |
| } |
| // Delete any (unique) instruction that uses the argument |
| for (Value *V : InstToDelete) { |
| if (!V) |
| continue; |
| auto *I = cast<Instruction>(V); |
| I->replaceAllUsesWith(getDefaultValue(I->getType())); |
| if (!I->isTerminator()) |
| I->eraseFromParent(); |
| } |
| |
| // No arguments to reduce |
| if (VMap.empty()) |
| continue; |
| |
| std::set<int> ArgIndexesToKeep; |
| for (const auto &[Index, Arg] : enumerate(F->args())) |
| if (ArgsToKeep.count(&Arg)) |
| ArgIndexesToKeep.insert(Index); |
| |
| auto *ClonedFunc = CloneFunction(F, VMap); |
| // In order to preserve function order, we move Clone after old Function |
| ClonedFunc->takeName(F); |
| ClonedFunc->removeFromParent(); |
| Program.getFunctionList().insertAfter(F->getIterator(), ClonedFunc); |
| |
| replaceFunctionCalls(*F, *ClonedFunc, ArgIndexesToKeep); |
| F->replaceAllUsesWith(ClonedFunc); |
| F->eraseFromParent(); |
| } |
| } |
| |
| void llvm::reduceArgumentsDeltaPass(TestRunner &Test) { |
| runDeltaPass(Test, extractArgumentsFromModule, "Reducing Arguments"); |
| } |