diff --git a/llvm/test/tools/llvm-reduce/operands-skip.ll b/llvm/test/tools/llvm-reduce/operands-skip.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-reduce/operands-skip.ll @@ -0,0 +1,59 @@ +; RUN: llvm-reduce %s -o %t --delta-passes=operands-skip --test FileCheck --test-arg %s --test-arg --match-full-lines --test-arg --check-prefix=INTERESTING --test-arg --input-file +; RUN: FileCheck %s --input-file %t --check-prefixes=REDUCED + +; INTERESTING: store i32 43, i32* {{(%imm|%indirect)}}, align 4 +; REDUCED: store i32 43, i32* %imm, align 4 + +; INTERESTING: store i32 44, i32* {{(%imm|%indirect|%phi)}}, align 4 +; REDUCED: store i32 44, i32* %phi, align 4 + +; INTERESTING: store i32 45, i32* {{(%imm|%indirect|%phi|%val)}}, align 4 +; REDUCED: store i32 45, i32* %val, align 4 + +; INTERESTING: store i32 46, i32* {{(%imm|%indirect|%phi|%val|@Global)}}, align 4 +; REDUCED: store i32 46, i32* @Global, align 4 + +; INTERESTING: store i32 47, i32* {{(%imm|%indirect|%phi|%val|@Global|%arg2)}}, align 4 +; REDUCED: store i32 47, i32* %arg2, align 4 + +; INTERESTING: store i32 48, i32* {{(%imm|%indirect|%phi|%val|@Global|%arg2|%arg1)}}, align 4 +; REDUCED: store i32 48, i32* %arg1, align 4 + +; INTERESTING: store i32 49, i32* {{(%imm|%indirect|%phi|%val|@Global|%arg2|%arg1|null)}}, align 4 +; REDUCED: store i32 49, i32* null, align 4 + +; REDUCED: store i32 50, i32* %arg1, align 4 +; REDUCED: store i32 51, i32* %arg1, align 4 + +@Global = global i32 42 + +define void @func(i32* %arg1, i32* %arg2) { +entry: + %val = getelementptr i32, i32* getelementptr (i32, i32* @Global, i32 1), i32 2 + br i1 undef, label %branch, label %loop + +branch: + %nondominating1 = getelementptr i32, i32* %val, i32 3 + br label %loop + +loop: + %phi = phi i32* [ null, %entry ], [ %nondominating1, %branch ], [ %nondominating2, %loop ] + %imm = getelementptr i32, i32* %phi, i32 4 + %indirect = getelementptr i32, i32* %imm, i32 5 + + store i32 43, i32* %imm, align 4 ; Don't reduce to %indirect (not "more reduced" than %imm) + store i32 44, i32* %imm, align 4 ; Reduce to %phi + store i32 45, i32* %imm, align 4 ; Reduce to %val + store i32 46, i32* %imm, align 4 ; Reduce to @Global + store i32 47, i32* %imm, align 4 ; Reduce to %arg1 + store i32 48, i32* %imm, align 4 ; Reduce to %arg2 + store i32 49, i32* %imm, align 4 ; Reduce to null + + %nondominating2 = getelementptr i32, i32* %indirect, i32 6 + br i1 undef, label %loop, label %exit + +exit: + store i32 50, i32* %arg2, align 4 ; Reduce to %arg1 (compactify function arguments) + store i32 51, i32* %arg1, align 4 ; Don't reduce + ret void +} diff --git a/llvm/test/tools/llvm-reduce/operands-to-args.ll b/llvm/test/tools/llvm-reduce/operands-to-args.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-reduce/operands-to-args.ll @@ -0,0 +1,56 @@ +; RUN: llvm-reduce %s -o %t --delta-passes=operands-to-args --test FileCheck --test-arg %s --test-arg --match-full-lines --test-arg --check-prefix=INTERESTING --test-arg --input-file +; RUN: FileCheck %s --input-file %t --check-prefixes=REDUCED,INTERESTING + +; REDUCED-LABEL: define void @func(i32 %k, i32* %Local, i32* %Global, float* %0) { + +; Keep one reference to the original value. +; INTERESTING: %[[LOCAL:Local[0-9]*]] = alloca i32, align 4 +; INTERESTING: store i32 42, i32* %[[LOCAL]], align 4 +; INTERESTING: store i32 42, i32* @Global, align 4 + +; Everything else must use the function argument. +; REDUCED: store i32 21, i32* %Local, align 4 +; REDUCED: store i32 21, i32* %Global, align 4 +; REDUCED: store i32 0, i32* %Local, align 4 +; REDUCED: store i32 0, i32* %Global, align 4 +; REDUCED: store float 0.000000e+00, float* %0, align 4 + +; Do not add any arguments for %Keep and @GlobalKeep. +; INTERESTING: %[[KEEP:LocalKeep[0-9]*]] = add i32 %k, 21 +; INTERESTING: store i32 %[[KEEP]], i32* @GlobalKeep, align 4 + +; INTERESTING-LABEL: define void @func_caller() { +; REDUCED: call void @func(i32 21, i32* undef, i32* undef, float* undef) + + +@Global = global i32 42 +@GlobalKeep = global i32 42 + +define void @func(i32 %k) { +entry: + %Local = alloca i32, align 4 + + store i32 42, i32* %Local, align 4 + store i32 42, i32* @Global, align 4 + + store i32 21, i32* %Local, align 4 + store i32 21, i32* @Global, align 4 + + store i32 0, i32* %Local, align 4 + store i32 0, i32* @Global, align 4 + + store float 0.000000e+00, float* bitcast (i32* @Global to float*), align 4 + + %LocalKeep = add i32 %k, 21 + store i32 %LocalKeep, i32* @GlobalKeep, align 4 + + ret void +} + + +define void @func_caller() { +entry: + call void @func(i32 21) + ret void +} + diff --git a/llvm/test/tools/llvm-reduce/remove-call-site-attributes.ll b/llvm/test/tools/llvm-reduce/remove-call-site-attributes.ll --- a/llvm/test/tools/llvm-reduce/remove-call-site-attributes.ll +++ b/llvm/test/tools/llvm-reduce/remove-call-site-attributes.ll @@ -1,6 +1,6 @@ ; Test that llvm-reduce can remove uninteresting operand bundles from calls. ; -; RUN: llvm-reduce --test FileCheck --test-arg --check-prefixes=CHECK-ALL,CHECK-INTERESTINGNESS --test-arg %s --test-arg --input-file %s -o %t +; RUN: llvm-reduce --delta-passes=operand-bundles,attributes --test FileCheck --test-arg --check-prefixes=CHECK-ALL,CHECK-INTERESTINGNESS --test-arg %s --test-arg --input-file %s -o %t ; RUN: cat %t | FileCheck --check-prefixes=CHECK-ALL,CHECK-FINAL %s ; CHECK-ALL: declare i32 @f1(i32, i32) diff --git a/llvm/tools/llvm-reduce/CMakeLists.txt b/llvm/tools/llvm-reduce/CMakeLists.txt --- a/llvm/tools/llvm-reduce/CMakeLists.txt +++ b/llvm/tools/llvm-reduce/CMakeLists.txt @@ -29,6 +29,8 @@ deltas/ReduceOperandBundles.cpp deltas/ReduceSpecialGlobals.cpp deltas/ReduceOperands.cpp + deltas/ReduceOperandsSkip.cpp + deltas/ReduceOperandsToArgs.cpp llvm-reduce.cpp DEPENDS diff --git a/llvm/tools/llvm-reduce/DeltaManager.cpp b/llvm/tools/llvm-reduce/DeltaManager.cpp --- a/llvm/tools/llvm-reduce/DeltaManager.cpp +++ b/llvm/tools/llvm-reduce/DeltaManager.cpp @@ -28,6 +28,8 @@ #include "deltas/ReduceModuleData.h" #include "deltas/ReduceOperandBundles.h" #include "deltas/ReduceOperands.h" +#include "deltas/ReduceOperandsSkip.h" +#include "deltas/ReduceOperandsToArgs.h" #include "deltas/ReduceSpecialGlobals.h" #include "llvm/Support/CommandLine.h" @@ -51,6 +53,8 @@ DELTA_PASS("arguments", reduceArgumentsDeltaPass) \ DELTA_PASS("instructions", reduceInstructionsDeltaPass) \ DELTA_PASS("operands", reduceOperandsDeltaPass) \ + DELTA_PASS("operands-to-args", reduceOperandsToArgsDeltaPass) \ + DELTA_PASS("operands-skip", reduceOperandsSkipDeltaPass) \ DELTA_PASS("operand-bundles", reduceOperandBundesDeltaPass) \ DELTA_PASS("attributes", reduceAttributesDeltaPass) \ DELTA_PASS("module-data", reduceModuleDataDeltaPass) diff --git a/llvm/tools/llvm-reduce/deltas/ReduceOperandsSkip.h b/llvm/tools/llvm-reduce/deltas/ReduceOperandsSkip.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-reduce/deltas/ReduceOperandsSkip.h @@ -0,0 +1,18 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDSSKIP_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDSSKIP_H + +#include "Delta.h" + +namespace llvm { +void reduceOperandsSkipDeltaPass(TestRunner &Test); +} // namespace llvm + +#endif /* LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDSSKIP_H */ diff --git a/llvm/tools/llvm-reduce/deltas/ReduceOperandsSkip.cpp b/llvm/tools/llvm-reduce/deltas/ReduceOperandsSkip.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-reduce/deltas/ReduceOperandsSkip.cpp @@ -0,0 +1,220 @@ +//===----------------------------------------------------------------------===// +// +// 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 "ReduceOperandsSkip.h" +#include "llvm/ADT/Sequence.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Operator.h" + +using namespace llvm; + +/// Collect all values that are directly or indirectly referenced by @p Root, +/// including Root itself. This is a BF search such that the more steps needed +/// to get to the reference, the more behind it is found in @p Collection. Each +/// step could be its own reduction, therefore we consider later values "more +/// reduced". +static SetVector collectReferencedValues(Value *Root) { + SetVector Refs; + std::deque Worklist; + Worklist.push_back(Root); + + while (!Worklist.empty()) { + Value *Val = Worklist.front(); + Worklist.pop_front(); + if (!Refs.insert(Val)) + continue; + + if (auto *O = dyn_cast(Val)) { + for (Use &Op : O->operands()) + Worklist.push_back(Op.get()); + } + } + + return Refs; +} + +/// Return a reduction priority for @p V. A higher values means "more reduced". +static int classifyReductivePower(Value *V) { + if (auto *C = dyn_cast(V)) { + if (C->isNullValue()) + return 7; + if (C->isOneValue()) + return 6; + return 5; + } + + if (isa(V)) + return 4; + + if (isa(V)) + return 3; + + if (isa(V)) + return 2; + + if (isa(V)) + return 1; + + if (isa(V)) + return -1; + + return 0; +} + +/// Calls @p Callback for every reduction opportunity in @p F. Used by +/// countOperands() and extractOperandsFromModule() to ensure consistency +/// between the two. +static void +opportunities(Function &F, + function_ref)> Callback) { + if (F.isDeclaration()) + return; + + // Need DominatorTree to find out whether an SSA value can be referenced. + DominatorTree DT(F); + + // Return whether @p LHS is "more reduced" that @p RHS. That is, whether + // @p RHS should be preferred over @p LHS in a reduced output. This is a + // partial order, a Value may not be preferable over another. + auto IsMoreReduced = [&DT](Value *LHS, Value *RHS) -> bool { + // A value is not more reduced than itself. + if (LHS == RHS) + return false; + + int ReductivePowerDiff = + classifyReductivePower(RHS) - classifyReductivePower(LHS); + if (ReductivePowerDiff != 0) + return ReductivePowerDiff < 0; + + // LHS is more reduced if it is defined further up the dominance tree. In a + // chain of definitions, + // + // %a = .. + // %b = op %a + // %c = op %b + // + // every use of %b can be replaced by %a, but not by a use of %c. That is, a + // use %c can be replaced in steps first by %b, then by %a, making %a the + // "more reduced" choice that skips over more instructions. + auto *LHSInst = dyn_cast(LHS); + auto *RHSInst = dyn_cast(RHS); + if (LHSInst && RHSInst) { + if (DT.dominates(LHSInst, RHSInst)) + return true; + } + + // Compress the number of used arguments by prefering the first ones. Unused + // trailing argument can be removed by the arguments pass. + auto *LHSArg = dyn_cast(LHS); + auto *RHSArg = dyn_cast(RHS); + if (LHSArg && RHSArg) { + if (LHSArg->getArgNo() < RHSArg->getArgNo()) + return true; + } + + return false; + }; + + for (Instruction &I : instructions(&F)) { + for (Use &Op : I.operands()) { + Value *OpVal = Op.get(); + + // Collect refenced values as potential replacement candidates. + SetVector ReferencedVals = collectReferencedValues(OpVal); + + // Regardless whether referenced, add the function arguments as + // replacement possibility with the goal of reducing the number of (used) + // function arguments, possibly created by the the operands-to-args. + for (Argument &Arg : F.args()) + ReferencedVals.insert(&Arg); + + // After all candidates have been added, it doesn't need to be a set + // anymore. + std::vector Candidates = ReferencedVals.takeVector(); + + // Remove ineligible candidates. + llvm::erase_if(Candidates, [&, OpVal](Value *V) { + // Candidate value must have the same type. + if (OpVal->getType() != V->getType()) + return true; + + // Only consider candidates that are "more reduced" than the original + // value. This explicitly also rules out candidates with the same + // reduction power. This is to ensure that repeated invocations of this + // pass eventually reach a fixpoint without switch back and forth + // between two opportunities with the same reductive power. + return !IsMoreReduced(V, OpVal); + }); + + if (Candidates.empty()) + continue; + + // collectReferencedValues pushed the more reductive values to the end of + // the collection, but we need them at the front. + std::reverse(Candidates.begin(), Candidates.end()); + + // Independency of collectReferencedValues's idea of reductive power, + // ensure the the partial order of IsMoreReduced is enforced. + llvm::stable_sort(Candidates, IsMoreReduced); + + Callback(Op, Candidates); + } + } +} + +static void extractOperandsFromModule(Oracle &O, Module &Program) { + for (Function &F : Program.functions()) { + SmallVector> Replacements; + opportunities(F, [&](Use &Op, ArrayRef Candidates) { + // Only apply the candidate the Oracle selected to keep that is the most + // reduced. Candidates with less reductive power can be interpreted as an + // intermediate step that is immediately replaced with the more reduced + // one. The number of shouldKeep() calls must be independent of the result + // of previous shouldKeep() calls to keep the total number of calls + // in-sync with what countOperands() has computed. + bool AlreadyReplaced = false; + for (Value *C : Candidates) { + bool Keep = O.shouldKeep(); + if (AlreadyReplaced || Keep) + continue; + + // Replacing the operand value immediately would influence the candidate + // set for the following operands. Delay it until after all candidates + // have been determined. + Replacements.push_back({&Op, C}); + + AlreadyReplaced = true; + } + }); + + for (std::pair P : Replacements) + P.first->set(P.second); + } +} + +static int countOperands(Module *Program) { + int Count = 0; + + for (Function &F : Program->functions()) { + opportunities(F, [&](Use &Op, ArrayRef Candidates) { + Count += llvm::size(Candidates); + }); + } + + return Count; +} + +void llvm::reduceOperandsSkipDeltaPass(TestRunner &Test) { + errs() << "*** Reducing operands by skipping over instructions ...\n"; + int Count = countOperands(&Test.getProgram()); + runDeltaPass(Test, Count, extractOperandsFromModule); +} diff --git a/llvm/tools/llvm-reduce/deltas/ReduceOperandsToArgs.h b/llvm/tools/llvm-reduce/deltas/ReduceOperandsToArgs.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-reduce/deltas/ReduceOperandsToArgs.h @@ -0,0 +1,18 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDSTOARGS_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDSTOARGS_H + +#include "Delta.h" + +namespace llvm { +void reduceOperandsToArgsDeltaPass(TestRunner &Test); +} // namespace llvm + +#endif /* LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDSTOARGS_H */ diff --git a/llvm/tools/llvm-reduce/deltas/ReduceOperandsToArgs.cpp b/llvm/tools/llvm-reduce/deltas/ReduceOperandsToArgs.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-reduce/deltas/ReduceOperandsToArgs.cpp @@ -0,0 +1,216 @@ +//===----------------------------------------------------------------------===// +// +// 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 "ReduceOperandsToArgs.h" +#include "Delta.h" +#include "llvm/ADT/Sequence.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Cloning.h" + +using namespace llvm; + +static bool canReplaceFunction(Function *F) { + return all_of(F->uses(), [](Use &Op) { + if (auto *CI = dyn_cast(Op.getUser())) + return &CI->getCalledOperandUse() == &Op; + return false; + }); +} + +static bool canReduceUse(Use &Op) { + Value *Val = Op.get(); + Type *Ty = Val->getType(); + + // Only replace operands that can be passed-by-value. + if (!Ty->isFirstClassType()) + return false; + + // Don't pass labels as arguments. + if (Ty->isLabelTy()) + return false; + + // No need to replace values that are already arguments. + if (isa(Val)) + return false; + + // Do not replace literals. + if (isa(Val)) + return false; + + // Do not convert direct function calls to indirect calls. + if (auto *CI = dyn_cast(Op.getUser())) + if (&CI->getCalledOperandUse() == &Op) + return false; + + return true; +} + +/// Goes over OldF calls and replaces them with a call to NewF. +static void replaceFunctionCalls(Function *OldF, Function *NewF) { + SmallVector Callers; + for (Use &U : OldF->uses()) { + auto *CI = cast(U.getUser()); + assert(&U == &CI->getCalledOperandUse()); + assert(CI->getCalledFunction() == OldF); + Callers.push_back(CI); + } + + // Call arguments for NewF. + SmallVector Args(NewF->arg_size(), nullptr); + + // Fill up the additional parameters with undef values. + for (auto ArgIdx : llvm::seq(OldF->arg_size(), NewF->arg_size())) { + Type *NewArgTy = NewF->getArg(ArgIdx)->getType(); + Args[ArgIdx] = UndefValue::get(NewArgTy); + } + + for (CallBase *CI : Callers) { + // Preserve the original function arguments. + for (auto Z : zip_first(CI->args(), Args)) + std::get<1>(Z) = std::get<0>(Z); + + // Also preserve operand bundles. + SmallVector OperandBundles; + CI->getOperandBundlesAsDefs(OperandBundles); + + // Create the new function call. + CallBase *NewCI; + if (auto *II = dyn_cast(CI)) { + NewCI = InvokeInst::Create(NewF, cast(II)->getNormalDest(), + cast(II)->getUnwindDest(), Args, + OperandBundles, CI->getName()); + } else { + assert(isa(CI)); + NewCI = CallInst::Create(NewF, Args, OperandBundles, CI->getName()); + } + NewCI->setCallingConv(NewF->getCallingConv()); + + // Do the replacement for this use. + if (!CI->use_empty()) + CI->replaceAllUsesWith(NewCI); + ReplaceInstWithInst(CI, NewCI); + } +} + +/// Add a new function argument to @p F for each use in @OpsToReplace, and +/// replace those operand values with the new function argument. +static void substituteOperandWithArgument(Function *OldF, + ArrayRef OpsToReplace) { + if (OpsToReplace.empty()) + return; + + SetVector UniqueValues; + for (Use *Op : OpsToReplace) + UniqueValues.insert(Op->get()); + + // Determine the new function's signature. + SmallVector NewArgTypes; + llvm::append_range(NewArgTypes, OldF->getFunctionType()->params()); + size_t ArgOffset = NewArgTypes.size(); + for (Value *V : UniqueValues) + NewArgTypes.push_back(V->getType()); + FunctionType *FTy = + FunctionType::get(OldF->getFunctionType()->getReturnType(), NewArgTypes, + OldF->getFunctionType()->isVarArg()); + + // Create the new function... + Function *NewF = + Function::Create(FTy, OldF->getLinkage(), OldF->getAddressSpace(), + OldF->getName(), OldF->getParent()); + + // In order to preserve function order, we move NewF behind OldF + NewF->removeFromParent(); + OldF->getParent()->getFunctionList().insertAfter(OldF->getIterator(), NewF); + + // Preserve the parameters of OldF. + ValueToValueMapTy VMap; + for (auto Z : zip_first(OldF->args(), NewF->args())) { + Argument &OldArg = std::get<0>(Z); + Argument &NewArg = std::get<1>(Z); + + NewArg.setName(OldArg.getName()); // Copy the name over... + VMap[&OldArg] = &NewArg; // Add mapping to VMap + } + + // Adjust the new parameters. + ValueToValueMapTy OldValMap; + for (auto Z : zip_first(UniqueValues, drop_begin(NewF->args(), ArgOffset))) { + Value *OldVal = std::get<0>(Z); + Argument &NewArg = std::get<1>(Z); + + NewArg.setName(OldVal->getName()); + OldValMap[OldVal] = &NewArg; + } + + SmallVector Returns; // Ignore returns cloned. + CloneFunctionInto(NewF, OldF, VMap, CloneFunctionChangeType::LocalChangesOnly, + Returns, "", /*CodeInfo=*/nullptr); + + // Replace the actual operands. + for (Use *Op : OpsToReplace) { + Value *NewArg = OldValMap.lookup(Op->get()); + auto *NewUser = cast(VMap.lookup(Op->getUser())); + NewUser->setOperand(Op->getOperandNo(), NewArg); + } + + // Replace all OldF uses with NewF. + replaceFunctionCalls(OldF, NewF); + + // Rename NewF to OldF's name. + std::string FName = OldF->getName().str(); + OldF->replaceAllUsesWith(ConstantExpr::getBitCast(NewF, OldF->getType())); + OldF->eraseFromParent(); + NewF->setName(FName); +} + +static void reduceOperandsToArgs(Oracle &O, Module &Program) { + SmallVector OperandsToReduce; + for (Function &F : make_early_inc_range(Program.functions())) { + OperandsToReduce.clear(); + for (Instruction &I : instructions(&F)) { + for (Use &Op : I.operands()) { + if (!canReduceUse(Op)) + continue; + if (O.shouldKeep()) + continue; + + OperandsToReduce.push_back(&Op); + } + } + + substituteOperandWithArgument(&F, OperandsToReduce); + } +} + +/// Counts the amount of operands in the module that can be reduced. +static int countOperands(Module &Program) { + int Count = 0; + + for (Function &F : Program.functions()) { + if (!canReplaceFunction(&F)) + continue; + for (Instruction &I : instructions(&F)) { + for (Use &Op : I.operands()) { + if (!canReduceUse(Op)) + continue; + Count += 1; + } + } + } + + return Count; +} + +void llvm::reduceOperandsToArgsDeltaPass(TestRunner &Test) { + outs() << "*** Converting operands to function arguments ...\n"; + int ArgCount = countOperands(Test.getProgram()); + return runDeltaPass(Test, ArgCount, reduceOperandsToArgs); +}